ClassNotFoundException when deserializing a binary class file's contents - java

I don't know much about Java. I'm trying to read a file containing an int and various instances of a class called "Automobile". When I deserialize it, though, the program throws a ClassNotFoundException and I can't seem to understand why.
Here's the code:
try {
FileInputStream fin = new FileInputStream(inputFile);
ObjectInputStream input = new ObjectInputStream(fin);
conto = input.readInt();
Automobile[] macchine = new Automobile[conto];
for(int i = 0; i < conto; i++) {
macchine[i] = (Automobile)input.readObject();
}
String targa;
System.out.print("\nInserire le cifre di una targa per rintracciare l'automobile: ");
targa = sc1.nextLine();
for(int i = 0; i < conto; i++) {
if(macchine[i].getTarga().equals(targa))
System.out.println(macchine[i]);
}
} catch(IOException e) {
System.out.println("Errore nella lettura del file "+inputFile);
} catch(java.lang.ClassNotFoundException e) {
System.out.println("Class not found");
}
Thanks in advance.
EDIT: here's the stacktrace
java.lang.ClassNotFoundException: es4.Automobile
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:604)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1575)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1732)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at es4p2.Main.main(Main.java:35)

When you deserialize a serialized object tree, the classes of all the objects have to be on the classpath. In this context, a ClassNotFoundException most likely means that one of the classes required is not on the classpath. You have to address this for deserialization to work.
In this case, the es4.Automobile is missing.
Could the problem be caused by a custom exception I made which is fired by Automobile?
The only other possibilities I can think of are:
es4.Automobile has a direct or indirect dependency on some other class that is missing
the static initialization of es4.Automobile or a dependent class has thrown an exception that has not been caught internally to the class.
But both of those should (I think) have resulted in a different stack trace.
I just noticed the package name is es4p2, not es4. Why does it say es4? Could it be because the program which saves the file uses another package name?
I've no idea why they are different. You'd need to talk to whoever wrote the code / produced the serialized objects. However, this is most likely the cause of your problem. A class with a different package name is a different class. Period.
You should always output (or better, log) the stacktrace when an unexpected exception is caught. That will tell you (and us) more about what has gone wrong, and in this case the name of the class that is missing.

This is and old question but this may help someone else. I faced the same issue and the problem was that I was not using the current thread class loader. You will find below the serializer class that I used in a grails project, should be quite straightforward use this in java
Hope this helps
public final class Serializer<T> {
/**
* Converts an Object to a byte array.
*
* #param object, the Object to serialize.
* #return, the byte array that stores the serialized object.
*/
public static byte[] serialize(T object) {
ByteArrayOutputStream bos = new ByteArrayOutputStream()
ObjectOutput out = null
try {
out = new ObjectOutputStream(bos)
out.writeObject(object)
byte[] byteArray = bos.toByteArray()
return byteArray
} catch (IOException e) {
e.printStackTrace()
return null
} finally {
try {
if (out != null)
out.close()
} catch (IOException ex) {
ex.printStackTrace()
return null
}
try {
bos.close()
} catch (IOException ex) {
ex.printStackTrace()
return null
}
}
}
/**
* Converts a byte array to an Object.
*
* #param byteArray, a byte array that represents a serialized Object.
* #return, an instance of the Object class.
*/
public static Object deserialize(byte[] byteArray) {
ByteArrayInputStream bis = new ByteArrayInputStream(byteArray)
ObjectInput input = null
try {
input = new ObjectInputStream(bis){
#Override protected Class<?> resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) return super.resolveClass(desc);
return Class.forName(desc.getName(), false, cl);
}
};
Object o = input.readObject()
return o
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace()
return null
} finally {
try {
bis.close()
} catch (IOException ex) {
}
try {
if (input != null)
input.close()
} catch (IOException ex) {
ex.printStackTrace()
return null
}
}
}

This generally happens if your class Automobile is not in the runtime classpath.

I fixed it in an easier way than the other answers -- my problem occurred when using the class in multiple projects.
If you have multiple projects, make sure that the specific class you're deserializing is in the exact same path! That means, the same package names etc inside that project. Otherwise it won't find it and cause the ClassNotFoundException to be thrown.
So if it's in
/myPackage/otherPackage/Test.java
Then make sure, that path is exactly the same in your other project.

