|
|
@@ -0,0 +1,109 @@
|
|
|
+package net.ranides.assira.functional;
|
|
|
+
|
|
|
+import net.ranides.assira.io.IOStreams;
|
|
|
+import net.ranides.assira.io.IOUtils;
|
|
|
+import net.ranides.assira.reflection.IClass;
|
|
|
+import net.ranides.assira.reflection.IClassLoader;
|
|
|
+
|
|
|
+import java.io.*;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class CodeSerializer {
|
|
|
+
|
|
|
+ public static Serializable withCode(Serializable object) {
|
|
|
+ return new CodeWriter(new ClassWithCode(object));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static IClassLoader LOADER = new IClassLoader("serializer", IClassLoader.getSystem()){
|
|
|
+
|
|
|
+ private final Map<String, byte[]> bytecodes = new HashMap<>();
|
|
|
+
|
|
|
+ private final Map<String, IClass<?>> loaded = new HashMap<>();
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IClass<?> defineClass(String name, byte[] bytecode) {
|
|
|
+ if(bytecodes.containsKey(name)) {
|
|
|
+ if (Arrays.equals(bytecode, bytecodes.get(name))) {
|
|
|
+ return loaded.get(name);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("Different bytecode!");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ IClass<?> ic = super.defineClass(name, bytecode);
|
|
|
+ bytecodes.put(name, bytecode);
|
|
|
+ loaded.put(name, ic);
|
|
|
+ return ic;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ private static class CodeWriter implements Serializable {
|
|
|
+
|
|
|
+ private final ClassWithCode extern;
|
|
|
+
|
|
|
+ public CodeWriter(ClassWithCode extern) {
|
|
|
+ this.extern = extern;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object readResolve() {
|
|
|
+ return extern.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class ClassWithCode implements Serializable {
|
|
|
+
|
|
|
+ private static final long serialVersionUID = 4L;
|
|
|
+
|
|
|
+ private String classname;
|
|
|
+
|
|
|
+ private byte[] bytecode;
|
|
|
+
|
|
|
+ private transient Object value;
|
|
|
+
|
|
|
+ public ClassWithCode(Object value) {
|
|
|
+ try {
|
|
|
+ Class<?> ic = value.getClass();
|
|
|
+ try(InputStream is = ic.getClassLoader().getResourceAsStream(ic.getName().replace('.', '/') + ".class")) {
|
|
|
+ this.classname = ic.getName();
|
|
|
+ this.bytecode = IOStreams.read(is);
|
|
|
+ this.value = value;
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
|
|
|
+ out.writeObject(classname);
|
|
|
+ out.writeObject(bytecode);
|
|
|
+
|
|
|
+ ObjectOutputStream oos = new ObjectOutputStream(out);
|
|
|
+ oos.writeObject(value);
|
|
|
+ oos.flush();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
|
|
|
+ classname = (String)in.readObject();
|
|
|
+ bytecode = (byte[]) in.readObject();
|
|
|
+ IClass<?> ic = LOADER.defineClass(classname, bytecode);
|
|
|
+ ObjectInputStreamWithClass oisc = new ObjectInputStreamWithClass(in, ic);
|
|
|
+ this.value = oisc.readObject();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class ObjectInputStreamWithClass extends ObjectInputStream {
|
|
|
+ private final IClass<?> clazz;
|
|
|
+
|
|
|
+ public ObjectInputStreamWithClass(InputStream in, IClass<?> clazz) throws IOException {
|
|
|
+ super(in);
|
|
|
+ this.clazz = clazz;
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("rawtypes")
|
|
|
+ protected Class<?> resolveClass(ObjectStreamClass classDesc) throws ClassNotFoundException, IOException {
|
|
|
+ return classDesc.getName().equals(clazz.name()) ? clazz.raw() : super.resolveClass(classDesc);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|