Repository: avaje/avaje-inject
Pull request #59 (2020-10-29 01:43:08)

#51 Add jpms module-info using Multi-Release jar

norrisjeremy: Have you given any thought to migrating to new jakarta.inject / jakarta.annotation-api? It might be good time to do the migration while you are introducing JPMS.
rbygrave: > Have you given any thought to migrating to new jakarta.inject / jakarta.annotation-api? 

I did a little bit. jakarta.inject uses Automatic-Module-Name in the manifest.

> It might be good time to do the migration while you are introducing JPMS.

Well I'm still learning but where I'm at at the moment is that I think it is ok for some modules to be "unnamed" ones.  My working theory is that unnamed modules are ok in the case that:
- It has no dependencies 
- If it had a module-info it would just contain ... export * / export all packages.

As I see it right now, both javax.inject and java.annotation-api match this description (and my example jpms app is fine with them as they are).  So right now if I was doing a full jpms app those modules are actually fine to use as is. I currently don't see anything to gain from say forking and making a jpms version of them (some people have done this) or moving to say jakarta.inject (even if they had a module-info which they don't).

Hmmm.
norrisjeremy: > Well I'm still learning but where I'm at at the moment is that I think it is ok for some modules to be "unnamed" ones. My working theory is that unnamed modules are ok in the case that:
> 
> * It has no dependencies
> * If it had a module-info it would just contain ... export * / export all packages.
> 
> As I see it right now, both javax.inject and java.annotation-api match this description (and my example jpms app is fine with them as they are). So right now if I was doing a full jpms app those modules are actually fine to use as is. I currently don't see anything to gain from say forking and making a jpms version of them (some people have done this) or moving to say jakarta.inject (even if they had a module-info which they don't).
> 
> Hmmm.

Part of my thought process for suggesting a migration to the new 2.x jakarta.inject + jakarta.annotation-api versions are two-fold:

1. I believe the module names for the old javax versions are technically reserved by the JDK itself (javax.inject / java.annotation). While I would think it's unlikely Oracle would decide to add modules with colliding names into the JDK itself, it's possible they could. And then I think hilarity could ensue, since there would be conflicting module names.

2. With adding in the JPMS support, it might be best to rip the bandaid off entirely all at once, and shift everything to using the new jakarta.inject + jakarta.annotation package names all at the same time. This would be admittedly highly disruptive to users of this library, since they would need to adjust all their classes to use the jakarta.inject + jakarta.annotation annotations instead of javax.inject + javax.annotation.

Anyway, that was just part of my thinking at the moment (while reviewing our internal projects and determining how best to migrate them from Guice to this library).

Patch:
diff --git a/inject-generator/pom.xml b/inject-generator/pom.xml
index 0d171d95..b4ce9f7d 100644
--- a/inject-generator/pom.xml
+++ b/inject-generator/pom.xml
@@ -1,38 +1,22 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
-
-  <groupId>io.avaje</groupId>
-  <artifactId>avaje-inject-generator</artifactId>
-  <version>1.4-SNAPSHOT</version>
-
   <parent>
-    <groupId>org.avaje</groupId>
-    <artifactId>java8-oss</artifactId>
-    <version>2.2</version>
+    <groupId>io.avaje</groupId>
+    <artifactId>avaje-inject-parent</artifactId>
+    <version>1.6-SNAPSHOT</version>
   </parent>
 
-  <scm>
-    <developerConnection>scm:git:git@github.com:avaje/avaje-inject.git</developerConnection>
-    <tag>HEAD</tag>
-  </scm>
-
-  <properties>
-    <inject.version>1.4-SNAPSHOT</inject.version>
-  </properties>
+  <artifactId>avaje-inject-generator</artifactId>
+  <name>avaje inject generator</name>
+  <description>annotation processor generating di as source code</description>
 
   <dependencies>
 
     <dependency>
       <groupId>io.avaje</groupId>
       <artifactId>avaje-inject</artifactId>