Ive had a similar problem with a ObjectInputStream reading serialized Objects. The classes for those Objects i added at runtime with a URLClassloader. The problem was that the ObjectInputStream did not use the Thread ClassLoader which i set with
Thread.currentThread().setContextClassLoader(cl);
but instead the AppClassLoader, which you cannot customize with java 9. So i made my own ObjectInputStream as a subtype of the original one and overidden the resolveClass Method:
#Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String name = desc.getName();
try {
return Class.forName(name, false, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException ex) {
Class<?> cl = primClasses.get(name);
if (cl != null) {
return cl;
} else {
throw ex;
}
}
}

Does your Automobile class have a field like this?
private static final long serialVersionUID = 140605814607823206L; // some unique number
If not, define one. Let us know if that fixes it.

You can access the class name from the message of the ClassNotFound exception - it's horrible to depend on this in the code - but it should give you some idea. I wish there were some better way of getting information about serialised objects without having to have the class available.

Related

Issue with loading class from jar file represented as byte array

I'm trying to create an instance of a class from jar file loaded on a byte array.
I'm receiving two args:
1. byte[] which represents jar file with required class
2. Qualified class name
When I'm testing it locally it works as expected, but when I upload exactly the same jar file with the same qualified class name remotely (using web application implemented with Spring MVC for back and AngularJS for front end deployed in Tomcat server) It can't find the required class:
java.lang.ClassNotFoundException
When I was debugging, it turned out, that classloader is properly invoked but no one class is loaded from jar.
I would be grateful if anyone can tell what can be the reason of that difference or how can I implement this functionality in other ways.
A method which loads class and returns an instance of it:
public static <T> T getInstanceOfLoadedClass(byte[] jarFileBytes, String qualifiedClassName) throws ClassFromJarInstantiationException {
LOGGER.info("Getting instance of class from loaded jar file. Class name: " + qualifiedClassName);
try {
return (T) Class.forName(qualifiedClassName, true, new ByteClassLoader(jarFileBytes)).newInstance();
} catch (InstantiationException | IllegalAccessException | IOException | ClassNotFoundException | NoSuchFieldException e) {
LOGGER.error("Exception was thrown while reading jar file for " + qualifiedClassName + "class.", e);
throw new ClassFromJarInstantiationException(e);
}
}
Custom ByteClassLoader:
public class ByteClassLoader extends ClassLoader {
private static final Logger LOGGER = Logger.getLogger(ByteClassLoader.class);
private final byte[] jarBytes;
private final Set<String> names;
public ByteClassLoader(byte[] jarBytes) throws IOException {
this.jarBytes = jarBytes;
this.names = loadNames(jarBytes);
}
private Set<String> loadNames(byte[] jarBytes) throws IOException {
Set<String> set = new HashSet<>();
try (ZipInputStream jis = new ZipInputStream(new ByteArrayInputStream(jarBytes))) {
ZipEntry entry;
while ((entry = jis.getNextEntry()) != null) {
set.add(entry.getName());
}
}
return Collections.unmodifiableSet(set);
}
#Override
public InputStream getResourceAsStream(String resourceName) {
if (!names.contains(resourceName)) {
return null;
}
boolean found = false;
ZipInputStream zipInputStream = null;
try {
zipInputStream = new ZipInputStream(new ByteArrayInputStream(jarBytes));
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
if (entry.getName().equals(resourceName)) {
found = true;
return zipInputStream;
}
}
} catch (IOException e) {
LOGGER.error("ByteClassLoader threw exception while reading jar byte stream for resource: "+resourceName, e);
e.printStackTrace();
} finally {
if (zipInputStream != null && !found) {
try {
zipInputStream.close();
} catch (IOException e) {
LOGGER.error("ByteClassLoader threw exception while closing jar byte stream for resource: "+resourceName, e);
e.printStackTrace();
}
}
}
return null;
} }
The problem was that the class required to be loaded was in a range of classloader while it was tested.
Hope it helps someone in solving this problem because it is really easy to miss.

I want to extract a string from the current exception thrown in a java program

I am calling a method from a third party jars whose class file is not accessible to me. So for some cases it throws exception logs and I want to extract string from the current log dynamically.
This is the java program method which throws exception
public String runLayoutTest(final String xmlFile){
try{
String gettingValue = "novalue";
boolean errorFlag = perform("runLayoutTest", new Reporter.Reportable() {
#Override
public boolean run() throws Exception {
String layoutXml = null;
//current directory
Path currentRelativePath = Paths.get("");
String currentProjectPath = currentRelativePath.toAbsolutePath().toString();
System.out.println("Current relative path is: " + currentProjectPath);
System.out.println("layoutXml "+xmlFile);
String x = client.runLayoutTest(currentProjectPath+"\\Excel\\"+xmlFile);
System.out.println("******* x ********"+x);
setX(x);
return true;
}
});
gettingValue = getX();
return gettingValue;
}catch(Exception e){
System.out.println("**Any one rule in Layout is failed**");
//System.out.println(e.getMessage());
return getX();
}
}
Here the client is a object of the third party jar file , And that is throwing me the exception on some odd cases .
The Exception logs are
com.experitest.client.InternalException: Exception caught while executing runLayoutTest: {"rule_1":{"Exists":true},"rule_2":{"Exists":true,"EqualHeight":false},"rule_3":{"AlignedLeft":false}}
at com.experitest.client.JavaClientInternals.executeInternally(JavaClientInternals.java:234)
at com.experitest.client.Client.execute(Client.java:237)
at com.experitest.client.Client.runLayoutTest(Client.java:1475)
at com.igate.framework.NativeDriver$79.run(NativeDriver.java:2753)
at com.igate.framework.Reporter.action(Reporter.java:81)........
From this exception I want to extract
runLayoutTest: {"rule_1":{"Exists":true},"rule_2":{"Exists":true,"EqualHeight":false},"rule_3":{"AlignedLeft":false}}
as a String.
Hence is there any method with which I can dynamically extract such String whenever it occurs.
And I still don't know the reason why my catch method is not getting called.
In your case you can just use Exception.getMessage() which will result in
Exception caught while executing runLayoutTest:
{"rule_1":{"Exists":true},"rule_2":{"Exists":true,"EqualHeight":false},"rule_3":{"AlignedLeft":false}}
Below can be used to get the json string. You can then parse the json to get desired data from it
String message = e.getMessage();
int colonIndex = message.indexOf(":");
String json = null;
if (colonIndex != -1) {
json = message.substring(colonIndex + 1);
}
Please note that this solution will not work in case of wrapped exception
I had to implement this code to attack directly to throwable detailMessage. I had to ignore all the hierarchy of classes that extended Exception because someone implemented a horrible getMessage method that was being overlapped with web components. In JUnit was no way to define unit test cases because framework's exceptions always tried to translate its error code into the 'session' (web) language...
public static Object getThrowableDetailMessage(Throwable throwable) {
try {
// Tiramos la puerta abajo y pedimos a Throwable que nos pase el código de error
Field f = Throwable.class.getDeclaredField("detailMessage");
f.setAccessible(true);
Object detailMessageFound = f.get(throwable);
return detailMessageFound;
} catch (SecurityException ex) {
//LOG
return null;
} catch (NoSuchFieldException e) {
//LOG
return null;
} catch (IllegalArgumentException e) {
//LOG
return null;
} catch (IllegalAccessException e) {
//LOG
return null;
}
}
You also can try to replace Throwable by InternalException and keep the reflection on this level of the hierachy. But like someone is already pointing. Make sure your code is being affected by the Exception you want to inspect.
I have implemented all the catches possible because I wanted to trace a different log for everyone of them. (For debugging and testing overall)
Note: My code has been implemented under JDK 1.6_18

What values to put in my properties file to get an IOException while loading?

I am reading a file from its classpath in Java project.
Sample Code:
public static Properties loadPropertyFile(String fileName) {
Properties properties = new Properties();
InputStream inputStream = PropertyReader.class.getClassLoader().getResourceAsStream(fileName);
if (inputStream != null) {
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
} else {
throw new Exception("Property file: [" + fileName + "] not found in the classpath");
}
return properties;
}
It's working fine. I am writing Junit tests for this code. How can I create a scenario for IOException in properties.load(inputStream)?
What values should I put in my properties.file to get IOException?
When you look into the implementation of Properties::load, you find out that the class never throws the exception explicitly. The only way to trigger an IOException would be to hand an InputStream that throws this exception upon invoking the input stream's read method.
Do you have control over the PropertyReader class? One way to emulate this error would be to instrument this class loader to return an errornous InputStream for a given test value of fileName to throw an IOException. Alternatively, you could make the method more flexible by changing the signature to:
public static Properties loadPropertyFile(String fileName) {
return loadPropertyFile(fileName, PropertyReader.class);
}
public static Properties loadPropertyFile(String fileName, ClassLoader cl) {
// your code...
}
with handing a class loader:
class TestLoader extends ClassLoader {
#Override
public InputStream getResourceAsStream() {
return new InputStream() {
#Override
public byte read() throws IOException {
throws new IOException();
}
}
}
}
You cannot add specific characters to the properties file that cause an IOException as the InputStream only reads bytes. Any encoding-realted problem will instead result in an IllegalArgumentException.
Have you considered a mocking framework? Mock the new operator for Properties to return a mock implementation that throws IOException when its load method is called.
mockitio and powermockito will allow you to do this.
One way of forcing the load call to throw IOException is to pass a closed stream. If you can refactor your method to accept a InputStream you can pass a closed stream to the method and check whether it is throwing an exception.
That said, Unit Tests are supposed to cover the code you write. It seems to me you are testing whether load throws an exception if the input stream has an error. Which is superfluous.
I know that this is an old thread, but since I recently ran into the same problem and based on Rafael Winterhalter's answer I came up with this test:
#Test
void testFailLoadProjectFile() throws NoSuchMethodException, SecurityException {
final var method = LogGeneratorComponent.class.getDeclaredMethod("loadProjectProperties", Properties.class);
Assertions.assertTrue(Modifier.isPrivate(method.getModifiers()), "Method is not private.");
method.setAccessible(true);
final var obj = new LogGeneratorComponent();
Assertions.assertThrows(InvocationTargetException.class, () -> method.invoke(obj, new Properties() {
private static final long serialVersionUID = 5663506788956932491L;
#Override
public synchronized void load(#SuppressWarnings("unused") final InputStream is) throws IOException {
throw new IOException("Invalid properties implementation.");
}
}), "An InvocationTargetException should have been thrown, but nothing happened.");
}
And this is my actual method:
private static Properties loadProjectProperties(final Properties properties) {
try (final var is = Thread.currentThread().getContextClassLoader().getResourceAsStream("project.properties")) {
properties.load(is);
return properties;
} catch (final IOException e) {
throw new UncheckedIOException("Error while reading project.properties file.", e);
}
}
Obviously OP can customize it to receive a fileName parameter and make it public in order to have a simpler test.
Empty or Invalid fileName parameter.
pass a blank string as file name

How to test a Class.forName call in Java code?

I've been messing around with ClassLoaders in java recently, trying to test code which uses dynamic loading of classes (using Class.forName(String name)) with a custom ClassLoader.
I've got my own custom ClassLoader set up, which is supposed to be configurable to throw a ClassNotFoundException when trying to load a given class.
public class CustomTestClassLoader extends ClassLoader {
private static String[] notAllowed = new String[]{};
public static void setNotAllowed(String... nonAllowedClassNames) {
notAllowed = nonAllowedClassNames;
}
public static String[] getNotAllowed() {
return notAllowed;
}
public CustomTestClassLoader(ClassLoader parent){super(parent);}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
for (String s : notAllowed) {
if (name.equals(s)) {
throw new ClassNotFoundException("Loading this class is not allowed for testing purposes.");
}
}
if(name.startsWith("java") || name.startsWith("sun") || getClass().getName().equals(name)) {
return getParent().loadClass(name);
}
Class<?> gotOne = super.findLoadedClass(name);
if (gotOne != null) {
return gotOne;
}
Class<?> c;
InputStream in = getParent().getResourceAsStream(name.replace('.', '/')+".class");
if (in == null) {
throw new ClassNotFoundException("Couldn't locate the classfile: "+name);
}
try {
byte[] classData = readBytes(in);
c = defineClass(name, classData, 0, classData.length);
} catch(IOException e) {
throw new ClassNotFoundException("Couldn't read the class data.", e);
} finally {
try {
in.close();
} catch (IOException e) {/* not much we can do at this point */}
}
if (resolve) {
resolveClass(c);
}
return c;
}
private byte[] readBytes(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[4194304];
int read = in.read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = in.read(buffer);
}
out.close();
return out.toByteArray();
}
}
I'm using -Djava.system.class.loader=com.classloadertest.test.CustomTestClassLoader to set this classloader as default ClassLoader.
I was hoping to be able to force a ClassNotFoundException by disallowing certain class names using CustomTestClassLoader.setNotAllowed(String...).
However, it only works for ClassLoader.loadClass, and not for Class.forName:
public void test() {
ClassLoader loader = this.getClass().getClassLoader();
CustomTestClassLoader custom = (CustomTestClassLoader)loader;
CustomTestClassLoader.setNotAllowed(NAME);
for (String s : custom.getNotAllowed())
System.out.println("notAllowed: "+s);
try {
System.out.println(Class.forName(NAME));
} catch (ClassNotFoundException e) {
System.out.println("forName(String) failed");
}
try {
System.out.println(Class.forName(NAME,false,custom));
} catch (ClassNotFoundException e) {
System.out.println("forName(String,boolean,ClassLoader) failed");
}
try {
System.out.println(custom.loadClass(NAME));
} catch (ClassNotFoundException e) {
System.out.println("ClassLoader.loadClass failed");
}
}
Now I expected all three try blocks to fail, since the documentation of Class.forName says it uses the ClassLoader of the caller (which should be custom/loader in this test).
However, only the final try block fails. Here is the output I get:
notAllowed: com.classloadertest.test.Test
class com.classloadertest.test.Test
class com.classloadertest.test.Test
ClassLoader.loadClass failed
Does Class.forName really use the classloader? And if so, which methods?
It seems to be using a native call, so I have no idea what it does under the covers.
Of course if anyone knows any alternative ways of testing a Class.forName() call, it would be much appreciated as well.
Class.forName() uses the classloader of the class where it is called from (e.g in your case the class that contains the test() method). So, if you are running it in a different environment this will cause the problem.
UPDATE That ClassLoader will be used in Class.forName() which loaded your Test class. And that may be the solution: It may be an Eclipse-defined classloader, that has access to your class, so it will load it. Despite that its parent (or root) classloaders have explicit rule to forbid the loading of that class.
I still recommend to make a wrapper class for this instantiation. You should load that class with your CustomTestClassLoader, then you can use Class.forName() in that class.

How to get an instanced Object from an incomplete class name?

world! I need to instantiate an Object from the name of its Class. I know that it is possible to do it, this way
MyObject myObject = null;
try {
Constructor constructor = Class.forName( "fully.qualified.class.name" ).getConstructor(); // Get the constructor without parameters
myObject = (MyObject) constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
The problem is that the name of my class is not fully qualified. Is there a way to get the complete name by only knowing the short name?
MyObject myObject = null;
for (Package p : Package.getPackages()) {
try {
myObject = Class.forName(p.getName() + "." + className).newInstance();
break;
} catch (ClassNotFoundException ex) {
// ignore
}
}
The Package.getPackages() call will give you every package known to the current classes ClassLoader and its ancestors.
Warning: this will be expensive because you are repeatedly throwing and catching exceptions. It may be possible to speed it up by testing:
this.getClass().getClassLoader().findResource(binaryClassName) != null
before calling Class.forName(...) or the equivalent.
Try this repeatedly for a package search path. ;)
String[] packages = ...;
String className = ...;
MyObject myObject = null;
for(String p : packages)
try {
myObject = Class.forName(p + '.' + className).newInstance();
break;
} catch (Exception ignored) {
}

Categories