Четвер, 8 жовтня 2009 р.

Maven2::Part #5

Assembly plugin


несколько слов об assembly plugin. Maven предоставляет возможность формировать delivery package с помощью assembly plugin. стандартная документация описывает процесс создания конечного пакета в формате bin и src, как это обычно выглядит для всех продуктов Apache Software Foundation. однако, с помощью этого плагина можно формировать пакеты абсолютно произвольного формата. для этого понадобиться дескриптор assembly.xml, в котором с помощью тэгов включаются и исключаются необходимые артефакты и исходники, а с помощью тэгов -- артефакты зависимостей, необходимые в delivery package. все остальные тэги assembly.xml можно не использовать. с другой стороны, если delivery package соотвествует формату, принятому в Apache Software Foundation, можно использовать стандартные конфигурации assembly.xml, которые представлены на сайте Maven

Неділя, 27 вересня 2009 р.

Maven2::Part #4

Управление зависимостями (Dependency Management)


в первую очередь следует обратить внимание на стандратную документацию Maven. она довольпо подробно объясняет, как использовать механизм управления зависимостями. я хочу оставновится на некоторых моментах, которые могут быть не ясны из стандартной документации

включение и исключение библиотек при объявлении зависимостей


управление зависимости в Maven организованно довольно грамотно. оно учитывает все зависимости явно и не явно указанные в проекте и самостоятельно разрешает коллизи конфликта версий библиотек в пользу указанных явно. так, если какая-то из подключенных библиотек, в свою очередь использует библиотеку, которая уже указана в зависимостях для проекта, то в данном случае будет использована библиотека, указанная в зависимостях проекта. в данном случае безусловным приоритетом обладает явная декларация, что следует учитывать в случае возникновения конфликтов версий.
что касается практики организации зависимостей, то тут необходимо отметить два момента

  1. при огранизации зависимостей разрабатываемого модуля или приложения от сторонних библиотек, необходимо помнить, что все зависимости, для которых указан scope compile или не указан scope, будут автоматически включены в зависимости модуля, который использует разрабатываемый модуль. или, более простыми словами, если ваш модуль будет использован в дальнейшей разработки, все зависимости scope compile, автоматически окажутся в вашем локально репозитории и директории lib для war и ear. в случае, когда в таком поведении системы нет строгой необходимости использование тега optional более чем желательно.
    в противном случае все пользователи вашего модуля будут получать на выходе гиганские объемы библиотек, которые они не используют. или же будут прибегать к практике из пункта 2

  2. при использовании сторонних библиотек следует обязательно обратить внимание на количество зависимостей, не отмеченных как optional, у используемого модуля. ярким примером являются релизы Spring весии 2.x, каждый модуль которого норовит вытащить из репозитория 90% фреймворка.
    для того, чтобы избежать такого поведения той или иной библиотеки из зависимостей, необходимо отключить все не нужные в приложении зависимости, не объявленные в pom библиотеки, как optional.

    в своей практике я часто оключал все зависимости подключенных библиотек, и включал явно только те, которые мне были нужны. однако, не могу сказать, что это оптимальный способ. скорее наоборот. с другой стороны этот способ дает полный и очевидный контроль над всеми без исключения зависимостями проекта, но pom файлы бывают очень большими и единоразовое их составление занимает довольно приличное время.


dependencyManagement vs dependencies. в чем разница?


разница между этими двумя секциями может быть сравнима с разницей между h и cpp файлами С++. dependencyManagement содержит декларацию зависимостей, а dependencies -- зависимости, которые непосредственно используются в проекте. в случае одномодульного проекта эта конструкция избыточна, однако для многомодульных проектов ее ценность тем выше, чем больше модулей составляют проект.

определить зависимости той или иной библиотеки можно окрыв ее pom файл внутри репозитория или используя специальный ресурс

управление зависмостями в multimodule projects


для многомодульных проектов сложно переоценить значение секции dependencyManagement. ее использование позволяет объявить, а следовательно и управлять, зависимости в в одном единственном месте включая все необхоимые значения scope, exclusions и optional. этим местом является parent pom файл всего проекта. в pom файле каждого из модулей, в секции dependencies достаточно указать groupId и artifactId кокретной зависимости или плагина. Maven автоматически использует остальные параметры аналогичной зависимости из parent pom файла

разница между pluginManagement и plugins аналогична, равно как и аналогична их избыточность, важность и использование для разных проектов.

Понеділок, 14 вересня 2009 р.

