Hola, Recientemente he estado tratando de encontrar una solución a un problema que me parece interesante comentar. El problema en cuestión tiene que ver con cómo empaquetar tareas Ant de forma que autoincluyan sus dependencias, y así no obligar a los proyectos que las usen a conocerlas y declararlas. Existe una librería, llamada one-jar [1], que permite encapsular en un solo jar tanto las clases de la aplicación como sus módulos, de forma que se pueda ejecutar sin necesidad de definir el classpath, con java -jar nombre-del-jar Este mismo principio es el que perseguía, pero no para aplicaciones de linea de comando sino tareas Ant. En lugar de llamar a un main(), sería suficiente instanciar una clase y dejar que Ant llame a su método execute(). Además, para hacerlo genérico, mi intención era crear una sola clase proxy, que fuera capaz de leer del manifest la tarea Ant real, y la instanciara dentro de un classloader que fuera capaz de ver las dependencias de dentro del propio jar. Al ponerme con ello, me di cuenta de que no podía crear un proxy estándar, dado que Ant instancia las clases por reflexión, y al obtiener una referencia a una clase Task directamente, no puedo envolverla de ninguna forma. La necesidad de usar un proxy se debe a que la idea era que esa clase fuera capaz de instanciar cualquier tarea Ant, independientemente de sus atributos o elementos. Y no poder envolverla hacía a priori imposible que Ant pudiera encontrar los mismos métodos en la clase proxy que en la original (esos métodos los va llamando Ant en función del contenido del build.xml). La única opción viable sería utilizar BCEL [2] para añadir métodos dinámicamente a la clase proxy conforme a la API pública de la tarea Ant original, y cuyo código fuera simplemente llamadas a los métodos homónimos originales. Una vez averiguo cómo generar estos métodos con BCEL (con la ayuda de la clase BCELifier), compruebo de que Ant no los encuentra. Investigo más y saco la conclusión de que el resultado de mis modificaciones es simplemente el bytecode de una clase que se llama igual que el proxy original, pero que no lo reemplaza. Para sustituir uno por otro, en algún foro comentan que hay que recurrir a una solución poco elegante: llamar al método ClassLoader.defineClass(String name, byte[] bytecode, ...), con el nuevo bytecode. El problema es que ese método está protegido, así que hay que tirar de AccessibleObject.setAccessible(true). Acepto a regañadientes el precio a pagar, y al probarlo compruebo que lamentablemente no funciona: el ClassLoader dice que la clase ya está cargada, y lo hace desde dentro de un método nativo. Así que la conclusión que saco ahora mismo es que se puede cambiar el bytecode en caliente, siempre que las clases no hayan sido ya leídas y cargadas. En mi caso, el código que trato de cambiar es el propio código que se está ejecutando (si bien no el mismo método). Podría investigar más y analizar si en realidad se puede hacer utilizando javaassist o aspectj, bien para utilizarlos, o bien para emular el procedimiento que utilizan. Otra opción sería estudiar si el classloader de Ant permitiría devolver el bytecode alternativo a pesar de que su clase padre vea el original. Temporalmente, he decidido sacrificar la flexibilidad, definir los setters explícitamente para la tarea Ant con la que estoy trabajando, y ver si al menos el mecanismo de carga de clases de One-Jar funciona correctamente dentro de Ant. Este es un ejemplo de un proyecto cuyas dificultades difícilmente se pueden prever de antemano, y por tanto estimar. Pero es interesante y estimulante si no tienes (o te creas) presión por encontrar una solución aceptable. Un saludo, Jose. [1] http://one-jar.sf.net [2] http://jakarta.apache.org/bcel
Attachment:
signature.asc
Description: This is a digitally signed message part.