Archivo

Archivo para Sábado, 18 de febrero de 2012

Maven contra Ant: Migrar nunca ha sido fácil

Sábado, 18 de febrero de 2012
Comentarios desactivados

En estos momentos estoy comenzando un proyecto para la compañía en la cual trabajo el cual consiste en mejorar la forma en como liberamos el código de nuestra aplicación en producción; Sin entrar en muchos detalles les puedo decir que estoy re-escribiendo el sistema de compilación que fué creado con Ant + Make en Maven.

El proyecto ha tenido sus dificultades (menores) y la curva de aprendizaje en mi caso no fué trivial ya que uno de los problemas que quería resolver es como compartir dependencias (archivos jar o ‘artefactos’) entre ellos:

  • Ant es declarativo. En Ant todos las acciones se especifican con mucho nivel de detalle, pero esto se hace tedioso especialmente cuando los proyectos son grandes o hay muchos proyectos (repetir una y otra vez). Makefile fué usado para manejar las dependencias entre proyectos de código nativo en C y luego para unir llamadas a Ant. Un desastre
  • Soporte para Eclipse, InteliJ: Debido a la forma en la compilación con Ant fué escrita, no se puede compilar en Windows de la misma manera que Linux. Muerta la portabilidad, además de que cada desarollador tenia que aprender ciertos trucos para poder compilar la aplicación en Linux.

Al principio traté de resolver el manejo de las dependencias en Ant usando Ivy. Pero la documentación de Ivy es espantosa y por irónico que parezca hace muchas referencias a Maven. Especialmente estoy último terminó por decidirme a usar Maven, convención sobre configuración, buen soporte especialmente en Eclipse e InteliJ.

No pienso escribir un tutorial sobre Ant contra Maven (Google les puede mostrar cientos de ellos), pero las comparaciones son obvias, miren por ejemplo como se compila a StupidZombie.com:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
< ?xml version="1.0"?>
<project name="StupidZombie" default="jar" basedir=".">
    <description>
StupidZombie is a program that allows you to ping several of your favorite Blog directories. The
idea is to avoid fill the same information over and over again.
 
StupidZombie es un programa el cual le permite hacer ping a varios de sus directorios de Blogs
favoritos. La idea es evitar rellenar la misma información una y otra vez.
    </description>
        <property file="build.properties"/>
 
        <path id="path.classpath">
                <pathelement location="${jar.xmlrpc-client}"/>
                <pathelement location="${ar.ws-commons-util}"/>
                <pathelement location="${jar.xmlrpc-common}"/>
                <pathelement location="${jar.codec}"/>
                <pathelement location="${jar.tidy}"/>
                <pathelement location="${jar.httpclient}"/>
                <pathelement location="${jar.net}"/>
                <pathelement location="${jar.log}"/>
        </path>
 
        <path id="path.test">
                <path refid="path.classpath"/>
                <pathelement location="${jar.junit}"/>
                <pathelement location="${build}"/>
                <pathelement location="${test}"/>
        </path>
 
    <target name="build" depends="init" description="Compile the source code">
        <javac srcdir="${src}" destdir="${build}" deprecation="true" optimize="false" debug="true">
                <include name="**/*.java"/>
                <classpath refid="path.classpath"/>
        </javac>
        <copy description="Copy the resourcebundles for all the classes" todir="${build}" overwrite="true">
                <fileset dir="${src}">
                        <include name="**/*.xml"/>
                </fileset>
                <fileset dir="${res}">
                                <include name="**/*.properties"/>
                </fileset>
        </copy>
    </target>
 
        <target name="init" description="Pre compilation tasks">
                <mkdir dir="${dist}"/>
                <mkdir dir="${build}"/>
                <mkdir dir="${test}"/>
        </target>
 
    <target name="clean" description="Artifact cleanup">
                <delete includeEmptyDirs="yes" failonerror="no">
                        <fileset dir="${build}"/>
                        <fileset dir="${dist}" />
                        <fileset dir="${test}" />
                </delete>
    </target>
 
        <target name="jar"
       <target
                name="jar"
                depends="build"
                description="Pack the project sources for distribution">
 
                        <manifest file="${jar.file.manifest}">
                                <attribute name="Main-class" value="com.stupidzombie.gui.StupidZombieGui"/>
                        </manifest>
 
                <copy description="Copy the images for the app" todir="${build}" overwrite="true">
                        <fileset dir="${img}">
                                        <include name="**/*"/>
                        </fileset>
                </copy>
 
                        <unjar src="${jar.xmlrpc-client}" dest="${build}"/>
                        <unjar src="${jar.ws-commons-util}" dest="${build}"/>
                        <unjar src="${jar.xmlrpc-common}" dest="${build}"/>
                        <unjar src="${jar.codec}" dest="${build}"/>
                        <unjar src="${jar.httpclient}" dest="${build}"/>
                        <unjar src="${jar.tidy}" dest="${build}"/>
                        <unjar src="${jar.log}" dest="${build}"/>
 
                        <jar jarfile="${dist}/${jar.project}"
                                basedir="${build}"
                                manifest="${jar.file.manifest}">
                                <exclude name="**/test/*"/>
                        </jar>
 
                        <signjar jar="${dist}/${jar.project}"
                                alias="StupidZombie"
                                keystore="StupidZombie.keystore"
                                storepass="StupidZombie"
                                verbose="false"
                        />
 
        </target>
      <target name="test"
                depends="jar"
                description="Unit tests">
                <echo>Running unit tests</echo>
                <javac srcdir="${src}"
                        destdir="${test}"
                        includes="com/stupidzombie/ping/**/Test*"
                        deprecation="true"
                        optimize="false"
                        debug="true">
                        <classpath refid="path.test"/>
                </javac>
 
                <junit fork="yes"
                        printsummary="on"
                        maxmemory="300m">
                        <classpath refid="path.test"/>
                        <formatter type="xml" />
                        <formatter type="plain" />
 
                        <sysproperty key="ping.blog.name"
                                value="StupidZombie"/>
 
                        <sysproperty key="ping.blog.url"
                                value="http://stupidzombie.com/blog"/>
 
                        <sysproperty key="ping.blog.directoryList"
                                value="technorati"/>
 
                        <test name="${testcase}"
                                todir="${test}"
                                if="testcase"/>
 
                        <batchtest </batchtest>
                </batchtest></junit>
 
                <junitreport todir="${test}">
                        <fileset dir="${test}">
                                <include name="TEST-*.xml"/>
                        </fileset>
                        <report format="frames" todir="${test}"/>
                </junitreport>
 
        </target>