Maven::Part #3

Multimodule projects


каждый pom.xml файл описывает build workflow для одного проекта, в конечной фазе которого мы имеем один артефакт установленный в локальный и удаленный репозитарий. однако Maven позволяет легко и удобно работать с многомодульными проектами. при этом задача управления зависимостями между внешними библиотеками и между модулями внтури проекта полностью решается самим Maven.

Особенности конфигурации


для того, чтобы сконфигурировть build workflow для multimodule project необходимо

  1. создать в корневом каталоге, общем для всех модулей, pom.xml файл родительского модуля, для которого указать

    ...
     <packaging>pom</packaging>
    ...

    такое значение артефакта указывается Maven на то, что данный проект является мета-проектом и не требует сборки конечного артефакта на выходе. в итоге в репозитариях будет просто сгенерирована мета-информация об этом проекте, без наличия конркетных артефактов.

  2. далее в родительском pom.xml файле мы должны определить конфигурацию модулей

    ...
     <modules>
      <module>module1_directory_name</module>
      <module>module2_directory_name</module>
    ...
      <module>moduleN_directory_name</module>
     </modules>
    ...

    где
    module_directory_name -- имя каталога, в котором находятся файлы конкретного модуля.

    Важно: имена модулей в родительском pom.xml и имена каталогов, в котором расположены файлы модуля должны быть идентичны. имена каталогов модулей обрабатываются как пути к этим каталогам. таким образом, если структура конкретного проекта предполагает взаимонезависимое расположение каталогов с модулями, то следует указывать их абсолютные или отностительные пути

  3. для pom.xml каждого из модулей, входящих в multimodule project необходимо указать

    ...
     <parent>
      <groupId>project_group_id</groupId>
      <actifactId>parent_artifact_id</artifactId>
      <version>project_version</version>
      [<relativePath>path_to_parent_pom.xml</relativePath>]
    ...

    где
    project_group_id -- идентификатор группы, в которой будут сохранены все артефакты проекта. в случае multimodel project может быть указана для родительского модуля и parent-секции модуля. для конкретных модулей наследуется из родительского, если не указана явно
    parent_artifact_id -- идентификатор артефакта родительского проекта. этот интификатор необходим в любом случае и для любого проекта. даже для мета-проекта, которым часто является родительский проект
    project_version -- текущая версия проекта. в случае multimodel project может быть указана для родительского модуля и parent-секции модуля. для конкретных модулей наследуется из родительского, если не указана явно
    relativePath -- относительный путь к родительскому pom.xml. его необходимо указать в том случае, если каталог уровнем выше каталога модуля, не содержит родительского pom.xml



Важно: родительский pom.xml может определять различные кастомизации стандартных и не стандартных плагинов, обшие для всех модулей. однако следует помнить, что каждая задача, определенная в родительском pom.xml проекта, будет выполнена для каждого модуля проекта.
есть предположение, что для версии Maven 2.2.1 данное поведение исправлено. но я еще не проверял этого в своих проектах

Неділя, 13 вересня 2009 р.

Maven2::Part #1

Maven build workflow


полный список фаз Maven build workflow можно найти в документации. за каждую фазу отвечает какой-нибудь плагин из группы org.apache.maven. каждый из плагинов, кроме цели по-умолчанию, как правило умеет еще много полезных вещей. поэтому даже для стандартных плагинов нужно и важно читать документацию. встраиваться в ту или иную фазу довольно просто

...
 <plugin>
   <groupId>com.mycompany.example</groupId>
   <artifactId>maven-touch-plugin</artifactId>
   <version>1.0</version>
   <executions>
     <execution>
       <phase>process-test-resources</phase>
       <goals>
         <goal>timestamp</goal>
       </goals>
     </execution>
   </executions>
 </plugin>
...


Важно: все кастомные цели вызываются в конце выполнения указанной фазы. т.е. если перед фазой package необходимо что-то сделать с ресурсами, то для этой цели фаза должна быть prerare-package, а не package


отдельно следует отметить фазы цикла package, install и deploy.
package -- фаза, когда происходит сборка артефакта. стандартные плагины, для сборки артефактов, входящие в поставку Maven, умеют собирать jar, war, ear, ejb, rar, shade. каждый из сборщиков умеет класть внутрь архива необходимые ресурсы и раскладывать их по необходимым местам автоматически.
install -- фаза, когда собранные на этапе package артефакты проекта устанавливаются в локальный репозитарий. плагин создает каталоги группы и версии, копирует в созданную структуру артефакт и генерирует необходимую метаинформацию
deploy -- фаза, когда проинсталлированные в локальный репозитарий артефакты, переносятся в удаленный репозитарий. тут важно отметить, что Maven следит за обновлениями удаленных репозитариев автоматически. так что для того, чтобы обновить какой-то модуль для удаленной команды, необходимо только задеплоить его в общий удаленные репозитарий.

