Archiva 1.2.1 sobre JBoss AS 4.0.5 en Windows 64

El primer paso es descargar JBoss 4.05 de la web de descargas de la comunidad de JBoss. Se descomprime en C:\ y se descarga el utilitario para instalarlo como servicio. Esta parte es muy simple y se explica en la siguiente url: http://www.jboss.org/community/wiki/JBossNativeWindows.

Una vez hecho esto, procedemos a montar Apache Archiva. Como vamos a usar de base de datos Derby, debemos copiar derby-10.1.3.1.jar y derbytools-10.1.3.1.jar en la carpeta server\default\lib.
Extraemos archiva-1.2.1.war en la carpeta server\default\deploy\archiva.war.

Creamos el archivo server\default\deploy\derby-ds.xml, con el siguiente contenido:

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
     <jndi-name>users2</jndi-name>
     <connection-url>jdbc:derby:database/archiva;create=true</connection-url>
     <driver-class>org.apache.derby.jdbc.EmbeddedDriver</driver-class>
     <user-name>sa</user-name>
     <password></password>
     <min-pool-size>5</min-pool-size>
     <max-pool-size>20</max-pool-size>
     <idle-timeout-minutes>5</idle-timeout-minutes>
     <track-statements/>
  </local-tx-datasource>
  <local-tx-datasource>
     <jndi-name>archiva</jndi-name>
     <connection-url>jdbc:derby:database/archiva;create=true</connection-url>
     <driver-class>org.apache.derby.jdbc.EmbeddedDriver</driver-class>
     <user-name>sa</user-name>
     <password></password>
     <min-pool-size>5</min-pool-size>
     <max-pool-size>20</max-pool-size>
     <idle-timeout-minutes>5</idle-timeout-minutes>
     <track-statements/>
  </local-tx-datasource>
</datasources>

Necesitamos crear también el server\default\deploy\archiva.war\META-INF\context.xml:

<Context path="/archiva" docBase="/">
  <Resource name="jdbc/users" auth="Container" 
            type="javax.sql.DataSource" username="sa" password=""  
            driverClassName="org.apache.derby.jdbc.EmbeddedDriver"
            url="jdbc:derby:database/users;create=true" />
  <Resource name="jdbc/archiva" auth="Container"
            type="javax.sql.DataSource" username="sa" password=""
            driverClassName="org.apache.derby.jdbc.EmbeddedDriver"
            url="jdbc:derby:database/archiva;create=true" />
  <Resource name="mail/Session" auth="Container"
            type="javax.mail.Session"
            mail.smtp.host="localhost"/>
</Context> 

En server\default\deploy\archiva.war\WEB-INF\classes\application.properties tenemos que añadir appserver.home y appserver.base:

user.agent=Apache Archiva/1.2.1
appserver.base=
appserver.home=

Y por último, añadimos el server\default\deploy\archiva.war\WEB-INF\jboss-web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
 <resource-ref>
   <res-ref-name>jdbc/users</res-ref-name>
   <jndi-name>java:/users2</jndi-name>
 </resource-ref>
 <resource-ref>
   <res-ref-name>jdbc/archiva</res-ref-name>
   <jndi-name>java:/archiva</jndi-name>
 </resource-ref>
 <resource-ref>
   <res-ref-name>mail/Session</res-ref-name>
   <jndi-name>java:/Mail</jndi-name>
 </resource-ref> 
</jboss-web>

Profit!

Installing Sun Java JDK in CentOS 5.2

Installing Sun Java JDK in CentOS is not as easy as I expected.

Download the Sun Java JDK from the Java SE Downloads page.

Using your root account, or the sudo command, run the jdk-6u11-linux-i586-rpm.bin (chmod it if necessary). This will install Java packages.

Type java -version. In my case, it show gcj info instead of Sun one.
For selecting the java command, we can use the alternatives program. Use:

/usr/sbin/alternatives --install /usr/bin/java java /usr/java/latest/bin/java 2

Now run

/usr/sbin/alternatives --config java

and select option 2.

