Repository: jooby-project/jooby
Pull request #3426 (2024-05-17 17:57:44)

Fix avaje module
@jknack 
AvajeInjectModule seems doesn't work as expected, at least for me.
With a very basic example (reproduced in test https://github.com/jooby-project/jooby/commit/c9c81fdc85470df43b086abb257d611a6fd0574a) Jooby inner services injection fails with class cast exception, for example `JsonMapper`:  
```text
Caused by: java.lang.ClassCastException: class io.jooby.internal.ServiceRegistryImpl$$Lambda$458/0x0000000800dbf350 cannot be cast to class com.fasterxml.jackson.databind.json.JsonMapper (io.jooby.internal.ServiceRegistryImpl$$Lambda$458/0x0000000800dbf350 and com.fasterxml.jackson.databind.json.JsonMapper are in unnamed module of loader 'app')
```

I assume this is due to the fact that services already wrapped into lambda in `singletone()` https://github.com/jooby-project/jooby/blob/03bb5875786f9414c9339902cf015e264d06e173/jooby/src/main/java/io/jooby/internal/ServiceRegistryImpl.java#L61

Hence,  the default binding need to be changed to
`beanScope.provideDefault(key.getType(), e.getValue()::get);`

Also, `Config` binding was removed as it is already in service registry
 


Patch:
diff --git a/modules/jooby-avaje-inject/pom.xml b/modules/jooby-avaje-inject/pom.xml
index ff8e2db0a..0a80f46ea 100644
--- a/modules/jooby-avaje-inject/pom.xml
+++ b/modules/jooby-avaje-inject/pom.xml
@@ -39,8 +39,26 @@
     </dependency>
 
     <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
+      <groupId>io.jooby</groupId>
+      <artifactId>jooby-netty</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>io.jooby</groupId>
+      <artifactId>jooby-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>io.jooby</groupId>
+      <artifactId>jooby-jackson</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.squareup.okhttp3</groupId>
+      <artifactId>okhttp</artifactId>
       <scope>test</scope>
     </dependency>
 
@@ -51,4 +69,34 @@
       <scope>test</scope>
     </dependency>
   </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>test</id>
+            <phase>test-compile</phase>
+          </execution>
+        </executions>
+        <configuration>
+           <compilerArgs>
+            <arg>-parameters</arg>
+          </compilerArgs>
+          <annotationProcessorPaths>
+            <path>
+              <groupId>io.jooby</groupId>
+              <artifactId>jooby-apt</artifactId>
+            </path>
+            <path>
+              <groupId>io.avaje</groupId>
+              <artifactId>avaje-inject-generator</artifactId>
+            </path>
+          </annotationProcessorPaths>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
 </project>
diff --git a/modules/jooby-avaje-inject/src/main/java/io/jooby/avaje/inject/AvajeInjectModule.java b/modules/jooby-avaje-inject/src/main/java/io/jooby/avaje/inject/AvajeInjectModule.java
index 7ac6f62a3..d495c959f 100644
--- a/modules/jooby-avaje-inject/src/main/java/io/jooby/avaje/inject/AvajeInjectModule.java
+++ b/modules/jooby-avaje-inject/src/main/java/io/jooby/avaje/inject/AvajeInjectModule.java
@@ -8,8 +8,6 @@ package io.jooby.avaje.inject;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import com.typesafe.config.Config;
-
 import io.avaje.inject.BeanScope;
 import io.avaje.inject.BeanScopeBuilder;
 import io.jooby.Extension;
@@ -68,7 +66,7 @@ public class AvajeInjectModule implements Extension {
             e -> {
               final var key = e.getKey();
               if (key.getName() == null) {
-                beanScope.provideDefault(key.getType(), e::getValue);
+                beanScope.provideDefault(key.getType(), e.getValue()::get);
               } else {
                 beanScope.bean(key.getName(), key.getType(), e.getValue());
               }
@@ -80,7 +78,6 @@ public class AvajeInjectModule implements Extension {
     // configuration properties
     final var config = environment.getConfig();
     beanScope.propertyPlugin(new JoobyPropertyPlugin(config));
-    beanScope.bean(Config.class, config);
 
     for (var entry : config.entrySet()) {
       String name = entry.getKey();


Test patch:
diff --git a/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/AvajeInjectModuleTest.java b/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/AvajeInjectModuleTest.java
new file mode 100644
index 000000000..aeb91e150
--- /dev/null
+++ b/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/AvajeInjectModuleTest.java
@@ -0,0 +1,25 @@
+package io.jooby.avaje.inject;
+
+import io.jooby.avaje.inject.app.TestApp;
+import io.jooby.test.JoobyTest;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AvajeInjectModuleTest {
+
+    static OkHttpClient client = new OkHttpClient();
+
+    @JoobyTest(TestApp.class)
+    public void shouldPropagateJoobyServicesToAvajeBeanScope(String serverPath) throws IOException {
+        Request request = new Request.Builder().url(serverPath + "ping").build();
+
+        try (Response response = client.newCall(request).execute()) {
+            assertEquals("test", response.body().string());
+        }
+    }
+}
diff --git a/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/app/Controller.java b/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/app/Controller.java
new file mode 100644
index 000000000..f455f6b2a
--- /dev/null
+++ b/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/app/Controller.java
@@ -0,0 +1,36 @@
+package io.jooby.avaje.inject.app;
+
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.typesafe.config.Config;
+import io.avaje.inject.InjectModule;
+import io.jooby.annotation.GET;
+import io.jooby.annotation.Path;
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
+import jakarta.inject.Singleton;
+
+@Singleton
+@Path("")
+@InjectModule(requires = {JsonMapper.class, Config.class, String.class})
+public class Controller {
+
+    private final JsonMapper jsonMapper;
+    private final Config config;
+    private final String env;
+
+    @Inject
+    public Controller(JsonMapper jsonMapper, Config config, @Named("application.env") String env) {
+        this.jsonMapper = jsonMapper;
+        this.config = config;
+        this.env = env;
+    }
+
+    @GET
+    @Path("/ping")
+    public String ping() {
+        jsonMapper.version();
+        config.isEmpty();
+
+        return this.env;
+    }
+}
diff --git a/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/app/TestApp.java b/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/app/TestApp.java
new file mode 100644
index 000000000..f091511c9
--- /dev/null
+++ b/modules/jooby-avaje-inject/src/test/java/io/jooby/avaje/inject/app/TestApp.java
@@ -0,0 +1,24 @@
+package io.jooby.avaje.inject.app;
+
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.typesafe.config.Config;
+import io.jooby.Jooby;
+import io.jooby.avaje.inject.AvajeInjectModule;
+import io.jooby.jackson.JacksonModule;
+
+public class TestApp extends Jooby {
+
+    {
+        install(new JacksonModule());
+        install(AvajeInjectModule.of());
+
+        mvc(Controller.class);
+
+        onStarted(() -> {
+            JsonMapper jsonMapper = require(JsonMapper.class);
+            Config config = require(Config.class);
+            jsonMapper.version();
+            config.isEmpty();
+        });
+    }
+}