Maven2::Part #0

Общая информация


дисклэймер: широкая айтишная общественность собирается обсуждать всякое. разумеется собраться в то время, когда я буду в Украине у широкой общественности никак не вышло :) посему посильным вкладом в первую встречу будут заметки. я так решил, да.

систем автоматической сборки проектов на Java сущетсвует наверняка очень много. но самыми популярными и используемыми являются Ant и Maven2 (актуальным проектом является Maven2, Maven is dead и это было предрешено). собственно обе системы являются Java-приложениями и обе системы разработаны Apache Sotware Foundation. более того, Maven2 умеет вызывать изнутри себя Ant.

пара слов об Ant.


прекрасная и очень простая система автоматической сборки. для сборки нужен файл build.xml или любой другой xml. есть набор тасков, поставляемых с продуктом, есть бесчисленное множество тасков, поставляемых отдельно. можно писать свои таски, если очень нужно и не менее очень хочется.
основная идея автоматической сборки проекта -- это, разумеется, вызов javac. компилятора Java. однако вместе с компиляцей файлов приходится решать задачи создания и очистки октружения. создания и удаления каталогов, временных файлов, упаковки и распаковки проекта в различного рода архивы, приправленные различного рода дескрипторами.
основная идея Ant -- у нас есть набор property, у нас есть набор тасков, который работает с имеющимеся property. весь процесс сборки может быть описан последовательностью вызовов тасков и описания их зависимостей. все. больше ничего нет. остальное мы создаем сами -- от структуры каталогов до последовательностей вызовов, форматов и имен архивов, и прочия, и прочия, и прочия. полная свобода дествий при весьма интересных ограничениях. но основная речь не об Ant

И тут пришел Maven


самая главная проблема полной свободы дествий есть отсутсвие стандартов по-умолчанию. нужно в 151й раз создавать одни и теже каталоги, в одной и той же последовательности, в одной и той же структуре от проекта к проекту, от компьютера к компьютеру. при это нужно убедить коллег, старших товарищей, младших товарищей, конфигурэйшин менеджеров и менеджеров проекта, что эта структура проекта хороша. а кому-то нравится своя, а третьему вообще ничего не нравится и всякая структура есть зло и насилие над личностью.
Maven решает проблему стандартизации кталогов, для хранения исходников, ресурсов и файлов, сопутсвующих исходникам (jsp, images, css, deployment descriptors, etc) очень просто -- у него есть формат каталогов по-умолчанию. разумеется можно все переопределить, переназначить и тому подобное. просто делать это каждый раз утомительно и время на это тратить не целесообразно, есть более приоритетные задачи.
1. Maven предоставляет стандартизованную структуру каталогов для хранения исходников
вторая проблема Ant -- необходимость самостоятельно строить workflow сборки. включая очистку старого окружения, создание нового, изменение версий артефактов, именование артефактов и delivery-архивов.
Maven решает данную проблему опять-таки наличие стандартного механизма именования артефактов, размещения файлов релиза и тестирования, размещения ресурсов и промежуточных каталогов. так же у Maven есть предопределнный workflow, где все операции следуют в четкой, заранее определенной последовательности. на входе у нас исходники проекта, на выходе -- готовые jar/war/ear. если нам необходимо, то путем задания опреденных правил, мы можем сформировать delivery-архив и даже технический сайт проекта. просто используя соотвествующие части workflow, без дополнительного кодирования или установки правил.
2. Maven имеет стандартный build worlflow, стандартную политику именования артифактов, стандартные имена output каталогов для кода проекта, и кода тестов. Так Maven сам заботиться о том, чтобы необходимые ресурсы попали в необходимые артефакты.
третья проблема, которая является проблемой не только и не столько Ant, сколько программирования на Java вообще -- это проблема хранения библиотек. если с кодом все более-менее ясно и систем хранения и версионирования кода существует более чем достаточно, то с готовыми библиотеками все несколько сложнее. как правило эти готовые библиотеки кладутся в SVN рядом с исходниками. что сущетсвенно увеличивает время выгрузки исходников из системы контроля версий. более того, если имя библиотеки не содержит номера версии, то обновить таковую песня отдельная. хорошим вариантом является хранение библиотек отдельно от кода проекта. это решает проблему скорости обновления и checkout исходников проекта, но все-равно в SVN у нас остается несколько сот метров бинарного кода, который там просто лежит.
одна из главных особенностей Maven -- это возможность управления зависимостями. собственно Maven и создавался для того, чтобы решить проблему хранения готовых библиотек, их обновления и управления ими.
в случае Maven мы имеем некоторое количество репозиториев, которые хранят бинарные артефакты и метаданные к ним единым и стандартным способом. на уровне конфигурации сборки проекта тот или иной репозитарий может быть влючен в список источников, для поиска артефактов. артифактом является каждая бинарная библиотека, включающая:

  1. groupId - уникальный идентификатор группы. физически это каталог в кортне репозитория, относительно которого хранятся уже непосрественно версии библиотеки. Maven поддерживает именование в стиле Java package, это предоставляет возможность создавать не один каталог, а целое дерево с помощью данного аттрибута

  2. articatId - имя библиотеки. физически это и есть финальный файл библиотеки.

  3. version - весрия. физически это подкаталог в последнем каталоге дерева группы и постфик имени файла артифакта.