-      <version>${inject.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>javax.inject</groupId>
-      <artifactId>javax.inject</artifactId>
-      <version>1</version>
+      <version>1.6-SNAPSHOT</version>
     </dependency>
 
     <dependency>
@@ -54,16 +38,6 @@
 
   <build>
     <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <version>3.1.0</version>
-        <configuration>
-          <archive>
-            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
 
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
@@ -76,6 +50,28 @@
           <compilerArgument>-proc:none</compilerArgument>
         </configuration>
       </plugin>
+
+      <plugin>
+        <groupId>org.moditect</groupId>
+        <artifactId>moditect-maven-plugin</artifactId>
+        <version>1.0.0.RC1</version>
+        <executions>
+          <execution>
+            <id>add-module-infos</id>
+            <phase>package</phase>
+            <goals>
+              <goal>add-module-info</goal>
+            </goals>
+            <configuration>
+              <jvmVersion>9</jvmVersion>
+              <module>
+                <moduleInfoFile>src/main/java9/module-info.java</moduleInfoFile>
+              </module>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
     </plugins>
   </build>
 
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/IncludeAnnotations.java b/inject-generator/src/main/java/io/avaje/inject/generator/IncludeAnnotations.java
index 9a52a25c..5026151b 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/IncludeAnnotations.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/IncludeAnnotations.java
@@ -1,39 +1,30 @@
 package io.avaje.inject.generator;
 
-import io.avaje.inject.Factory;
-import io.avaje.inject.Primary;
-import io.avaje.inject.Secondary;
-
-import javax.inject.Named;
-import javax.inject.Singleton;
 import java.util.HashSet;
 import java.util.Set;
 
 class IncludeAnnotations {
 
+  private static final String[] EXCLUDED_PREFIX = {"javax.annotation.", "javax.inject.", "io.avaje.inject.", "lombok."};
+
   /**
    * Annotations that we don't bother registering lists for.
    */
   private static final Set<String> EXCLUDED_ANNOTATIONS = new HashSet<>();
 
   static {
-    EXCLUDED_ANNOTATIONS.add("javax.annotation.Generated");
-    EXCLUDED_ANNOTATIONS.add(Singleton.class.getName());
-    EXCLUDED_ANNOTATIONS.add(Named.class.getName());
-    EXCLUDED_ANNOTATIONS.add(Factory.class.getName());
-    EXCLUDED_ANNOTATIONS.add(Primary.class.getName());
-    EXCLUDED_ANNOTATIONS.add(Secondary.class.getName());
     EXCLUDED_ANNOTATIONS.add(Constants.KOTLIN_METADATA);
     EXCLUDED_ANNOTATIONS.add(Constants.PATH);
-    EXCLUDED_ANNOTATIONS.add(Constants.GENERATED_9);
   }
 
   /**
    * Return true if the annotation should be included.
    */
   static boolean include(String annotationType) {
-    if (annotationType.startsWith("lombok.")) {
-      return false;
+    for (String prefix : EXCLUDED_PREFIX) {
+      if (annotationType.startsWith(prefix)) {
+        return false;
+      }
     }
     return !EXCLUDED_ANNOTATIONS.contains(annotationType);
   }
diff --git a/inject-generator/src/main/java9/module-info.java b/inject-generator/src/main/java9/module-info.java
new file mode 100644
index 00000000..126b1691
--- /dev/null
+++ b/inject-generator/src/main/java9/module-info.java
@@ -0,0 +1,8 @@
+module io.avaje.inject.generator {
+
+  requires java.compiler;
+  requires io.avaje.inject;
+  requires java.annotation;
+
+  provides javax.annotation.processing.Processor with io.avaje.inject.generator.Processor;
+}
diff --git a/inject-generator/src/main/resources/META-INF/MANIFEST.MF b/inject-generator/src/main/resources/META-INF/MANIFEST.MF
deleted file mode 100644
index 322c658e..00000000
--- a/inject-generator/src/main/resources/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,2 +0,0 @@
-Manifest-Version: 1.0
-Automatic-Module-Name: io.avaje.inject.generator
diff --git a/inject/README.md b/inject/README.md
index b04c2737..1ee00519 100644
--- a/inject/README.md
+++ b/inject/README.md
@@ -1,6 +1,18 @@
-# DInject
+# Avaje Inject
 APT based dependency injection for server side developers - https://dinject.io
 
+### Example module use
+
+```java
+module org.example {
+
+  requires io.avaje.inject;
+
+  // register org.example._di$Factory from generated sources
+  provides io.avaje.inject.core.BeanContextFactory with org.example._di$Factory;
+}
+```
+
 ## Similar to Dagger (https://google.github.io/dagger/)
 
 - Uses Java annotation processing for dependency injection
@@ -9,22 +21,22 @@ APT based dependency injection for server side developers - https://dinject.io
 - A `Library only` (a DI library and that's it ~25k in size)
 
 
-## Differences to Dagger 
+## Differences to Dagger
 
 - Aimed specifically for server side development (rather than Andriod)
 - Supports lifecycle methods with `@PostConstruct` and `@PreDestory`
 - Supports `@Factory` and `@Bean`
-- Provides API to obtain all bean instances that implement an interface  
+- Provides API to obtain all bean instances that implement an interface
 - Provides API to obtain all bean instances that have an annotation
 - Integration with server side web frameworks like Rapidoid, Sparkjava, Javlin etc
-  
+
 
 ## Differences to Micronaut DI (https://docs.micronaut.io/latest/guide/index.html#ioc)
 
 - DInject generates source code rather that bytecode
 - DInject generates construction order at compile time
 - DInject is aiming to be a `Library only` (so a DI library and that's it without any dependencies)
-- DInject is expected to stay a lot smaller with minimal dependencies 
+- DInject is expected to stay a lot smaller with minimal dependencies
 - DInject is expected to be a lot faster (due to the pre-ordering of construction in generated code)
 
 ## Differences to Spring DI (https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html)
@@ -32,11 +44,11 @@ APT based dependency injection for server side developers - https://dinject.io
 - DInject only has Singleton scope
 - DInject doesn't yet have support for something like `@Value`
 - DInject provides similar DI features but with code generation via APT
-  and hence much lower startup time and much lower cost (so better suited to micro-services). 
-- No use of reflection or scanning even for multi-module dependency injection 
+  and hence much lower startup time and much lower cost (so better suited to micro-services).
+- No use of reflection or scanning even for multi-module dependency injection
 
 
 ## Plans
 
-- Additionally library for reading configuration / application.yaml 
+- Additionally library for reading configuration / application.yaml
 - Configuration supports onChange listening (aka dynamic configuration API)
diff --git a/inject/pom.xml b/inject/pom.xml
index 189f13f9..44bdd2ab 100644
--- a/inject/pom.xml
+++ b/inject/pom.xml
@@ -1,25 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
-
-  <groupId>io.avaje</groupId>
-  <artifactId>avaje-inject</artifactId>
-  <version>1.4-SNAPSHOT</version>
-
   <parent>
-    <groupId>org.avaje</groupId>
-    <artifactId>java8-oss</artifactId>
-    <version>2.2</version>
+    <groupId>io.avaje</groupId>
+    <artifactId>avaje-inject-parent</artifactId>
+    <version>1.6-SNAPSHOT</version>
   </parent>
 
-  <scm>
-    <developerConnection>scm:git:git@github.com:avaje/avaje-inject.git</developerConnection>
-    <tag>HEAD</tag>
-  </scm>
-
-  <properties>
-    <maven-javadoc-plugin.version>3.2.0</maven-javadoc-plugin.version>
-  </properties>
+  <artifactId>avaje-inject</artifactId>
+  <name>avaje inject</name>
+  <description>avaje dependency injection library</description>
 
   <dependencies>
 
@@ -32,13 +22,13 @@
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
-      <version>1.7.25</version>
+      <version>1.7.30</version>
     </dependency>
 
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
-      <version>2.23.4</version>
+      <version>3.6.0</version>
       <optional>true</optional>
     </dependency>
 
@@ -63,14 +53,24 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <version>3.1.0</version>
-        <configuration>
-          <archive>
-            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
-          </archive>
-        </configuration>
+        <groupId>org.moditect</groupId>
+        <artifactId>moditect-maven-plugin</artifactId>
+        <version>1.0.0.RC1</version>
+        <executions>
+          <execution>
+            <id>add-module-infos</id>
+            <phase>package</phase>
+            <goals>
+              <goal>add-module-info</goal>
+            </goals>
+            <configuration>
+              <jvmVersion>9</jvmVersion>
+              <module>
+                <moduleInfoFile>src/main/java9/module-info.java</moduleInfoFile>
+              </module>
+            </configuration>
+          </execution>
+        </executions>
       </plugin>
     </plugins>
   </build>
diff --git a/inject/src/main/java/io/avaje/inject/BeanContextBuilder.java b/inject/src/main/java/io/avaje/inject/BeanContextBuilder.java
index 1e31711c..fe50b4f8 100644
--- a/inject/src/main/java/io/avaje/inject/BeanContextBuilder.java
+++ b/inject/src/main/java/io/avaje/inject/BeanContextBuilder.java
@@ -360,7 +360,9 @@ public class BeanContextBuilder {
 
     Set<String> moduleNames = factoryOrder.orderFactories();
     if (moduleNames.isEmpty()) {
-      throw new IllegalStateException("No modules found suggests using Gradle and IDEA but with a setup issue?" +
+      throw new IllegalStateException("No modules found. When using java module system we need an explicit provides clause in module-info like:\n\n" +
+        " provides io.avaje.inject.core.BeanContextFactory with org.example._di$Factory;\n\n" +
+        " Otherwise perhaps using Gradle and IDEA but with a setup issue?" +
         " Review IntelliJ Settings / Build / Build tools / Gradle - 'Build and run using' value and set that to 'Gradle'. " +
         " Refer to https://dinject.io/docs/gradle#idea");
     }
diff --git a/inject/src/main/java9/module-info.java b/inject/src/main/java9/module-info.java
new file mode 100644
index 00000000..99931c77
--- /dev/null
+++ b/inject/src/main/java9/module-info.java
@@ -0,0 +1,12 @@
+module io.avaje.inject {
+
+  exports io.avaje.inject;
+  exports io.avaje.inject.core;
+
+  requires transitive javax.inject;
+  requires transitive org.slf4j;
+  requires static org.mockito;
+
+  uses io.avaje.inject.core.BeanContextFactory;
+
+}
diff --git a/inject/src/main/resources/META-INF/MANIFEST.MF b/inject/src/main/resources/META-INF/MANIFEST.MF
deleted file mode 100644
index b280e482..00000000
--- a/inject/src/main/resources/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,2 +0,0 @@
-Manifest-Version: 1.0
-Automatic-Module-Name: io.avaje.inject
diff --git a/pom.xml b/pom.xml
index bb387522..83ac990e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,11 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.avaje</groupId>
+    <artifactId>java8-oss</artifactId>
+    <version>2.2</version>
+  </parent>
 
   <groupId>io.avaje</groupId>
   <artifactId>avaje-inject-parent</artifactId>
-  <version>1</version>
+  <version>1.6-SNAPSHOT</version>
   <packaging>pom</packaging>
+  <name>avaje inject parent</name>
+  <description>parent pom for avaje inject library</description>
+
+  <scm>
+    <developerConnection>scm:git:git@github.com:avaje/avaje-inject.git</developerConnection>
+    <tag>HEAD</tag>
+  </scm>
 
   <modules>
     <module>inject</module>


Test patch:
diff --git a/inject-test/pom.xml b/inject-test/pom.xml
index 98221494..9c92bda5 100644
--- a/inject-test/pom.xml
+++ b/inject-test/pom.xml
@@ -1,33 +1,26 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
-
-  <groupId>io.avaje</groupId>
-  <artifactId>avaje-inject-test</artifactId>
-  <version>1</version>
-
   <parent>
-    <groupId>org.avaje</groupId>
-    <artifactId>java8-oss</artifactId>
-    <version>2.2</version>
+    <groupId>io.avaje</groupId>
+    <artifactId>avaje-inject-parent</artifactId>
+    <version>1.6-SNAPSHOT</version>
   </parent>
 
-  <properties>
-    <inject.version>1.4-SNAPSHOT</inject.version>
-  </properties>
+  <artifactId>avaje-inject-test</artifactId>
 
   <dependencies>
 
     <dependency>
       <groupId>io.avaje</groupId>
       <artifactId>avaje-inject</artifactId>
-      <version>${inject.version}</version>
+      <version>${project.version}</version>
     </dependency>
 
     <dependency>
       <groupId>io.avaje</groupId>
       <artifactId>avaje-inject-generator</artifactId>
-      <version>${inject.version}</version>
+      <version>${project.version}</version>
       <scope>provided</scope>
     </dependency>
 
diff --git a/inject-test/src/test/java/io/avaje/inject/SystemContextTest.java b/inject-test/src/test/java/io/avaje/inject/SystemContextTest.java
index e1c66259..c992a7a3 100644
--- a/inject-test/src/test/java/io/avaje/inject/SystemContextTest.java
+++ b/inject-test/src/test/java/io/avaje/inject/SystemContextTest.java
@@ -1,11 +1,11 @@
 package io.avaje.inject;
 
+import org.example.coffee.fruit.Fruit;
 import org.example.coffee.list.ASomei;
 import org.example.coffee.list.BSomei;
 import org.example.coffee.list.Somei;
 import org.junit.jupiter.api.Test;
 
-import javax.annotation.Priority;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -33,7 +33,7 @@ public class SystemContextTest {
   @Test
   public void getBeansWithAnnotation() {
 
-    final List<Object> beans = SystemContext.getBeansWithAnnotation(Priority.class);
+    final List<Object> beans = SystemContext.getBeansWithAnnotation(Fruit.class);
     assertThat(beans).hasSize(2);
   }
 }
diff --git a/inject-test/src/test/java/org/example/coffee/fruit/BananaService.java b/inject-test/src/test/java/org/example/coffee/fruit/BananaService.java
index 79d41a97..ca2bd080 100644
--- a/inject-test/src/test/java/org/example/coffee/fruit/BananaService.java
+++ b/inject-test/src/test/java/org/example/coffee/fruit/BananaService.java
@@ -3,6 +3,7 @@ package org.example.coffee.fruit;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+@Fruit
 @Singleton
 class BananaService {
 
diff --git a/inject-test/src/test/java/org/example/coffee/fruit/Fruit.java b/inject-test/src/test/java/org/example/coffee/fruit/Fruit.java
new file mode 100644
index 00000000..4eecd51f
--- /dev/null
+++ b/inject-test/src/test/java/org/example/coffee/fruit/Fruit.java
@@ -0,0 +1,9 @@
+package org.example.coffee.fruit;
+
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Retention(RUNTIME)
+public @interface Fruit {
+}
diff --git a/inject-test/src/test/java/org/example/coffee/fruit/PeachService.java b/inject-test/src/test/java/org/example/coffee/fruit/PeachService.java
index 68e76eff..a50924f8 100644
--- a/inject-test/src/test/java/org/example/coffee/fruit/PeachService.java
+++ b/inject-test/src/test/java/org/example/coffee/fruit/PeachService.java
@@ -2,6 +2,7 @@ package org.example.coffee.fruit;
 
 import javax.inject.Singleton;
 
+@Fruit
 @Singleton
 class PeachService {