I have been playing around with a CustomerClassLoader as i am trying to load a .class file into JUnitCore.runClasses(...); but i am getting the following error
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: SimpleTest (wrong name: JUnit/SimpleTest)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
at Java.CustomClassLoader.access$000(CustomClassLoader.java:16)
at Java.CustomClassLoader$1.run(CustomClassLoader.java:45)
at java.security.AccessController.doPrivileged(Native Method)
at Java.CustomClassLoader.findClass(CustomClassLoader.java:33)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
.....
I don't know what it wrong as I am passing in the correct class name and path for the class. here is the code that i am using
CustomClassLoader
import java.io.FileInputStream;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
public class CustomClassLoader extends ClassLoader {
String repoLocation = "C:/TempBINfolder/bin/JUnit/";
public CustomClassLoader() { }
public CustomClassLoader(ClassLoader parent) {
super(parent);
}
#Override
protected Class<?> findClass(final String name)
throws ClassNotFoundException {
AccessControlContext acc = AccessController.getContext();
try {
return (Class)AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
FileInputStream fi = null;
try {
String path = name.replace('.', '/');
fi = new FileInputStream(repoLocation + path
+ ".class");
byte[] classBytes = new byte[fi.available()];
fi.read(classBytes);
return defineClass(name, classBytes, 0,
classBytes.length);
}catch(Exception e )
{
throw new ClassNotFoundException(name);
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
return super.findClass(name);
}
}
}
And this is how i am calling it
ClassLoader cls= new CustomClassLoader(ClassLoader.getSystemClassLoader());
Class stringClass = null;
try {
stringClass = cls.loadClass("SimpleTest");
} catch (ClassNotFoundException ex) {
Logger.getLogger(CompilerForm.class.getName()).log(Level.SEVERE, null, ex);
}
try {
stringClass.newInstance();
} catch (InstantiationException ex) {
Logger.getLogger(CompilerForm.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(CompilerForm.class.getName()).log(Level.SEVERE, null, ex);
}
JUnitCore.runClasses(stringClass);
Does anyone know why i am getting this error?
Change
stringClass = cls.loadClass("SimpleTest");
to
stringClass = cls.loadClass("JUnit.SimpleTest");
(i.e Add the fully qualified package name of the class file)
and
String repoLocation = "C:/TempBINfolder/bin/JUnit/";
to
String repoLocation = "C:/TempBINfolder/bin/";
You must be declaring your SimpleTest class to be in JUnit package, but you are trying to load is as simply SimpleTest (no package). Try eiteher:
remove the package declaration
remove Junit from location: String repoLocation = "C:/TempBINfolder/bin/";
Class name should be fully qualified junit.samples.SimpleTest
stringClass = cls.loadClass("SimpleTest");
should be
stringClass = cls.loadClass("junit.sample.SimpleTest");
Sometimes this happens to me, and usually it's solved by a clean and build. If that doesn't work, I usually restart my IDE.
Related
I wrote my classloader:
package ru.sberbank.school.homework8;
import ru.sberbank.school.homework8.plugin.Plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class PluginManager extends ClassLoader {
private final String pluginRootDirectory;
public PluginManager(String pluginRootDirectory) {
this.pluginRootDirectory = pluginRootDirectory;
}
public Plugin load(String pluginName, String pluginClassName) {
String name = pluginName + "." + pluginClassName;
try {
Class clazz;
try {
clazz = super.findSystemClass(name);
} catch (ClassNotFoundException e) {
String fileName = pluginRootDirectory + "\\" + pluginName + "\\" + pluginClassName + ".class";
try (FileInputStream fin = new FileInputStream(fileName)) {
byte[] buffer = new byte[(int) (new File(fileName).length())];
fin.read(buffer);
clazz = defineClass(name, buffer, 0, buffer.length);
}
}
return (Plugin)clazz.newInstance();
} catch (IOException | InstantiationException | IllegalAccessException ignored) {
return null;
}
}
}
When I run it:
package ru.sberbank.school.homework8;
import ru.sberbank.school.homework8.plugin.Plugin;
public class PluginManagerTest {
public static void main(String[] args) {
String pluginRootDirectory = "D:\\sbt\\target\\classes\\ru\\sberbank\\school\\homework8";
PluginManager pluginManager = new PluginManager(pluginRootDirectory);
Plugin plugin = pluginManager.load("plugin", "PluginImpl");
if (plugin != null) {
plugin.doUseful();
}
}
}
Exception in thread "main" java.lang.NoClassDefFoundError:
plugin/PluginImpl (wrong name:
ru/sberbank/school/homework8/plugin/PluginImpl) at
java.lang.ClassLoader.defineClass1(Native Method)
I get NoClassDefFoundError. Why??? How can I fix it???
Help me, please!
package ru.sberbank.school.homework8.plugin;
public class PluginImpl implements Plugin {
#Override
public void doUseful() {
System.out.println("My plugin!");
}
}
You get this error because you don't provide the correct FQN of your class, indeed in your load method, you try to find the class corresponding to pluginName + "." + pluginClassName that will be in your case plugin.PluginImpl but the package name of your class PluginImpl is actually ru.sberbank.school.homework8.plugin such that the real FQN of your class is ru.sberbank.school.homework8.plugin.PluginImpl.
To fix this problem, you need to replace:
Plugin plugin = pluginManager.load("plugin", "PluginImpl");
With:
Plugin plugin = pluginManager.load("ru.sberbank.school.homework8.plugin", "PluginImpl");
Or you could modify your method load to add a prefix assuming that you will always retrieve your plugins from the same root package:
public Plugin load(String pluginName, String pluginClassName) {
String name = "ru.sberbank.school.homework8." + pluginName + "." + pluginClassName;
I was trying to perform dynamic class loading from a jar file, unfortunately there was an error:
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1347093252 in class file com/life/Life
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.lang.ClassLoader.defineClass(ClassLoader.java:465)
at gloria.MyClassLoader.loadClass(MyClassLoader.java:38)
at gloria.Gloria.main(Gloria.java:9)
Java Result: 1
Here's my code:
MainClass.java
public class MainClass {
public static void main(String[] args) {
try{
ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();
MyClassLoader classLoader = new MyClassLoader(parentClassLoader);
Class myObjectClass = classLoader.loadClass("com.life.Life");
//create new class loader so classes can be reloaded.
classLoader = new MyClassLoader(parentClassLoader);
myObjectClass = classLoader.loadClass("com.life.Life");
Life life = (Life) myObjectClass.newInstance();
System.out.println("Message: " + life.getMessage());
}catch(Exception e){
e.printStackTrace();
}
}
}
MyClassLoader.java
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class MyClassLoader extends ClassLoader{
public MyClassLoader(ClassLoader parent) {
super(parent);
}
#Override
public Class loadClass(String name) throws ClassNotFoundException {
if(!"com.life.Life".equals(name))
return super.loadClass(name);
try {
String url = "http://192.168.1.229:8081/downloads/Life.jar";
URL myUrl = new URL(url);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass("com.life.Life", classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
Life.java
public interface Life {
public String getMessage();
}
What Im doing right here is to instantiate an object that from a jar file and load it in runtime. What's wrong with my code? Any idea?
0xCAFEBABE is the usual first 4 bytes of a Java file.
Your value 1347093252 is 0x504B0304 in hex, which is the magic value for a ZIP file.
As jar is also a zip file. This means that your jar can be corrupt. Try re-building the jar.
You are trying to use a JAR/ZIP file as a CLASS file; a JAR file contains JAR files, but isn't one.
BTW, why won't something like new UrlClassLoader("jar:http://http://192.168.1.229:8081/downloads/!/") work? This will also transparently handle loading classes that Life needs and are located in that JAR.
Ich have wrote a little module system, where modules are packaged into jars.
The application has to load these modules at start.
For loading these jars and extract the classes which implements the IModule interface or extends the Module class, i have written an ModuleLoader, by inspiring an tutorial.
The class Module is also implementing the IModule interface.
Now, if i start the application, there is thrown an exception.
IModuleLoader moduleLoader = new DefaultModuleLoader();
List<IModule> moduleList = moduleLoader.loadModulesFromDir(moduleDir);
Exception:
Exception in thread "main" java.lang.NoClassDefFoundError: com/corundumstudio/socketio/AuthorizationListener
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:367)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.jukusoft.jbackendengine.backendengine.module.impl.DefaultModuleLoader.extractClassesFromJAR(DefaultModuleLoader.java:137)
at com.jukusoft.jbackendengine.backendengine.module.impl.DefaultModuleLoader.extractClassesFromJARs(DefaultModuleLoader.java:120)
at com.jukusoft.jbackendengine.backendengine.module.impl.DefaultModuleLoader.loadModulesFromDir(DefaultModuleLoader.java:43)
at com.jukusoft.jbackendengine.backendengine.module.impl.DefaultModuleManager.loadModulesFromDir(DefaultModuleManager.java:91)
at com.jukusoft.jbackendengine.backendengine.module.impl.DefaultModuleManager.loadAndStart(DefaultModuleManager.java:100)
at com.jukusoft.jbackendengine.backendengine.builder.DefaultBackendEngineBuilder.buildBackendEngine(DefaultBackendEngineBuilder.java:26)
at com.jukusoft.jbackendengine.backendengine.builder.DefaultBackendEngineBuilder.buildBackendEngine(DefaultBackendEngineBuilder.java:16)
at com.jukusoft.jbackendengine.backendengine.factory.BackendEngineFactory.createNewDefaultBackendEngine(BackendEngineFactory.java:22)
at com.jukusoft.jbackendengine.backendengine.ServerEngineMain.main(ServerEngineMain.java:14)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.ClassNotFoundException: com.corundumstudio.socketio.AuthorizationListener
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 25 more
Here is the code of class ModuleLoader:
package com.jukusoft.jbackendengine.backendengine.module.impl;
import com.jukusoft.jbackendengine.backendengine.module.IModule;
import com.jukusoft.jbackendengine.backendengine.module.IModuleLoader;
import com.jukusoft.jbackendengine.backendengine.module.ModuleUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
/**
* Created by Justin on 03.07.2015.
*/
public class DefaultModuleLoader implements IModuleLoader {
#Override
public List<IModule> loadModulesFromDir(File modulesDir) throws IOException {
FilenameFilter filenameFilter = new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.endsWith(".jar") || name.endsWith(".jam");
}
};
if (!modulesDir.exists()) {
System.out.println("directory modules doesnt exists, creating directory modules.");
modulesDir.mkdirs();
}
File[] files = modulesDir.listFiles(filenameFilter);
System.out.println("" + files.length + " .jar files in directory " + modulesDir.getAbsolutePath() + " found.");
ClassLoader classLoader = new URLClassLoader(fileArrayToURLs(files));
List<Class<IModule>> moduleClasses = extractClassesFromJARs(files, classLoader);
System.out.println("" + moduleClasses.size() + " modules found.");
return createModuleObjects(moduleClasses);
}
#Override
public List<IModule> loadModulesFromFile(File moduleFile) throws IOException {
ClassLoader classLoader = new URLClassLoader(fileToURL(moduleFile));
List<Class<IModule>> moduleClasses = extractClassesFromJAR(moduleFile, classLoader);
return createModuleObjects(moduleClasses);
}
private URL[] fileArrayToURLs (File[] files) throws MalformedURLException {
URL[] urlsArray = new URL[files.length];
for (int i = 0; i < files.length; i++) {
urlsArray[i] = files[i].toURI().toURL();
}
return urlsArray;
}
private URL[] fileToURL (File file) throws MalformedURLException {
URL[] urlsArray = new URL[1];
urlsArray[0] = file.toURI().toURL();
return urlsArray;
}
public boolean isModuleClass (Class<?> cls) {
//System.out.println("isModuleClass() " + cls.getName() + ".");
/*try {
for (Class<?> cls1 : cls.getInterfaces()) {
System.out.println("Found interface " + cls1.getName() + " in class " + cls.getName() + ".");
if ((cls1.equals(IModule.class) || cls1.equals(Module.class)) && ModuleUtils.containsModuleInfo(cls)) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}*/
return IModule.class.isAssignableFrom(cls) || Module.class.isAssignableFrom(cls);
}
public IModule createModuleObject (Class<IModule> moduleClass) throws IllegalAccessException, InstantiationException {
return moduleClass.newInstance();
}
private List<IModule> createModuleObjects(List<Class<IModule>> classList) {
List<IModule> modules = new ArrayList<IModule>(classList.size());
for (Class<IModule> module : classList) {
try {
modules.add(module.newInstance());
} catch (InstantiationException e) {
System.err.println("Cannot create new instance of class " + module.getName() + ".");
e.printStackTrace();
} catch (IllegalAccessException e) {
System.err.println("IllegalAccess for module: " + module.getName() + ".");
e.printStackTrace();
}
}
return modules;
}
private List<Class<IModule>> extractClassesFromJARs(File[] jars, ClassLoader cl) throws IOException {
List<Class<IModule>> classList = new ArrayList<Class<IModule>>();
for (File jar : jars) {
classList.addAll(extractClassesFromJAR(jar, cl));
}
return classList;
}
#SuppressWarnings("unchecked")
private List<Class<IModule>> extractClassesFromJAR(File jarFile, ClassLoader classLoader) throws IOException {
List<Class<IModule>> classList = new ArrayList<Class<IModule>>();
JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile));
JarEntry ent = null;
while ((ent = jarInputStream.getNextJarEntry()) != null) {
if (ent.getName().toLowerCase().endsWith(".class")) {
try {
Class<?> cls = classLoader.loadClass(ent.getName().substring(0, ent.getName().length() - 6).replace('/', '.'));
if (isModuleClass(cls)) {
classList.add((Class<IModule>) cls);
}
} catch (ClassNotFoundException e) {
System.err.println("Cannot load class " + ent.getName() + ".");
//e.printStackTrace();
}
}
}
jarInputStream.close();
return classList;
}
}
the difference between ClassNotFoundException and NoClassDefFoundError are that the first happens because the named class can't be found on the classpath, the second (which is what you have) occurs when a class can be found but that some other error happens when the class is loaded. With this in mind perhaps there's a static piece of code or initialised member variable that's causing an exception. I have a vague recollection that a class I had once caused this error because I declared and initialised a variable to a resource that at runtime was null i.e. InputStream in = blah.class.getResourceAsStream(..blah...) so when my class that had in as a member was loaded it threw an NPE.
Helps if I actually read the whole stack trace before answering, yes the def error is caused by the ClassNotFoundException which makes things easier it just means that Authorization class isn't accessible on the classpath
Is
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
</dependency>
present in your maven pom.xml?
If yes, try to add corresponding netty-socketio***.jar to classpath where your run your program.
This question already has an answer here:
loading class at runtime in java ClassNotFoundException
(1 answer)
Closed 7 years ago.
The following code works correctly but in some cases when Class.forName method accesses the file it throws ClassNotFoundException because the file is not completely written, I solve the problem in those cases by increasing the sleep time.
But I want a way that forces the thread sleeps until file becomes completely written in stead of the static sleep(5000) ?
public void run(String className, String code) {
String pkgname = "pkg" + (int) (Math.random() * 10000);
String sb = "";
sb += "package " + pkgname + ";\n";
sb += code;
File javaFile = new File(serverPath + pkgname + "/" + className + ".java");
if (javaFile.getParentFile()
.exists() || javaFile.getParentFile().mkdirs()) {
try {
FileWriter writer = new FileWriter(javaFile);
writer.write(sb);
writer.flush();
writer.close();
try {
Thread.sleep(5000);// here is the problem
} catch (InterruptedException ex) {
Logger.getLogger(InlineCompiler.class.getName()).log(Level.SEVERE, null, ex);
}
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int compilationResult = compiler.run(null, null, null, javaFile.getPath());
try {
String[] params = new String[0];
Method method = Class.forName(pkgname + "." + className).getMethod("main", String[].class);
method.invoke(null, (Object) params);
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException | IllegalAccessException ex) {
Logger.getLogger(InlineCompiler.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (IOException | ClassNotFoundException exp) {
Logger.getLogger(InlineCompiler.class.getName()).log(Level.SEVERE, null, exp);
}
}}
Again: my problem is not the logic of the code or of Compiling and executing the file dynamically but it is only related to the sleeping time.
Exception when I decreased sleep to 2000
Jun 27, 2015 3:06:28 AM compile.InlineCompiler run
SEVERE: null
java.lang.ClassNotFoundException: pkg5958.test
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:259)
at compile.InlineCompiler.run(InlineCompiler.java:49)
Writing the file is not the problem:
java.lang.ClassNotFoundException: pkg5958.test means that some a class named test in a package named pkg5989 that the runtime is trying to reference is not on the classpath. This has nothing to do with Thread.sleep().
The directory you are generating the .class file into is not on your classpath.
I could not reproduce your problem, did not think I could.
This works and there is not a single call to Thread.sleep()!
Q31083880.java
import javax.annotation.Nonnull;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
public class Q31083880
{
public static int compile(#Nonnull final File file)
{
final JavaCompiler c = ToolProvider.getSystemJavaCompiler();
return c.run(null, null, null, file.getAbsolutePath());
}
public static void invoke(#Nonnull final String name)
{
try
{
final URLClassLoader cl = (URLClassLoader) ClassLoader.getSystemClassLoader();
final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
final File f = new File(System.getProperty("user.home"));
method.invoke(cl, f.toURI().toURL());
final Method m;
try
{
m = Class.forName(name).getMethod("main", String[].class);
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
final String[] args = new String[0];
m.invoke(null, new Object[]{args});
}
catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | MalformedURLException e)
{
throw new RuntimeException(e);
}
}
public static void main(final String[] args)
{
final File f = new File(System.getProperty("user.home"), "Test.java");
try
{
final FileWriter fw = new FileWriter(f);
try
{
final String source = "public class Test { public static void main(String[] args) { System.out.println(\"Testing\"); } }";
fw.write(source);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
finally
{
try { fw.close(); } catch (final IOException e) { System.err.println(e.getMessage()); }
}
System.out.println(compile(f));
invoke("Test");
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
This compiles to System.getProperty("user.home"); as an example.
It is an exercise to the reader to change this behavior, which should be easy.
I have created program which loads a java(JPanel) file which user chooses.User basically chooses a Java file which gets compiled by JavaCompiler and next generated class file is loaded.
But problem is coming when any changes are done in the java file(JPanel) through some text editor ,since any new changes are not reflected in the class file even after closing the program and re-running the project.
I think same class file is loaded again and again from memory.
is there any way to clear the loaded class from memory?
Compilation:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler != null) {
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(diagnostics, null, null);
Iterable<? extends JavaFileObject> fileObjects = stdFileManager.getJavaFileObjectsFromFiles(filesToCompile);
List<String> optionList = new ArrayList<String>();
// set compiler's classpath to be same as the runtime's
rootDir=Utility.createRootDir();
optionList.addAll(Arrays.asList("-d", rootDir.getAbsolutePath(), "-classpath", System.getProperty("java.class.path")));
// optionList.add(()
try {
stdFileManager.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
CompilationTask task = compiler.getTask(null, stdFileManager,null, optionList, null, fileObjects);
Boolean result = task.call();
try {
stdFileManager.flush();
stdFileManager.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Loading:
loader = new URLClassLoader(new URL[] { rootDir.toURI().toURL() });
cls = Class.forName(Utility.extractFQDN(sourceFile)+"."+Utility.extractClassName(sourceFile),true, loader);
panel= (JPanel) cls.newInstance();
I have checked the compiled class file with decompiler it has updated code but I don't know why previous class file is being loaded from memory by class loader.
Edit:
Here's an SSCCE compiling strings repeatedly to the same class name and demonstrating new behavior. To avoid the whole mess with files it does everything in memory. I think this should be easily adaptable to your application.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
public class Compile {
static class OutFile extends SimpleJavaFileObject {
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
OutFile(String name) {
super(URI.create("memory:///" + name.replace('.','/') + Kind.CLASS.extension), Kind.CLASS);
}
#Override
public OutputStream openOutputStream() throws IOException {
return out;
}
}
static class InFile extends SimpleJavaFileObject {
final String code;
InFile(String name, String code) {
super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
static class Loader extends ClassLoader {
private final Map<String, OutFile> files = new HashMap<String, OutFile>();
public OutFile add(String className) {
OutFile file = new OutFile(className);
files.put(className, file);
return file;
}
#Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
if(c == null) {
OutFile file = files.get(name);
if(file == null) {
return super.loadClass(name, resolve);
}
c = defineClass(name, file.out.toByteArray(), 0, file.out.size());
}
if(resolve) {
resolveClass(c);
}
return c;
}
}
static class FileManager extends ForwardingJavaFileManager<JavaFileManager> {
private final Loader loader = new Loader();
protected FileManager(JavaFileManager fileManager) {
super(fileManager);
}
public Class<?> getClass(String name) throws ClassNotFoundException {
return loader.loadClass(name);
}
#Override
public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
return loader.add(className);
}
}
public static void compileAndRun(String source) throws Exception {
InFile in = new InFile("Main", "class Main {\npublic static void main(String[] args) {\n" + source + "\n}\n}");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
FileManager manager = new FileManager(compiler.getStandardFileManager(null, null, null));
compiler.getTask(null, manager, null, null, null, Collections.singletonList(in)).call();
Method method = manager.getClass("Main").getMethod("main", String[].class);
method.setAccessible(true);
method.invoke(null, (Object)new String[0]);
}
public static void main(String[] args) throws Exception {
compileAndRun("System.out.println(\"Hello\");");
compileAndRun("System.out.println(\"World\");");
}
}
Original:
ClassLoader (and subclasses like URLClassLoader) will always ask the parent class loader to load the class, if there is a parent. If you don't explicitly set a parent when constructing it, the parent is set to the system class loader. So, all the new class loaders you create are deferring back to the system class loader, which already has the class defined.
To get the behavior that you want, set the parent to null:
loader = new URLClassLoader(new URL[] { rootDir.toURI().toURL() }, null);
Edit: Note that is only a problem because you are compiling your classes to the root directory, which is also on the classpath of the system class loader. If you compiled to some temp directory, the system class loader would not be able to find the class, and the URL loader would load the class itself.