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!

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.

FindBugs

Las herramientas de análisis estático de código permiten encontrar fallos potenciales mediante búsquedas de patrones en el código.

La pionera en esto del análisis estático fue Lint, una herramienta que apareció en 1979 y estaba incluía en el propio compilador. Desde entonces Lint es usado como nombre genérico de este tipo de herramientas.

Puedes ver una lista de herramientas de análisis estático para distintas plataformas en la Wikipedia.

Había utilizado FxCop en el trabajo, y le eché un vistazo a JSLint con OpenLayers. Pero nunca me había parado a buscar una herramienta similar para aplicaciones Java.

Pues bien, recientemente encontré FindBugs. Y por supuesto, la he probado con ArgoUML.
FindBugs Logo

FindBugs es una herramienta opensource desarrollada por la Universidad de Maryland. Desarrollada en Java, tiene una interfaz simple pero efectiva.

Seleccionamos donde están nuestros jar y el código asociado y se pone a analizar el código (puede tardar un ratillo).

Create project

Nos muestra un árbol con los errores clasificados por categorías, mostrando para cada uno el código donde aparece y cómo debería mejorarse.

Analisis de Código

Gracias a esta herramienta se puede incrementar el rendimiento de una aplicación además de eliminar bugs potenciales. En las próximas semanas trabajaré en eliminar estos fallos en ArgoUML.

PD: Esto es posible porque desde ayer tengo permisos de escritura en todo el repositorio 🙂

Consumiendo servicios web con Apache AXIS

Consumir servicios web en Java puede ser tan sencillo como el hacerlo desde .NET.
Para ello vamos a utilizar Apache AXIS

En primer lugar, creamos nuestro servicio web ASP.NET. Tendrá esta pinta:

using System;
using System.Web.Services;
using System.Web.Services.Protocols;

namespace WebService1
{
    [WebService(Namespace = "http://tempuri.org/")]
    [SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service1 : System.Web.Services.WebService
    {
        [WebMethod]
        public Cotizacion ObtenerCotizacion (string code)
        {
            Cotizacion cot = new Cotizacion();
            cot.Code = code;
            cot.Value = 12.2f;
            return cot;
        }
    }

    public class Cotizacion
    {
        private string _code;
        public string Code
        {
            get { return _code; }
            set { _code = value; }
        }

        private float _value;
        public float Value
        {
            get { return _value; }
            set { _value = value; }
        }
    }
}

Como podéis ver, es un servicio muy simple, con un método ObtenerCotizacion que recibe el código de una empresa y devuelve un ficticio valor de cotización.
Vamos a ver cómo construir el proxy para utilizarlo desde Java.

AXIS trae una herramienta para generar el archivo de descripción WSDL desde un archivo Java, o el proceso contrario, crear el código Java desde un archivo WSDL.

Para ello, debemos ejecutar:

java org.apache.axis.wsdl.WSDL2Java <url del wsdl>

En nuestro caso,

java org.apache.axis.wsdl.WSDL2Java http://localhost:1871/Service1.asmx?wsdl

Esto nos genera un paquete con el namespace del servicio web, en nuestro caso org.tempuri, que contendrá varias clases:

  • Service1.java
  • Service1Locator.java
  • Service1Soap.java
  • Service1Soap12Stub.java
  • Service1SoapStub.java
  • Cotizacion.java

Una vez el proxy está creado por AXIS, todo es coser y cantar. Veamos como invocarlo:

package ConsumingWebServices;

import org.apache.axis.AxisFault;
import org.tempuri.Cotizacion;
import org.tempuri.Service1Locator;
import org.tempuri.Service1Soap;

public class Main {
    
    public static void main(String[] args) {
        try {  
            Service1Locator loc = new Service1Locator();
            Service1Soap port = loc.getService1Soap();
            Cotizacion res = port.obtenerCotizacion("MSFT");
            System.out.println("La cotizaci&Atilde;�&Acirc;&sup3;n de " +res.getCode() + 
                    " es " + res.getValue());
        }
        catch(AxisFault e) {
            System.err.println("FaultCode:"+e.getFaultCode()+"\\n");
            System.err.println("FaultReason:"+e.getFaultReason()+"\\n");
            System.err.println("FaultActor:"+e.getFaultActor()+"\\n");
            System.err.println("FaultRole:"+e.getFaultRole()+"\\n");
            System.err.println("FaultNode:"+e.getFaultNode()+"\\n");
        }
        catch (Exception oops) {
            System.err.println(oops.getMessage());
        }
    }
}

Si ejecutamos el ejemplo, la consola mostrará:
La cotización de MSFT es 12.2

First commit

ArgoUML

I had said nothing about this before, but my mentor and Linus Tolke (the leader of ArgoUML) had setup the environment for doing my work on Google Summer of Code. You can find it at http://argouml-sequence.tigris.org/ (I expect to update the main page soon).

I have no much time for working on this yet (I must finish MaCMAS), but I want to have some work made before May. So I began reading again the cookbook, I configured a new workspace with the ArgoUML code templates and their coding style rules and I start coding a little proof of concept. I created a new plugin, and I add a menu item to the ArgoUML menu bar. This worked fine.

After that, I played with enabling and disabling plugins, and I found that the UI had some problems with the menu bar. I add a call to updateUI in the enable/disable methods of my plugin and all worked fine. I think maybe this call must be at the add/remove method of the GenericArgoMenuBar class, I will suggest this to Bob tomorrow.

I have done the installation of the plugin manually. I must learn about Ant and Eclipse for doing this automatically, because we will need debugging on the future.

With this, Google Summer of Code has started for me 🙂


ArgoUML

No he dicho nada sobre esto en la entrada anterior, pero mi mentor y Linus Tolke (el lider de ArgoUML) han dispuesto el entorno para llevar a cabo mi trabajo en Google Summer of Code. Puedes encontrarlo en http://argouml-sequence.tigris.org/ (espero actualizar la página principal pronto).

No tengo mucho tiempo para dedicarle a esto todavía (debo terminar MaCMAS), pero quiero llegar con algún trabajo adelantado a mayo. Por eso he comenzado a releer el Cookbook, configurado un nuevo workspace con las plantillas de código de ArgoUML y sus reglas de estilo en el código y he empezado a programar una pequeña prueba de concepto. He creado un nuevo plugin, y he añadido un item a la barra de menú de ArgoUML. Funcionó a la primera.

Después de eso, jugueteé habilitando y deshabilitando plugins, y encontré que la UI tenía algunos problemas con la barra de herramientas. Añadí una llamada a updateUI en los métodos enable/disable de mi plugin y se arregló. Pienso que quizá esta llamada debería estar en los métodos add/remove de la clase GenericArgoMenuBar, se lo sugeriré a Bob mañana para ver que piensa.

He hecho la instalación del plugin manualmente. Debo aprender acerca de Ant y Eclipse para hacer esto automáticamente, porque necesitaremos depurar en el futuro.

Con esto, Google Summer of Code ha empezado para mí 🙂