четвер, 6 січня 2011 р.

Создание своих архетипов и каталогов в Maven

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

Многим может не нравится, что мавен в самом деле отбирает у разработчика свободу выбора структуры проекта и прямо таки диктует ее, но в самом ли деле эта свобода настолько важна, чтобы делать изза нее жирный аргумент против? Не думаю. Есть другие, более серьезные, на мой взгляд, недостатки, в первую очередь - трудность диагностики проблем при сборке и недостаточная документированность мавена и плагинов.

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

Поиск плгинов и их настройка - это тоже мучительные круги ада, но по сравнению с xml-программированием на ant'e это еще ничего.

Однако при правильном подходе и набитом скилле, мавен практически не ощущаетсяв работе, что мне очень нравится.
Создаем локальный каталог

Собственно, в этой статье речь пойдет не о муках, а о архетипах, их каталогах и создании собственных архетипов и каталогов.

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

Любой, кто хотьябы раз создавал проект на мавене, сталкивался с архетипами. Например, типичный метод создания проекта, который витает в интернете:

mvn archetype:create                                    \
  -DarchetypeGroupId=<archetype-groupId>                \
  -DarchetypeArtifactId=<archetype-artifactId>          \
  -DarchetypeVersion=<archetype-version>                \
  -DgroupId=<my.groupid>                                \
  -DartifactId=<my-artifactId>

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

Если мы например, захотим создать простоe приложение, то мы используем архетип под названием maven-archetype-quickstart, например вот так:

mvn archetype:create                                    \
  -DarchetypeGroupId=org.apache.maven.archetypes        \
  -DarchetypeArtifactId=maven-archetype-quickstart      \
  -DarchetypeVersion=1.0                                \
  -DgroupId=org.example                                 \
  -DartifactId=simpleapp

Есть более удобный способ создания проекта, с помощью цели archetype:generate. При вызове, в интерактивном режиме будет предложено ввести параметры нового проекта.

mvn archetype:generate

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

Погуглив немного, я нашел для себя решение этой проблемы, не совсем как по мне окончательное, но вполне себе с намеком на элегантность. Архетипы можно обьединяются в каталоги! Но как это может помочь?

Каталог определяется URL'ом, где он расположен, кроме того в мавене есть три предопределенных каталога, или если хотите, алиаса: internal, remote и local.

internalсодержит архетипы, встроенные в maven, их немного и они уже де-факто идут с самим дистрибутивом
remoteцентральный каталог maven, находится по адресу http://repo1.maven.org/maven2/archetype-catalog.xml, его местоположение зависит от текущих настроек мавена, например возможно переопределить этот урл на одно из зеркал репозитория
localкаталог из локального репозитория, обычно находится в ~/.m2/archetype-catalog.xml

У цели archetype:generate есть параметр archetypeCatalog, с помощью которого можно указать список каталогов, где нужно искать возможные архетипы. По умолчанию, значение параметра 'remote,local'. Но если убрать оттуда remote, то получим почти то, что нужно.

Например, вот так:

grim@blackbox:~/projects$ mvn archetype:generate -DarchetypeCatalog=local

[ ...булшит... ]

Choose archetype:
1: local -> maven-archetype-quickstart (quickstart)
2: local -> maven-archetype-archetype (archetype)
3: local -> maven-archetype-webapp (webapp)
Choose a number: 1:

Подсмотреть структуру файла можно из каталога remote. Например, чтобы получить список выше, файл должен иметь вид

<?xml version="1.0" encoding="UTF-8"?>
<archetype-catalog xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 http://maven.apache.org/xsd/archetype-catalog-1.0.0.xsd"
    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <archetypes>
    <archetype>
      <groupId>org.apache.maven.archetypes</groupId>
      <artifactId>maven-archetype-quickstart</artifactId>
      <version>1.1</version>
      <description>quickstart</description>
    </archetype>
    <archetype>
      <groupId>org.apache.maven.archetypes</groupId>
      <artifactId>maven-archetype-archetype</artifactId>
      <version>1.0</version>
      <description>archetype</description>
    </archetype>
    <archetype>
      <groupId>org.apache.maven.archetypes</groupId>
      <artifactId>maven-archetype-webapp</artifactId>
      <version>1.0</version>
      <description>webapp</description>
    </archetype>
  </archetypes>
</archetype-catalog>


Немного поколдовав с конфигурацией мавена, можно сделать такую ситуацию постоянной, нужно создать профиль, в котором выставить значение переменной archetypeCatalog. Для этого в ваш файл settings.xml нужно добавить

  <profiles>

    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
           <archetypeCatalog>local</archetypeCatalog>
        </properties>
    </profile>

  </profiles>

Усе! Теперь, при вызове, цель archetype:generate вместо тонн мусора, будет выводить вам то, что скажете.

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

grim@blackbox:~/projects$ mvn archetype:crawl -Dcatalog=~/.m2/archetype-catalog.xml

Вопреки официальной документации параметр называется не catalogFile, а catalog.

Создаем свой архетип

Вторая проблема, которую мы рассмотрим - это создание собственного архетипа. Зачем это нужно? Нужно это по той простой причине, что при всем кажущемся обилии архетипов, среди них не оказалось подходящего для простой веб-разработки. Есть несколько близких по духу, например тот же maven-archetype-webapp, но дескрипторы в нем устаревшие, нету log4j и нормального темплейта jsp. В итогое после создания пустого проекта, нужно перейти в режим работы напильником, и с помощью гугла переделывать все как надо. В конце концов на десятый раз мне это надоело, и я решил создать свой собственный архетип, самый лучший и самый правильный. Более конкретно, создадим заготовку для простенького приложения на spring-mvc c использованием Servlet 2.5/JSP 2.1/JSTL 1.2 c готовым к работе логированием.

