injectable = Optional.ofNullable(job.getClass().getAnnotation(Injectable.class));
if (injectable.isPresent()) {
- DIUtil.inject(injectable.get().value().getName(), job);
+ DIUtil.inject(injectable.get().value(), job);
}
}
diff --git a/java/src/main/java/com/google/appengine/tools/pipeline/impl/servlets/PipelineServlet.java b/java/src/main/java/com/google/appengine/tools/pipeline/impl/servlets/PipelineServlet.java
index 43d21b2c..bbc45f77 100755
--- a/java/src/main/java/com/google/appengine/tools/pipeline/impl/servlets/PipelineServlet.java
+++ b/java/src/main/java/com/google/appengine/tools/pipeline/impl/servlets/PipelineServlet.java
@@ -14,6 +14,7 @@
package com.google.appengine.tools.pipeline.impl.servlets;
+import com.google.appengine.tools.pipeline.DaggerDefaultContainer;
import com.google.appengine.tools.pipeline.DefaultDIModule;
import com.google.appengine.tools.pipeline.PipelineOrchestrator;
import com.google.appengine.tools.pipeline.PipelineRunner;
@@ -112,7 +113,7 @@ public void init() throws ServletException {
// TODO: fix this? may have second copy IF user overrides; OK?
if (pipelineManager == null) {
- DIUtil.inject(DefaultDIModule.class.getName(), this);
+ DIUtil.inject(DaggerDefaultContainer.class, this);
}
//TODO: move these to DI?
diff --git a/java/src/main/java/com/google/appengine/tools/pipeline/impl/util/DIUtil.java b/java/src/main/java/com/google/appengine/tools/pipeline/impl/util/DIUtil.java
index 8ca6219f..51b55177 100644
--- a/java/src/main/java/com/google/appengine/tools/pipeline/impl/util/DIUtil.java
+++ b/java/src/main/java/com/google/appengine/tools/pipeline/impl/util/DIUtil.java
@@ -1,41 +1,52 @@
package com.google.appengine.tools.pipeline.impl.util;
+/**
+ * Copyright Worklytics, Co. 2024.
+ */
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
-import dagger.ObjectGraph;
+
import lombok.NonNull;
import lombok.extern.java.Log;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
- * Helper to get an ObjectGraph out of a Dagger Module.
+ * Helper to get a component instance out of Dagger, support runtime injection of classes.
*
- * This is mostly intended to be used for jobs, thus Default Module is linked somehow to AsyncModule.
*
*/
-
@Log
public class DIUtil {
private static final Object lock = new Object();
- private static final Map objectGraphCache = new ConcurrentHashMap<>(1);
+ private static final Map, Object> componentCache = new ConcurrentHashMap<>(1);
- private static final Map overriddenObjectGraphCache = new ConcurrentHashMap<>(1);
+ private static final Map, Object> overriddenComponentCache = new ConcurrentHashMap<>(1);
// just for logging purposes
private static boolean overridden = false;
/**
* Injects and Injectable instance through Reflection
+ * @param componentClass component class (Dagger-generated)
* @param injectable class to inject
*/
- public static void inject(String moduleClassFQN, Object injectable) {
- ObjectGraph objectGraph = getFromModuleClass(moduleClassFQN);
- objectGraph.inject(injectable);
+ public static void inject(Class> componentClass, Object injectable) {
+ Object objectGraph = getFromComponentClass(componentClass);
+
+ try {
+ Method injectMethod = objectGraph.getClass().getMethod("inject", injectable.getClass());
+ injectMethod.setAccessible(true); //avoid 'volatile' thing
+ injectMethod.invoke(objectGraph, injectable);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new RuntimeException("Couldn't inject object " + e.getMessage(), e);
+ }
}
/**
@@ -47,47 +58,41 @@ public static void inject(String moduleClassFQN, Object injectable) {
* - Module has a static instance of the graph it builds and provided by method of signature
* public static ObjectGraph getObjectGraph()
*
- * @param moduleFQNClassName module fully qualified name
+ * @param componentClass module fully qualified name
* @return the object graph for that module
* @throws RuntimeException if the class is not a Dagger Module or doesn't meet the requirements
*/
- public static ObjectGraph getFromModuleClass(@NonNull String moduleFQNClassName) {
+ public static Object getFromComponentClass(@NonNull Class> componentClass) {
synchronized (lock) {
if (overridden) {
- return overriddenObjectGraphCache.getOrDefault(moduleFQNClassName, objectGraphCache.get(moduleFQNClassName));
+ return overriddenComponentCache.getOrDefault(componentClass, componentCache.get(componentClass));
}
try {
- Class> moduleClass = Class.forName(moduleFQNClassName);
- Preconditions.checkArgument(moduleClass.isAnnotationPresent(dagger.Module.class), "Class is not a Dagger Module: " + moduleFQNClassName);
-
- ObjectGraph objectGraph = ObjectGraph.create(moduleClass.newInstance());
- objectGraphCache.put(moduleFQNClassName, objectGraph);
- return objectGraph;
- } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
+ Object component = componentClass.getMethod("create").invoke(null);
+ componentCache.put(componentClass, component);
+ return component;
+ } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
throw new RuntimeException("Couldn't get an object graph " + e.getMessage(), e);
}
}
}
- /**
- * A test helper to directly override the default module with the test's graph
- * @param graph the object graph to override for default module
- */
- @VisibleForTesting
- public static void overrideGraphForTests(String key, ObjectGraph graph) {
- synchronized (lock) {
- overriddenObjectGraphCache.put(key, graph);
- overridden = true;
- }
- }
+
+ @VisibleForTesting
+ public static void overrideComponentInstanceForTests(Class> clazz, Object componentInstance) {
+ synchronized (lock) {
+ overriddenComponentCache.put(clazz, componentInstance);
+ overridden = true;
+ }
+ }
/**
- * Resets the objectGraphCache to its original state (intended to be used on tests' teardown)
+ * Resets the componentCache to its original state (intended to be used on tests' teardown)
*/
@VisibleForTesting
- public static void resetGraph() {
+ public static void resetComponents() {
synchronized (lock) {
- overriddenObjectGraphCache.clear();
+ overriddenComponentCache.clear();
overridden = false;
log.fine("Reset overridden DI Module");
}
diff --git a/java/src/test/java/com/google/appengine/tools/pipeline/DITest.java b/java/src/test/java/com/google/appengine/tools/pipeline/DITest.java
index 9f220bdd..921eff62 100644
--- a/java/src/test/java/com/google/appengine/tools/pipeline/DITest.java
+++ b/java/src/test/java/com/google/appengine/tools/pipeline/DITest.java
@@ -2,12 +2,14 @@
import com.google.appengine.tools.pipeline.impl.backend.AppEngineEnvironment;
import com.google.appengine.tools.pipeline.impl.util.DIUtil;
+import dagger.Component;
import dagger.Module;
import javax.inject.Inject; // jakarta.inject.Inject not working with dagger yet (maybe in 2.0??)
import dagger.Provides;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -17,18 +19,20 @@
public class DITest extends PipelineTest {
-
- @Module(
- injects = {
- JobWithDependency.class,
- },
- includes = {
- DefaultDIModule.class,
- },
- complete = false,
- library = true,
- overrides = true
+ @Component(
+ modules = {
+ //DefaultDIModule.class,
+ DIModule.class,
+ }
)
+ public interface Container extends DIContainer {
+
+ //NOTE: have to do this for every class that may need to be injected (eg, Jobs, Tests, etc)
+ void inject(JobWithDependency job);
+ }
+
+
+ @Module
public static class DIModule {
@Provides
@@ -41,17 +45,22 @@ public String getService() {
@Override
public String getVersion() {
- return "v0.1";
+ return "v0.1-test";
}
};
}
+ }
-
-
+ @BeforeEach
+ public void setup() {
+ Container container = DaggerDITest_Container.create();
+ container.inject(this);
+ DIUtil.overrideComponentInstanceForTests(DaggerDITest_Container.class, container);
}
+
@NoArgsConstructor
- @Injectable(DIModule.class)
+ @Injectable(DaggerDITest_Container.class)
public static class JobWithDependency extends Job0 {
@Inject
@@ -67,7 +76,7 @@ public Value run() throws Exception {
public void directInjection() {
JobWithDependency job = new JobWithDependency();
- DIUtil.inject(DIModule.class.getName(), job);
+ DIUtil.inject(DaggerDITest_Container.class, job);
assertNotNull(job.appEngineEnvironment);
}
@@ -76,6 +85,6 @@ public void directInjection() {
public void testJobWithDependency(PipelineService pipelineService) {
String pipelineId = pipelineService.startNewPipeline(new JobWithDependency());
String value = waitForJobToComplete(pipelineService, pipelineId);
- assertEquals("dsaf", value);
+ assertEquals("v0.1-test", value);
}
}