Criando classes em tempo de execução

Olá galera, bom eu to meio sem tempo então vou logo ao assunto. Alguem sabe criar uma classe em tempo de execução? por exemplo, abro uma conexão com o repositorio de dados e pego a metadata da base, dai verifico os atributos de uma determinada entidade da base e com base nisso, em tempo de execução, crio uma classe referente aquela entidade da base com os seus atributos e todo o resto.

Quem sabe fazer isso, por reflexão já me falaram que da mas não quiseram me da a dica então sei que entre vos alguem sabe, se possivel gostaria que me ajuda-se com isso.

Att,

Você quer criar uma classe ou um objeto em tempo de execução?

Uma classe ainda não dá… mas um objeto é possível fazer em tempo de execução.

[quote=balthazar]Olá galera, bom eu to meio sem tempo então vou logo ao assunto. Alguem sabe criar uma classe em tempo de execução? por exemplo, abro uma conexão com o repositorio de dados e pego a metadata da base, dai verifico os atributos de uma determinada entidade da base e com base nisso, em tempo de execução, crio uma classe referente aquela entidade da base com os seus atributos e todo o resto.
[/quote]

É possivel criar definições de classes em runtime via manipulação de bytecode. Bibliotecas como a Javassist fazem isso. Só que não se pode criar uma classe do zero. Ou se implementa uma interface pre-definida ou se extende um classe pre-definida.
Por outro lado, mesmo que vc pudesse criar uma classe me runtime, seria inutil Para criar o programa vc não teria como se referir a essa classe (pois ela não está definia) então estaria sendo se referindo a alguma representação da classe que vc sinda não conhece ou usando reflection constantemente. Ou seja, é inutil construir uma classe so zero em runtime

Bem, eu já fiz algo parecido, não sei se funciona para ambiente web, mas para desktop era normal.

Primeiro eu criava um arquivo texto com o código que eu queria e depois
usava as classes de compilação do Java para compilar minha classe e por último carregava a mesma. Nessa minha classe eu tinha um método que fazia o que eu queria, então usando reflexão eu invocava o mesmo.

No Java 1.6 a API de compilação/carga é documentada (e é diferente da do java < 1.6), o que não acontecia nas versões anteriores.

Eu tenho uma classe que criei, faz uns 2 anos já, que usa a API de compilação do Java 1.5.

[code]
/**

  • Classe que define os métodos de compilação e
  • execução do código que será gerado e compilado.
  • @author David Buzatto
    */

import com.sun.tools.javac.*;

import java.io.;
import java.lang.reflect.
;
import java.net.*;

import javax.swing.*;

