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.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
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.inject.Named;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.logging.Log;
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.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
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.logging.MavenLogWrapper;
import de.vandermeer.asciitable.v2.RenderedTable;
import de.vandermeer.asciitable.v2.V2_AsciiTable;
import de.vandermeer.asciitable.v2.render.V2_AsciiTableRenderer;
import de.vandermeer.asciitable.v2.render.WidthLongestLine;
import de.vandermeer.asciitable.v2.row.ContentRow;
import de.vandermeer.asciitable.v2.themes.V2_E_TableThemes;
/**
* 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 DEFAULT_WORKFLOW_DIR = "META-INF/workflows";
private static final String SYSPROP_PRINT_WF = "printWorkflow";
private static final String SYSPROP_PRINT_STEPS = "printSteps";
@Component
public ArtifactResolver _resolver;
@Parameter(defaultValue = "${settings}", readonly = true, required = true)
public Settings _settings;
@Parameter(readonly = true, defaultValue = "${repositorySystemSession}")
public RepositorySystemSession _repoSystemSession;
@Parameter(readonly = true, defaultValue = "${project.remotePluginRepositories}")
public List _pluginRepos;
@Parameter(property = "workflow")
public File workflowDescriptor;
@Parameter(defaultValue = "true", property = "enableLogTimestamps")
@MojoProduces
@Named("enableLogTimestamps")
public boolean enableLogTimestamps;
@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 (printDefaultWorkflow()) {
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();
Map steps = getAllProcessingSteps(weldContainer);
if (printAvailableSteps(steps)) {
return;
}
ProcessingWorkflow workflow = WorkflowUtil.parseWorkflow(getWorkflowDescriptor(), getGoalName());
WorkflowUtil.addExecutionContexts(workflow);
WorkflowExecutor executor = new WorkflowExecutor(workflow, steps, getProject(), getLog());
executor.validate(!this._settings.isOffline());
executor.execute();
} finally {
if (weldContainer != null && weldContainer.isRunning()) {
weldContainer.shutdown();
}
}
}
private boolean printDefaultWorkflow() throws MojoExecutionException {
if (System.getProperty(SYSPROP_PRINT_WF) == null) {
return false;
}
PluginDescriptor pluginDescriptor = getPluginDescriptor();
StringBuilder sb = new StringBuilder();
if (StringUtils.isNotBlank(pluginDescriptor.getGoalPrefix())) {
sb.append(pluginDescriptor.getGoalPrefix());
} else {
sb.append(pluginDescriptor.getGroupId()).append(':').append(pluginDescriptor.getArtifactId()).append(':')
.append(pluginDescriptor.getVersion());
}
sb.append(':').append(getGoalName());
Log log = createLogWrapper();
log.info("Default workflow for '" + sb + "':");
String goalName = getGoalName();
int x = 77 - goalName.length();
int a = x / 2;
int b = x % 2 == 1 ? a + 1 : a;
StringBuilder separator = new StringBuilder();
separator.append(Strings.repeat("=", a)).append(' ').append(goalName).append(' ').append(Strings.repeat("=", b));
System.out.println(separator);
InputStream in = null;
try {
in = getWorkflowDescriptor();
ByteStreams.copy(in, System.out);
} catch (IOException e) {
throw new MojoExecutionException("A problem occurred during the serialization of the defualt workflow.", e);
} finally {
Closeables.closeQuietly(in);
}
System.out.println(separator);
return true;
}
private boolean printAvailableSteps(Map steps) throws MojoExecutionException {
if (System.getProperty(SYSPROP_PRINT_STEPS) == null) {
return false;
}
V2_AsciiTable table = new V2_AsciiTable();
table.addRule();
ContentRow header = table.addRow("ID", "DESCRIPTION", "REQUIRES ONLINE");
header.setAlignment(new char[] { 'c', 'c', 'c' });
table.addStrongRule();
List sortedIds = Lists.newArrayList(steps.keySet());
Collections.sort(sortedIds);
for (String id : sortedIds) {
ProcessingStep annotation = steps.get(id).getClass().getAnnotation(ProcessingStep.class);
ContentRow data = table.addRow(annotation.id(), annotation.description(), annotation.requiresOnline());
data.setAlignment(new char[] { 'l', 'l', 'c' });
table.addRule();
}
V2_AsciiTableRenderer renderer = new V2_AsciiTableRenderer();
renderer.setTheme(V2_E_TableThemes.UTF_STRONG_DOUBLE.get());
renderer.setWidth(new WidthLongestLine().add(10, 20).add(20, 50).add(10, 10));
RenderedTable renderedTable = renderer.render(table);
Log log = createLogWrapper();
log.info(
"The following processing steps are available on classpath and can be configured as part of a custom workflow.");
System.out.println(renderedTable);
return true;
}
@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 {
Set fields = Sets.newHashSet(getClass().getFields());
fields.addAll(Sets.newHashSet(getClass().getDeclaredFields()));
for (Field f : fields) {
if (f.isAnnotationPresent(MojoProduces.class)) {
try {
f.setAccessible(true);
event.addBean(
new CdiBeanWrapper