Running java -version now should show Sun JVM info.

Remember that maybe you want to do the same with javac and other Java commands.

Hope this helps.

Acelerando Maven2

El primer paso, si no lo has hecho ya, es instalar Maven2. Desde que compré mi nuevo portátil no he vuelto a usar Maven, por lo que describiré muy brevemente los pasos que he seguido.

Descarga Maven 2.0.9 y extraelo en tu directorio de archivos de programa (en Windows, lo extraigo en C:\Program Files). Añade una nueva variable de entorno llamada MVN con el valor C:\Program Files\apache-maven-2.0.9\bin, y edita el Path para referenciarla (añade ;%MVN% al final de tu Path). Abre una nueva consola y ejecuta mvn -v. Si devuelve la versión instalada de Maven2, todo marcha bien 🙂

Maven2 automáticamente descarga todas las dependencias que se necesiten para compilar una aplicación (vienen definidas en el POM). Por tanto, para acelerar estos procesos, es interesante añadir un mirror cercano geográficamente. En mi caso, el CICA es mirror de Apache, por lo que está en mi propia ciudad 🙂

Para más información, consulta Maven Guide Mirror Settings.

PD: El asunto de esta entrada era sólo un paso necesario para una futura entrada, y estaba escrito en inglés, pero al final era esfuerzo inútil (ya veréis por que motivo en próximos capítulos). He preferido publicar este minipost antes que borrar una parte del esbozo del siguiente.

Microsoft + Eclipse

Sam Ramji (Port25) anunció en la EclipseCon que Microsoft colaborará con la Eclipse Foundation para que SWT utilice WPF como motor de renderizado.

Manipulación de cadenas en Java

Debatíamos Bastian y yo en los comentarios de la entrada sobre FindBugs acerca de la necesidad o no de realizar las concatenaciones de cadenas en Java utilizando las clases String o StringBuilder. Bastian apuntaba que en [1] se decía que el compilador de Sun desde la versión Java 1.5 ya realizaba estas optimizaciones.

Al final la conversación quedó en que como deberes yo haría una prueba, y durante un viaje en autobús la realicé.

El código de ambos tests es el siguiente:

Test con String:

    public class LoopsStringConcatenation {
    	public static void main(String[] args) {
    		long t0 = System.currentTimeMillis();
    		String s = new String();
    		for (int i=0; i<10000; i++) {
    			s += "a";
    		}
    		long t = System.currentTimeMillis() - t0;
    		System.out.println(t);
    	}
    }
&#91;/sourcecode&#93;
Test con StringBuilder:
&#91;sourcecode language="java"&#93;
    public class LoopsStringBuilder {
    	public static void main(String&#91;&#93; args) {
    		long t0 = System.currentTimeMillis();
    		StringBuilder s = new StringBuilder();
    		for (int i=0; i<10000; i++) {
    			s.append("a");
    		}
    		long t = System.currentTimeMillis() - t0;		
    		System.out.println(t);
    	}
    }
&#91;/sourcecode&#93;
Si ejecutamos los dos tests, podemos comprobar que el método que utiliza String es bastante más lento que el método que usa StringBuilder (sí, "bastante más lento" va a servir en este caso, no voy a poner números pues esto es una prueba no formal).

No obstante, vamos a comprobar las instrucciones de bytecode que se generan en ambos casos, y vamos a ver cuál es la diferencia.

Bytecode del test con String:
&#91;sourcecode language="java"&#93;
 0 invokestatic #16 <java/lang/System.currentTimeMillis>
 3 lstore_1
 4 new #22 <java/lang/String>
 7 dup
 8 invokespecial #24 <java/lang/String.<init>>
