Reflection

23 respostas
Paulo_Faulstich

Olá pessoal.

Estou tendo um problema para carregar a classe através de reflection.

Por exemplo. Desenvolvi um código que pegava uma classe que estava no mesmo pacote que eu utilizava a reflection portanto era estatico.

Class cls = Calculadora.class;

Dessa forma consegui fazer tranquilo, o problema é que agora quero carregar essa classe através de um JFileChooser navegando pelos diretórios do SO.

Com essa linha de código consigo pegar todo o caminho do arquivo que eu abri com o JFileChooser.

File file = fc.getSelectedFile();
System.out.println("path = " + file.getPath());

Que retornou C:\paulo … o caminho onde esta o meu .class inclusive.

Agora vem a pergunta.

Como faço para ao invés de passar o Calculadora.class estatico como era antes eu carregue o .class que estou selecionando com o JFileChooser?

Vlw. :shock:

23 Respostas

T

Você já ouviu falar de Class.forName? Ele não serve só para carregar drivers JDBC.

Veja a documentação de java.lang.Class ( http://java.sun.com/javase/6/docs/api/java/lang/Class.html )

victorwss

Isso ajuda?

Class<?> classeFoo = Class.forName("br.com.exemplo.pacote.subpacote.Foo");
Paulo_Faulstich

Dai pessoal, obrigado pelo ajuda continuo tentando.

File file = fc.getSelectedFile();
Class cls = Class.forName(file.getPath());

pelo que vi o metodo forName em sua implementação substitui as barras do SO por . e concatena com o .class só que esse meu getPath ta pegando tudo inclusive o nome da classe como acho que é por isso que ele não esta encontrando a classe.

Segue implementação do metodo forName que mostra como ele pega a classe

protected Class<?> findClass(final String name)

throws ClassNotFoundException

{

try {

return (Class)

AccessController.doPrivileged(new PrivilegedExceptionAction() {

public Object run() throws ClassNotFoundException {

String path = name.replace(., /).concat(".class");

Resource res = ucp.getResource(path, false);

if (res != null) {

try {

return defineClass(name, res);

} catch (IOException e) {

throw new ClassNotFoundException(name, e);

}

} else {

throw new ClassNotFoundException(name);

}

}

}, acc);

} catch (java.security.PrivilegedActionException pae) {

throw (ClassNotFoundException) pae.getException();

}

}

Retorno system out para file.getPath() --> path = C:\Paulo\ApplicationsNetBeans\Tcc\build\classes\br\com\tcc\bean\Classe.class

To afim de dar um replace tirando o .class que ta vindo junto no meu path mas acho que é muita gambiarra mas voyu testar para ver se é isso que ta fazendo se perder e não achar a classe.

Esta dando uma Exception.

java.lang.ClassNotFoundException: C:\Paulo\ApplicationsNetBeans\Tcc\build\classes\br\com\tcc\bean\Classe.class java.net.URLClassLoader$1.run(URLClassLoader.java:200)

Sigo tentando.

Vlw

sergiotaborda

O Class.forName só funciona se a classe está no classpath.
Se a classe está em outro lugar qualquer ela tem que ser adicionada ao classepath antes.
Isso pode ser feito com um Classloader feito especialmente para isso.

victorwss

sergiotaborda:
O Class.forName só funciona se a classe está no classpath.
Se a classe está em outro lugar qualquer ela tem que ser adicionada ao classepath antes.
Isso pode ser feito com um Classloader feito especialmente para isso.

Ou seja: Se a sua classe não estiver no classpath, você pode criar uma subclasse de java.lang.ClassLoader que acha a classe desejada para você. Não sei direito como os ClassLoaders funcionam, mas eu sei que é mais simples do que parece: normalmente o problema se restringe a encontrar a classe a ser carregada. A parte de carregá-la na memória e inicializá-la já é herdada do ClassLoader padrão.

Paulo_Faulstich

Vou dar uma lida no classLoader.

Sigo tentando.

Vlw

T

Procure por java.net.URLClassLoader, ele aceita diretórios e arquivos .jar.

Paulo_Faulstich

Vou dar uma olhada.

Qualquer novidade retorno.

Vlw.

Paulo_Faulstich

Dae pessoal, fiz mais um pedaço do meu código.

Acho que agora é algum detalhe que está faltando.

Alguém que conheça ClassLoader() pode dar um help?

private void btnFindActionPerformed(java.awt.event.ActionEvent evt) {

fc = new JFileChooser();

fc.setDialogTitle(Find the Class);

fc.addChoosableFileFilter(new ImageFilter());

fc.setFileSelectionMode(JFileChooser.FILES_ONLY);

int returnVal = fc.showDialog(Layout.this, Open);
if (returnVal != JFileChooser.APPROVE_OPTION) {
        System.out.println("Usuario cancelou a abertura do arquivo ");
    } else {
        try {
             URL[] urlsToLoadFrom = new URL[] {new URL("c:\\Calculadora")};
            
            URLClassLoader loader = new URLClassLoader (urlsToLoadFrom) ; 
                            
            //System.out.println("Nome da classe = " + loader1.getClass()); 
            
            /* Pega o nome da Classe que foi selecionada */
            String sPath = fc.getSelectedFile().getName();                                 
            
            /* Retira o .class da classe selecionada */
            sPath = sPath.substring(0, sPath.indexOf('.'));   

            Class cls = Class.forName (sPath, true, loader) ;
       } catch (MalformedURLException ex) {
            Logger.getLogger(Layout.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(Layout.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(Layout.class.getName()).log(Level.SEVERE, null, ex);
        }

}

Ta ocorrendo uma exception:

java.net.MalformedURLException: unknown protocol: c

Acho que esta mascarando o erro.

Acho que falta pouco.

T

Se a URL se referir a um JAR, ela deve ser criada mais ou menos assim:

URL url = new URL (“jar”, “”, “file:c:/diretorio/arquivo.jar!/”);

(note o file: e o “!/” depois do nome do jar. Além disso, mesmo que for em Windows, troque “” por “/” para evitar alguns probleminhas.)

Se for um diretório, use:

URL url = new URL (“file”, “”, “c:/diretorio/”);

(note que sempre termina com “/”)

OK?

Paulo_Faulstich

É um arquivo.class

que depois eu vou utilizar reflection em cima desse .class

T

Então você precisa especificar para o URLClassLoader o diretório onde está esse .class. OK?
Por exemplo, digamos que você tenha uma classe “br.com.exemplo.Calculadora” que está no arquivo “C:\Calculador\br\com\exemplo\Calculadora.class”. Obviamente você tem de passar o diretório “C:\Calculadora” (e trocando os “” por “/”, como de costume).

Paulo_Faulstich

Dae cara, primeiramente obrigado pela força.

Encontrei mais um problema agora:

URL[] urlsToLoadFrom = new URL[]{new URL("file", "", "C:/Paulo/ApplicationsNetBeans/Tcc/build/classes/")};

URLClassLoader loader = new URLClassLoader(urlsToLoadFrom);

Class cls = Class.forName("br.com.tcc.control.Calculadora", true, loader);

Quando eu passo o primeiro parametro estatico do Class.forName("br.com.tcc.control.Calculadora") tudo fica lindo funcionando.

Pergunta tem algum get() magico que pegue o pacote que a minha classe se encontra? Do modo que eu estava fazendo anteriormente eu só pegava o nome da classe ocorrendo uma exception.

Ex:

fc = new JFileChooser();

URL[] urlsToLoadFrom = new URL[]{new URL("file", "", "C:/Paulo/ApplicationsNetBeans/Tcc/build/classes/")};

URLClassLoader loader = new URLClassLoader(urlsToLoadFrom);

String sPath = fc.getSelectedFile().getName();

/* Retira o .class da classe selecionada */

sPath = sPath.substring(0, sPath.indexOf(’.’));

//Class<?> cls = Class.forName("br.com.tcc.control.Calculadora");

Class cls = Class.forName(sPath , true, loader);

Fico no aguardo

T

A sua pergunta, valendo $1.000.000, é “eu tenho um .class. Qual é o nome completo da classe contida dentro dele”?

Se você precisa de tal tipo de informação, é hora de usar um pacote como o ASM (se não me engano em http://asm.objectweb.org ) , que consegue fazer tal coisa.

victorwss

O método deprecated defineClass(byte[] b, int off, int len) de ClassLoader talvez quebre esse galho para você. Tudo que você precisa fazer é abrir o arquivo .class, enfiar todo o conteúdo dele em um array de bytes e invocar este método.

Mas atenção. Esta não é solução ideal porque confia em código deprecated, o que nunca é bom. Isso é só um quebra galho.

T

Pelo que entendi, ele quer abrir um arquivo .class arbitrário, e instanciar uma classe que esteja dentro dele. Para ele conseguir isso, é necessário determinar o nome correto da classe, o que só é possível se abrirmos o arquivo .class com o ASM, por exemplo.

Paulo_Faulstich

Dae pessoal, andei dando uma lida na documentação da ASM inclusive baixei a lib e fiz alguns testes.

Realmente com a ASM eu consigo pegar o nome completo da minha classe junto com o classpath dessa forma.

String teste = Type.getInternalName(“Calculadora.class”);

o retorno se eu der um print nessa string teste é o full name: “br.com.teste.Calculadora”.

Legal realmente veio o nome completo da classe.

Só que o problema é que continua estatico eu tendo que passar a minha Calculadora.class.

Se tivesse um método que tranformace uma string “Calculadora.class” para um objeto do tipo Class resolveria o meu problema. Pq a string com o nome da classe eu tenho com o JFileChooser.

Enfim bateu na trave a asm :smiley:

Se alguém ainda tiver alguma dica …

Vlw.

victorwss

Acho que agora é só juntar as partes.
O ASM obtém o nome da classe dentro do arquivo.
E daí o Classloader e/ou o Class.forName carrega a classe.

Paulo_Faulstich

Ainda não.

Pois o ASM traz o class path da classe só que eu tenho que passar o nome da classe estatico do mesmo jeito, dai não adianta

Tipo.

Eu só consigo pegar a o nome da classe como string com o JFileChooser eu precisava que ele fosse uma classe.

Ou consiguir instanciar um Class apartir da string que eu tenho.

O ASM faz assim.

String teste = Type.getInternalName(Calculadora.class);

dentro da string teste vai ter o caminho da classe o classpath --> "br.com.teste.Calculadora" só que o problema é que eu tenho que passsar um class para este método e um class eu não tenho, tenho apenas a String com o nome da classe "Calculadora.class" String só isso.

Teria que tranformar esta string para um class que dai eu poderia fazer a minha aplicação dizamica.

Continuo tentando.

Vlw.

victorwss

Fiz meio no improviso, mas vê se resolve.

public class MeuClassLoader extends ClassLoader {
    public MeuClassLoader() {
        super();
    }

    public Class&lt;?&gt; carregarClasse(File arquivo) {
        FileInputStream stream = null;
        byte[] bytes = null;
        try {
            stream = new FileInputStream(arquivo);
            byte[] bytes = new byte[(int) file.length()];
            stream.read(bytes);
        } catch (IOException e) {
            // Faz qualquer coisa para tratar.
        } finally {
            try {
                if (stream != null) stream.close();
            } catch (IOException e) {
                // Faz qualquer coisa para tratar.
            }
        }

        return super.defineClass(converteNome(arquivo.getName()), bytes, 0, bytes.length);
    }

    private String converteNome(String pathName) {
        pathName = pathName.replaceAll(".class$", "");
        pathName = pathName.replaceAll("/", ".");
        pathName = pathName.replaceAll(".class", "");
        return pathName;
    }
}

Daí se isso não der certo, você pode tentar fazer alguma maluquisse no converteNome para ele achar o nome correto do pacote. Ou então fazer alguma coisa que na base da tentativa e erro vá quebrando o nome do arquivo em partes menores até conseguir.

Paulo_Faulstich

Dae Rapaziada!

Segue Fonte funcionando!

Vlw Pela força.

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package br.com.tcc.view;

/**
 *
 * @author paulo
 */
import java.io.File;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import java.io.OutputStream;

import java.lang.reflect.Method;

import java.net.URL;
import java.net.URLClassLoader;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Teste {

   public Teste() {
   }

   public static void main(String[] args) throws Exception {
       
       // diretorio onde estah o .class - Que no meu caso eu pego através do arquivo que foi selecionado com o JFileChooser
       String path = "C:\\Development\\ApplicationsNetBeans\\TesteSoftware\\build\\classes\\br\\com\\tcc\\control\\Calculadora.class";              
       String classFormat = formatNameClass(path); 
       String pathFormat = formatCaminhoPath(classFormat + ".class", path);
       File file = new File(pathFormat);
       
       URL[] urls = { file.toURL() };
       URLClassLoader classloader =
       new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
       
       try {
           Class cls = classloader.loadClass(classFormat);
       } catch(NoClassDefFoundError e) {
           Pattern p = Pattern.compile(":\\s.*");
           Matcher m = p.matcher(e.getMessage());
           if(m.find()) {
               int i = m.start();
               int f = m.end();              
               
               String pathPackage = e.getMessage().substring(i + 2, f - 1);
               String str = pathPackage.replace("/", ".");
              
               File dir = new File(pathFormat + "/" + pathPackage);
               
               if(dir.mkdirs()) {
                   System.out.println("criou");
               } else {
                   System.out.println("nao criou");
               }
               
               String nameClass = "/" + classFormat + ".class";
               
               if(copyFile(pathFormat + classFormat,
                   pathFormat + "/" + pathPackage + ".class")) {
                   System.out.println("copiou");
               } else {
                   System.out.println("nao copiou");
               }
               
               Class cls = classloader.loadClass(str);
               System.out.println(cls.getSimpleName());
               Method[] mt = cls.getDeclaredMethods();
               
               for(Method t : mt) {
                   System.out.println(t.getName());
               }
           }
       }
       
   }

   public static String formatNameClass(String path) {      
       
       path = path.replace("\\", "/");       
     
       String[] newPath = path.split("/");
       String classFormat = newPath[newPath.length -1];
       
       formatCaminhoPath(classFormat, path);
       
       classFormat = classFormat.replaceAll(".class$", "");
       classFormat = classFormat.replaceAll("/", ".");
       classFormat = classFormat.replaceAll(".class", "");
       
       return classFormat;
   }
   
   public static String formatCaminhoPath(String nameClasse, String path) {
       
       String pathFormat = path.replace(nameClasse, "");            
       
       return pathFormat;
   }
   
   public static boolean copyFile(String inFile, String outFile) {
       
       InputStream is = null;
       OutputStream os = null;
       byte[] buffer;
       boolean success = true;
       
       try {
           is = new FileInputStream(inFile);
           os = new FileOutputStream(outFile);
           buffer = new byte[is.available()];
           is.read(buffer);
           os.write(buffer);
       } catch(IOException e) {
           success = false;
       } catch(OutOfMemoryError e) {
           success = false;
       } finally {
           try {
               if(is != null) {
                   is.close();
               }
               if(os != null) {
                   os.close();
               }
           } catch(IOException e) {
           }
       }
       return success;
   }
}
Paulo_Faulstich

Esqueci de comentar, criei o metodo copyFile( ) pq pode ocorrer do arquio .class estar em um diretorio errado ou até mesmo nem existir os diretórios do classpath do rquivo .class.

Esta ferramenta que eu estou fazendo é para automatização de testes.

Da forma com que estou fazendo o testador não precisa ter nada na maquina dele apenas o .class

Então precisei criar os diretórios do classpath do arquivo .class que esta sendo testado.

Ex. Calculadora.class para br.com.teste.Calculadora.class a segunda estrutura de diretórios foi a que eu precisei criar.

Não utilizei a melhores praticas ainda mas já funciona.

Vlw pessoal.

victorwss

É, está bem feio e gambiarrento. Mas já chegou em algo que funciona, daí é só lapidar ele.

Criado 23 de abril de 2008
Ultima resposta 28 de abr. de 2008
Respostas 23
Participantes 4