[RESOLVIDO] Compilação dinâmica em Java (Java Magazine 94)

13 respostas
Jubarius

Boa tarde a todos,

Estou estudando os códigos referentes ao artigo sobre compilação dinâmica no Java da revista Java Magazine 94. Os códigos, dando um jeitinho aqui outro ali, funcionaram. E realmente compila e executa códigos que possuam somente saída no terminal ( o famoso System.out.println), porém quando tento compilar um código mais complexo, como um que contenha um JFrame, o mesmo gera o seguinte erro:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no freetype in java.library.path
	at java.lang.ClassLoader.loadLibrary(Unknown Source)
	at java.lang.Runtime.loadLibrary0(Unknown Source)
	at java.lang.System.loadLibrary(Unknown Source)
	at sun.font.FontManagerNativeLibrary$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.font.FontManagerNativeLibrary.<clinit>(Unknown Source)
	at sun.font.SunFontManager$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.font.SunFontManager.<clinit>(Unknown Source)
	at sun.awt.PlatformFont.<init>(Unknown Source)
	at sun.awt.windows.WFontPeer.<init>(Unknown Source)
	at sun.awt.windows.WToolkit.getFontPeer(Unknown Source)
	at java.awt.Font.getPeer_NoClientCode(Unknown Source)
	at java.awt.Font.getPeer(Unknown Source)
	at sun.awt.windows.WComponentPeer._setFont(Native Method)
	at sun.awt.windows.WComponentPeer.setFont(Unknown Source)
	at sun.awt.windows.WWindowPeer.initialize(Unknown Source)
	at sun.awt.windows.WFramePeer.initialize(Unknown Source)
	at sun.awt.windows.WComponentPeer.<init>(Unknown Source)
	at sun.awt.windows.WCanvasPeer.<init>(Unknown Source)
	at sun.awt.windows.WPanelPeer.<init>(Unknown Source)
	at sun.awt.windows.WWindowPeer.<init>(Unknown Source)
	at sun.awt.windows.WFramePeer.<init>(Unknown Source)
	at sun.awt.windows.WToolkit.createFrame(Unknown Source)
	at java.awt.Frame.addNotify(Unknown Source)
	at java.awt.Window.show(Unknown Source)
	at java.awt.Component.show(Unknown Source)
	at java.awt.Component.setVisible(Unknown Source)
	at java.awt.Window.setVisible(Unknown Source)
	at TesteFrm.montaTela(TesteFrm.java:21)
	at TesteFrm.<init>(TesteFrm.java:11)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
	at java.lang.reflect.Constructor.newInstance(Unknown Source)
	at java.lang.Class.newInstance0(Unknown Source)
	at java.lang.Class.newInstance(Unknown Source)
	at compilador.Teste.main(Teste.java:52)

Olhando o erro, a princípio, achei que era algum problema no classpath ou alguma lib faltando, porém o código não faz referência a nenhum elemento que esteja fora do padrão do Java.

Este é o código que estou executando:

package compilador;

public class Teste {

	public static void main(String[] args) throws Exception {
		JavaCodeCompiler<Runnable> compiler = new JavaCodeCompiler<Runnable>();
		
		//Código sem erro
//		String source = "public class RunableImpl implements Runnable{" +
//				"public void run(){" +
//				"System.out.println(\"Foi!!!\");" +
//				"}" +
//				"}";
		
		//Código que gera o erro
		String source = "import java.awt.FlowLayout;\n"+ 
				"\n"+ 
				"import java.awt.Dimension;\n"+
				"import javax.swing.JFrame;\n"+ 
				"import javax.swing.JLabel;\n"+ 
				"\n"+ 
				"public class TesteFrm implements Runnable{\n"+ 
				"\n"+  
				"	\n"+ 
				"	public TesteFrm(){\n"+ 
				"		montaTela();\n"+ 
				"	}\n"+ 
				"	\n"+ 
				"	private void montaTela(){\n" +
				"       JFrame jf = new JFrame();"+ 
				"		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n"+ 
				"		JLabel l = new JLabel(\"Teste\");\n"+ 
				"		\n"+ 
				"		jf.getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER));\n"+ 
				"		jf.getContentPane().add(l);\n"+ 
				"		\n"+ 
				"		jf.setVisible(true);\n"+
				"		jf.pack();\n"+
				"	}\n"+ 
				"\n"+ 
				"	\n"+ 
				"	public void run() {\n"+ 
				"		montaTela();\n"+ 
				"		\n"+ 
				"	}\n"+ 
				"}\n";
;


		
//		A chamada deste método é assim compiler.compile(nome_pacote, nome_classe, código);

//		Chamada sem erro
//		Class<Runnable> clazz = compiler.compile(null, "RunableImpl", source);

//		Chamada que gera o erro
		Class<Runnable> clazz = compiler.compile(null, "TesteFrm", source);

//		Onde o erro ocorre
		Runnable r = clazz.newInstance();
		
		r.run();
		
		
	}
	
}

Agradeço a todos que puderem dar alguma ajuda.

Inté…

13 Respostas

CristianPalmaSola10

Voce adicionou algum jar para poder usar a clase: JavaCodeCompiler

se sim poderia passar a url de download dela,

valeu

Jubarius

Blz Cristian

Seguinte… Não utilizei nenhum jar, porém, como coloquei no post, tive de dar um jeitinho bem nesse código, na linha que contém o comando

System.setProperty("java.home", "C:\\Program Files\\Java\\jdk1.7.0");

No código original (baixado do site da JM no endereço http://videos.web-03.net/REVISTAS/java/download/94/src-JM9%28nhghj%294.zip) ele não consta, mas para poder rodar aqui eu tive de utilizá-lo para setar a localização da pasta que contém o jdk, acredito que as minhas variáveis de ambiente devem estar com problemas, mas assim resolveu. Este comando é a primeira instrução executada pelo método construtor desta classe.

Inté…

CristianPalmaSola10

Ja tentou copiar o codigo do source e colocar em uma classe java par ver se não ha nenhum erro de compilação ?

Jubarius

Pior que já tentei, até mudei a forma da classe, antes eu criava a JFrame através de herança, depois imaginei que por estar invocando somente a interface ela podia não estar inicializando a JFrame e então mudei e testei a classe. Chamando ela como uma classe normal no Eclipse funciona normalmente, porém quando ela é compilada através do código gera aquele erro. O pior ainda é que eu não faço idéia do que pode ser aquela exceção, pelo que pesquisei é como se faltasse alguma lib ou coisa do tipo, mas só é utilizado componente padrão do Java.

Inté…

CristianPalmaSola10

Cara posta o import desse JavaCodeCompiler do tentando pega ele aqui mas naum da dando, deve estar dentro de um jar, qual ?

Jubarius

No meu caso não precisou, mas no artigo o autor mencionou que dependendo da versão do jdk, era necessário por o o jar “tools.jar”, que fica dentro da pasta libs, que fica dentro da pasta de instalação do jdk. Naquela instrução que eu mencionei, você colocou o caminho para a instalação do jdk?

CristianPalmaSola10

Sabe apartir de qual versao esta presente esta classe, adicionei o tools.jar, mas aqui naum consiguo pecar essa classe na minha ide.

Jubarius

A partir da 6. Que erro que está dando aí?

CristianPalmaSola10

bom eu tenho jdk 6 update alguma coisa

naum chaga a dar erro, simplemente é como se a classe naum existisse pois ela naum é vista.

Digito o começo mas ela naum aparece.

o que sera que esta acontecendo tem alguma ideia ?

Jubarius

Para falar ao bem da verdade… Eu não tenho idéia do que possa ser. Eu acabei de baixar o código que eu postei dentro do zip e testar dentro de uma VM com winXP e Eclipse. Funcionou, tipo… tive de ajeitar o caminho para a pasta do jdk, porque gerava o erro de “Compilador não encontrado”, mas todas as classes foram encontradas. Tenta baixar de volta. Ou então faz que nem eu fiz, baixei o .zip, descompactei, entrei no Eclipse e pedi para importar o conteúdo da pasta para dentro de um projeto (Existing Projects into Workspace).

CristianPalmaSola10

Entaum baixei e a tal classe da dentro daquele arquivo baixado, adicionei as classes

criei um jrame zerado copiei e colei o codigo numa varivel string ai mandei esse kra compila o codigo pra mim e naum aconteceu nada

naum de erro mas tambem naum me abriu a tela

Jubarius

Você chegou a adicionar algum elemento ao JFrame, ou usar o método setVisible ou pack, no meu caso, se tirar essas linhas ele também deixa de dar erro, mas também nao aparece nada na tela. É aí que começa a apitar aquela exceção.

Jubarius

Boa tarde Cristian,

Valeu pela ajuda. Consegui resolver o problema, era na definição da propriedade “java.home”. Eu executei mais uma vez dentro do Eclipse e pedi para imprimir a propriedade e vi que a mesma apontava para a pasta da jre e não do jdk. Daí foi só apontar para a pasta da jre e pronto, problema resolvido. O meu código do JavaCodeCompiler ficou assim:

package compilador;

import java.util.Arrays;
import java.util.jar.JarOutputStream;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;



public class JavaCodeCompiler<T> {
	private JavaCompiler compiler;
	private AppFileManager fileManager;
	private AppClassLoader classLoader;
	private DiagnosticCollector<JavaFileObject> diagnostics;
	
	public JavaCodeCompiler() throws Exception{
		
                //Setei para a pasta da jdk
		System.setProperty("java.home", "C:\\Program Files\\Java\\jdk1.7.0");
                     
                //Peguei o compilador		
		compiler = ToolProvider.getSystemJavaCompiler();
		
                //Voltei para a pasta da jre
		System.setProperty("java.home", "C:\\Program Files\\Java\\jre7");

		
		if(compiler == null){
			throw new Exception("Compilador não encontrador");
		}
		
		classLoader = new AppClassLoader(getClass().getClassLoader());
		diagnostics = new DiagnosticCollector<JavaFileObject>();
		
		StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null);
		
		fileManager = new AppFileManager(standardFileManager, classLoader);
		
	}
	
	public synchronized Class<T> compile(String nomePacote, String nomeClasse, String codigo) throws CompilerException{
		try{
			String nomeClasseQualificado = CompilerUtils.getNomeClasseQualified(nomePacote, nomeClasse);
			AppJavaFileObject sourceObj = new AppJavaFileObject(nomeClasse, codigo);
			AppJavaFileObject compiledObj = new AppJavaFileObject(nomeClasseQualificado);
			
			fileManager.setObjects(sourceObj, compiledObj);
			//TODO Ver como se manipular outputStream
//			JarOutputStream jo = new JarOutputStream(compiledObj.openOutputStream());
			CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, Arrays.asList(sourceObj));
			
			boolean result = task.call();
			
			if(!result){
				throw new CompilerException("A compilação falhou!!!", diagnostics);
			}
			
			Class<T> newClass =(Class<T>) classLoader.loadClass(nomeClasseQualificado);
			
			return newClass;
		}catch(Exception e){
			throw new CompilerException(e, diagnostics);
		}
	}
	
}

Se alguém souber de uma solução mais elegante ou dar uma explicação sobre o porque que isto acontece, fique a vontade.

Mais uma vez, valeu pela força Cristian, é conversando no fórum que a gente consegue resolver os “pobrema” que a gente “axa”.

Inté…

Criado 30 de julho de 2012
Ultima resposta 30 de jul. de 2012
Respostas 13
Participantes 2