jueves, 17 de mayo de 2012

Manipulacion de bytecode con Javassist. Incluir funcionalidad a las anotaciones.


La reflexión es el método tradicional que ofrece Java para hacer  programación dinámica donde el modo de instanciar objetos y donde y cuando usar los métodos de estas instancias no se conoce en tiempo de compilación. Pero la reflexión presenta dos problemas:

-       No deja manipular el código. Es decir se puede, por ejemplo, definir un método nuevo en tiempo de ejecución.
-       Es muy lento (Mirar articulo http://www.ibm.com/developerworks/java/library/j-dyn0610/)

La manipulación de bytecode permite lo mismo que la reflexión, además se puede incluir código nuevo en tiempo de ejecución y es más rápida que la reflexión.

Existen varios frameworks para manipular bytecode. Yo recomiendo Javassist porque es prácticamente como la reflexión nativa de Java pero de manera ampliada.

El siguiente ejemplo ilustra de manera simple como usar javassist para potenciar el uso de una anotación.

public @interface PonleValor
{
   
}
class Hello {
    
     @PonleValor
     private String valor;
    
    
    public void say() {
        System.out.println("Hello es " + valor);
    }
}


public class Test {
   

     public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("es.trap.Hello");
       
        for (Object field:cc.getClassFile().getFields()){
           
           
           FieldInfo info = (FieldInfo)field;
          
           for (Object atrib :info.getAttributes()){
              
              
              
               if (atrib instanceof AnnotationsAttribute){
              
                   AnnotationsAttribute anota = (AnnotationsAttribute)atrib;
                  
                   String name = anota.getAnnotations()[0].getTypeName();
                   
                   if (name.equals(  "es.indra.trap.PonleValor"))                  
                       cc.getConstructors()[0].insertAfter( "{" + info.getName()+  "= \"Ahora vale algo\"; }" );
               }
                  
               
           }
           
        }
      
        Class c = cc.toClass();
        Hello h = (Hello)c.newInstance();
        h.say();
    }


}

Como se puede ver en ejemplo, si instanciamos la clase Hello con Javassist (como si se hiciera con reflexión) y una de sus variables tiene la anotación PonValor entonces se le pondrá un valor de forma automática.

El resultado si se ejecuta este programa es:

Hello es Ahora vale algo

martes, 8 de mayo de 2012

Obtener peticion HTTP en Java

Una peticion HTTP no es mas que un String que envia un emisor para probocar una respuesta en un receptor.

Si se quiere ver una peticion HTTP lo mas facil es enviarla a un puerto donde este un socket escuchando.

Por ejemplo:

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws Exception {
       ServerSocket server = new ServerSocket(8080);

       Socket so = server.accept();

       DataInputStream ent = new  DataInputStream(so.getInputStream());

       byte [] sal = new byte[256];
       while (ent.read(sal) != 0)
        System.out.println(new String(sal));
      }
}


Si se hace la peticion (por ejemplo con el SOAPUI) a localhost:8080/WS la respuesta seria:

POST /WS HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Host: localhost:8080
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: "
"
Content-Length: 473

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<wsHola xmlns="http://es.indra.transporte.webservices.cscreplacement.core">

<respuesta>Hola Mundo</respuesta>

</wsHola>
</soapenv:Body>
</soapenv:Envelope>

Y ya esta.