Уже довольно продолжительное время (около года набежит) активно пользуюсь системой сборки Apache Maven и вполне ею доволен. Несмотря на свои очевидные и не очень недостатки, неоспоримым преимуществом является автоматическое управление зависимостями, хорошая структурированность проектов и отсутствие скриптов сборки как таковых, а следовательно проблем с ними.
Многим может не нравится, что мавен в самом деле отбирает у разработчика свободу выбора структуры проекта и прямо таки диктует ее, но в самом ли деле эта свобода настолько важна, чтобы делать изза нее жирный аргумент против? Не думаю. Есть другие, более серьезные, на мой взгляд, недостатки, в первую очередь — трудность диагностики проблем при сборке и недостаточная документированность мавена и плагинов.
Был случай на проекте, когда новый член команды не смог собрать билд, получив маловразумительный вывод, что и только после двух часов копания выяснилось, что в поме была прибита пятая версия явы как основная, а она не была установлена.
Поиск плагинов и их настройка — это тоже мучительные круги ада, но по сравнению с xml-программированием на ant'e это еще ничего.
Однако при правильном подходе и набитом скилле, мавен практически не ощущается в работе, что мне очень нравится.
Собственно, в этой статье речь пойдет не о муках, а о архетипах, их каталогах и создании собственных архетипов и каталогов.
С помощью этих нехитрых концепций можно ощутимо упростить себе жизнь при создании нового проекта.
Архетип в мавене — это шаблон нового проекта, со структурой и заготовками исходных и конфигурационных файлов.
Любой, кто хотя бы раз создавал проект на мавене, сталкивался с архетипами. Например, типичный метод создания проекта, который витает в интернете:
Вопрос, откуда взять нужные параметры, всегда меня волновал, обычно я вежливо спрашиваю у Гугла и он мне обычно отвечает :)
Если мы например, захотим создать простоe приложение, то мы используем архетип под названием maven-archetype-quickstart, например вот так:
Есть более удобный способ создания проекта, с помощью цели archetype:generate. При вызове, в интерактивном режиме будет предложено ввести параметры нового проекта.
Но тут возникает проблема. Мавен предлагает выбрать тип нового проекта из списка готовых шаблонов, а список состоит ни много ни мало из более чем 300 вариантов. Найти там нужный шаблон — задача довольно нетривиальная, обычно я сбрасываю вывод в файл и потом грепом ищу то что нужно.
Погуглив немного, я нашел для себя решение этой проблемы, не совсем как по мне окончательное, но вполне себе с намеком на элегантность. Архетипы можно обьединять в каталоги! Но как это может помочь?
Каталог определяется URL'ом, где он расположен, кроме того в мавене есть три предопределенных каталога, или если хотите, алиаса: internal, remote и local.
У цели archetype:generate есть параметр archetypeCatalog, с помощью которого можно указать список каталогов, где нужно искать возможные архетипы. По умолчанию, значение параметра 'remote,local'. Но если убрать оттуда remote, то получим почти то, что нужно.
Например, вот так:
Подсмотреть структуру файла можно из каталога remote. Например, чтобы получить список выше, файл должен иметь вид
Немного поколдовав с конфигурацией мавена, можно сделать такую ситуацию постоянной, нужно создать профиль, в котором выставить значение переменной archetypeCatalog. Для этого в ваш файл settings.xml нужно добавить
Усе! Теперь, при вызове, цель archetype:generate вместо тонн мусора, будет выводить вам то, что скажете.
К слову у плагина archetype есть любопытная цель crawl, которая сканирует ваш локальный репозиторий на предмет наличия архетипов и генерирует из всех найденных каталог. К сожалению, документация немного врет и по умолчанию файл генерируется совсем не там где ожидает увидеть его мавен. Готовить нужно так:
Вопреки официальной документации параметр называется не 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.
Чтобы не мучится в консоли, пересядем в эклипс, хотя конечно же все, что написано ниже, можно сделать и с помощью vim'а или еще чегото такого.
После чего делаем импорт и смотрим на структуру более детально.
Проект состоит из одной папки resources, в которой содержися две подпапки: archetype-resources и META-INF. В первой храниться костяк будущих выдающихся проектов, которые еще будут созданы поколениями програмистов после нашей смерти, во второй хранится файлик META-INF/maven/archetype.xml. Это дескриптор архетипа. В нем будет хранится описание того, что входит в архетип.
Дополним костяк, всем чем нужно: добавим туда простенькую jsp страничку, более-менее сносный web.xml, простенький контроллер и конфиг log4j.properties. Все это вы найдете в архиве, который можно скачать. Из интересных моментов, на которые стоит обратить внимание, это замены, которые делает мавен при создании проекта. В архетипе реализован механизм темплейтов на основе Velocity, который практически не документирован, если ктото уверен в обратном, поделитесь, буду благодарен.
В частности в контроллере через темплейты реализована подстановка имени пакета.
Тот же самый ход использован в конфигурации Spring MVC и при генерации целевого pom.xml. Пока что список известных мне переменных довольно скуден: $groupId, $artifactId, $version и $package. Думаю, всем понятно, что каждый значит, все это указывается при создании проекта из архетипа.
Вообще тема темплейтов интересная и нераскрытая, думаю немного погодя раскопаю ее поглубже, а пока то что есть, вполне достаточно для наших скромных нужд.
Когда процесс подготовки шаблонов завершен, нужно подготовить файл-дескриптор архетипа, тот самый archetype.xml.
Тэг должен совпадать с artifactId нашего архетипа. Тэги <sources> и <resources> прдназначены для темплейтов разных частей архетипа. В частности ресурсы из папки webapp нужно указывать в тэге <resources>. Есть еще несколько допустимых секций. Ниже перечислены допустимые секции и папки, куда уйдут указанные в них шаблоны.
После того как все готово, можно установить наш архетип в репозиторий с помощью
и теперь он готов к использованию.
Наш маленький проект готов к запуску
После чего по ссылке http://localhost:8080/baremvcapp можно узреть сие творение.
Теперь архетип, можно сказать приготовлен, добавим его в локальный каталог руками или с помощью цели archetype:crawl.
Теперь при вызове archetype:generate в списке должна появиться строчка
с чем я нас и поздравляю.
В заключение осталось добавить, что описанный выше способ устарел (увы), и помечен как deprecated, но все еще работает, а про right way документации как то кот наплакал. Все что мне пока известно, это что дескриптор изменили, теперь он называется archetype-metadata.xml и имеет более мощный синтаксис. Надеюсь хватит еще на десяток лет вперед статьи писать.
Архив, который можно скачать
Исходники, которые можно посмотреть
UPD: Есть еще цель archetype:create-from-project, которая сгенерирует шаблон из вашего проекта. Шаблон сгенерируется в папку target/generated-sources/archetype. То есть фактически из любого вашего проекта можно сгенерировать архетип и потом использовать его в качестве отправной точки. Спасибо 1nd1go
Многим может не нравится, что мавен в самом деле отбирает у разработчика свободу выбора структуры проекта и прямо таки диктует ее, но в самом ли деле эта свобода настолько важна, чтобы делать изза нее жирный аргумент против? Не думаю. Есть другие, более серьезные, на мой взгляд, недостатки, в первую очередь — трудность диагностики проблем при сборке и недостаточная документированность мавена и плагинов.
Был случай на проекте, когда новый член команды не смог собрать билд, получив маловразумительный вывод, что и только после двух часов копания выяснилось, что в поме была прибита пятая версия явы как основная, а она не была установлена.
Поиск плагинов и их настройка — это тоже мучительные круги ада, но по сравнению с 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>
Тэг должен совпадать с 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)
с чем я нас и поздравляю.
В заключение осталось добавить, что описанный выше способ устарел (увы), и помечен как deprecated, но все еще работает, а про right way документации как то кот наплакал. Все что мне пока известно, это что дескриптор изменили, теперь он называется archetype-metadata.xml и имеет более мощный синтаксис. Надеюсь хватит еще на десяток лет вперед статьи писать.
Архив, который можно скачать
Исходники, которые можно посмотреть
Использованные ресурсы
- Guide to Creating Archetypes — http://maven.apache.org/plugins/maven-archetype-plugin-1.0-alpha-7/examples/archetype.html
- Maven Archetype Plugin — http://maven.apache.org/archetype/maven-archetype-plugin/
- Maven – размышления после двух лет использования — http://habrahabr.ru/blogs/personal/102181/
UPD: Есть еще цель archetype:create-from-project, которая сгенерирует шаблон из вашего проекта. Шаблон сгенерируется в папку target/generated-sources/archetype. То есть фактически из любого вашего проекта можно сгенерировать архетип и потом использовать его в качестве отправной точки. Спасибо 1nd1go