diff --git a/api/maven-api-core/pom.xml b/api/maven-api-core/pom.xml
index 33463ae51d9c..49d3429c7ad4 100644
--- a/api/maven-api-core/pom.xml
+++ b/api/maven-api-core/pom.xml
@@ -52,8 +52,8 @@
maven-api-plugin
- jakarta.inject
- jakarta.inject-api
+ org.apache.maven
+ maven-api-di
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java b/api/maven-api-core/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java
index 2fcbe07991ce..b12f9a4159bb 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java
@@ -22,8 +22,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
-import jakarta.inject.Scope;
-
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -32,6 +30,8 @@
* Indicates that the annotated bean has a lifespan limited to a given mojo execution,
* which means each mojo execution will result in a different instance being injected.
*
+ * TODO: this is currently not implemented
+ *
* @since 4.0.0
*/
@Scope
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/di/SessionScoped.java b/api/maven-api-core/src/main/java/org/apache/maven/api/di/SessionScoped.java
index d5e9a0ddbe53..ec4d5a8ea7d4 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/di/SessionScoped.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/di/SessionScoped.java
@@ -22,8 +22,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
-import jakarta.inject.Scope;
-
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -32,6 +30,8 @@
* Indicates that annotated component should be instantiated before session execution starts
* and discarded after session execution completes.
*
+ * TODO: this is currently not implemented
+ *
* @since 4.0.0
*/
@Scope
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java
index 1cfdfc6c775a..903d224f3811 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Execute.java
@@ -35,7 +35,7 @@
*/
@Experimental
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Execute {
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java
index bc4fcc479bba..4f01f0f6d22b 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java
@@ -38,7 +38,7 @@
*/
@Experimental
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Mojo {
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Parameter.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Parameter.java
index 415f6a507d28..64b129ae4ebe 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Parameter.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Parameter.java
@@ -41,7 +41,7 @@
*/
@Experimental
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Inherited
public @interface Parameter {
diff --git a/api/maven-api-di/pom.xml b/api/maven-api-di/pom.xml
new file mode 100644
index 000000000000..c20d05e05437
--- /dev/null
+++ b/api/maven-api-di/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+ 4.0.0
+
+
+ org.apache.maven
+ maven-api
+ 4.0.0-alpha-13-SNAPSHOT
+
+
+ maven-api-di
+ Maven 4 API :: Dependency Injection
+ Maven 4 API - Dependency Injection
+
+
+
+ org.apache.maven
+ maven-api-meta
+
+
+
+
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Inject.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Inject.java
new file mode 100644
index 000000000000..d31a4d11fbb0
--- /dev/null
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Inject.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({FIELD, CONSTRUCTOR, METHOD})
+@Retention(RUNTIME)
+@Documented
+public @interface Inject {}
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Named.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Named.java
new file mode 100644
index 000000000000..d38dfadc2e54
--- /dev/null
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Named.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+public @interface Named {
+ String value() default "";
+}
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Priority.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Priority.java
new file mode 100644
index 000000000000..f2eae991c5c9
--- /dev/null
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Priority.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({TYPE, METHOD})
+@Retention(RUNTIME)
+@Documented
+public @interface Priority {
+ int value();
+}
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Provides.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Provides.java
new file mode 100644
index 000000000000..2a782505ebb3
--- /dev/null
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Provides.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Can be used on a static method to provide a bean.
+ */
+@Target(METHOD)
+@Retention(RUNTIME)
+@Documented
+public @interface Provides {}
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Qualifier.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Qualifier.java
new file mode 100644
index 000000000000..863618280d20
--- /dev/null
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Qualifier.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(ANNOTATION_TYPE)
+@Retention(RUNTIME)
+@Documented
+public @interface Qualifier {}
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Scope.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Scope.java
new file mode 100644
index 000000000000..80b50b5d210d
--- /dev/null
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Scope.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target(ANNOTATION_TYPE)
+@Retention(RUNTIME)
+@Documented
+public @interface Scope {}
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Singleton.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Singleton.java
new file mode 100644
index 000000000000..79b440ce767a
--- /dev/null
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Singleton.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Scope
+@Documented
+@Retention(RUNTIME)
+public @interface Singleton {}
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/Typed.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Typed.java
new file mode 100644
index 000000000000..b2c78606c1ca
--- /dev/null
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/Typed.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({FIELD, METHOD, TYPE})
+@Retention(RUNTIME)
+@Documented
+public @interface Typed {
+ Class>[] value() default {};
+}
diff --git a/api/pom.xml b/api/pom.xml
index 426e69eb7a4c..6fc32f33324b 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -33,6 +33,7 @@
maven-api-meta
+ maven-api-dimaven-api-xmlmaven-api-modelmaven-api-plugin
diff --git a/maven-core/pom.xml b/maven-core/pom.xml
index 0ccd1f7df6d1..fcd9cbf90186 100644
--- a/maven-core/pom.xml
+++ b/maven-core/pom.xml
@@ -33,6 +33,10 @@ under the License.
+
+ org.apache.maven
+ maven-di
+ org.apache.mavenmaven-model
diff --git a/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java b/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java
index f41a1d64d5f1..334eafe3af9c 100644
--- a/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java
+++ b/maven-core/src/main/java/org/apache/maven/configuration/internal/EnhancedComponentConfigurator.java
@@ -52,16 +52,26 @@ public void configureComponent(
try {
ClassRealmConverter.pushContextRealm(realm);
- new EnhancedConfigurationConverter()
- .processConfiguration(
- converterLookup,
- component,
- realm, //
- configuration,
- evaluator,
- listener);
+ this.configureComponent(component, configuration, evaluator, (ClassLoader) realm, listener);
} finally {
ClassRealmConverter.popContextRealm();
}
}
+
+ public void configureComponent(
+ Object component,
+ PlexusConfiguration configuration,
+ ExpressionEvaluator evaluator,
+ ClassLoader loader,
+ ConfigurationListener listener)
+ throws ComponentConfigurationException {
+ new EnhancedConfigurationConverter()
+ .processConfiguration(
+ converterLookup,
+ component,
+ loader, //
+ configuration,
+ evaluator,
+ listener);
+ }
}
diff --git a/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScopeModule.java b/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScopeModule.java
index c79182839c9d..149b94f97c5d 100644
--- a/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScopeModule.java
+++ b/maven-core/src/main/java/org/apache/maven/execution/scope/internal/MojoExecutionScopeModule.java
@@ -37,7 +37,7 @@ public MojoExecutionScopeModule(MojoExecutionScope scope) {
@Override
protected void configure() {
bindScope(MojoExecutionScoped.class, scope);
- bindScope(org.apache.maven.api.di.MojoExecutionScoped.class, scope);
+ // bindScope(org.apache.maven.api.di.MojoExecutionScoped.class, scope);
bind(MojoExecutionScope.class).toInstance(scope);
bind(MavenProject.class)
.toProvider(MojoExecutionScope.seededKeyProvider())
diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
index 22019842f122..7292c8c94231 100644
--- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
+++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
@@ -22,31 +22,25 @@
import javax.inject.Named;
import javax.inject.Singleton;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
+import java.io.*;
+import java.lang.annotation.Annotation;
import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
-import com.google.inject.AbstractModule;
-import com.google.inject.name.Names;
import org.apache.maven.RepositoryUtils;
+import org.apache.maven.api.Project;
+import org.apache.maven.api.Session;
import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.classrealm.ClassRealmManager;
+import org.apache.maven.di.Injector;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.scope.internal.MojoExecutionScope;
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
+import org.apache.maven.internal.impl.DefaultLog;
import org.apache.maven.internal.impl.DefaultMojoExecution;
import org.apache.maven.internal.impl.InternalSession;
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
@@ -429,29 +423,6 @@ private void discoverPluginComponents(
((DefaultPlexusContainer) container)
.discoverComponents(
pluginRealm,
- new AbstractModule() {
- @Override
- protected void configure() {
- if (pluginDescriptor != null) {
- for (MojoDescriptor mojo : pluginDescriptor.getMojos()) {
- if (mojo.isV4Api()) {
- try {
- mojo.setRealm(pluginRealm);
- Class> cl = mojo.getImplementationClass();
- if (cl == null) {
- cl = pluginRealm.loadClass(mojo.getImplementation());
- }
- bind(org.apache.maven.api.plugin.Mojo.class)
- .annotatedWith(Names.named(mojo.getId()))
- .to((Class) cl);
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException("Unable to load mojo class", e);
- }
- }
- }
- }
- }
- },
new SessionScopeModule(container.lookup(SessionScope.class)),
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
new PluginConfigurationModule(plugin.getDelegate()));
@@ -528,115 +499,189 @@ public T getConfiguredMojo(Class mojoInterface, MavenSession session, Moj
Thread.currentThread().setContextClassLoader(pluginRealm);
try {
- T mojo;
+ if (mojoDescriptor.isV4Api()) {
+ return loadV4Mojo(mojoInterface, session, mojoExecution, mojoDescriptor, pluginDescriptor, pluginRealm);
+ } else {
+ return loadV3Mojo(mojoInterface, session, mojoExecution, mojoDescriptor, pluginDescriptor, pluginRealm);
+ }
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ container.setLookupRealm(oldLookupRealm);
+ }
+ }
- try {
- mojo = container.lookup(mojoInterface, mojoDescriptor.getRoleHint());
- } catch (ComponentLookupException e) {
- Throwable cause = e.getCause();
- while (cause != null
- && !(cause instanceof LinkageError)
- && !(cause instanceof ClassNotFoundException)) {
- cause = cause.getCause();
- }
+ private T loadV4Mojo(
+ Class mojoInterface,
+ MavenSession session,
+ MojoExecution mojoExecution,
+ MojoDescriptor mojoDescriptor,
+ PluginDescriptor pluginDescriptor,
+ ClassRealm pluginRealm)
+ throws PluginContainerException, PluginConfigurationException {
+ T mojo;
+
+ InternalSession sessionV4 = InternalSession.from(session.getSession());
+ Project project = sessionV4.getProject(session.getCurrentProject());
+ org.apache.maven.api.MojoExecution execution = new DefaultMojoExecution(sessionV4, mojoExecution);
+ org.apache.maven.api.plugin.Log log = new DefaultLog(
+ LoggerFactory.getLogger(mojoExecution.getMojoDescriptor().getFullGoalName()));
+ try {
+ Set classes = new HashSet<>();
+ try (InputStream is = pluginRealm.getResourceAsStream("META-INF/maven/org.apache.maven.api.di.Inject");
+ BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
+ reader.lines().forEach(classes::add);
+ }
+ Injector injector = Injector.create();
+ // Add known classes
+ // TODO: get those from the existing plexus scopes ?
+ injector.bindInstance(Session.class, sessionV4);
+ injector.bindInstance(Project.class, project);
+ injector.bindInstance(org.apache.maven.api.MojoExecution.class, execution);
+ injector.bindInstance(org.apache.maven.api.plugin.Log.class, log);
+ // Add plugin classes
+ for (String className : classes) {
+ Class> clazz = pluginRealm.loadClass(className);
+ injector.bindImplicit(clazz);
+ }
+ mojo = mojoInterface.cast(injector.getInstance(mojoDescriptor.getImplementationClass()));
- if ((cause instanceof NoClassDefFoundError) || (cause instanceof ClassNotFoundException)) {
- ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
- PrintStream ps = new PrintStream(os);
- ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
- + pluginDescriptor.getId() + "'. A required class is missing: "
- + cause.getMessage());
- pluginRealm.display(ps);
-
- throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
- } else if (cause instanceof LinkageError) {
- ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
- PrintStream ps = new PrintStream(os);
- ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
- + pluginDescriptor.getId() + "' due to an API incompatibility: "
- + e.getClass().getName() + ": " + cause.getMessage());
- pluginRealm.display(ps);
-
- throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
- }
+ } catch (Exception e) {
+ throw new PluginContainerException(mojoDescriptor, pluginRealm, "Unable to lookup Mojo", e);
+ }
- throw new PluginContainerException(
- mojoDescriptor,
- pluginRealm,
- "Unable to load the mojo '" + mojoDescriptor.getGoal()
- + "' (or one of its required components) from the plugin '"
- + pluginDescriptor.getId() + "'",
- e);
- }
+ XmlNode dom = mojoExecution.getConfiguration() != null
+ ? mojoExecution.getConfiguration().getDom()
+ : null;
- if (mojo instanceof ContextEnabled) {
- MavenProject project = session.getCurrentProject();
+ PlexusConfiguration pomConfiguration;
- Map pluginContext = session.getPluginContext(pluginDescriptor, project);
+ if (dom == null) {
+ pomConfiguration = new DefaultPlexusConfiguration("configuration");
+ } else {
+ pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
+ }
- if (pluginContext != null) {
- pluginContext.put("project", project);
+ ExpressionEvaluator expressionEvaluator =
+ new PluginParameterExpressionEvaluatorV4(sessionV4, project, execution);
- pluginContext.put("pluginDescriptor", pluginDescriptor);
+ for (MavenPluginConfigurationValidator validator : configurationValidators) {
+ validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
+ }
- ((ContextEnabled) mojo).setPluginContext(pluginContext);
- }
- }
+ populateMojoExecutionFields(
+ mojo,
+ mojoExecution.getExecutionId(),
+ mojoDescriptor,
+ pluginRealm,
+ pomConfiguration,
+ expressionEvaluator);
+
+ return mojo;
+ }
+
+ private T loadV3Mojo(
+ Class mojoInterface,
+ MavenSession session,
+ MojoExecution mojoExecution,
+ MojoDescriptor mojoDescriptor,
+ PluginDescriptor pluginDescriptor,
+ ClassRealm pluginRealm)
+ throws PluginContainerException, PluginConfigurationException {
+ T mojo;
- if (mojo instanceof Mojo) {
- Logger mojoLogger = LoggerFactory.getLogger(mojoDescriptor.getImplementation());
- ((Mojo) mojo).setLog(new MojoLogWrapper(mojoLogger));
+ try {
+ mojo = container.lookup(mojoInterface, mojoDescriptor.getRoleHint());
+ } catch (ComponentLookupException e) {
+ Throwable cause = e.getCause();
+ while (cause != null && !(cause instanceof LinkageError) && !(cause instanceof ClassNotFoundException)) {
+ cause = cause.getCause();
}
- if (mojo instanceof Contextualizable) {
- pluginValidationManager.reportPluginMojoValidationIssue(
- PluginValidationManager.IssueLocality.EXTERNAL,
- session,
- mojoDescriptor,
- mojo.getClass(),
- "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL.");
+ if ((cause instanceof NoClassDefFoundError) || (cause instanceof ClassNotFoundException)) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
+ PrintStream ps = new PrintStream(os);
+ ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
+ + pluginDescriptor.getId() + "'. A required class is missing: "
+ + cause.getMessage());
+ pluginRealm.display(ps);
+
+ throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
+ } else if (cause instanceof LinkageError) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
+ PrintStream ps = new PrintStream(os);
+ ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
+ + pluginDescriptor.getId() + "' due to an API incompatibility: "
+ + e.getClass().getName() + ": " + cause.getMessage());
+ pluginRealm.display(ps);
+
+ throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
}
- XmlNode dom = mojoExecution.getConfiguration() != null
- ? mojoExecution.getConfiguration().getDom()
- : null;
+ throw new PluginContainerException(
+ mojoDescriptor,
+ pluginRealm,
+ "Unable to load the mojo '" + mojoDescriptor.getGoal()
+ + "' (or one of its required components) from the plugin '"
+ + pluginDescriptor.getId() + "'",
+ e);
+ }
- PlexusConfiguration pomConfiguration;
+ if (mojo instanceof ContextEnabled) {
+ MavenProject project = session.getCurrentProject();
- if (dom == null) {
- pomConfiguration = new DefaultPlexusConfiguration("configuration");
- } else {
- pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
- }
+ Map pluginContext = session.getPluginContext(pluginDescriptor, project);
- ExpressionEvaluator expressionEvaluator;
- InternalSession sessionV4 = InternalSession.from(session.getSession());
- if (mojoDescriptor.isV4Api()) {
- expressionEvaluator = new PluginParameterExpressionEvaluatorV4(
- sessionV4,
- sessionV4.getProject(session.getCurrentProject()),
- new DefaultMojoExecution(sessionV4, mojoExecution));
- } else {
- expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
- }
+ if (pluginContext != null) {
+ pluginContext.put("project", project);
+
+ pluginContext.put("pluginDescriptor", pluginDescriptor);
- for (MavenPluginConfigurationValidator validator : configurationValidators) {
- validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
+ ((ContextEnabled) mojo).setPluginContext(pluginContext);
}
+ }
+
+ if (mojo instanceof Mojo) {
+ Logger mojoLogger = LoggerFactory.getLogger(mojoDescriptor.getImplementation());
+ ((Mojo) mojo).setLog(new MojoLogWrapper(mojoLogger));
+ }
- populateMojoExecutionFields(
- mojo,
- mojoExecution.getExecutionId(),
+ if (mojo instanceof Contextualizable) {
+ pluginValidationManager.reportPluginMojoValidationIssue(
+ PluginValidationManager.IssueLocality.EXTERNAL,
+ session,
mojoDescriptor,
- pluginRealm,
- pomConfiguration,
- expressionEvaluator);
+ mojo.getClass(),
+ "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL.");
+ }
- return mojo;
- } finally {
- Thread.currentThread().setContextClassLoader(oldClassLoader);
- container.setLookupRealm(oldLookupRealm);
+ XmlNode dom = mojoExecution.getConfiguration() != null
+ ? mojoExecution.getConfiguration().getDom()
+ : null;
+
+ PlexusConfiguration pomConfiguration;
+
+ if (dom == null) {
+ pomConfiguration = new DefaultPlexusConfiguration("configuration");
+ } else {
+ pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
}
+
+ InternalSession sessionV4 = InternalSession.from(session.getSession());
+ ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
+
+ for (MavenPluginConfigurationValidator validator : configurationValidators) {
+ validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
+ }
+
+ populateMojoExecutionFields(
+ mojo,
+ mojoExecution.getExecutionId(),
+ mojoDescriptor,
+ pluginRealm,
+ pomConfiguration,
+ expressionEvaluator);
+
+ return mojo;
}
private void populateMojoExecutionFields(
@@ -892,4 +937,29 @@ private List resolveExtensionArtifacts(
pluginDependenciesResolver.resolvePlugin(extensionPlugin, null, null, repositories, session);
return toMavenArtifacts(root);
}
+
+ static class NamedImpl implements Named {
+ private final String value;
+
+ NamedImpl(String value) {
+ this.value = value;
+ }
+
+ public String value() {
+ return this.value;
+ }
+
+ @SuppressWarnings("checkstyle:MagicNumber")
+ public int hashCode() {
+ return 127 * "value".hashCode() ^ this.value.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ return o instanceof Named && this.value.equals(((Named) o).value());
+ }
+
+ public Class extends Annotation> annotationType() {
+ return Named.class;
+ }
+ }
}
diff --git a/maven-core/src/main/java/org/apache/maven/session/scope/internal/SessionScopeModule.java b/maven-core/src/main/java/org/apache/maven/session/scope/internal/SessionScopeModule.java
index 5adcd0bd251d..f87334439920 100644
--- a/maven-core/src/main/java/org/apache/maven/session/scope/internal/SessionScopeModule.java
+++ b/maven-core/src/main/java/org/apache/maven/session/scope/internal/SessionScopeModule.java
@@ -46,7 +46,7 @@ public SessionScopeModule(SessionScope scope) {
@Override
protected void configure() {
bindScope(SessionScoped.class, scope);
- bindScope(org.apache.maven.api.di.SessionScoped.class, scope);
+ // bindScope(org.apache.maven.api.di.SessionScoped.class, scope);
bind(SessionScope.class).toInstance(scope);
bind(MavenSession.class)
diff --git a/maven-core/src/test/java/org/apache/maven/session/scope/SessionScopeProxyTest.java b/maven-core/src/test/java/org/apache/maven/session/scope/SessionScopeProxyTest.java
index fd3efe8aca7a..5988c64c9e32 100644
--- a/maven-core/src/test/java/org/apache/maven/session/scope/SessionScopeProxyTest.java
+++ b/maven-core/src/test/java/org/apache/maven/session/scope/SessionScopeProxyTest.java
@@ -23,8 +23,8 @@
import javax.inject.Singleton;
import com.google.inject.OutOfScopeException;
+import org.apache.maven.SessionScoped;
import org.apache.maven.api.Session;
-import org.apache.maven.api.di.SessionScoped;
import org.apache.maven.session.scope.internal.SessionScope;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
diff --git a/maven-di/pom.xml b/maven-di/pom.xml
new file mode 100644
index 000000000000..67a71f1d6573
--- /dev/null
+++ b/maven-di/pom.xml
@@ -0,0 +1,51 @@
+
+
+
+ 4.0.0
+
+ org.apache.maven
+ maven
+ 4.0.0-alpha-13-SNAPSHOT
+
+
+ maven-di
+ Maven Dependency Injection
+ Provides the implementation for the Dependency Injection mechanism in Maven
+
+
+
+ org.apache.maven
+ maven-api-di
+
+
+ org.apache.maven
+ maven-api-core
+
+
+ org.apache.maven
+ maven-xml-impl
+
+
+ org.codehaus.plexus
+ plexus-xml
+
+
+
+
diff --git a/maven-di/src/main/java/org/apache/maven/di/Injector.java b/maven-di/src/main/java/org/apache/maven/di/Injector.java
new file mode 100644
index 000000000000..9e9b65a33f2a
--- /dev/null
+++ b/maven-di/src/main/java/org/apache/maven/di/Injector.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.di;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.maven.di.impl.InjectorImpl;
+
+public interface Injector {
+
+ //
+ // Builder API
+ //
+
+ static Injector create() {
+ return new InjectorImpl();
+ }
+
+ Injector bindScope(Class extends Annotation> scopeAnnotation, Scope scope);
+
+ Injector bindImplicit(Class> cls);
+
+ Injector bindInstance(Class cls, T instance);
+
+ //
+ // Bean access
+ //
+
+ void injectInstance(T instance);
+
+ T getInstance(Class key);
+
+ T getInstance(Key key);
+}
diff --git a/maven-di/src/main/java/org/apache/maven/di/Key.java b/maven-di/src/main/java/org/apache/maven/di/Key.java
new file mode 100644
index 000000000000..cdf9bc040c7d
--- /dev/null
+++ b/maven-di/src/main/java/org/apache/maven/di/Key.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.di;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.di.impl.ReflectionUtils;
+import org.apache.maven.di.impl.TypeUtils;
+import org.apache.maven.di.impl.Types;
+import org.apache.maven.di.impl.Utils;
+
+/**
+ * The key defines an identity of a binding. In any DI, a key is usually a type of the object along
+ * with some optional tag to distinguish between bindings which make objects of the same type.
+ *
+ * In ActiveJ Inject, a key is also a type token - special abstract class that can store type information
+ * with the shortest syntax possible in Java.
+ *
+ * For example, to create a key of type Map<String, List<Integer>>, you can just use
+ * this syntax: new Key<Map<String, List<Integer>>>(){}.
+ *
+ * If your types are not known at compile time, you can use {@link Types#parameterizedType} to make a
+ * parameterized type and give it to a {@link #ofType Key.ofType} constructor.
+ *
+ * @param binding type
+ */
+public abstract class Key {
+ private final Type type;
+ private final @Nullable Object qualifier;
+
+ private int hash;
+
+ protected Key() {
+ this(null);
+ }
+
+ protected Key(@Nullable Object qualifier) {
+ this.type = TypeUtils.simplifyType(getTypeParameter());
+ this.qualifier = qualifier;
+ }
+
+ protected Key(Type type, @Nullable Object qualifier) {
+ this.type = TypeUtils.simplifyType(type);
+ this.qualifier = qualifier;
+ }
+
+ static final class KeyImpl extends Key {
+ KeyImpl(Type type, Object qualifier) {
+ super(type, qualifier);
+ }
+ }
+
+ public static Key of(Class type) {
+ return new KeyImpl<>(type, null);
+ }
+
+ public static Key of(Class type, @Nullable Object qualifier) {
+ return new KeyImpl<>(type, qualifier);
+ }
+
+ public static Key ofType(Type type) {
+ return new KeyImpl<>(type, null);
+ }
+
+ public static Key ofType(Type type, @Nullable Object qualifier) {
+ return new KeyImpl<>(type, qualifier);
+ }
+
+ private Type getTypeParameter() {
+ // this cannot possibly fail so not even a check here
+ Type typeArgument = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ Object outerInstance = ReflectionUtils.getOuterClassInstance(this);
+ // // the outer instance is null in static context
+ return outerInstance != null
+ ? Types.bind(typeArgument, Types.getAllTypeBindings(outerInstance.getClass()))
+ : typeArgument;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * A shortcut for {@link Types#getRawType(Type)}(key.getType()).
+ * Also casts the result to a properly parameterized class.
+ */
+ @SuppressWarnings("unchecked")
+ public Class getRawType() {
+ return (Class) Types.getRawType(type);
+ }
+
+ /**
+ * Returns a type parameter of the underlying type wrapped as a key with no qualifier.
+ *
+ * @throws IllegalStateException when underlying type is not a parameterized one.
+ */
+ public Key getTypeParameter(int index) {
+ if (type instanceof ParameterizedType) {
+ return new KeyImpl<>(((ParameterizedType) type).getActualTypeArguments()[index], null);
+ }
+ throw new IllegalStateException("Expected type from key " + getDisplayString() + " to be parameterized");
+ }
+
+ public @Nullable Object getQualifier() {
+ return qualifier;
+ }
+
+ /**
+ * Returns an underlying type with display string formatting (package names stripped)
+ * and prepended qualifier display string if this key has a qualifier.
+ */
+ public String getDisplayString() {
+ return (qualifier != null ? Utils.getDisplayString(qualifier) + " " : "")
+ + ReflectionUtils.getDisplayName(type);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Key>)) {
+ return false;
+ }
+ Key> that = (Key>) o;
+ return type.equals(that.type) && Objects.equals(qualifier, that.qualifier);
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = hash;
+ if (hashCode == 0) {
+ hash = 31 * type.hashCode() + (qualifier == null ? 0 : qualifier.hashCode());
+ }
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ return (qualifier != null ? qualifier + " " : "") + type.getTypeName();
+ }
+}
diff --git a/maven-di/src/main/java/org/apache/maven/di/Scope.java b/maven-di/src/main/java/org/apache/maven/di/Scope.java
new file mode 100644
index 000000000000..c05334f319f2
--- /dev/null
+++ b/maven-di/src/main/java/org/apache/maven/di/Scope.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.di;
+
+import java.lang.annotation.Annotation;
+import java.util.function.Supplier;
+
+public interface Scope {
+
+ Supplier scope(Key key, Annotation scope, Supplier unscoped);
+}
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java b/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
new file mode 100644
index 000000000000..fc46f84619f0
--- /dev/null
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.di.impl;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.di.Key;
+
+import static java.util.stream.Collectors.joining;
+
+public abstract class Binding {
+ private final Set> dependencies;
+ private Annotation scope;
+ private int priority;
+ private Key> originalKey;
+
+ protected Binding(Key extends T> originalKey, Set> dependencies) {
+ this(originalKey, dependencies, null, 0);
+ }
+
+ protected Binding(Key> originalKey, Set> dependencies, Annotation scope, int priority) {
+ this.originalKey = originalKey;
+ this.dependencies = dependencies;
+ this.scope = scope;
+ this.priority = priority;
+ }
+
+ public static Binding toInstance(T instance) {
+ return new BindingToInstance<>(instance);
+ }
+
+ public static Binding to(TupleConstructorN constructor, Class>[] types) {
+ return Binding.to(constructor, Stream.of(types).map(Key::of).toArray(Key>[]::new));
+ }
+
+ public static Binding to(TupleConstructorN constructor, Key>[] dependencies) {
+ return to(constructor, dependencies, 0);
+ }
+
+ public static Binding to(TupleConstructorN constructor, Key>[] dependencies, int priority) {
+ return new BindingToConstructor<>(null, constructor, dependencies, priority);
+ }
+
+ // endregion
+
+ public Binding scope(Annotation scope) {
+ this.scope = scope;
+ return this;
+ }
+
+ public Binding prioritize(int priority) {
+ this.priority = priority;
+ return this;
+ }
+
+ public Binding withKey(Key> key) {
+ this.originalKey = key;
+ return this;
+ }
+
+ public Binding initializeWith(BindingInitializer bindingInitializer) {
+ return new Binding(
+ this.originalKey,
+ Stream.of(this.dependencies, bindingInitializer.getDependencies())
+ .flatMap(Set::stream)
+ .collect(Collectors.toSet()),
+ this.scope,
+ this.priority) {
+ @Override
+ public Supplier compile(Function, Supplier>> compiler) {
+ final Supplier compiledBinding = Binding.this.compile(compiler);
+ final Consumer consumer = bindingInitializer.compile(compiler);
+ return () -> {
+ T instance = compiledBinding.get();
+ consumer.accept(instance);
+ return instance;
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Binding.this.toString();
+ }
+ };
+ }
+
+ public abstract Supplier compile(Function, Supplier>> compiler);
+
+ public Set> getDependencies() {
+ return dependencies;
+ }
+
+ public Annotation getScope() {
+ return scope;
+ }
+
+ public String getDisplayString() {
+ return dependencies.stream().map(Key::getDisplayString).collect(joining(", ", "[", "]"));
+ }
+
+ public Key> getOriginalKey() {
+ return originalKey;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ @Override
+ public String toString() {
+ return "Binding" + dependencies.toString();
+ }
+
+ @FunctionalInterface
+ public interface TupleConstructorN {
+ R create(Object... args);
+ }
+
+ public static class BindingToInstance extends Binding {
+ final T instance;
+
+ BindingToInstance(T instance) {
+ super(null, Collections.emptySet());
+ this.instance = instance;
+ }
+
+ @Override
+ public Supplier compile(Function, Supplier>> compiler) {
+ return () -> instance;
+ }
+
+ @Override
+ public String toString() {
+ return "BindingToInstance[" + instance + "]" + getDependencies();
+ }
+ }
+
+ public static class BindingToConstructor extends Binding {
+ final TupleConstructorN constructor;
+
+ BindingToConstructor(
+ Key extends T> key, TupleConstructorN constructor, Key>[] dependencies, int priority) {
+ super(key, new HashSet<>(Arrays.asList(dependencies)), null, priority);
+ this.constructor = constructor;
+ }
+
+ @Override
+ public Supplier compile(Function, Supplier>> compiler) {
+ return () -> {
+ Object[] args = getDependencies().stream()
+ .map(compiler)
+ .map(Supplier::get)
+ .toArray();
+ return constructor.create(args);
+ };
+ }
+
+ @Override
+ public String toString() {
+ return "BindingToConstructor[" + constructor + "]" + getDependencies();
+ }
+ }
+}
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/BindingInitializer.java b/maven-di/src/main/java/org/apache/maven/di/impl/BindingInitializer.java
new file mode 100644
index 000000000000..8561db988399
--- /dev/null
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/BindingInitializer.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.di.impl;
+
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.maven.di.Key;
+
+import static java.util.stream.Collectors.toSet;
+
+public abstract class BindingInitializer {
+
+ private final Set> dependencies;
+
+ protected BindingInitializer(Set> dependencies) {
+ this.dependencies = dependencies;
+ }
+
+ public Set> getDependencies() {
+ return dependencies;
+ }
+
+ public abstract Consumer compile(Function, Supplier>> compiler);
+
+ public static BindingInitializer combine(List> bindingInitializers) {
+ Set> deps = bindingInitializers.stream()
+ .map(BindingInitializer::getDependencies)
+ .flatMap(Collection::stream)
+ .collect(toSet());
+ return new BindingInitializer(deps) {
+ @Override
+ public Consumer compile(Function, Supplier>> compiler) {
+ return instance -> bindingInitializers.stream()
+ .map(bindingInitializer -> bindingInitializer.compile(compiler))
+ .forEach(i -> i.accept(instance));
+ }
+ };
+ }
+}
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/DIException.java b/maven-di/src/main/java/org/apache/maven/di/impl/DIException.java
new file mode 100644
index 000000000000..4aca38c16e97
--- /dev/null
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/DIException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.di.impl;
+
+import org.apache.maven.di.Injector;
+
+/**
+ * A runtime exception that is thrown on startup when some static conditions fail
+ * (missing or cyclic dependencies, incorrect annotations etc.) or in runtime when
+ * you ask an {@link Injector} for an instance it does not have a {@link Binding binding} for.
+ */
+public final class DIException extends RuntimeException {
+ public DIException(String message) {
+ super(message);
+ }
+
+ public DIException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java b/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
new file mode 100644
index 000000000000..49df5788484d
--- /dev/null
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.di.impl;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.apache.maven.api.di.Provides;
+import org.apache.maven.api.di.Singleton;
+import org.apache.maven.api.di.Typed;
+import org.apache.maven.di.Injector;
+import org.apache.maven.di.Key;
+import org.apache.maven.di.Scope;
+
+public class InjectorImpl implements Injector {
+
+ private final Map, Set>> bindings = new HashMap<>();
+ private final Map, Scope> scopes = new HashMap<>();
+
+ public InjectorImpl() {
+ bindScope(Singleton.class, new SingletonScope());
+ }
+
+ public T getInstance(Class key) {
+ return getInstance(Key.of(key));
+ }
+
+ public T getInstance(Key key) {
+ return getCompiledBinding(key).get();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void injectInstance(T instance) {
+ ReflectionUtils.generateInjectingInitializer(Key.of((Class) instance.getClass()))
+ .compile(this::getCompiledBinding)
+ .accept(instance);
+ }
+
+ public Injector bindScope(Class extends Annotation> scopeAnnotation, Scope scope) {
+ if (scopes.put(scopeAnnotation, scope) != null) {
+ throw new DIException(
+ "Cannot rebind scope annotation class to a different implementation: " + scopeAnnotation);
+ }
+ return this;
+ }
+
+ public Injector bindInstance(Class clazz, U instance) {
+ Key> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
+ Binding binding = Binding.toInstance(instance);
+ return doBind(key, binding);
+ }
+
+ @Override
+ public Injector bindImplicit(Class> clazz) {
+ Key> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
+ Binding> binding = ReflectionUtils.generateImplicitBinding(key);
+ return doBind(key, binding);
+ }
+
+ private Injector doBind(Key> key, Binding> binding) {
+ doBindImplicit(key, binding);
+ Class> cls = key.getRawType().getSuperclass();
+ while (cls != Object.class && cls != null) {
+ key = Key.of(cls, key.getQualifier());
+ doBindImplicit(key, binding);
+ cls = cls.getSuperclass();
+ }
+ return this;
+ }
+
+ protected Injector bind(Key key, Binding b) {
+ Set> bindingSet = bindings.computeIfAbsent(key, $ -> new HashSet<>());
+ bindingSet.add(b);
+ return this;
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private Set> getBindings(Key key) {
+ return (Set) bindings.get(key);
+ }
+
+ public Supplier getCompiledBinding(Key key) {
+ Set> res = getBindings(key);
+ if (res != null) {
+ List> bindingList = new ArrayList<>(res);
+ Comparator> comparing = Comparator.comparing(Binding::getPriority);
+ bindingList.sort(comparing.reversed());
+ Binding binding = bindingList.get(0);
+ return compile(binding);
+ }
+ if (key.getRawType() == List.class) {
+ Set> res2 = getBindings(key.getTypeParameter(0));
+ if (res2 != null) {
+ List> bindingList =
+ res2.stream().map(this::compile).collect(Collectors.toList());
+ //noinspection unchecked
+ return () -> (Q) new WrappingList<>(bindingList, Supplier::get);
+ }
+ }
+ if (key.getRawType() == Map.class) {
+ Key> k = key.getTypeParameter(0);
+ Key