public class Engine {

public Engine() {
	
	// inicializa o contador de classes
	contadorClasses = 0;
	
}

// método que realiza a compilação do código inserido
public boolean compilar( String codigo ) {
	
	// criando código da classe
	String codClasse = 
		  "/**\n"
		+ " * Classe gerada dinamicamente.\n"
		+ " *\n"
		+ " * @author David Buzatto\n"
		+ " */\n\n\n"
		+ "import static java.lang.Math.*;\n\n"
		+ "import java.math.*;\n\n\n"
		+ "public class Expressao" + contadorClasses + " {\n\n"
		+ "    public double calcular( double x ) {\n\n"
		+ "        return " + codigo + ";\n\n"
		+ "    }\n\n"
		+ "}";
		
	try {
		
		// deixa um log da compilacao num arquivo chamado logCompilacao.txt
		PrintWriter saida = new PrintWriter(
			new FileWriter( "logCompilacao.txt" ) );
		
		// grava o codigo-fonte no disco
		String arquivoFonte = "Expressao" 
			+ contadorClasses + ".java";
		FileWriter writer = new FileWriter( arquivoFonte );
		
		//grava no arquivo o código
		writer.write( codClasse );
		writer.close();
		
		// compila o código gerado,
		// saida é onde será gravada a saída do compilador
		int resultadoCompilacao = Main.compile(
			new String[] { arquivoFonte }, saida );
		
		if ( resultadoCompilacao == 0 ) {
			
			return true;
			
		} else {
			
			// lê o arquivo de resultados e imprime na tela
			BufferedReader resultado = new BufferedReader(
				new FileReader( "logCompilacao.txt" ) );
				
			String linha;
			
			while( ( linha = resultado.readLine() ) != null ) {
				
				System.out.println( linha + "\n" );
				
			}
			
			saida.close();
			
			return false;
			
		}
		
	} catch( IOException exc ) {
		
		System.out.println( "Erros ao gravar arquivo: \n" + exc.getMessage());
		exc.printStackTrace();
		
		return false;
		
	}
	
}

// executa a classe, selecionando o método de cálculo
public double executar( double x ) {
	
	double resultado = 0;
	
	try {
		
		URLClassLoader ucl = URLClassLoader.newInstance(
			new URL[] {
			getClass().getResource( "Empressao" + contadorClasses ) } 
		);
		
		Class classe = ucl.loadClass( "Empressao" + contadorClasses );
		
		// cria instância da classe
		Object instancia = classe.newInstance();
		
		// obtém o método desejado
		Method metodoCalcular = classe.getMethod( 
			"calcular", double.class );
		
		// invoca o método, passando como parâmetro 
		// uma instância da classe que ele pertence
		resultado = ( Double ) metodoCalcular.invoke( 
			instancia, x );
		
		/*// referência a classe
		Class classe = Class.forName( "Expressao" 
			+ contadorClasses );
		
		// cria instância da classe
		Object instancia = classe.newInstance();
		
		// obtém o método desejado
		Method metodoCalcular = classe.getMethod( 
			"calcular", double.class );
		
		// invoca o método, passando como parâmetro 
		// uma instância da classe que ele pertence
		resultado = ( Double ) metodoCalcular.invoke( 
			instancia, x );*/
	
	} catch ( Exception exc ) {
		
		exc.printStackTrace();
		
	}
	
	// incrementa o contador de classes
	contadorClasses++;
	
	return resultado;
	
}

// conta quantas classes foram compiladas e carregadas
private int contadorClasses;

}[/code]

Essa implementação tem um problema, pois toda vez que uma classe é gerada/compilada/carregada a mesma fica carregada na máquina virtual… Tenta dar uma pesquisada em como fazer a “descarga” da classe.

Até mais!

[quote=davidbuzatto]Tenta dar uma pesquisada em como fazer a “descarga” da classe.[/quote]Puts, eu não aconselho. O cara, pra fazer o quê você está sugerindo, vai ter que mexer com gc e o classloader. Isso pode complicar muito e, IMO, não tem nada a ver com o problema dele.

Cara, você não poderia simplificar não? Tipo, construir seus sqls dinamicamente com os dados obtidos no metadata não seria suficiente?

Dependendo do que você quiser fazer, pode usar o seguinte:

  • Criar uma classe dinamicamente como o Buzzato indicou (criando um fonte Java e o compilando);
  • Criar uma classe a partir dos bytecodes (usando algum framework como o ASM, http://asm.objectweb.org )*;
  • Ou simplesmente criar um Map &lt String, Object &gt mapeando o nome do campo ao seu valor, e isso não envolve a criação de novas classes.

Se o seu problema fosse "quero criar uma classe dinamicamente que implementa determinada interface", poderia usar a classe java.lang.Proxy.

  • Uma vez eu experimentei fazer isso para criar um "Comparator" genérico, usando ASM. É legal mas dá trabalho demais, e é meio chato de fazer manutenção. Se você sabe que seu código deverá sofrer bastante manutenção posterior, nem tente usar uma solução dessas.

[quote=thingol]- Criar uma classe dinamicamente como o Buzzato indicou (criando um fonte Java e o compilando);
[/quote]
mas ainda terá o problema da “descarga” da classe e suas dependências!
O ASM resolve isso?

Olá galera! bom, de repente eu esteja tentando fazer da maneira mas difícil ou complexa. Bom, vou deixar essa questão para mais tarde só que agora quero mostrar a vocês um outro problema que estou tendo. :roll:

Primeiramente eu to criando um objeto que e constantemente monitorado pelo sistema e caso seja executado qualquer método setter desse objeto, ele deve receber uma modificação de estado. O estado desse objeto e mapeado em uma classe Enum e por default o estado e clear mas caso ocorra um setter ele deve receber um dirty nesse estado.

Bem, eu construí um código de demonstração para expressa esse problema. Peguem e executem. Debuguem no para perceber qual o problema. 8)

