ClassCastException thrown when calling .asSubclass from separate jar - java

I have a program through which I have the following three classes. These first two are in jar1.jar:
Main (uses the jar loading trick found here):
package prob1;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class Main {
static List<Class<?>> classes = new ArrayList<Class<?>>();
static String pathToJar = "/Users/vtcakavsmoace/Desktop/jar2.jar";
public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {
loadJar();
classes.get(0).asSubclass(A.class).newInstance().someMethod();
}
private static void loadJar() throws IOException, ClassNotFoundException {
JarFile jarFile = new JarFile(pathToJar);
Enumeration<JarEntry> e = jarFile.entries();
URL[] urls = { new URL("jar:file:" + pathToJar + "!/") };
URLClassLoader cl = URLClassLoader.newInstance(urls);
while (e.hasMoreElements()) {
JarEntry je = e.nextElement();
if (je.isDirectory() || !je.getName().endsWith(".class")) {
continue;
}
String className = je.getName().substring(0, je.getName().length() - 6);
className = className.replace('/', '.');
classes.add(cl.loadClass(className));
}
jarFile.close();
}
}
A:
package prob1;
public interface A {
void someMethod();
}
And a second class found in jar2.jar, B:
package prob2;
import prob1.A;
public class B implements A {
#Override
public void someMethod() {
System.out.println("Hello, world!");
}
}
As you can see, class B obviously implements interface A. However, when loading class B, and then attempting to call asSubclass on it, fails with the following exception:
Exception in thread "main" java.lang.reflect.InvocationTargetException
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:497)
at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
Caused by: java.lang.ClassCastException: class prob2.B
at java.lang.Class.asSubclass(Class.java:3404)
at prob1.Main.main(Main.java:20)
... 5 more
Note that this does not fail when run through Eclipse.
What am I doing wrong here? How can I fix this?

Okay, found the answer using the knowledge that Erwin Bolwidt gave me; in class Main, replacing:
URLClassLoader cl = URLClassLoader.newInstance(urls);
with:
URLClassLoader cl = URLClassLoader.newInstance(urls, Main.class.getClassLoader());
Fixes the problem. :P

Related

Copy all the files from a directory to another

i'm trying to copy all the files inside a directory to another(but i want it to not copy the folders). I'm trying to use Files.copy but i'm getting this error:
Exception in thread "main" java.nio.file.FileAlreadyExistsException:
Here's my actual code:
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Exercici1 {
public static void copiarArchivos(String pathSource,String pathOutcome, String sufix) throws IOException {
File origen = new File(pathSource);
String[] contenidoOrigen = origen.list();
for(String string:contenidoOrigen){
File interno = new File(origen,string);
if (interno.isDirectory()){
copiarArchivos(interno.getPath(),pathOutcome,sufix);
} else {
Path targetOutcome = Paths.get(pathOutcome);
Path targetSource = Paths.get(interno.getPath());
Files.copy(targetSource,targetOutcome);
}
}
}
public static void main(String[] args) throws IOException {
copiarArchivos("Vampiro_Mascarada","pruebaPDF",".pdf");
}
}
My folder structure is like this:
/out
/pruebasPDF
/src
/Vampiro_Mascarada
/1.pdf
/2.pfdf
/Images
/1.png
/2.png
You need to use to Files.copy(source,dest,CopyOption) with the REPLACE_EXISTING option.

jcurses errors while try to import in IntelliJ