Внимание, в следующем абзаце возможно зависание от количества упоминаний слова архетип.

Для начала нужно создать проект для нашего архетипа, используя архетип maven-archetype-archetype, например с помощью того же archetype:сreate.

grim@blackbox:~/projects$ mvn archetype:create      \
   -DarchetypeArtifactId=maven-archetype-archetype  \
   -DartifactId=baremvc                             \
   -DgroupId=example

Чтобы не мучится в консоли, пересядем в эклипс, хотя конечно же все, что написано ниже, можно сделать и с помощью vim'а или еще чегото такого.
grim@blackbox:~/projects$ cd baremvc && mvn eclipe:eclipse

После чего делаем импорт и смотрим на структуру более детально.

 

Проект состоит из одной папки resources, в которой содержится две подпапки: archetype-resources и META-INF. В первой храниться костяк будущих выдающихся проектов, которые еще будут созданы поколениями програмистов после нашей смерти, во второй хранится файлик META-INF/maven/archetype.xml. Это дексриптор архетипа. В нем будет хранится описание того, что входит в архетип.

Дополним костяк, всем чем нужно: добавим туда простенькую jsp страничку, более-менее сносный web.xml, простенький контроллер и конфиг log4j.properties. Все это вы найдете в архиве, который можно скачать. Из интересных моментов, на которые стоит обратить внимание, это замены, которые делает мавен при создании проекта. В архетипе реализован механизм темплейтов на основе Velocity, который практически не документирован, если ктото уверен в обратном, поделитесь, буду благодарен.

В частности в контроллере через темплейты реализована подстановка имени пакета.

// HomeController.java
package $package;

import org.apache.commons.logging.Log;

@Controller
public class HomeController {

...


Тот же самый ход использован в конфигурации Spring MVC и при генерации целевого pom.xml. Пока что список известных мне переменных довольно скуден: $groupId, $artifactId, $version и $package. Думаю, всем понятно, что каждый значит, все это указывается при создании проекта из архетипа.

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

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

<archetype xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype/1.0.0 http://maven.apache.org/xsd/archetype-1.0.0.xsd">

  <id>baremvc</id>
  <sources>
    <source>src/main/java/HomeController.java</source>
  </sources>
  <resources>
      <resource>src/main/resources/log4j.properties</resource>
      <resource>src/main/webapp/home.jsp</resource>
      <resource>src/main/webapp/WEB-INF/applicationContext.xml</resource>
      <resource>src/main/webapp/WEB-INF/web.xml</resource>
  </resources>
  <testSources />
</archetype>   

Тэг <id></id> должен совпадать с artifactId нашего архетипа. Тэги <sources> и <resources> прдназначены для темплейтов разных частей архетипа. В частности ресурсы из папки webapp нужно указывать в тэге <resources>. Есть еще несколько допустимых секций. Ниже перечислены допустимые секции и папки, куда уйдут указанные в них шаблоны.
  • <sources> => src/main/java
  • <resources> => src/main/resources
  • <testSources> => src/test/java
  • <testResources> => src/test/resources
  • <siteResources> => src/site


После того как все готово, можно установить наш архетип в репозиторий с помощью

grim@blackbox:~/projects/baremvc$ mvn clean install

и теперь он готов к использованию.

grim@blackbox:~/projects/baremvc$ cd ..
grim@blackbox:~/projects$ mvn archetype:create          \
  -DarchetypeGroupId=org.example                        \
  -DarchetypeArtifactId=baremvc                         \
  -DarchetypeVersion=1.0                                \
  -DgroupId=org.example                                 \
  -DartifactId=baremvcapp

Наш маленький проект готов к запуску

grim@blackbox:~/projects/baremvcapp$ cd baremvcapp
grim@blackbox:~/projects/baremvcapp$ mvn tomcat:run

После чего по ссылке http://localhost:8080/baremvcapp можно узреть сие творение.

Теперь архетип, можно сказать приготовлен, добавим его в локальный каталог руками или с помощью цели archetype:crawl.

grim@blackbox:~/projects$ mvn archetype:crawl -Dcatalog=/home/grim/.m2/archetype-catalog.xml

Теперь при вызове archetype:generate в списке должна появиться строчка

1: local -> baremvc (baremvc)

с чем я нас и поздравляю.

В заключение осталось добавить, что описанный выше способ устарел (увы), и помечен как depreceted, но все еще работает, а про right way документации как то кот наплакал. Все что мне пока известно, это что дескриптор изменили, теперь он называется archetype-metadata.xml и имеет более мощный синтаксис. Надеюсь хватит еще на десяток лет вперед статьи писать.



Архив, который можно скачать


Исходный код, который можно посмотреть

Использованные ресурсы

  1. Guide to Creating Archetypes - http://maven.apache.org/plugins/maven-archetype-plugin-1.0-alpha-7/examples/archetype.html
  2. Maven Archetype Plugin - http://maven.apache.org/archetype/maven-archetype-plugin/
  3. Maven – размышления после двух лет использования - http://habrahabr.ru/blogs/personal/102181/

    1 коментар:

    1. Добавил ссылку на исходник проекта в репозитории Google Code

      ВідповістиВидалити