Mas de ante-mão irei dizer. O método main retorna um objeto monitoravel utilizando o metodo Proxy_Test.create e altera o método setnome desse objeto de modo que o Proxy, através do metodo invoke, capture essa alteração. Daí ele realiza um teste simples para saber se o método executado e ou não um setter (essa e uma forma de verificação bem simples). Caso seja, o proxy deve altera o estado o objeto que esta sendo monitorado, entretanto, quando chega em setStateMethod.invoke(proxy, parameters) que e justamente a linha que executa a alteração do estado, essa alteração não ocorre o laço retorna para o if e o processo começa todo novamente. :oops:

Minha pergunta e bem simples. Onde ta meu erro? O que to fazendo que ta saindo errado? Ou melhor, quem consegue corrigir isso ai? :wink:

Como visto, o código abaixo ta completo ou pelo menos eu espero que esteja (qualquer coisa posso passá-lo novamente) e pode ser executado direto debugando pelo main e indo pelo invoke que e o cara que não deixa passar. Olhem abaixo…

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class Proxy_Test implements InvocationHandler, Serializable{
	
  protected Class dominioClass;
	

  protected Proxy_Test(Class dominioClass){
       this.dominioClass = dominioClass;
  }
	

  public static Object create(Class dominioClass){	
      return Proxy.newProxyInstance(dominioClass.getClassLoader(), new Class[] {	dominioClass  }, new Proxy_Test(dominioClass));
  }


  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable, Exception {
      String methodName = method.getName();
		
      // verifica se e um metodo set e caso seja entao muda
      // o estado do objeto proxy
      if(methodName.startsWith("set")) {
			
         Class[] paramTypes = { EnumState.class };
         Object[] parameters = { EnumState.osDirty };
			
         // pega o metodo setsState do objeto proxy
         Method setStateMethod = proxy.getClass().getMethod("setState", paramTypes);			

         // executa o metodo setState passando o parametro desejado
         setStateMethod.invoke(proxy, parameters);		
		
         /*
         * nesse ponto e executado o getState para verificar se ele realmente foi modificado para EnumState.osDirty	  
         Method getStateMethod = proxy.getClass().getMethod("getState");
         System.out.println(getStateMethod.invoke(proxy));
         */
      }
      return null;
  }
	
  public static void main(String args[]) {
      Usuario b = (Usuario) Proxy_Test.create(Usuario.class);    	
      b.setNome("bobo da corte");

      System.out.println(b.getNome());		
      System.out.println(b.getState());
  }

  public enum EnumState{
     osClear, osDelete, osDirty;
  }

  public interface Dominio{
    public void setState(EnumState arg0);
    public EnumState getState();
  }

  public interface Usuario implements Dominio {
    public void setNome(String nome);
    public String getNome();
  }

}

Pessoal, espero que alguém ai me ajude pos realmente to precisando disso imensamente. Qualquer duvida me perguntem que terei maior prazer em responder. :lol:

Att,

Olá

Nem olhei seu código porque se um dia precisasse de monitorar um setter eu usaria AOP.

[]s
Luca

Programação pra aspectos correto?

De maneira bem simples, voce pode explicar como AOP funciona e como pode resolver meu problema.

:wink:

Cara você precisa criar Beans Dinamicos os malditos DynaBeans.
É facil e pratico

da uma lida nisso

http://www.devmedia.com.br/articles/viewcomp.asp?comp=3295&vt=-1

neste artigo tem um lugar que ele pega um arquivo .properties pra dizer quais os attibutos do bean, mas da pra substituir ele pelo resultado de uma proc ou resultset.

Realmente, a forma como os mini-aplicativos são criados é bastante interessante. Obrigado!

Att,

Precisei fazer isso para o meu TCC, use o Javassist, veja CtClass, CtField e CtMethod

Dica:

CtClass classe = ClassPool.getDefault().makeClass(“NomeDaClasse”);

Groovy?

ta… e se o caso for de criar objetos em tempo de execução?

O meu priblema é o mesmo do amigo que iniciou o tópico, mas eu quero criar um objeto que retorne os dados de uma linha de retorno de uma SQL, tipo como o mysql_fetch_object() do PHP faz.

Já tenho tudo certinho, basta o retorno (o principal… hehehe)

Alguém me ajuda, por favor!!!