Pull to refresh

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

Reading time 8 min
Views 34K
Уже довольно продолжительное время (около года набежит) активно пользуюсь системой сборки 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>   
	

Тэг должен совпадать с 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 и имеет более мощный синтаксис. Надеюсь хватит еще на десяток лет вперед статьи писать.

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

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

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


  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/


UPD: Есть еще цель archetype:create-from-project, которая сгенерирует шаблон из вашего проекта. Шаблон сгенерируется в папку target/generated-sources/archetype. То есть фактически из любого вашего проекта можно сгенерировать архетип и потом использовать его в качестве отправной точки. Спасибо 1nd1go
Tags:
Hubs:
+29
Comments 17
Comments Comments 17

Articles