Общее·количество·просмотров·страницы

Java Dev Notes - разработка на Java (а также на JavaScript/Python/Flex и др), факты, события из АйТи

Показаны сообщения с ярлыком ant. Показать все сообщения
Показаны сообщения с ярлыком ant. Показать все сообщения

среда, 10 июня 2009 г.

Получение Revision Number из SVN с помощью Ant

Несколько часов назад у меня появилась задача: в веб-интерфейсе разрабатываемой системы необходимо показывать номер ревизии (Revision Number) из Subversion. Все исходники проекта хранятся в Subversion, поэтому перед компиляцией (или перед сборкой) необходимо запрашивать Subversion на получение Revision Number проекта, и его вставлять в интерфейс. Естественно, это должно выполняться автоматически перед каждой сборкой.

Решено сделать следующее:
1) на место, куда требуется вставлять Revision Number (страничка head.jsp, которая вставляется инклюдом во все остальные странички), вставляется лексема __REVISION_NUMBER__, которая перед компиляцией заменяется на актуальный Revision Number, полученный из Subversion
2) создается цель (target) в Ant, которая получает Revision Number из SVN, и сохраняет его в соответствующее свойство (property)
3) применяется команда <replace>, которая заменяет лексему __REVISION_NUMBER__ на значение свойства, куда сохранили Revision Number

Все стандартно, но встает вопрос - как получить Revision Number из Subversion. Немножко погуглив, я нашел решение, про которое и расскажу.

В Subversion есть команда info, которая выдает информацию о локальном либо удаленном (находящимся в репозитории) файле. Выдается следующая информация:

  • Path
  • Name
  • URL
  • Revision
  • Repository Root
  • Repository UUID
  • Node Kind
  • Last Changed Author
  • Last Changed Revision
  • Last Changed Date
  • Text Last Updated
  • Properties Last Updated
  • Checksum
  • Lock Token
  • Lock Owner
  • Lock Creation Date


Например, при выполнении команды

svn info build.xml

на консоль будет выведена следующая информация:

Path: build.xml
Name: build.xml
URL: svn://svn/PROJECT/trunk
Repository Root: svn://svn
Repository UUID: (UUID репозитория)
Revision: 3558
Node Kind: file
Schedule: normal
Last Changed Author: awm
Last Changed Rev: 3443
Last Changed Date: (date goes here...)
Text Last Updated: (date goes here...)
Checksum: (checksum goes here....)


Для нас здесь важно свойство Revision. Именно его значение мы и собираемся вставлять вместо лексемы __REVISION_NUMBER__. Причем не важно, какой именно файл будет передан в качестве аргумента команде svn info. Дело в том, что номер ревизии является общим для всех файлом в проекте, поэтому выполнить команду svn info мы можем для любого файле проекта, в частности, и для файла сборки build.xml.

Удобно и то, что формат представления информации командой svn info понимается командами Ant, в частности, командой <property file=""/> и <loadproperties/>. Это означает, что скинув дамп вывода в какой-нибудь файл, мы затем можем без изменений передать этой файл этим командам, и они прочитают свойства, которые выведены командой svn info:
Name, URL, Repository Root, Repository UUID, Revision, Node Kind, Schedule, Last Changed, Author, Last Changed Rev, Last Changed Date, Text Last Updated, Checksum.

Была написана следующая цель в файле сборки:



<target name="get.revision.and.replace" description="Get revision number, and places it into JSPs">
<exec executable="svn" output="svndump">
<arg value="info"/>
<!-- any filename can be here, because we need only Revision property, which is general for all filenames -->
<arg value="build.xml"/>
</exec>
<loadproperties srcfile="svndump"/>
<echo message="current revision is ${Revision}"/>
<delete file="svndump"/>
<replace file="${basedir}/project/WebContent/templates/head.jsp" token="__REVISION_NUMBER__" value="${Revision}"/>
<replace file="${basedir}/project/WebContent/templates/head_login.jsp" token="__REVISION_NUMBER__" value="${Revision}"/>
</target>


Атрибут output команды <exec> определяет файл, куда записывается вывод исполняемой команды. Как уже говорилось, формат этого вывода таков (Имя: значение), что понимается командами загрузки свойств Ant без изменений, что очень удобно. После загрузки свойств командой <loadproperties srcfile="svndump"/> файл вывода svndump удаляется командой <delete file="svndump"/>. В конце концов, два раза вызывается команда <replace>, которая и заменяет в определенных файлах требуемую лексему на номер ревизии.

