com.itemis.maven.plugins.cdi.AbstractCDIMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cdi-plugin-utils Show documentation
Show all versions of cdi-plugin-utils Show documentation
Provides an abstract Mojo that enables CDI-based dependency injection for Maven Plugins.
package com.itemis.maven.plugins.cdi;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.inject.Named;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Settings;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.impl.ArtifactResolver;
import org.eclipse.aether.repository.RemoteRepository;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.itemis.maven.plugins.cdi.annotations.MojoProduces;
import com.itemis.maven.plugins.cdi.annotations.ProcessingStep;
import com.itemis.maven.plugins.cdi.internal.beans.CdiBeanWrapper;
import com.itemis.maven.plugins.cdi.internal.beans.CdiProducerBean;
import com.itemis.maven.plugins.cdi.internal.util.CDIUtil;
import com.itemis.maven.plugins.cdi.internal.util.MavenUtil;
import com.itemis.maven.plugins.cdi.internal.util.workflow.ProcessingWorkflow;
import com.itemis.maven.plugins.cdi.internal.util.workflow.WorkflowExecutor;
import com.itemis.maven.plugins.cdi.internal.util.workflow.WorkflowUtil;
import com.itemis.maven.plugins.cdi.internal.util.workflow.WorkflowValidator;
import com.itemis.maven.plugins.cdi.logging.MavenLogWrapper;
/**
* An abstract Mojo that enables CDI-based dependency injection for the current maven plugin.
* This Mojo enables you to decouple different parts of your plugin implementation and also dynamically inject
* additional funktionality into your plugin.
*
*
* ATTENTION: Please do not use annotations such as {@code @javax.inject.Inject} or
* {@code @javax.enterprise.inject.Produces} directly in your Mojo! There are special replacements for that in the
* annotations package of this library. Using CDI annotations directly in the Mojo would trigger Maven's own CDI
* adaption!
*
*
* Using this abstract Mojo as the parent of your own Mojo, you can simply see the Mojo class as a data container whose
* single responsibility is to provide parameters for your business logic implementations. Simply get the
* Mojo parameters injected and use the producer annotation to provide the bean to your implementations:
*
*
* @Parameter
* @MojoProduces
* @Named("sourcePath")
* private String sourcePath;
*
* @Parameter(defaultValue = "${reactorProjects}", readonly = true, required = true)
* @MojoProduces
* @Named("reactorProjects")
* private List<MavenProject> reactorProjects;
*
*
* Or use a producer method for the logger:
*
*
* @MojoProduces
* public MavenLogWrapper createLogWrapper() {
* MavenLogWrapper log = new MavenLogWrapper(getLog());
* if (this.enableLogTimestamps) {
* log.enableLogTimestamps();
* }
* return log;
* }
*
*
* ATTENTION: Make sure to not override the {@link #execute()} method since this method is responsible for the
* CDI setup and will
* trigger your business logic impelementations automatically.
* Implement your business logic in one or more classes that are annotated with {@link ProcessingStep} and implement
* {@link CDIMojoProcessingStep}. Then orchestrate your standard business workflow in a worflow descriptor file.
*
*
* The Workflow Descriptor
*
* - The descriptor is located under META-INF/workflows
* - The name of the workflow descriptor file must match the name of the goal. F.i. goal="perform"
* workflow-file="META-INF/workflows/perform"
* - A simple workflow lists just all processing step ids in the respective order (each id on a new line).
* - Steps that are encapsuled in
parallel{}
are executed in parallel. All other steps will be executed
* sequentially.
* - A line starting with a
#
will be treated as a comment.
*
*
* A Sample Workflow
* goal=perform
* workflow-file=META-INF/workflows/perform
*
*
* init
* # The following steps can be run in parallel since they do not modify the project but only perform some checks
* parallel {
* checkUser
* checkConnection
* checkAether
* }
* compute
* upload
* validate
*
*
* @author Stanley Hillner
* @since 1.0.0
*/
public class AbstractCDIMojo extends AbstractMojo implements Extension {
private static final String SYSPROP_PRINT_WF = "printWorkflow";
private static final String SYSPROP_PRINT_STEPS = "printSteps";
@Component
private ArtifactResolver _resolver;
@Parameter(defaultValue = "${settings}", readonly = true, required = true)
private Settings _settings;
@Parameter(readonly = true, defaultValue = "${repositorySystemSession}")
private RepositorySystemSession _repoSystemSession;
@Parameter(readonly = true, defaultValue = "${project.remotePluginRepositories}")
private List _pluginRepos;
@Parameter(property = "mojoExecution", readonly = true)
private MojoExecution _mojoExecution;
@Parameter(property = "session", readonly = true)
private MavenSession _session;
@Parameter(property = "workflow")
private File workflowDescriptor;
@Parameter(defaultValue = "true", property = "enableLogTimestamps")
@MojoProduces
@Named("enableLogTimestamps")
private boolean enableLogTimestamps;
private ProcessingWorkflow workflow;
private Map allAvailableProcessingSteps = Maps.newHashMap();
@MojoProduces
public final MavenLogWrapper createLogWrapper() {
MavenLogWrapper log = new MavenLogWrapper(getLog());
if (this.enableLogTimestamps) {
log.enableLogTimestamps();
}
return log;
}
@Override
public final void execute() throws MojoExecutionException, MojoFailureException {
if (System.getProperty(SYSPROP_PRINT_WF) != null) {
WorkflowUtil.printWorkflow(getGoalName(), getPluginDescriptor(), Optional.fromNullable(this.workflowDescriptor),
createLogWrapper());
return;
}
System.setProperty("org.jboss.logging.provider", "slf4j");
String logLevel = "info";
if (getLog().isDebugEnabled()) {
logLevel = "debug";
}
System.setProperty("org.slf4j.simpleLogger.log.org.jboss.weld", logLevel);
Weld weld = new Weld();
weld.addExtension(this);
addPluginDependencies(weld);
WeldContainer weldContainer = null;
try {
weldContainer = weld.initialize();
if (System.getProperty(SYSPROP_PRINT_STEPS) != null) {
WorkflowUtil.printAvailableSteps(this.allAvailableProcessingSteps, createLogWrapper());
return;
}
WorkflowUtil.addExecutionContexts(getWorkflow());
Map processingSteps = getAllProcessingSteps(weldContainer);
PluginParameterExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(this._session,
this._mojoExecution);
WorkflowExecutor executor = new WorkflowExecutor(getWorkflow(), processingSteps, getLog(), expressionEvaluator);
executor.validate(!this._settings.isOffline());
executor.execute();
} finally {
if (weldContainer != null && weldContainer.isRunning()) {
weldContainer.shutdown();
}
}
}
private ProcessingWorkflow getWorkflow() throws MojoExecutionException, MojoFailureException {
if (this.workflow == null) {
InputStream wfDescriptor = WorkflowUtil.getWorkflowDescriptor(getGoalName(), getPluginDescriptor(),
Optional.fromNullable(this.workflowDescriptor), createLogWrapper());
try {
WorkflowValidator.validateSyntactically(wfDescriptor);
} catch (RuntimeException e) {
throw new MojoFailureException(e.getMessage());
}
wfDescriptor = WorkflowUtil.getWorkflowDescriptor(getGoalName(), getPluginDescriptor(),
Optional.fromNullable(this.workflowDescriptor), createLogWrapper());
this.workflow = WorkflowUtil.parseWorkflow(wfDescriptor, getGoalName());
}
return this.workflow;
}
@SuppressWarnings("unused")
// will be called automatically by the CDI container for all annotated types
private void skipUnusedStepsFromBeanDiscovery(@Observes ProcessAnnotatedType> event, BeanManager beanManager)
throws MojoExecutionException, MojoFailureException {
// https://github.com/shillner/maven-cdi-plugin-utils/issues/14
Class> type = event.getAnnotatedType().getJavaClass();
ProcessingStep annotation = type.getAnnotation(ProcessingStep.class);
if (annotation != null) {
// adding the step to the list of all available processing steps
String id = annotation.id();
Preconditions.checkState(!this.allAvailableProcessingSteps.containsKey(id),
"The processing step id '" + id + "' is not unique!");
this.allAvailableProcessingSteps.put(id, annotation);
// vetoing the bean discovery of a step that is not part of the current workflow
// this prevents the issue that data shall be injected that isn't produced anywhere!
ProcessingWorkflow workflow = getWorkflow();
if (!workflow.containsStep(annotation.id())) {
event.veto();
}
}
}
@SuppressWarnings("unused")
// will be called automatically by the CDI container once the bean discovery has finished
private void processMojoCdiProducerFields(@Observes AfterBeanDiscovery event, BeanManager beanManager)
throws MojoExecutionException {
Class> cls = getClass();
Set fields = Sets.newHashSet();
while (cls != AbstractCDIMojo.class) {
fields.addAll(Sets.newHashSet(cls.getFields()));
fields.addAll(Sets.newHashSet(cls.getDeclaredFields()));
cls = cls.getSuperclass();
}
for (Field f : fields) {
if (f.isAnnotationPresent(MojoProduces.class)) {
try {
f.setAccessible(true);
event.addBean(
new CdiBeanWrapper