Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialization without reflection in compiled classes

Due to the limitation on the client JVM, I can not use any of the popular serializers due to the fact that reflection is not supported. I am looking for a tool that perform byte-code manipulation to achieve serialization by injecting writer and reader methods into the already compiled class. I need byte-code manipulation java code to bind it with my code for building process.

I have been doing this by generating code and injecting it into the source code before compiling to use my custom serializer. I would like to avoid this approach as I don't want yo modify the source files in any way.

I am aware of Kryo and other XML and JSON serializers, but they don't match my needs.

Thanks.

like image 381
Juan Garcia Avatar asked Apr 23 '15 10:04

Juan Garcia


People also ask

Does serialization depend on reflection?

Java serialization uses reflection to scrape all necessary data from the object's fields, including private and final fields. If a field contains an object, that object is serialized recursively. Even though you might have getters and setters, these functions are not used when serializing an object in Java.

What is reflection based serialization?

Similar to the Flex side, serializing complex objects can be faciliated using class AttributeBasedSerializer . This serializer uses reflection to serialize objects of different class types.

What is reflection in Java serialization?

Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.

Is Java record serializable?

Serialization with records With Java Serialization, a record class is made serializable just like a normal class, simply by implementing java. io.


2 Answers

Try javassist. It's probably the most straightforward bytecode generation library for your particular project.

That's because you'll be able to reuse part of your current code generator as javassist is able to parse some simple forms of java code.

For instance you can do:

CtClass clazz = ...;
CtMethod m = CtNewMethod.make(
    "public void serialize(java.io.DataOutput out) {  out.writeInt(x); }",
     clazz);

If you want to dive deep into bytecode generation you can try asm. Asm will allow you to write bytecode directly but it seems to be an overkill for you type of problem.

Implementation with javassist

Here is the base skeleton to do this with javassist:

Path inputDir = Paths.get("target/classes");
Path outputDir = Paths.get("target/classes2");

ClassPool classPool = new ClassPool(true);
classPool.appendClassPath(inputDir.toString());

// get all class names from a certain directory
String[] classNames = Files.walk(inputDir)
        .filter(p -> (!Files.isDirectory(p)) && p.toString().endsWith(".class"))
        .map(p -> inputDir.relativize(p).toString())
        .map(s -> s.substring(0, s.length() - 6).replace(File.separatorChar, '.'))
        .toArray(size -> new String[size]);

for (String className : classNames) {
    CtClass clazz = classPool.get(className);
    // add further filtering to select the classes you want.

    // ex: "public void serializer(java.io.DataOutput out) { out.writeInt(x); } }"
    String serializerBody = generateSerializer(clazz);
    clazz.addMethod(CtNewMethod.make(serializerBody, clazz));

    // ex: "public void deserializer(java.io.DataInput in) { x = in.readInt(); } }";
    String deserializerBody = generateDeserializer(clazz);
    clazz.addMethod(CtNewMethod.make(deserializerBody, clazz));

    // save the modified class
    clazz.setModifiers(clazz.getModifiers() & ~Modifier.ABSTRACT);
    byte[] bytes = clazz.toBytecode();
    Path outFile = outputDir.resolve(className.replace('.', '/') + ".class");
    Files.createDirectories(outFile.getParent());
    Files.write(outFile, bytes);
}

Dependency: org.javassist:javassist:3.19.0-GA

like image 93
Daniel Sperry Avatar answered Oct 22 '22 19:10

Daniel Sperry


You can use my library Byte Buddy for this purpose. Byte Buddy is a byte code manipulation library and it allows you to easily add methods to any existing class. Furthermore, it allows you inject redefined code into a jar file. This way, you could ask Byte Buddy to redefine classes to add the required methods. However, note that adding methods to a class might change its implicit serialization uuid.

If your classes must not be loaded until your application starts up, Byte Buddy allows you to redefine classes without loading them. For this, you can use Byte Buddys type pool.

like image 41
Rafael Winterhalter Avatar answered Oct 22 '22 21:10

Rafael Winterhalter