четверг, 4 июня 2009 г.

Использование фильтров в Apache Ant - ClassConstants

Apache Ant используется для компиляции, сборки и развертывания проектов на Java. Ant является стандартом де-факто в мире Java. Он предоставляет множество возможностей, одна из которых - это фильтрация (преобразование) содержимого обрабатываемых файлов. В данном сообщении рассмотрено использование фильтров в Ant.

Представьте, что при выполнении процесса сборки вашего приложения вам необходимо обработать некоторые текстовые файлы. Например, в одном файле необходимо заменить некоторые сочетания символов (tokens) на другие, а в другой файл должно быть записано содержимое нескольких файлом, т.е. произведена конкатенация файлов. В этом случае необходимо использовать фильтры Ant. Фильтры используются для обработки содержимого файлов при их копировании, перемещении. Работать с фильтрами могут следующие команды (tasks):

<сoncat>
<copy>
<loadfile>
<loadproperties>
<move>


Фильтры могут объединяться в цепочки фильтров (FilterChains), таким образом позволяя применить к обрабатываемому файлу несколько фильтров в определенной последовательности. Такой механизм является аналогом Unix pipes.

Рассмотрим имеющиеся фильтры:

ClassConstants

Выводит все константы (поля, объявленные final) Java-класса. Модификатор доступа не влияет на вывод. Рассмотрим пример: вывод констант класса в текстовый файл.

Класс следующий:

TestClassConstants.java:


package ru.testant;

import java.util.ArrayList;

public class TestClassConstants {

public final int A = 1;
protected final double B = 0.123;
private final String C = "test string";
private final ArrayList D = new ArrayList();
int E = 2;
}


после этого создадим следующую структуру каталогов (PROJECT_HOME - корневой каталог проекта):
PROJECT_HOME/build/classes
PROJECT_HOME/src/ru/testant


В каталог PROJECT_HOME/src/ru/testant поместим наш файл TestClassConstants.java

Затем создадим следующий файл сборки


build.xml:


<project name="ant-filters1" default="testcc" basedir=".">
<description>
Test ClassConstants Ant filter
</description>

<property name="src.dir" value="src"/>
<property name="build.dir" value="build/classes"/>

<target name="compile" description="Compile sources">
<javac srcdir="${src.dir}" destdir="${build.dir}"/>
</target>

<target name="testcc" description="Test ClassConstants filter" depends="compile">
<copy file="${build.dir}/ru/testant/TestClassConstants.class" encoding="ISO-8859-1" tofile="11.txt">
<filterchain>
<classconstants/>
</filterchain>
</copy>
</target>
</project>


и поместим его в корневой каталог проекта (т.е. PROJECT_HOME). Основное внимание надо обратить на цель testcc. В ней происходит копирование обработанного контента из файла TestClassConstants.class в файл 11.txt, который будет размещен в PROJECT_HOME. Кодировка ISO-8859-1 должна использоваться в этом фильтре, т.к. при ее использовании не происходит потери информации при перекодировании characters --> int и обратно.

Фильтр ClassConstants использует библиотеку Apache BCEL (Byte Code Engineering Library), которую надо скачать с сайта http://jakarta.apache.org/bcel и поместить в каталог ANT_HOME/lib (где ANT_HOME - каталог, куда установлен ANT). Этот каталог не надо указывать в classpath, поскольку JARы из него добавляются в classpath автоматически перед началом работы Ant.

В итоге, наш проект включает в себя следующие файлы:
  • PROJECT_HOME/build.xml

  • PROJECT_HOME/src/ru/testant/TestClassConstants.java

  • ANT_HOME/lib/bcel-5.2.jar


Затем вызывается цель testcc:

> ant testcc

Получившийся файл 11.txt имеет следующий вид:


11.txt:

A=1
B=0.123
C=test string


Видно, что константа D не сохранена в файл, поскольку она не является примитивным типом, а также не сохранено неконстантное поле E.

Если фильтр применить внутри команды <loadproperties>, то тогда константы класса будут загружены в виде свойств, где имя константы - будет именем свойства, а значение константы - значением свойства. Пример:


<loadproperties srcfile="${build.dir}/ru/testant/TestClassConstants.class" encoding="ISO-8859-1">
<filterchain>
<classconstants/>
</filterchain>
</loadproperties>

Постоянные читатели