</project>

Además de tener que guardar los archivos Jar en SVN (mala practica) el archivo de compilación es grande.

Mientras que en Maven:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
< ?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
                <groupid>com.stupidzombie</groupid>
                <artifactid>stupidzombie</artifactid>
                <version>20120206</version>
        </parent>
        <modelversion>4.0.0</modelversion>
        <artifactid>ping</artifactid>
        <packaging>jar</packaging>
        <name>ping</name>
        <build>
                <plugins>
                        <plugin>
                                <groupid>org.apache.maven.plugins</groupid>
                                <artifactid>maven-jar-plugin</artifactid>
                                <configuration>
                                        <archive>
                                                <manifestfile>src/main/resources/META-INF/MANIFEST.MF</manifestfile>
                                        </archive>
                                </configuration>
                        </plugin>
                        <plugin>
                                <artifactid>maven-assembly-plugin</artifactid>
                                <configuration>
                                        <descriptors>
                                                <descriptor>src/assemble/bigjar.xml</descriptor>
                                        </descriptors>
                                        <archive>
                                                <manifestfile>src/main/resources/META-INF/MANIFEST.MF</manifestfile>
                                        </archive>
                                </configuration>
                        </plugin>
                </plugins>
        </build>
        <dependencies>
                <dependency>
                        <groupid>org.apache.xmlrpc</groupid>
                        <artifactid>xmlrpc-client</artifactid>
                        <version>3.1.2</version>
                </dependency>
                <dependency>
                        <groupid>ws-commons-util</groupid>
                        <artifactid>ws-commons-util</artifactid>
                        <version>1.0.1</version>
                        <scope>compile</scope>
                </dependency>
                <dependency>
                        <groupid>commons-codec</groupid>
                        <artifactid>commons-codec</artifactid>
                        <version>1.3</version>
                        <scope>compile</scope>
                </dependency>
                <dependency>
                        <groupid>commons-httpclient</groupid>
                        <artifactid>commons-httpclient</artifactid>
                        <version>3.1</version>
                        <scope>compile</scope>
                </dependency>
                <dependency>
                        <groupid>commons-net</groupid>
                        <artifactid>commons-net</artifactid>
                        <version>2.0</version>
                        <scope>compile</scope>
                </dependency>
                <dependency>
                        <groupid>jtidy</groupid>
                        <artifactid>jtidy</artifactid>
                        <version>4aug2000r7-dev</version>
                </dependency>
        </dependencies>
</project>

Mi migración a Maven no estuvo libre de dolores:

  • En Eclipse existen varios módulos con soporte para Maven; con IAM y M2E. IAM nunca me funcionó y M2E me dió problemas con SubClipse por lo que tuve que desinstalarlo para usar Subversive.
  • Usar Maven sin un controlador de artefactos como Archiva no tiene sentido (archiva facilita muchas cosas, como el manejo de versiones). Eso significa que hay que aprender también a instalar y configurar archiva, en especial para controlar como los desarolladores liberan artefactos.
  • Aún tengo muchas preguntas en Maven, como por ejemplo el uso de perfiles y de ‘snapshots‘. Sin embargo ya logré colocar los artefactos de mis proyectos con buen manejo de versiones y para quienes lo desean en mi grupo, pueden trabajar con M2E en Eclipse.

    Mucha gente habla maravillas de Gradle, sin embargo me parece que no tiene tanta tracción como Maven, soporte básico para Eclipse, además de que hay que aprender algo de GROOVY (no es necesariamente algo malo). ¿Alguno de ustedes ha tenido experiencia real con este entorno?

    kodegeek

    A %d blogueros les gusta esto: