Dynamic Class Loading : Exception in thread "main" java.lang.ClassFormatError - java

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.

Related

Breakpoint in ByteBuddy advice class with IntelliJ IDEA

I am looking for a way to debug an advice class within IntelliJ.
In the following code sample, ByteBuddy is replacing the constructor of FileInputStream.
The method FileInputStreamCtorString.onEnter is being called as expected, right before the call to the constructor.
However, there is a problem during the execution of the onEnter method, apparently related to calling some static method and setting static field.
I would like to put a breakpoint inside the onEnter method for further investigation.
How should I configure IntelliJ to make the breakpoint stop as expected?
package com.example.javaagent.instrumentation;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.*;
import java.util.logging.Logger;
import static java.util.logging.Level.FINE;
import static net.bytebuddy.matcher.ElementMatchers.*;
public class FileInputStreamConstructorInstrumentationTests2 {
#Test
public void testByteBuddyfileInputStreamConstructor() {
ByteBuddyAgent.install();
AgentBuilder.Identified.Extendable extendableAgentBuilder = new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
.with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
.with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
.ignore(none())
.type(named("java.io.FileInputStream"))
.transform(new AgentBuilder.Transformer.ForAdvice());
extendableAgentBuilder = extendableAgentBuilder.transform(
new AgentBuilder.Transformer.ForAdvice()
.include(Thread.currentThread().getContextClassLoader())
.advice(
isConstructor().and(takesArguments(1)).and(takesArgument(0, String.class)),
this.getClass().getName() + "$FileInputStreamCtorString"));
extendableAgentBuilder.installOnByteBuddyAgent();
String filename = "example.txt";
createFile(filename);
try {
FileInputStream inputStream = new FileInputStream(filename);
int data = inputStream.read();
while (data != -1) {
System.out.print((char) data);
data = inputStream.read();
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
deleteFile(filename);
}
public static class FileInputStreamCtorString {
public static boolean iveBeenHere = false;
#Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(#Advice.Argument(0) String name) {
System.out.println("FileInputStreamCtorString: entered with param "+ name);
System.out.println("FileInputStreamCtorString: second log line");
JustPrint.sayHello();
System.out.println("FileInputStreamCtorString: after calling static method");
iveBeenHere = true;
System.out.println("FileInputStreamCtorString: after setting static field");
}
}
public static class JustPrint{
public static void sayHello(){
System.out.println("Did I say 'hello'?");
}
}
private static void deleteFile(String filename) {
File file = new File(filename);
// Check if the file exists
if (file.exists()) {
// If the file exists, delete it
if (file.delete()) {
System.out.println("File deleted successfully");
} else {
System.out.println("Failed to delete file");
}
} else {
System.out.println("File does not exist");
}
}
private static void createFile(String filename) {
File file = new File(filename);
// Check if the file already exists
if (!file.exists()) {
// If the file does not exist, create a new file
try {
file.createNewFile();
System.out.println("File created successfully");
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("File already exists");
}
}
}
When running the test testByteBuddyfileInputStreamConstructor, I get the following output:
[Byte Buddy] BEFORE_INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport#f9879ac on sun.instrument.InstrumentationImpl#37f21974
[Byte Buddy] REDEFINE BATCH #0 [1 of 1 type(s)]
[Byte Buddy] TRANSFORM java.io.FileInputStream [null, module java.base, Thread[Test worker,5,main], loaded=true]
[Byte Buddy] REDEFINE COMPLETE 1 batch(es) containing 1 types [0 failed batch(es)]
[Byte Buddy] INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport#f9879ac on sun.instrument.InstrumentationImpl#37f21974
File created successfully
FileInputStreamCtorString: entered with param example.txt
FileInputStreamCtorString: second log line
File deleted successfully
So, I'm sure that the following source line is reached and I would like the debugger to stop there:
System.out.println("FileInputStreamCtorString: entered with param "+ name);
Byte Buddy inlines advice code, meaning that your code is never executed, it only serves as a copy-paste template. You can set the delegate property in the enter and exit annotations to true if you wanted to invoke the advice methods instead.

How to access and read a .txt file from a runnable jar

How can i load a text file with a runnable .jar file, It works fine when it's not jarred but after i jar the application it can't locate the file. Here's what i'm using to load the text file.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class PriceManager {
private static Map<Integer, Double> itemPrices = new HashMap<Integer, Double>();
public static void init() throws IOException {
final BufferedReader file = new BufferedReader(new FileReader("prices.txt"));
try {
while (true) {
final String line = file.readLine();
if (line == null) {
break;
}
if (line.startsWith("//")) {
continue;
}
final String[] valuesArray = line.split(" - ");
itemPrices.put(Integer.valueOf(valuesArray[0]), Double.valueOf(valuesArray[1]));
}
System.out.println("Successfully loaded "+itemPrices.size()+" item prices.");
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (file != null) {
file.close();
}
}
}
public static double getPrice(final int itemId) {
try {
return itemPrices.get(itemId);
} catch (final Exception e) {
return 1;
}
}
}
Thanks for any and all help.
There are two reasons for this. Either the file is now embedded within the Jar or it's not...
Assuming that the file is not stored within the Jar, you can use something like...
try (BufferedReader br = new BufferedReader(new InputStreamReader(PriceManager.class.getResourceAsStream("/prices.txt")))) {...
If the prices.txt file is buried with the package structure, you will need to provide that path from the top/default package to where the file is stored.
If the file is external to the class/jar file, then you need to make sure it resides within the same directory that you are executing the jar from.
if this is your package structure:
Correct way of retrieving resources inside runnable or.jar file is by using getResourceAsStream.
InputStream resourceStream = TestResource.class.getResourceAsStream("/resources/PUT_Request_ER.xml");
If you do getResource("/resources/PUT_Request_ER.xml"), you get FileNotFoundException as this resource is inside compressed file and absolute file path doesn't help here.

How to read/write an object that has linked lists in a file

I have these classes: "MyClass1", "MyClass2", "MyClass3" and "MyMainClass",
public class MyMainClass implements Serializable {
private String att1, att2, att3;
private int att4, att5, att6;
private LinkedList <MyClass1> myClass1List = new LinkedList<MyClass1>();
private LinkedList <MyClass2> myClass2List = new LinkedList<MyClass2>();
private LinkedList <MyClass3> myClass3List = new LinkedList<MyClass3>();
}
My program create registers (Objects) of "MyMainClass" and deposit it in a LinkedList. I want to save the LinkedList of the objects in a file to get them after i reopen my program. What's the way to do it? I have tried with ObjectOutputStream, but doesn't work. Thanks.
Edit:
My code to add an object(I just read an example and tried):
public static void addObject (MyMainClass p) {
try {
outputStream = new ObjectOutputStream(new FileOutputStream("myfile.dat"));
outputStream.writeObject(p);
} catch (FileNotFoundException ex) {
ex.printStackTrace();
System.exit(1);
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1);
} finally {
try {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1);
}
}
}
Note: "MyClass1", "MyClass2", "MyClass3" are Serializable.
I would make "myClass1", "myClass2", and "myClass3" Serializable, then wrap myClass1List, myClass2List, and myClass3List (and any other data you want to save) in another serializable class so you can use serialization/deserialization to save and restore all of the program state at once.
Unless myMainClass is that wrapper, in which case you need to declare that they all implement Serializable.
myMainClass isn't marked Serializable. Also, are myClass1, myClass2, and myClass3 serializable as well? If not, they should be.
On another note, please follow Java naming conventions; class name should start with an uppercase letter.
UPDATE
Are you sure that it's not writing to the file, or is it that the code is throwing exceptions that you cannot see?
In all your catch blocks, you have System.exit(1), which gives you absolutely no information about any exceptions that are happening; you're essentially swallowing them. You should at least print out the stacktrace (ex.printStackTrace()) so you can see what is going wrong.
I used following for my highschool project long time ago. Due to my poor English skills I do not really understand what class you wish to save and load (LinkedList or myMainClass), but I used this solution to successfully store and load any of my custom objects. I hope you find it handy.
Usage:
myMainClass object;
//
// ... your code fillin up the content of object
//
MyIO io = new MyIO();
io.save("", "myfile.dat", object); // "" as first argument will make java use current working directory
// to load the object:
myMainObject object = (myMainObject) io.load("", "myfile.dat");
Source:
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
public class MyIO {
// String path - path to the directory where the file is supposed to be saved.
// String filename - the name of the file
// Object data - object that you wish to save in the file. In your case "myMainClass"
public void save(String path, String filename, Object data) {
try {
FileOutputStream fos = new FileOutputStream(path + filename, false);
GZIPOutputStream gzos = new GZIPOutputStream(fos);
ObjectOutputStream out = new ObjectOutputStream(gzos);
out.writeObject(data);
out.flush();
out.close();
} catch (IOException e) {
System.out.println(e);
}
}
// String path - path to the directory where the file is stored
// String filename - the name of the file
// The function returns java object which can be cast to myMainClass.
public Object load(String path, String filename) {
try {
FileInputStream fis = new FileInputStream(path + filename);
GZIPInputStream gzis = new GZIPInputStream(fis);
ObjectInputStream in = new ObjectInputStream(gzis);
Object data = in.readObject();
in.close();
return data;
} catch (Exception e) {
System.out.println(e);
}
return null;
}
}

Java classLoader dilemma with locked jars

I was playing around with classLoaders in Java and noticed a strange thing. If a classLoader loads a class from a jar, this jar is locked indefinitely even if you unreference your classLoader.
In the below example, the jar contains a class called HelloWorld. What I do is try to load the class contained in the jar via a classLoader which adds the jar dynamically. If you set skip to true and do not call Class.forName, you can delete the jar but if you do not skip and even if you unreference the classLoader (classLoader = null), the jar cannot be deleted until the JVM exits.
Why is that?
PS: I am using java 6 and the code is very verbose for testing purposes
package loader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class TestClassLoader {
private URLClassLoader classLoader;
public TestClassLoader() throws MalformedURLException, IOException {
System.out.println("Copying jar");
if (copyJar()) {
System.out.println("Copying SUCCESS");
performFirstCheck();
} else {
System.out.println("Copying FAILED");
}
}
public static void main(String[] args) throws IOException {
System.out.println("Test started");
TestClassLoader testClassLoader = new TestClassLoader();
System.out.println("Bye!");
}
public void performFirstCheck() throws IOException {
System.out.println("Checking class HelloWorld does not exist");
if (!checkClassFound(TestClassLoader.class.getClassLoader(), false)) {
System.out.println("Deleting jar");
deleteJar();
System.out.println("First Check SUCCESS");
performSecondCheck();
} else {
System.out.println("First Check FAILED");
}
}
private void performSecondCheck() throws IOException {
System.out.println("Copying jar");
if (copyJar()) {
System.out.println("Copying SUCCESS");
createClassLoaderAndCheck();
} else {
System.out.println("Copying FAILED");
}
}
private void createClassLoaderAndCheck() throws MalformedURLException {
System.out.println("Creating classLoader");
createClassLoader();
System.out.println("Checking class HelloWorld exist");
if (checkClassFound(classLoader, true)) {
System.out.println("Second Check SUCCESS");
classLoader = null;
System.out.println("Deleting jar");
if (deleteJar()) {
System.out.println("Deleting SUCCESS");
} else {
System.out.println("Deleting FAILED");
}
} else {
System.out.println("Second Check FAILED");
}
}
public void createClassLoader() throws MalformedURLException {
URL[] urls = new URL[1];
File classFile = new File("C:\\Users\\Adel\\Desktop\\classes.jar");
urls[0] = classFile.toURI().toURL();
classLoader = new URLClassLoader(urls);
}
public boolean checkClassFound(ClassLoader classLoader, boolean skip) {
if (skip) {
System.out.println("Skiping class loading");
return true;
} else {
try {
Class.forName("HelloWorld", true, classLoader);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
public URLClassLoader getClassLoader() {
return classLoader;
}
public boolean copyJar() throws IOException {
File sourceJar = new File("C:\\Users\\Adel\\Desktop\\Folder\\classes.jar");
File destJar = new File("C:\\Users\\Adel\\Desktop\\classes.jar");
if (destJar.exists()) {
return false;
} else {
FileInputStream finput = new FileInputStream(sourceJar);
FileOutputStream foutput = new FileOutputStream(destJar);
byte[] buf = new byte[1024];
int len;
while ((len = finput.read(buf)) > 0) {
foutput.write(buf, 0, len);
}
finput.close();
foutput.close();
return true;
}
}
public boolean deleteJar() {
File destJar = new File("C:\\Users\\Adel\\Desktop\\classes.jar");
return destJar.delete();
}
}
I have found an answer and a workaround.
Based on this article and this amazing related article, it is a bad habit to use Class.forName(className, true, classLoader) because it keeps the class cached in the memory indefinitely.
The solution was to use classLoader.loadClass(clasName) instead, then once finished, unreference the classLoader and call the garbage collector using:
classLoader = null;
System.gc();
Hope this helps others! :)
Background Info:
My project was a complexe one: we had a GWT server acting as a RMI client to another server. So to create instances, GWT needed to download the classes from the server and load them. Later, GWT would resend instance to the server to persist them in database using Hibernate. In order to support hot deployment, we opted for dynamically class loading where a user would upload a jar and notifies the server who would load the classes from it and present them as available to GWT server
In Java 7 URLClassLoader has a #close() method that fixes this.

Java Custom Class Loader

Any ideas why I am getting this error? (Yes, I looked up the error, and still haven't found a solution)
My error:
Exception in thread "main" java.lang.ClassFormatError: Truncated class file
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at org.fellixombc.mysql.util.MysqlClassLoader.findClass(MysqlClassLoader.java:22)
at org.fellixombc.mysql.util.MysqlClassLoader.loadClass(MysqlClassLoader.java:14)
at org.fellixombc.mysql.Main.main(Main.java:9)
Files:
Main.java
package org.fellixombc.mysql;
import org.fellixombc.mysql.util.MysqlClassLoader;
public class Main {
public static void main(String[] args) {
MysqlClassLoader mcl = new MysqlClassLoader();
try {
mcl.loadClass("org.fellixombc.mysql.net.Client");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Client.java:
package org.fellixombc.mysql.net;
public class Client {
public Client() {
System.out.println("Hello!");
}
}
MysqlClassLoder.java:
package org.fellixombc.mysql.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class MysqlClassLoader extends ClassLoader {
public MysqlClassLoader() {
super(MysqlClassLoader.class.getClassLoader());
}
#Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
return findClass(className);
}
#Override
public Class<?> findClass(String className) throws ClassNotFoundException {
byte[] b = null;
try {
b = loadClassData(className);
Class c = defineClass(className, b, 0, b.length);
if(c != null)
return c;
return super.findClass(className);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private byte[] loadClassData(String className) throws IOException {
int size = className.length();
byte buff[] = new byte[size];
// Open the file
FileInputStream fis = new FileInputStream("bin/" + className.replace('.', File.separatorChar) + ".class");
fis.available();
fis.read(buff);
fis.close();
return buff;
}
}
Yes, you're reading at most a byte count equal to the number of characters in the filename. Instead, you need to read the whole file. Here's one method, using readFully as you suggested.
File f = new File("bin/" + className.replace('.', File.separatorChar) + ".class");
DataInputStream is = new DataInputStream(new FileInputStream(f));
int len = (int)f.length();
byte[] buff = new byte[len];
is.readFully(buff);
is.close();
return buff;
Since you're not handling built-in classes like Object, I think you need to catch the FileNotFoundException from loadClassData in your findClass, then call super.findClass. E.g.:
try {
try {
b = loadClassData(className);
}
catch(FileNotFoundException fnf) {
return super.findClass(className);
}
Class c = defineClass(className, b, 0, b.length);
if(c != null)
return c;
return super.findClass(className);
} catch (IOException e) {
e.printStackTrace();
}
return null;
You are reading only N bytes (N=length of class name) from the .class file into the buffer (in loadClassData) before returning it.
You need to read the contents of the entire class before you return the buffer for the class to be properly defined.

Categories

Resources