каждый модуль Maven и каждый модуль Вашего проекта под управлением Maven, так же есть артефактом. при первом обращении к тому или иному артефакту, Maven скачивает его из глобального репозитория в локальный, сформированный на Вашем локальном диске. в дальнейшем этот локальный репозиторий будет оновлятся новыми файлами и новыми версиями артефактов автоматически.
build workflow Maven позвольяет публиковать артефакты в любом, выбранном Вами репозитории, в связке с проектом Archiva это позволяет легко и просто создать корпоративное зеркало необходимых репозиториев и публиковать туда свои артефакты. что решает проблему обновления версий опять-таки стандартным способом.
3. Maven имеет мощный механизм управления внутренними и внешними зависимостями проекта. Maven стандартным образом решает проблему хранения бинарных файлов отдельно от исходного кода

Важно: проект Apache Ivy позволяет использовать репозитории артефактов Maven для управления зависимостями внутри проектов, собираемых с помощью Ant


С чего начинается Maven


для старта проекта под управлением Maven необходимо создать pom.xml (Project Object Model) файл в каталоге, который стане корневым для Вашего проекта. и разумеется скачать сам Maven.

Вівторок, 1 вересня 2009 р.

Spring MVC: не очевидное

Есть ряд совершенно не очевидных вещей внутри Spring MVC, например
1) чтобы форма работала и обрабатывалась нормально нужно, чтобы у нее обязательно были прописаны id и name. и то, и другое, одновременно. иначе возможны сбои
2) чтобы механизм автоматического binding'а элементов формы работал, нужно, чтобы имена полей формы совпадали с именами полей класса, в который они байндятся. Т.е.


Java class:
public class BackingBean {
private long id;
private String name;

public void setId(long id) {
this.id = id;
}

public long getId() {
return id;
}

public void setName(String name) {
this.name = name;
}

public long get() {
return name;
}
}

JSP:
<form id="foo" name"foo" method="post" action="/somewhere-on-server">
<spring:bind path="backingBean.id">
<input type="text" name="id"/>
</spring:bind>
<spring:bind path="backingBean.name">
<input type="text" name="name"/>
</spring:bind>
</form>

5) метод formBackingObject всегда создает новый экземпляр объекта, если его перегрузить таким образом, чтобы он делал проверку наличия уже созданного объекта в аттрибутах запроса, это позволит уменьшить количество объектов в памяти и сократить время, необходимое для создания этого объекта
4) в Spring Portlet MVC метод onSubmitRender вызывается только в том случае, когда состояние формы было изменено. поэтому в этом методе нельзя реализовывать обработку RenderRequest's, общих для всего контроллера. для обработки общих запросов можно использовать методы showForm, handleRenderRequestInternal, handleRenderRequest
p.s. а в многом остальном Spring MVC прекрасен, как и весь Spring пожалуй

Четвер, 20 серпня 2009 р.

конфигурация Java Service Wrapper для старта Tomcat6

есть в этом мире такая прекрасная библиотека, как Java Service Wrapper. она позволяет любую Java программу запустить как системную службу (особенно это важно для Windows, потому что иного способа часто нет). но беда этой прекрасной библиотеки в том, что разработчики не обновляют документацию на сайте. поэтому ниже файл конфигурации этой библиотеки для запуска tomcat-6.x.x как сервиса


