blob: 64a63f667478c8afd07205976a07f1c03852181a [file] [log] [blame]
package org.apache.karaf.boot.maven;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.karaf.boot.plugin.api.BootPlugin;
import org.apache.karaf.boot.plugin.api.StreamFactory;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
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.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.xbean.finder.ClassFinder;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.sonatype.plexus.build.incremental.BuildContext;
@Mojo(name = "generate", threadSafe = true, defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, inheritByDefault = false)
public class GenerateMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject mavenProject;
@Parameter(defaultValue = "${session}", required = true, readonly = true)
private MavenSession mavenSession;
@Component
private BuildPluginManager pluginManager;
@Parameter(defaultValue="${project}", required=true)
protected MavenProject project;
@Component
private BuildContext buildContext;
public void execute() throws MojoExecutionException {
try {
File buildDir = new File(project.getBuild().getDirectory());
File generatedDir = createGeneratedDir(buildDir);
URLClassLoader loader = projectClassLoader();
handleBootPlugins(loader, buildDir, generatedDir);
handleMavenPlugins(loader);
runMavenBundlePlugin();
} catch (Exception e) {
throw new MojoExecutionException("karaf-boot-maven-plugin failed", e);
}
}
private void handleMavenPlugins(URLClassLoader loader) throws Exception {
Enumeration<URL> resources = loader.getResources("karaf-boot/plugins.xml");
while (resources.hasMoreElements()) {
URL res = resources.nextElement();
executePluginDef(res.openStream());
}
}
private void executePluginDef(InputStream is) throws Exception {
Xpp3Dom pluginDef = Xpp3DomBuilder.build(is, "utf-8");
Plugin plugin = loadPlugin(pluginDef);
Xpp3Dom config = pluginDef.getChild("configuration");
PluginDescriptor pluginDesc = pluginManager.loadPlugin(plugin,
mavenProject.getRemotePluginRepositories(),
mavenSession.getRepositorySession());
Xpp3Dom executions = pluginDef.getChild("executions");
for ( Xpp3Dom execution : executions.getChildren()) {
Xpp3Dom goals = execution.getChild("goals");
for (Xpp3Dom goal : goals.getChildren()) {
MojoDescriptor desc = pluginDesc.getMojo(goal.getValue());
pluginManager.executeMojo(mavenSession, new MojoExecution(desc, config));
}
}
}
Plugin loadPlugin(Xpp3Dom pluginDef) {
String groupId = pluginDef.getChild("groupId").getValue();
String artifactId = pluginDef.getChild("artifactId").getValue();
String version = pluginDef.getChild("version").getValue();
Plugin plugin = new Plugin();
plugin.setGroupId(groupId);
plugin.setArtifactId(artifactId);
plugin.setVersion(version);
return plugin;
}
private File createGeneratedDir(File buildDir) {
File generatedDir = new File(buildDir, "generated-resources");
Resource resource = new Resource();
resource.setDirectory(generatedDir.getPath());
project.addResource(resource);
return generatedDir;
}
private void handleBootPlugins(URLClassLoader loader, File buildDir, File generatedDir)
throws MalformedURLException, InstantiationException, IllegalAccessException, IOException {
ClassFinder finder = new ClassFinder(loader, Arrays.asList(loader.getURLs()));
List<Class<? extends BootPlugin>> plugins = finder.findImplementations(BootPlugin.class);
Map<String, List<String>> headers = new HashMap<String, List<String>>();
for (Class<? extends BootPlugin> pluginClass : plugins) {
applyBootPlugin(generatedDir, finder, headers, pluginClass);
}
addImportDefault(headers);
File bndInst = new File(buildDir, "org.apache.karaf.boot.bnd");
writeBndFile(bndInst, headers);
}
private void applyBootPlugin(File generatedDir, ClassFinder finder, Map<String, List<String>> combined,
Class<? extends BootPlugin> pluginClass)
throws InstantiationException, IllegalAccessException {
BootPlugin plugin = pluginClass.newInstance();
Class<? extends Annotation> annotation = plugin.getAnnotation();
List<Class<?>> classes = finder.findAnnotatedClasses(annotation);
if (!classes.isEmpty()) {
Map<String, List<String>> headers = plugin.enhance(classes, generatedDir, new BuildStreamFactory());
combine(combined, headers);
}
}
private void addImportDefault(Map<String, List<String>> combined) {
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Import-Package", Arrays.asList("*"));
combine(combined, headers);
}
private void writeBndFile(File bndInst, Map<String, List<String>> combined) throws IOException {
try (
OutputStream os = buildContext.newFileOutputStream(bndInst);
OutputStreamWriter writer = new OutputStreamWriter(os, "utf-8")
) {
for (String key : combined.keySet()) {
writer.append(String.format("%s: %s\n", key, String.join(",", combined.get(key))));
}
}
}
private void combine(Map<String, List<String>> combined, Map<String, List<String>> headers) {
for (String key : headers.keySet()) {
List<String> values = headers.get(key);
if (!combined.containsKey(key)) {
combined.put(key, new ArrayList<>(values));
} else {
List<String> cValues = combined.get(key);
cValues.addAll(values);
}
}
}
private URLClassLoader projectClassLoader() throws MalformedURLException {
List<URL> urls = new ArrayList<URL>();
urls.add(new File(project.getBuild().getOutputDirectory()).toURI().toURL());
for (Artifact artifact : project.getArtifacts()) {
File file = artifact.getFile();
if (file != null) {
urls.add(file.toURI().toURL());
}
}
URLClassLoader loader = new URLClassLoader(urls.toArray(new URL[urls.size()]), getClass().getClassLoader());
return loader;
}
private void runMavenBundlePlugin() throws Exception {
InputStream is = this.getClass().getResourceAsStream("/configuration.xml");
MojoExecution execution = new MojoExecution(getMavenBundleMojo(), Xpp3DomBuilder.build(is, "utf-8"));
getLog().info("Invoking maven-bundle-plugin");
pluginManager.executeMojo(mavenSession, execution);
}
private MojoDescriptor getMavenBundleMojo() throws Exception {
Plugin plugin = new Plugin();
plugin.setGroupId("org.apache.felix");
plugin.setArtifactId("maven-bundle-plugin");
plugin.setVersion("3.0.0");
plugin.setInherited(true);
plugin.setExtensions(true);
PluginDescriptor desc = pluginManager.loadPlugin(plugin, mavenProject.getRemotePluginRepositories(), mavenSession.getRepositorySession());
return desc.getMojo("bundle");
}
private final class BuildStreamFactory implements StreamFactory {
@Override
public OutputStream create(File file) {
try {
file.getParentFile().mkdirs();
return buildContext.newFileOutputStream(file);
} catch (IOException e) {
throw new RuntimeException("Error creating file " + file, e);
}
}
}
}