I`m trying to import jcurses library to IntelliJ but get an error.
File -> Project Structure -> Modules -> import jar file.
Then in code:
import jcurses.widgets.Window;
class main {
public static void main(String[] args){
Window win = new Window(800, 600, true, "test");
win.setVisible(true);
}
Exception in thread "main" java.lang.ExceptionInInitializerError
at jcurses.system.InputChar.<clinit>(InputChar.java:25)
at jcurses.widgets.Window.<clinit>(Window.java:209)
at main.main(main.java:7)
Caused by: java.lang.RuntimeException: couldn't find jcurses library
at jcurses.system.Toolkit.getLibraryPath(Toolkit.java:121)
at jcurses.system.Toolkit.<clinit>(Toolkit.java:37)
Could someone point out where is my mistake?
Thanks in advance!
The answer is that dll must be in the same directory as jar file. Thanks to everyone!
Libray download url
https://sourceforge.net/projects/javacurses/files/javacurses/0.9.5/
The path of the library is in the root directory of the project
dynamically add the library
import java.io.File;
import java.lang.reflect.Field;
import java.util.Arrays;
public class LoadLibrary {
public static void main(String cor[]) throws Exception {
// Cargando librerias necesarias
loadLibraryJCourses();
}
public static void loadLibraryJCourses() throws Exception{
loadDynamicLibrary(new File("lib/bin/jcourses/").getAbsolutePath(),"libjcurses64");
}
private static void addLibraryPath(String pathToAdd) throws Exception {
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
//get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
//check if the path to add is already present
for (String path : paths) {
if (path.equals(pathToAdd)) {
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
private static void loadDynamicLibrary(String pathLibraryDirectory, String libraryName) throws Exception{
File pathLibraryFile = new File(pathLibraryDirectory);
addLibraryPath(pathLibraryFile.getAbsolutePath());
System.loadLibrary(libraryName);
}
}

Weird NoClassDefFoundError of Java class

Environment: jdk1.7
javax.servlet-api-3.0.1.jar is needed for this test.
Reproduce steps:
javac Test1.java -cp javax.servlet-api-3.0.1.jar build Test1.java with javax.servlet-api-3.0.1.jar
javac Test2.java -cp javax.servlet-api-3.0.1.jar build Test2.java with javax.servlet-api-3.0.1.jar
javac Test3.java build Test3.java
java -classpath .:javax.servlet-api-3.0.1.jar Test3 run Test3 with dependency. Following is the output. It's OK here.
hello world1
hello world2
But when this command java Test3 run, Exception is thrown. The result at the end of this post. The weird thing is that "hello world1" could be printed out, but instead of printing out "hello world2" an exception is thrown.
Test1.java
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
public class Test1 {
public void getRequest(HttpServletResponse resp) throws IOException {
OutputStream os = resp.getOutputStream();
resp.getOutputStream().close();
}
public void hello(String world) {
System.out.println("hello " + world);
}
}
Test2.java
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
public class Test2 {
public void getRequest(HttpServletResponse resp) throws IOException {
OutputStream os = resp.getOutputStream();
try {
resp.getOutputStream().close();
} catch (Exception e) {
}
}
public void hello(String world) {
System.out.println("hello " + world);
}
}
Test3.java
public class Test3 {
public static void main(String[] args) {
new Test1().hello("world1");
new Test2().hello("world2");
}
}
output of the final step. Test2.hello("world2") throw an exception:
hello world1
Exception in thread "main" java.lang.NoClassDefFoundError: javax/servlet/ServletOutputStream
at cn.test.abc1.Test3.main(Test3.java:9)
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:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletOutputStream
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 6 more
I am very confused with the Exception. Because I didn't use any code in the Class ServletOutputStream. And the difference of the Test1 and Test2 is only a try blocked.
This Question is not a duplicated question as it marked. Because JVM should not throw an Exception when a try block get involved.
After compare the output of javap -v -c Test?, I get the answer.
The javac create StackMapTable attribute for try/catch block of Test2.java.
If StackMapTable is found on class loading, JVM will perform bytecode verification and check referenced class. That's why it throw java.lang.NoClassDefFoundError.
https://stackoverflow.com/a/25110513 describe more detail about StackMapTable.
Old wrong answer
I compile the Test1.java and decompile it, the source code become:
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletOutputStream;
public class Test1 {
public void getRequest(HttpServletResponse resp) throws IOException {
ServletOutputStream os = resp.getOutputStream();
resp.getOutputStream().close();
}
public void hello(String world) {
System.out.println("hello " + world);
}
}
Since the return object of resp.getOutputStream() is javax.servlet.ServletOutputStream. JVM still have to verify this class is sub-class of java.io.OutputStream before case it at runtime, so it try to load ServletOutputStream.class. But JVM cannot find it and throw ClassNotFoundException.
Makes perfect sense to me. Test3 references Test2 and Test1 and both need javax.servlet-api-3.0.1.jar on the classpath at runtime.

Though my class was loaded, Class.forName throws ClassNotFoundException

The code is as follows
what it does is it loads all the classes inside a jar file which I placed inside my home directory .
import java.io.File;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
import java.net.URLClassLoader;
import java.net.URL;
import java.util.Enumeration;
import java.lang.ClassLoader;
public class Plugin extends ClassLoader {
public static void main(String[] args) throws Exception {
File file = new File(System.getProperty("user.home") + "/HelloWorld.jar");
URLClassLoader clazzLoader = URLClassLoader.newInstance(new URL[]{file.toURI().toURL()});
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry element = entries.nextElement();
if (element.getName().endsWith(".class")) {
try {
Class c = clazzLoader.loadClass(element.getName().replaceAll(".class", "").replaceAll("/", "."));
c.newInstance(); // this proves that class is loaded
} catch (Exception e) {
e.printStackTrace();
}
}
}
Class cls = Class.forName("HelloWorld");
cls.newInstance();
Plugin p = new Plugin();
p.checkIfLoaded();
}
public void checkIfLoaded() {
System.out.println("coming in");
if (findLoadedClass("HelloWorld") != null){
System.out.println("Yepee, HelloWorld class is loaded !");
}
}
}
My HelloWorld is as in https://github.com/HarishAtGitHub/doc/blob/master/makeExecutableJar/HelloWorld.java
and the jar is got using the instructions in my github account mentioned above .
c.newInstance() works .
How did I confirm ?
the static block got executed ...
but Class.forName("HelloWorld") throws ClassNotFoundException
also findLoadedClass("HelloWorld") is null ..
I cannot understand why this strange behaviour ?
Please guide ...
This is a classloader issue.
As per the Javadocs to Class.forName, you are looking up the class using the classloader of the current class. As your main class, this will be the JVM's bootstrap classloader (and will more or less just include the standard library plus anything you provided as a -cp command line argument). It is not going to delegate to the classloader that you instantiated as a local variable, and so will not return classes that that classloader could find.
If you were to specify the classloader explicitly, and call
Class.forName("HelloWorld", true, clazzloader)
then the classloader you just created will be searched instead and your class should be found.
Because Class.forName(String) uses currentClassLoader and you have load the class in different ClassLoader.
According with javadoc, invoking Class.forName(String) is equivalent to:
Class.forName(className, true, currentLoader)

understanding urlclassloader, how to access a loaded jar's classes

I am trying to understand how to access/make available a jar file using URLClassLoader.
Firstly I am loading the jar file with
package myA;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import org.jgroups.JChannel;
public class loader {
JChannel channel;
String user_name=System.getProperty("user.name", "n/a");
private void start() throws Exception {
channel=new JChannel(); // use the default config, udp.xml
channel.connect("ChatCluster");
}
public void loadMe()throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
URL classUrl;
classUrl = new URL("file:///home/myJars/jgroups-3.4.2.Final.jar");
URL[] classUrls = { classUrl };
URLClassLoader ucl = new URLClassLoader(classUrls);
Class<?> c = ucl.loadClass("org.jgroups.JChannel");
for(Field f: c.getDeclaredFields()) {
System.out.println("Field name=" + f.getName());
}
Object instance = c.newInstance();
//Method theMethod = c.getMethod("main");
//theMethod.invoke(instance);
}
public static void main(String[] args) throws Exception {
new loader().loadMe();
new loader().start();
}
}
the printout shows the declared fields that are in jgroups-3.4.2.Final.jar, however it then throws a classnotfound error.
java -cp myA.jar myA.loader
Field name=DEFAULT_PROTOCOL_STACK
Field name=local_addr
Field name=address_generator
Field name=name
Field name=cluster_name
Field name=my_view
Field name=prot_stack
Field name=state_promise
Field name=state_transfer_supported
Field name=flush_supported
Field name=config
Field name=stats
Field name=sent_msgs
Field name=received_msgs
Field name=sent_bytes
Field name=received_bytes
Field name=probe_handler
Exception in thread "main" java.lang.NoClassDefFoundError: org/jgroups/JChannel
at myA.loader.start(loader.java:23)
at myA.loader.main(loader.java:45)
Caused by: java.lang.ClassNotFoundException: org.jgroups.JChannel
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 2 more
I don't understand why the printout shows that the class is loaded but then it is not found?
thx
Art
Your code has maybe a couple of problems. First, you instantiate the loader 2 times within main, so the second instance is independent from the first one and might not be aware that the first one loaded the class file definition of JChannel.
Moreover you've defined JChannel as a member of loader before, therefore the JRE should require the class definition for it at startup - else it should not know what this field should be. I've replaced it with the class you've loaded via the URLClassLoader which you should instantiate in start().
package myA;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import org.jgroups.JChannel;
public class Loader
{
Class<?> clazz;
String user_name=System.getProperty("user.name", "n/a");
private void start() throws Exception
{
if (this.clazz == null)
throw new Exception("Channel class was not loaded properly");
Object channel = this.clazz.newInstance(); // use the default config, udp.xml
Method chatCluster = this.clazz.getDeclaredMethod("connect", new Class[] { String.class });
chatCluster.invoke(channel, "ChatCluster");
}
public void loadMe() throws Exception
{
URL classUrl;
classUrl = new URL("file:///home/myJars/jgroups-3.4.2.Final.jar");
URL[] classUrls = { classUrl };
URLClassLoader ucl = new URLClassLoader(classUrls);
Class<?> c = ucl.loadClass("org.jgroups.JChannel");
for(Field f: c.getDeclaredFields())
{
System.out.println("Field name=" + f.getName());
}
this.clazz = c;
Object instance = c.newInstance();
//Method theMethod = c.getMethod("main");
//theMethod.invoke(instance);
}
public static void main(String[] args) throws Exception
{
Loader loader = new Loader();
loader.loadMe();
loader.start();
}
}
You should further add some error handling to the code.
Exception in thread "main" java.lang.NoClassDefFoundError: org/jgroups/JChannel
at myA.loader.start(loader.java:23)
The code fails on this line:
channel=new JChannel(); // use the default config, udp.xml
The type JChannel is not visible to loader's ClassLoader. This will be obvious if you try:
loader.class
.getClassLoader()
.loadClass("org.jgroups.JChannel");
You should not have any compile-time references to a dependency that will not be on the type's classpath at runtime.
Loading with a new child ClassLoader does not add that type to some global class pool. The loaders are hierarchical with child-parent relationships.

Categories

Resources