I try to extend ClassLoader. My ClassLoader.loadClass is:
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {// i put "throw"s here to clean code below
Class<?> result = null;
byte[] bytes = null;
try {
bytes = getClassFromFS(pathToClass); //get .class file from file system in bytes[]
} catch (FileNotFoundException ex) {
Logger.getLogger(MyLoader.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(MyLoader.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("MyLoader:мой loadClass загружает класс");
return defineClass(name, bytes, 0, bytes.length); // the proplem is here !!!
return super.loadClass(name, resolve);
}
lines in the "main" thread
/*first argument - path to file. File exist, I checked it*/
myClassLoader = new MyLoader("D:\\\\customclassloader\\ClassX.class", ClassLoader.getSystemClassLoader());
classX = (SimpleInterface) myClassLoader.loadClass("customclassloader.ClassX",true).newInstance();
</pre>
then I have exception
<pre>Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:650)
at java.lang.ClassLoader.defineClass(ClassLoader.java:786)
at java.lang.ClassLoader.defineClass(ClassLoader.java:635)
at customclassloader.MyLoader.loadClass(MyLoader.java:61)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:792)
at java.lang.ClassLoader.defineClass(ClassLoader.java:635)// the proplem is here !!!
at customclassloader.MyLoader.loadClass(MyLoader.java:61)
at customclassloader.CustomClassLoader.main(CustomClassLoader.java:32)
ClassX:
package customclassloader;
/**
*
* #author roman
*/
public class ClassX {
static {
System.out.println("класс ClassX инициируеться");
}
public ClassX() {
System.out.println("класс ClassX конструируеться");
}
public void f(){
System.out.println("класс ClassX выполняет f();");
}
}
I donn't understend.I name class "customclassloader.ClassX". Why do it show me the name "java.lang"?
The ClassX.class compiled in the same project and the same package.
It shows java.lang because java.lang.Object is the superclass of all classes which the loader is trying to load.
you can check this link for sample
http://www.javaworld.com/jw-10-1996/jw-10-indepth.html?page=2
" the next step is to check if the primordial class loader can resolve this class name. This check is essential to both the sanity and security of the system. For example, if you return your own instance of java.lang.Object to the caller, then this object will share no common superclass with any other object! The security of the system can be compromised if your class loader returned its own value of java.lang.SecurityManager, which did not have the same checks as the real one did. "
It seems that Java forbid you to put your own classes in the package java.lang.
And because you're extending ClassLoader, which is in java.lang, your class is considered as a part of the java.lang package.
Try to put it in another package, using the package statement on top of your import instructions
PS : answer as seen here : java.lang.SecurityException: Prohibited package name: java.lang
Related
I have a java application which was developed in Eclipse.
There is a folder on the system which contains a lot of ".java" files. These ".java" files are classes which some user has written. I wish to load all these java classes and compile them inside my java application.
Another property of all the ".java" files are that all the classes written inside extend a class which is inside my original application.
I used the following to read and compile all the classes.
File parentFile = new File(rulesDir + "\\");
String fileName = rulesDir + "\\" + ruleName + ".java";
File ruleFile = new File(fileName);
// Compile source file.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, ruleFile.getPath());
// Load and instantiate compiled class.
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { parentFile.toURI().toURL() });
Class<? extends AbstractRule> cls = (Class<? extends AbstractRule>)Class.forName(ruleName, true, classLoader);
If I run the above code inside Eclipse, it works fine. When I run the application as a jar from elsewhere, it throws an ClassNotFoundException for the line
Class<? extends AbstractRule> cls = (Class<? extends AbstractRule>)Class.forName(ruleName, true, classLoader);
Why is this happening? What is different that it executes in Eclipse and doesn't via command line?
From the documentation for Class.forName
name - fully qualified name of the desired class
So, in order to get that fully qualified class name, you need to manipulate your rulesDir variable to replace the backslashes with periods, then prepend that to your ruleName variable, combined with another period, to get the fully qualified class name. Then you'll be able to use the ClassLoader to load the class. The fully qualified name is required so that the ClassLoader can find your resource from the root of your classpath.
NB I make the assumption that your rulesDir is a relative path from the base of your classpath. If it is not, then you'll have extra manipulation to do here
See code manipulation below:
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import rules.AbstractRule;
public class MainApp {
public static void main(String[] args) {
try {
System.out.println("Test");
// NB Amended here to now take project root, relative path to rules directory and class name. So that compilation can take place based on the absolute path and class loading from the relative one.
compile("C:\\Media\\Code\\manodestra_java\\src\\tmp", "rules", "TestRule");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void compile(String projectRoot, String rulesDir, String ruleName) throws Exception {
File parentFile = new File(projectRoot + "\\" + rulesDir + "\\");
System.out.println(parentFile);
String fileName = parentFile.getCanonicalPath() + "\\" + ruleName + ".java";
File ruleFile = new File(fileName);
System.out.println(ruleFile);
// Compile source file.
System.out.println("Compiling...");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, ruleFile.getPath());
// Load and instantiate compiled class.
System.out.println("Loading class...");
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { parentFile.toURI().toURL() });
System.out.println("Class Loader: " + classLoader);
ruleName = rulesDir.replace("\\", ".") + "." + ruleName;
Class<? extends AbstractRule> clazz = (Class<? extends AbstractRule>)Class.forName(ruleName, true, classLoader);
System.out.println(clazz);
}
}
For the sake of my testing, this class was defined in the default package and I created a rules directory below that level to contain my subclasses of AbstractRule. So, rules.TestRule was my fully qualified path to my class name. But, yours could be...
com.example.testapplication.rules.TestRule, etc.
And that's what would be required in Class.forName. There's a path to your classpath root, then the relative path from there to your java files (which is equivalent to the package of your classes), then the actual class names under that package path.
This is my "test-addon"
And I'm trying to load my "main class" using:
Class<?> jarClass;
try {
ClassLoader cls = ClassLoader.getSystemClassLoader();
jarClass = cls.loadClass("main.Addon");
} catch (ClassNotFoundException e) {
throw new InvalidPluginException("\"Addon class\" was not found", e);
}
As you can see in the image, the class exists, but it still returns:
Line 21: jarClass = cls.loadClass("main.Addon");
QUESTION: why does this happen
The jar or directory that contains main.Addon isn't on the classpath.
Try Addon (no package specifier). In Maven-style projects, src/main is the root (default) package.
The problem was that I used the SystemClassLoader, not my own.
A simple fix for this was: jarClass = Class.forName("main.Addon", true, this);
I exported my plugin project as a Product and when I run the product (eclipse application), the main plugin (org.example.framework.core) cannot find a class defined in another plugin (org.example.abc) which implements an extension to an extension point provided by the main plugin. The class is one of the elements defined in the extension. However, when I run the project in Eclipse, everything runs fine!
Here is the log (atvste.ppt.ptfwDescription.abc.decoderInfo is a package in org.example.abc plugin):
0 [Worker-2] ERROR org.example.framework.core.ptfw.codec.decode.MsgDecoderInfo org.example.framework.core.ptfw.codec.decode.MsgDecoderInfo.createInstance(MsgDecoderInfo.java:114) : can not create class for :atvste.ppt.ptfwDescription.abc.decoderInfo.MsgDecoderInfoABC atvste.ppt.ptfwDescription.abc.decoderInfo.MsgDecoderInfoABC cannot be found by org.example.framework.core_1.0.0.201404111439
java.lang.ClassNotFoundException: atvste.ppt.ptfwDescription.abc.decoderInfo.MsgDecoderInfoABC cannot be found by org.example.framework.core_1.0.0.201404111439
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:501)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:421)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:412)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at org.example.framework.core.ptfw.codec.decode.MsgDecoderInfo.createInstance(MsgDecoderInfo.java:104)
at org.example.framework.core.ptfw.codec.decode.DecoderInfo.<init>(DecoderInfo.java:56)
at org.example.framework.core.ptfw.codec.decode.DecoderInfo.createInstance(DecoderInfo.java:125)
at org.example.framework.core.ptfw.codec.ptfwObjectsFactory.decoderInfoItf_createInstance(ptfwObjectsFactory.java:200)
at org.example.framework.persistence.jaxb.ptfwDescriptionPersistenceJaxb.createptfwDescription(ptfwDescriptionPersistenceJaxb.java:326)
at org.example.framework.persistence.jaxb.ptfwDescriptionPersistenceJaxb.fillptfwDescription(ptfwDescriptionPersistenceJaxb.java:247)
at org.example.framework.persistence.jaxb.ptfwDescriptionPersistenceJaxb.createInstance(ptfwDescriptionPersistenceJaxb.java:232)
at org.example.framework.persistence.jaxb.ptfwDescriptionPersistenceJaxb.open(ptfwDescriptionPersistenceJaxb.java:146)
at org.example.framework.core.ptfw.codec.ptfwDescription.createInstance(ptfwDescription.java:152)
at org.example.framework.core.ptfw.codec.command.CmdLoadptfwDescription.loadptfwDescription(CmdLoadptfwDescription.java:50)
at org.example.framework.core.ptfw.codec.command.CmdLoadptfwDescription.execute(CmdLoadptfwDescription.java:40)
at org.example.framework.core.runtime.JobService$2.run(JobService.java:93)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:53)
EDIT: Function for creating instance of the class not found
public static IMessageDecoderInfo createInstance(XmlgMsgDecoderInfo pMsgDecoderInfoType,
IMessageDecoderInfo msgDecoder)
{
String className = pMsgDecoderInfoType.getClassName();
if(className!=null)
{
try
{
Class<?> formalArgs[] = new Class[1];
formalArgs[0] = XmlgMsgDecoderInfo.class;
Class<?> clazz;
if (msgDecoder != null)
{
clazz = msgDecoder.getClass();
}
else
{
clazz = Class.forName( className );
}
Constructor<?> constructor = clazz.getConstructor(formalArgs);
java.lang.Object actualArgs[] =
{ pMsgDecoderInfoType };
return (IMessageDecoderInfo)constructor.newInstance(actualArgs);
}catch(Exception e) {
String error = "can not create class for :" +className+ " " + e.getMessage();
if (LOGGER.isEnabledFor(Level.ERROR)) {
LOGGER.error(error, e);
}
throw new CreatePtfwElementRuntimeException(error, e);
}
}
return new MsgDecoderInfo(pMsgDecoderInfoType);
}`
Because of the complex class loader system used by Eclipse you cannot use Class.forName to load a class in another plugin.
If your code is processing an extension point definition you will have the IConfigurationElement for the configuration element that specifies the class name. You can use
IConfigurationElement configElement = ....;
Object classInstance = configElement.createExecutableExtension("class attribute name");
where 'class attribute name' is the name of the attribute in the extension point element that specifies the class to load. The class being created must have a no argument constructor.
Alternatively you can load a class using:
Bundle bundle = Platform.getBundle("plugin id");
Class<?> theClass = bundle.loadClass("class name");
... construct as usual ...
If you have the IConfigurationElement you can get the plugin id using
String pluginId = configElement.getContributor().getName();
I've the following scenario:
I've a byte[] that contains the .class data of a class (loaded from the file system)
And I have another byte[] of this some object of this class that was previously Serialized to some other stream.
First do load the byte[] of the .class file with my custom class loader which is:
public class MainSearchClassLoader extends ClassLoader
{
public MainSearchClassLoader()
{
super(MainSearchClassLoader.class.getClassLoader());
}
public Class<?> findClass(String name) throws ClassNotFoundException
{
try
{
byte[] bytecode = FileUtil.readClassByteCode();
return super.defineClass(ReflectionUtil.getStubBinaryClassName() , bytecode, 0, bytecode.length);
} catch (IOException e)
{
e.printStackTrace();
}
return null;
}
}
Then I am trying to de-serialize this instance using the following code:
public static Object getObjectFromBytes(final byte[] bytes)
{
Object object = null;
try
{
object = new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject();
} catch (final Exception ioe)
{
ioe.printStackTrace();
}
return object;
}
That takes the serialized bytes and supposed to return instance (of the pre-loaded class using my custom class loader) .. I got the following exception:
11/03/06 14:23:27 oracle.classloader.util.AnnotatedClassNotFoundException:
Missing class: mainSearchObjects.dc_index
Dependent class: java.io.ObjectInputStream
Loader: jre.bootstrap:1.5.0_06
Code-Source: unknown
Configuration: jre bootstrap
This load was initiated at MainSearch.web.MainSearch:0.0.0 using the Class.forName() method.
The missing class is not available from any code-source or loader in the system.
11/03/06 14:23:27 at oracle.classloader.PolicyClassLoader.handleClassNotFound (PolicyClassLoader.java:2068) [/D:/jdevstudio10134/j2ee/home/lib/pcl.jar (from system property java.class.path), by sun.misc.Launcher$AppClassLoader#14916158]
at oracle.classloader.PolicyClassLoader.internalLoadClass (PolicyClassLoader.java:1679) [/D:/jdevstudio10134/j2ee/home/lib/pcl.jar (from system property java.class.path), by sun.misc.Launcher$AppClassLoader#14916158]
at oracle.classloader.PolicyClassLoader.loadClass (PolicyClassLoader.java:1635) [/D:/jdevstudio10134/j2ee/home/lib/pcl.jar (from system property java.class.path), by sun.misc.Launcher$AppClassLoader#14916158]
at oracle.classloader.PolicyClassLoader.loadClass (PolicyClassLoader.java:1620) [/D:/jdevstudio10134/j2ee/home/lib/pcl.jar (from system property java.class.path), by sun.misc.Launcher$AppClassLoader#14916158]
at java.lang.ClassLoader.loadClassInternal (ClassLoader.java:319) [jre bootstrap, by jre.bootstrap:1.5.0_06]
at java.lang.Class.forName0 (Native method) [unknown, by unknown]
at java.lang.Class.forName (Class.java:242) [jre bootstrap, by jre.bootstrap:1.5.0_06]
at java.io.ObjectInputStream.resolveClass (ObjectInputStream.java:574) [jre bootstrap, by jre.bootstrap:1.5.0_06]
at java.io.ObjectInputStream.readNonProxyDesc (ObjectInputStream.java:1538) [jre bootstrap, by jre.bootstrap:1.5.0_06]
at java.io.ObjectInputStream.readClassDesc (ObjectInputStream.java:1460) [jre bootstrap, by jre.bootstrap:1.5.0_06]
at java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:1693) [jre bootstrap, by jre.bootstrap:1.5.0_06]
at java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1299) [jre bootstrap, by jre.bootstrap:1.5.0_06]
at java.io.ObjectInputStream.readObject (ObjectInputStream.java:339) [jre bootstrap, by jre.bootstrap:1.5.0_06]
...........
I understood that.. The bootstrap class loader that is used by the de-serization code cannot see the class loaded by one of its children (my class loader) and I think this is a correct behaiour, isn't it?
So, Isn't a solution to this problem??
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4340158
You need your own ObjectInputStream.
I've got a ClassLoader extending class with following method
#Override
public Class<?> findClass(String className) throws ClassNotFoundException {
try {
/**
* Get a bytecode from file
*/
byte b[] = fetchClassFromFS(pathtobin + File.separator
+ className.replaceAll("\\.", escapeSeparator(File.separator)) + ".class");
return defineClass(className, b, 0, b.length);
} catch (FileNotFoundException ex) {
return super.findClass(className);
} catch (IOException ex) {
return super.findClass(className);
}
}
That as u can see uses defineClass() method from its parent - ClassLoader. The issue is when i'm trying to execute a class' (i recieve with my ClassLoader extension - let it be ru.xmppTesting.test.Disco) method getMethods() while getting an instance of this class i get the following
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/Header
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
at java.lang.Class.privateGetPublicMethods(Unknown Source)
at java.lang.Class.getMethods(Unknown Source)
at DOTGraphCreator.createGraphFromClasses(DOTGraphCreator.java:85)
at DOTGraphCreator.generateDotGraphFile(DOTGraphCreator.java:56)
at DOTGraphCreator.main(DOTGraphCreator.java:46)
Caused by: java.lang.ClassNotFoundException: org.apache.http.Header
at java.lang.ClassLoader.findClass(Unknown Source)
at SourceClassLoader.findClass(SourceClassLoader.java:27)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 7 more
As far as i can see that is because class org.apache.http.Header could not be found as defined. Because it is not.
So here's a question:
how can and must i define and link this Header class (and lots of others from my .jar libs) along with definition of ru.xmppTesting.test.Disco and others similar to have them defined on the fly?
If your are importing org.apache.http.Header from your dinamic loaded class, you need it to be accesible at your classpath.
If you don't want to load all the potentially needed jars on your classpath, you could try with a hack i have found here:
import java.lang.reflect.*;
import java.io.*;
import java.net.*;
public class ClassPathHacker {
private static final Class[] parameters = new Class[]{URL.class};
public static void addFile(String s) throws IOException {
File f = new File(s);
addFile(f);
}//end method
public static void addFile(File f) throws IOException {
addURL(f.toURL());
}//end method
public static void addURL(URL u) throws IOException {
URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Class sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod("addURL",parameters);
method.setAccessible(true);
method.invoke(sysloader,new Object[]{ u });
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}//end try catch
}//end method
}//end class
But, I must say, it could not be portable to some JVMs (not always the SystemClassLoader is a subclass of URLClassLoader)...
*EDIT: * In fact, as you have replaced the classloader with your own, perhaps you have some troubles...