From d5954e1a511e4aa139548abf33c51d27e016f8c4 Mon Sep 17 00:00:00 2001
From: Jan Vales <jan@jvales.net>
Date: Wed, 9 May 2018 04:51:05 +0200
Subject: [PATCH] [2.2.2] Transparent DI works.

---
 .../ass2/di/InjectionControllerFactory.java   | 12 +++---
 .../java/dst/ass2/di/agent/InjectorAgent.java | 43 ++++++++++++++++---
 .../ass2/di/impl/InjectionControllerImpl.java | 18 ++++++--
 3 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/ass2-di/src/main/java/dst/ass2/di/InjectionControllerFactory.java b/ass2-di/src/main/java/dst/ass2/di/InjectionControllerFactory.java
index f20b258..ca11b33 100644
--- a/ass2-di/src/main/java/dst/ass2/di/InjectionControllerFactory.java
+++ b/ass2-di/src/main/java/dst/ass2/di/InjectionControllerFactory.java
@@ -7,6 +7,7 @@ import dst.ass2.di.impl.InjectionControllerImpl;
  */
 public class InjectionControllerFactory {
     private static IInjectionController standalone = null;
+    private static IInjectionController transparent = null;
 
     /**
      * Returns the singleton {@link IInjectionController} instance.<br/>
@@ -29,8 +30,10 @@ public class InjectionControllerFactory {
      * @return the instance
      */
     public static synchronized IInjectionController getTransparentInstance() {
-        // TODO
-        return null;
+        if (transparent == null) {
+            transparent = getNewTransparentInstance();
+        }
+        return transparent;
     }
 
     /**
@@ -39,7 +42,7 @@ public class InjectionControllerFactory {
      * @return the newly created instance
      */
     public static IInjectionController getNewStandaloneInstance() {
-        return new InjectionControllerImpl();
+        return new InjectionControllerImpl(false);
     }
 
     /**
@@ -49,7 +52,6 @@ public class InjectionControllerFactory {
      * @return the instance
      */
     public static IInjectionController getNewTransparentInstance() {
-        // TODO
-        return null;
+        return new InjectionControllerImpl(true);
     }
 }
diff --git a/ass2-di/src/main/java/dst/ass2/di/agent/InjectorAgent.java b/ass2-di/src/main/java/dst/ass2/di/agent/InjectorAgent.java
index d0b5298..e311a10 100644
--- a/ass2-di/src/main/java/dst/ass2/di/agent/InjectorAgent.java
+++ b/ass2-di/src/main/java/dst/ass2/di/agent/InjectorAgent.java
@@ -1,18 +1,51 @@
 package dst.ass2.di.agent;
 
+import dst.ass2.di.annotation.Component;
+import javassist.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.lang.instrument.ClassFileTransformer;
 import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
 import java.security.ProtectionDomain;
 
 public class InjectorAgent implements ClassFileTransformer {
+    public static void premain(String args, Instrumentation instrumentation) {
+        instrumentation.addTransformer(new InjectorAgent());
+    }
 
     @Override
     public byte[] transform(ClassLoader loader, String className,
-            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
-            byte[] classfileBuffer) throws IllegalClassFormatException {
+                            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
+                            byte[] classfileBuffer) throws IllegalClassFormatException {
+
+        ClassPool classPool = new ClassPool();
+        classPool.appendClassPath(new LoaderClassPath(loader));
+        try {
+            CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
 
-        // TODO
-        return classfileBuffer;
-    }
 
+            // only @Components may continue.
+            if (!ctClass.hasAnnotation(Component.class)) {
+                return classfileBuffer;
+            }
+
+            // insert our code into constructors
+            for (CtConstructor ctor : ctClass.getConstructors()) {
+                try {
+                    ctor.insertAfter("dst.ass2.di.InjectionControllerFactory.getTransparentInstance().initialize(this);");
+                } catch (CannotCompileException e) {
+                    e.printStackTrace();
+                    return classfileBuffer;
+                }
+            }
+
+            // output new bytecode.
+            return ctClass.toBytecode();
+        } catch (IOException | CannotCompileException e) {
+            e.printStackTrace();
+            return classfileBuffer;
+        }
+    }
 }
diff --git a/ass2-di/src/main/java/dst/ass2/di/impl/InjectionControllerImpl.java b/ass2-di/src/main/java/dst/ass2/di/impl/InjectionControllerImpl.java
index 18a2072..29fc287 100644
--- a/ass2-di/src/main/java/dst/ass2/di/impl/InjectionControllerImpl.java
+++ b/ass2-di/src/main/java/dst/ass2/di/impl/InjectionControllerImpl.java
@@ -16,7 +16,11 @@ import java.util.concurrent.atomic.AtomicLong;
 public class InjectionControllerImpl implements IInjectionController {
     private final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>();
     private final AtomicLong ids = new AtomicLong(0L);
+    private final boolean isTransparent;
 
+    public InjectionControllerImpl(boolean b) {
+        isTransparent = b;
+    }
 
     private void initializeClass(Object obj, Class<?> clazz, Long id) throws IllegalAccessException {
         if (clazz == null || !clazz.isAnnotationPresent(Component.class)) {
@@ -64,7 +68,10 @@ public class InjectionControllerImpl implements IInjectionController {
                     } catch (InstantiationException e) {
                         e.printStackTrace();
                     }
-                    initialize(fieldObject);
+                    // only do this in standalone mode.
+                    if (!isTransparent) {
+                        initialize(fieldObject);
+                    }
                 }
 
                 boolean accessible = f.isAccessible();
@@ -84,7 +91,8 @@ public class InjectionControllerImpl implements IInjectionController {
             throw new InjectionException(clazz + " has no @ComponentID");
         }
 
-        initializeClass(obj, clazz.getSuperclass(), id);
+        // only do this in standalone mode.
+        if (!isTransparent) initializeClass(obj, clazz.getSuperclass(), id);
     }
 
 
@@ -125,7 +133,11 @@ public class InjectionControllerImpl implements IInjectionController {
             } catch (InstantiationException | IllegalAccessException e) {
                 e.printStackTrace();
             }
-            initialize(single);
+
+            // only do this in standalone mode.
+            if (!isTransparent) {
+                initialize(single);
+            }
         }
         return single;
     }
-- 
2.43.0