Tuesday 5 August 2008

Usando ClassLoaders... extendendo... hot-deploy

Ae pessoal, se voces um dia precisarem fazer um hot-deploy, aqui vai umas dicas e um programa de hot-deploy bem simpes, sem seguranca.
Primeiro vamos criar a classe que ira ser carregada, ira se chamar Hello para nao perdermos o vicio. rsss
Hello.java
public class Hello {
public Hello() {
System.out.println("Hello versão 3 Turbinada!!!!!!");
}
}

Para voce conseguir fazer este funcionar, e necessario que voce exporte a classe como um .jar, que no meu exemplo se chama Hello.jar. Se preferir coloque em outro projeto a classe que ira extender a ClassLoader, para que voce possa ficar alterando a Hello.java e regerando o Hello.jar, fazendo isso voce ira conseguir ver a classe sendo carregada. O que fiz foi ficar mudando a string do println() para ficar vendo quando era carregado a nova classe.
Basicamente a voce ira precisar sobre-escrever o metodo findClass, que devera "ler" o arquivo como binario o aquivo, e dar um super.defineClass... para dizer a JVM que ira carregar aquele arquivo binario que vc leu e esta armazenado no Class.
Mas para voce "ler" o arquivo binario e necessario escrever um metodo que ira suprir o metodo findClass com o arquivo em modo binario.
Aqui esta o exemplo que fiz:
ClassLoaderTest.java
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ClassLoaderTest {
public static void main(String args[]) {
try {
URL url = new URL("file:///C://dev//projects//TestClassLoader//bin//test//Hello.jar");
while(true) {
Thread.sleep(10000);
ClassLoader cl = URLClassLoader.newInstance(new URL[] {url});
Object obj = cl.loadClass("test.Hello").newInstance();
System.out.println(obj.toString());
}
}
catch(Exception ex) {
ex.printStackTrace();
}
}

So isto ja serviria para carregar a classe, agora o correto seria voce extender a classe, ClassLoader ou URLClassLoader e sobreescrever o metodo findClass, para que ele carregue a(s) sua(s) classe(s). Mas lembre-se que para se fazer isso e necessario que voce "leia" o arquivo como binario, aqui vai um exemplo desses dois metodos

@Override
public Class findClass(String nameClass) throws ClassNotFoundException {
// lança ClassNotFoundException se o download falhar:
byte[] bytes;
Class c = null;
try {
bytes = readBinaryClass(nameClass);
c = super.defineClass(nameClass, bytes, 0, bytes.length);
} catch (FileNotFoundException ex) {
ex.printStackTrace();
Logger.getLogger(ClassLoaderTest.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
ex.printStackTrace();
Logger.getLogger(ClassLoaderTest.class.getName()).log(Level.SEVERE, null, ex);
}
return c;
}

public byte[] readBinaryClass(String nameClass) throws FileNotFoundException, IOException {
int n = 0;
Byte singleByte = 0;
byte[] readedBytes = null;
Integer byteBin = 0;
List fileBin = new ArrayList();
File classBin = new File(nameClass);
BufferedInputStream in = new BufferedInputStream(new FileInputStream(classBin));
while ((byteBin = in.read()) != -1) {
readedBytes[n] = Byte.parseByte(byteBin.toString());
if (in.read() != -1) {
n++;
}
}
return readedBytes;
}
}

Desculpem mas aqui nao foi possivel fazer a identacao.
Mas seguindo, voces poderao notar que leio o arquivo, que podera ser um .jar ou um .class como binario e retorno para o Class c = ... a classe e que por sua vez o ira carregar a nova classe no heap da JVM, descartando a antiga.
E um procedimento simples, mas que encontrei muita dificuldade em encontra-lo na web, algo simples assim. Achei alguns tutoriais um no GUJ muito bom, mas falta um exemplo pronto para que possamos testar e irmos adaptando ao que queremos.
Espero ter ajudado, e aceito comentarios... bons e ruins.

No comments: