/*
* Copyright (C) 2012 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.crsh.lang.impl.java;
import org.crsh.AbstractTestCase;
import org.crsh.util.Utils;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.ByteArrayAsset;
import org.jboss.shrinkwrap.api.exporter.ExplodedExporter;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.Callable;
/** @author Julien Viet */
public class CompilerTestCase extends AbstractTestCase {
public void testCompile() throws Exception {
Compiler compiler = new Compiler();
List<JavaClassFileObject> files = compiler.compile("A",
"public class A implements java.util.concurrent.Callable<String> {\n" +
"public String call() {\n" +
"return \"hello\";\n" +
"}\n" +
"}");
assertEquals(1, files.size());
LoadingClassLoader loader = new LoadingClassLoader(Thread.currentThread().getContextClassLoader(), files);
Class<?> A = loader.findClass("A");
Callable<String> asCallable = (Callable<String>)A.newInstance();
String ret = asCallable.call();
assertEquals("hello", ret);
}
public void testImportFromFolder() throws Exception {
doTestImport(new ClassLoaderFactory() {
@Override
public ClassLoader getClassLoader(JavaArchive jar) throws Exception {
File tmp = File.createTempFile("root", "tmp");
assertTrue(tmp.delete());
assertTrue(tmp.mkdir());
tmp.deleteOnExit();
jar.as(ExplodedExporter.class).exportExploded(tmp, "root");
URL url = new File(tmp, "root").toURI().toURL();
return new URLClassLoader(new URL[]{url});
}
});
}
public void testImportFromJar() throws Exception {
doTestImport(new ClassLoaderFactory() {
@Override
public ClassLoader getClassLoader(JavaArchive jar) throws Exception {
File tmp = File.createTempFile("crash", ".jar");
assertTrue(tmp.delete());
jar.as(ZipExporter.class).exportTo(tmp);
return new URLClassLoader(new URL[]{tmp.toURI().toURL()});
}
});
}
public void testImportFromNestedJar() throws Exception {
doTestImport(new ClassLoaderFactory() {
@Override
public ClassLoader getClassLoader(final JavaArchive jar) throws Exception {
WebArchive war = ShrinkWrap.create(WebArchive.class);
war.setManifest(Thread.currentThread().getContextClassLoader().getResource("META-INF/MANIFEST.MF"));
war.addAsLibrary(jar);
final File tmp = File.createTempFile("crash", ".war");
assertTrue(tmp.delete());
war.as(ZipExporter.class).exportTo(tmp);
final byte[] bytes = Utils.readAsBytes(jar.get("foo/A.class").getAsset().openStream());
return new ClassLoader(Thread.currentThread().getContextClassLoader()) {
Class<?> aClass = null;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("foo.A")) {
if (aClass == null) {
// Normally we should use the bytes from the nested crash.jar but it's too difficult
// so we just use what we compiled before as it is the same bytecode and already
// available here
aClass = defineClass(name, bytes, 0, bytes.length);
}
return aClass;
} else {
return super.loadClass(name);
}
}
@Override
protected Enumeration<URL> findResources(String name) throws IOException {
if ("META-INF/MANIFEST.MF".equals(name)) {
URL u1 = new URL("jar:" + tmp.toURI().toURL() + "!/META-INF/MANIFEST.MF");
URL u2 = new URL("jar:" + ("jar:" + tmp.toURI().toURL() + "!/WEB-INF/lib/crash.jar") + "!/META-INF/MANIFEST.MF");
return Collections.enumeration(Arrays.asList(u1, u2));
} else if ("foo".equals(name)) {
String u = "jar:" + ("jar:" + tmp.toURI().toURL() + "!/WEB-INF/lib/crash.jar") + "!/foo/";
return Collections.enumeration(Collections.singleton(new URL(u)));
} else {
return super.findResources(name);
}
}
};
}
});
}
private interface ClassLoaderFactory {
ClassLoader getClassLoader(JavaArchive jar) throws Exception;
}
private void doTestImport(ClassLoaderFactory factory) throws Exception {
Compiler compiler = new Compiler();
List<JavaClassFileObject> files = compiler.compile("foo.A", "package foo;\n public class A {}");
assertEquals(1, files.size());
JavaClassFileObject aFile = files.get(0);
JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "crash.jar");
jar.add(new ByteArrayAsset(aFile.getBytes()), "foo/A.class");
jar.setManifest(Thread.currentThread().getContextClassLoader().getResource("META-INF/MANIFEST.MF"));
ClassLoader cl = factory.getClassLoader(jar);
//
compiler = new Compiler(cl);
files = compiler.compile("B",
"import foo.A;\n" +
"public class B implements java.util.concurrent.Callable<A> {\n" +
"public A call() {\n" +
"return new A();\n" +
"}\n" +
"}");
assertEquals(1, files.size());
LoadingClassLoader loader = new LoadingClassLoader(cl, files);
Class<?> B = loader.findClass("B");
Callable<?> asCallable = (Callable<?>)B.newInstance();
Object ret = asCallable.call();
assertNotNull(ret);
Class<?> A = ret.getClass();
assertEquals("foo.A", A.getName());
assertEquals(cl, A.getClassLoader());
}
public void testCompilationFailure() throws Exception {
Compiler compiler = new Compiler();
try {
compiler.compile("foo.A", "package foo;\n public class A {");
}
catch (CompilationFailureException e) {
List<Diagnostic<? extends JavaFileObject>> errors = e.getErrors();
assertEquals(1, errors.size());
Diagnostic<? extends JavaFileObject> error = errors.get(0);
assertEndsWith("/foo/A.java", error.getSource().getName());
}
}
}