11 astore_3
12 iconst_0
13 istore 4
15 goto 41 (+26)
18 new #25 <java/lang/StringBuilder>
21 dup
22 aload_3
23 invokestatic #27 <java/lang/String.valueOf>
26 invokespecial #31 <java/lang/StringBuilder.<init>>
29 ldc #34 <a>
31 invokevirtual #36 <java/lang/StringBuilder.append>
34 invokevirtual #40 <java/lang/StringBuilder.toString>
37 astore_3
38 iinc 4 by 1
41 iload 4
43 sipush 10000
46 if_icmplt 18 (-28)
49 invokestatic #16 <java/lang/System.currentTimeMillis>
52 lload_1
53 lsub
54 lstore 4
56 getstatic #44 <java/lang/System.out>
59 lload 4
61 invokevirtual #48 <java/io/PrintStream.println>
64 return

Bytecode del test con StringBuilder:

 0 invokestatic #16 <java/lang/System.currentTimeMillis>
 3 lstore_1
 4 new #22 <java/lang/StringBuilder>
 7 dup
 8 invokespecial #24 <java/lang/StringBuilder.<init>>
11 astore_3
12 iconst_0
13 istore 4
15 goto 28 (+13)
18 aload_3
19 ldc #25 <a>
21 invokevirtual #27 <java/lang/StringBuilder.append>
24 pop
25 iinc 4 by 1
28 iload 4
30 sipush 10000
33 if_icmplt 18 (-15)
36 invokestatic #16 <java/lang/System.currentTimeMillis>
39 lload_1
40 lsub
41 lstore 4
43 getstatic #31 <java/lang/System.out>
46 lload 4
48 invokevirtual #35 <java/io/PrintStream.println>
51 return

Como vemos, es cierto que el compilador utiliza StringBuilder en vez de String. Sin embargo, la diferencia de rendimiento se debe a que la variable local s es un String, y en cada iteración se invoca a toString() para poder asignarlo, lo que nos crea un nuevo objeto String en cada iteración.

Veamos pues en qué caso esta optimización del compilador es útil.
Si realizamos sucesivas concatenaciones, en distintas sentencias, estamos en el mismo caso que el anterior. El resultado debe ser un String, por lo que se realizar diversas llamadas al método toString() que nos penalizan.

Si concatenamos cadenas en una misma sentencia, siendo éstas variables, entonces el compilador si usa StringBuilder, y en este único caso si es interesante usar el operador + para mejorar la legibilidad.

Como curiosidad, en caso de que sean constantes (por ejemplo, s += “a” + “a” + “a”;) en tiempo de compilación ya se optimiza (en el ejemplo, quedaría bytecode equivalente a s += “aaa”;).

Volviendo a nuestro caso de concatenación de variables en una misma sentencia, sea el siguiente fragmento de código:

        String t = "a";
        String s = new String();
        s += t + t + t;

Genera:

 4 ldc #22 <a>
 6 astore_3
 7 new #24 <java/lang/String>
10 dup
11 invokespecial #26 <java/lang/String.<init>>
14 astore 4
16 new #27 <java/lang/StringBuilder>
19 dup
20 aload 4
22 invokestatic #29 <java/lang/String.valueOf>
25 invokespecial #33 <java/lang/StringBuilder.<init>>
28 aload_3
29 invokevirtual #36 <java/lang/StringBuilder.append>
32 aload_3
33 invokevirtual #36 <java/lang/StringBuilder.append>
36 aload_3
37 invokevirtual #36 <java/lang/StringBuilder.append>
40 invokevirtual #40 <java/lang/StringBuilder.toString>
43 astore 4

Podemos comprobar que aquí si ahorramos las llamadas a toString(), y por tanto es igual de eficiente que usar StringBuilder y se mejora la legibilidad.

Resumiendo, veamos cómo debemos manejar las cadenas:

  1. Usa el operador + si se concatena en una misma sentencia. Es más legible y el bytecode generado es el mismo.
  2. Usa StringBuilder en otro caso.
  3. En este caso no hemos tenido en cuenta si diversos hilos acceden a la misma cadena. En caso de que eso pudiera ocurrir, debes usar StringBuffer. Es algo más lento que StringBuilder, pero es thread safe

Espero que os haya gustado, y que Bastian se dé por satisfecho. Por mi parte, sólo queda agradecerle el hacerme más amenos los viajes en autobús.