#********************************************************************
# Wrapper License Properties (Ignored by Community Edition)
#********************************************************************
# Include file problems can be debugged by removing the first '#'
# from the following line:
##include.debug
#include ../conf/wrapper-license.conf
#include ../conf/wrapper-license-%WRAPPER_HOST_NAME%.conf

#********************************************************************
# Wrapper Java Properties
#********************************************************************
# Java Application
wrapper.java.command=%JAVA_HOME%/bin/java

# Tell the Wrapper to log the full generated Java command line.
#wrapper.java.command.loglevel=INFO

# Java Main class. This class must implement the WrapperListener interface
# or guarantee that the WrapperManager class is initialized. Helper
# classes are provided to do this for you. See the Integration section
# of the documentation for details.
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperStartStopApp

# Java Classpath (include wrapper.jar) Add class path elements as
# needed starting from 1
wrapper.java.classpath.1=../lib/wrapper.jar
wrapper.java.classpath.2=%JAVA_HOME%/lib/tools.jar
wrapper.java.classpath.3=../bin/bootstrap.jar

# Java Library Path (location of Wrapper.DLL or libwrapper.so)
wrapper.java.library.path.1=../lib

# Java Bits. On applicable platforms, tells the JVM to run in 32 or 64-bit mode.
wrapper.java.additional.auto_bits=TRUE

# Java Additional Parameters
wrapper.java.additional.1=-Dcatalina.base=..
wrapper.java.additional.2=-Dcatalina.home=..
wrapper.java.additional.3=-Djava.io.tempdir=../temp
wrapper.java.additional.4=-XX:MaxPermSize=256m

# Initial Java Heap Size (in MB)
wrapper.java.initmemory=128

# Maximum Java Heap Size (in MB)
wrapper.java.maxmemory=512

# Application parameters. Add parameters as needed starting from 1
wrapper.app.parameter.1=org.apache.catalina.startup.Bootstrap
wrapper.app.parameter.2=1
wrapper.app.parameter.3=start
wrapper.app.parameter.4=org.apache.catalina.startup.Bootstrap
wrapper.app.parameter.5=true
wrapper.app.parameter.6=1
wrapper.app.parameter.7=stop

#********************************************************************
# Wrapper Logging Properties
#********************************************************************
# Enables Debug output from the Wrapper.
# wrapper.debug=TRUE

# Format of output for the console. (See docs for formats)
wrapper.console.format=PM

# Log Level for console output. (See docs for log levels)
wrapper.console.loglevel=INFO

# Log file to use for wrapper output logging.
wrapper.logfile=../logs/wrapper.log

# Format of output for the log file. (See docs for formats)
wrapper.logfile.format=LPTM

# Log Level for log file output. (See docs for log levels)
wrapper.logfile.loglevel=INFO

# Maximum size that the log file will be allowed to grow to before
# the log is rolled. Size is specified in bytes. The default value
# of 0, disables log rolling. May abbreviate with the 'k' (kb) or
# 'm' (mb) suffix. For example: 10m = 10 megabytes.
wrapper.logfile.maxsize=0

# Maximum number of rolled log files which will be allowed before old
# files are deleted. The default value of 0 implies no limit.
wrapper.logfile.maxfiles=0

# Log Level for sys/event log output. (See docs for log levels)
wrapper.syslog.loglevel=NONE

#********************************************************************
# Wrapper Windows Properties
#********************************************************************
# Title to use when running as a console
wrapper.console.title=Tomcat Servlet Container 6.0.18

#********************************************************************
# Wrapper Windows NT/2000/XP Service Properties
#********************************************************************
# WARNING - Do not modify any of these properties when an application
# using this configuration file has been installed as a service.
# Please uninstall the service before modifying this section. The
# service can then be reinstalled.

# Name of the service
wrapper.ntservice.name=tomcat6

# Display name of the service
wrapper.ntservice.displayname=Tomcat Servlet Container 6.0.18

# Description of the service
wrapper.ntservice.description=Tomcat Servlet Container 6.0.18

Bundled NT service wrapper

# Service dependencies. Add dependencies as needed starting from 1
wrapper.ntservice.dependency.1=

# Mode in which the service is installed. AUTO_START or DEMAND_START
wrapper.ntservice.starttype=AUTO_START

# Allow the service to interact with the desktop.
wrapper.ntservice.interactive=false