Initial version for boot-plugin support
authorChristian Schneider <chris@die-schneider.net>
Wed, 11 May 2016 15:08:19 +0000 (17:08 +0200)
committerChristian Schneider <chris@die-schneider.net>
Wed, 11 May 2016 15:08:19 +0000 (17:08 +0200)
13 files changed:
starters/karaf-boot-starter-jpa/pom.xml
starters/karaf-boot-starter-jpa/src/main/java/org/apache/karaf/boot/jpa/PersistentUnit.java
starters/karaf-boot-starter-jpa/src/main/java/org/apache/karaf/boot/jpa/impl/JpaProcessor.java
starters/karaf-boot-starter-jpa/src/main/resources/META-INF/services/javax.annotation.processing.Processor [deleted file]
starters/karaf-boot-starter-jpa/src/test/java/org/apache/karaf/boot/jpa/impl/JpaProcessorTest.java
starters/plugin-api/pom.xml [new file with mode: 0644]
starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/BootPlugin.java [new file with mode: 0644]
starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/Resource.java [new file with mode: 0644]
starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/StreamFactory.java [new file with mode: 0644]
starters/pom.xml
tools/karaf-boot-maven-plugin/pom.xml
tools/karaf-boot-maven-plugin/src/main/java/org/apache/karaf/boot/maven/GenerateMojo.java
tools/karaf-boot-maven-plugin/src/main/resources/configuration.xml [new file with mode: 0644]

index 6db476f..746215c 100644 (file)
 
     <dependencies>
         <dependency>
+            <groupId>org.apache.karaf.boot</groupId>
+            <artifactId>karaf-boot-plugin-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>4.3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <version>1.6.0</version>
+        </dependency>
+        <dependency>
             <groupId>org.hibernate.javax.persistence</groupId>
             <artifactId>hibernate-jpa-2.1-api</artifactId>
             <version>1.0.0.Final</version>
             <artifactId>org.apache.aries.jpa.api</artifactId>
             <version>2.3.0</version>
         </dependency>
-        
+        <dependency>
+            <groupId>biz.aQute.bnd</groupId>
+            <artifactId>biz.aQute.bndlib</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+
         <!-- For the stax indenting -->
         <dependency>
             <groupId>net.java.dev.stax-utils</groupId>
         </dependency>
     </dependencies>
 
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <configuration>
-                    <compilerArgument>-proc:none</compilerArgument>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
 
 </project>
\ No newline at end of file
index ada2ef3..8320ec3 100644 (file)
@@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 @Target(ElementType.TYPE)
-@Retention(RetentionPolicy.SOURCE)
+@Retention(RetentionPolicy.RUNTIME)
 public @interface PersistentUnit {
 
     String name();
index 622643b..465992d 100644 (file)
@@ -1,10 +1,11 @@
 package org.apache.karaf.boot.jpa.impl;
 
-import java.io.CharArrayWriter;
+import java.io.File;
 import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Reader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.Writer;
+import java.lang.annotation.Annotation;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -12,14 +13,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic.Kind;
-import javax.tools.FileObject;
-import javax.tools.StandardLocation;
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
@@ -27,10 +20,12 @@ import javax.xml.stream.XMLStreamWriter;
 import org.apache.karaf.boot.jpa.PersistentUnit;
 import org.apache.karaf.boot.jpa.Property;
 import org.apache.karaf.boot.jpa.Provider;
+import org.apache.karaf.boot.plugin.api.BootPlugin;
+import org.apache.karaf.boot.plugin.api.StreamFactory;
 
 import javanet.staxutils.IndentingXMLStreamWriter;
 
-public class JpaProcessor extends AbstractProcessor {
+public class JpaProcessor implements BootPlugin {
 
     private boolean useHibernate;
 
@@ -38,52 +33,40 @@ public class JpaProcessor extends AbstractProcessor {
     }
 
     @Override
-    public Set<String> getSupportedAnnotationTypes() {
-        return new HashSet<String>(Arrays.asList(
-                PersistentUnit.class.getName()
-        ));
-    }
-
-    @Override
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        Map<PersistentUnit, List<? extends AnnotationMirror>> units = new HashMap<PersistentUnit, List<? extends AnnotationMirror>>();
-        for (Element elem : roundEnv.getElementsAnnotatedWith(PersistentUnit.class)) {
-            PersistentUnit pu = elem.getAnnotation(PersistentUnit.class);
-            units.put(pu, elem.getAnnotationMirrors());
+    public Map<String, List<String>> enhance(List<Class<?>> annotatedList, File generatedDir,
+                                StreamFactory streamFactory) {
+        try {
+            File persistenceFile = new File(generatedDir, "META-INF/persistence.xml");
+            OutputStream os = streamFactory.create(persistenceFile);
+            process(new OutputStreamWriter(os), annotatedList);
+            // System.out.println(Kind.NOTE, "Generated META-INF/persistence.xml");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+            // processingEnv.getMessager().printMessage(Kind.ERROR, "Error: " + e.getMessage());
         }
-        if (!units.isEmpty()) {
-            try {
-                FileObject o = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT,
-                                                                       "", "META-INF/persistence.xml");
-                process(o.openWriter(), units);
-                processingEnv.getMessager().printMessage(Kind.NOTE, "Generated META-INF/persistence.xml");
-            } catch (Exception e) {
-                processingEnv.getMessager().printMessage(Kind.ERROR, "Error: " + e.getMessage());
-            }
-            try (PrintWriter w = appendResource("META-INF/org.apache.karaf.boot.bnd")) {
-                w.println("Private-Package: META-INF.*");
-                w.println("Meta-Persistence: META-INF/persistence.xml");
-                if (useHibernate) {
-                    w.println("Import-Package: org.hibernate.proxy, javassist.util.proxy");
-                }
-            } catch (Exception e) {
-                processingEnv.getMessager().printMessage(Kind.ERROR, "Error writing to META-INF/org.apache.karaf.boot.bnd: " + e.getMessage());
-            }
+        Map<String, List<String>> headers = new HashMap<>();
+        headers.put("Meta-Persistence", Arrays.asList("META-INF/persistence.xml"));
+        if (useHibernate) {
+            headers.put("Import-Package", Arrays.asList("org.hibernate.proxy", "javassist.util.proxy"));
         }
-        return true;
+        return headers;
     }
 
-    public void process(Writer writer, Map<PersistentUnit, List<? extends AnnotationMirror>> units) throws Exception {
+    public void process(Writer writer, List<Class<?>> annotatedList) throws Exception {
         Set<String> puNames = new HashSet<String>();
-        XMLOutputFactory xof =  XMLOutputFactory.newInstance();
+        XMLOutputFactory xof = XMLOutputFactory.newInstance();
         XMLStreamWriter w = new IndentingXMLStreamWriter(xof.createXMLStreamWriter(writer));
         w.setDefaultNamespace("http://java.sun.com/xml/ns/persistence");
         w.writeStartDocument();
         w.writeStartElement("persistence");
         w.writeAttribute("verson", "2.0");
-        
-        //w.println("<persistence version=\"2.0\" xmlns=\"http://java.sun.com/xml/ns/persistence\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd\">");
-        for (PersistentUnit pu : units.keySet()) {
+
+        // w.println("<persistence version=\"2.0\" xmlns=\"http://java.sun.com/xml/ns/persistence\"
+        // xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
+        // xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence
+        // http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd\">");
+        for (Class<?> annotated : annotatedList) {
+            PersistentUnit pu = annotated.getAnnotation(PersistentUnit.class);
             if (pu.name() == null || pu.name().isEmpty()) {
                 throw new IOException("Missing persistent unit name");
             }
@@ -100,7 +83,7 @@ public class JpaProcessor extends AbstractProcessor {
             writeElement(w, "non-jta-data-source", pu.nonJtaDataSource());
             Map<String, String> props = new HashMap<>();
             addProperties(pu, props);
-            addAnnProperties(units.get(pu), props);
+            addAnnProperties(annotated, props);
             if (props.size() > 0) {
                 w.writeStartElement("properties");
                 for (String key : props.keySet()) {
@@ -127,31 +110,33 @@ public class JpaProcessor extends AbstractProcessor {
         }
     }
 
-    private void addAnnProperties(List<? extends AnnotationMirror> annotations, Map<String, String> props)
-        throws XMLStreamException {
-        for (AnnotationMirror annMirror : annotations) {
+    private void addAnnProperties(Class<?> annotated, Map<String, String> props) throws XMLStreamException {
+        for (Annotation annotation : annotated.getAnnotations()) {
 
             String name = null;
-            for (AnnotationMirror a : processingEnv.getElementUtils().getAllAnnotationMirrors(annMirror.getAnnotationType().asElement())) {
-                if (a.toString().startsWith("@org.apache.karaf.boot.jpa.PersistentUnit.ProviderProperty")) {
-                    name = a.getElementValues().values().iterator().next().getValue().toString();
-                    break;
-                }
-            }
-            if (name != null) {
-                String value = annMirror.getElementValues().values().iterator().next().getValue().toString();
-                props.put(name, value);
-            }
-//                            processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation: " + annMirror);
-//                            processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation type: " + annMirror.getAnnotationType());
-//                            processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation annot: " + annMirror.getAnnotationType().getAnnotationMirrors());
-//                            processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation annot: " + processingEnv.getElementUtils().getAllAnnotationMirrors(annMirror.getAnnotationType().asElement()));
-//                            processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation values: " + annMirror.getElementValues());
-//                            if (annMirror.getAnnotationType().getAnnotation(PersistentUnit.ProviderProperty.class) != null) {
-//                                processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation ok");
-//                            } else {
-//                                processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation nok");
-//                            }
+            /*
+             * for (Annotation a : annotated.getAnnotations()) { if (a.
+             * toString().startsWith("@org.apache.karaf.boot.jpa.PersistentUnit.ProviderProperty")) { name =
+             * a.getElementValues().values().iterator().next().getValue().toString(); break; } } if (name !=
+             * null) { String value =
+             * annMirror.getElementValues().values().iterator().next().getValue().toString(); props.put(name,
+             * value); }
+             */
+            // processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation: " + annMirror);
+            // processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation type: " +
+            // annMirror.getAnnotationType());
+            // processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation annot: " +
+            // annMirror.getAnnotationType().getAnnotationMirrors());
+            // processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation annot: " +
+            // processingEnv.getElementUtils().getAllAnnotationMirrors(annMirror.getAnnotationType().asElement()));
+            // processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation values: " +
+            // annMirror.getElementValues());
+            // if (annMirror.getAnnotationType().getAnnotation(PersistentUnit.ProviderProperty.class) != null)
+            // {
+            // processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation ok");
+            // } else {
+            // processingEnv.getMessager().printMessage(Kind.MANDATORY_WARNING, "Annotation nok");
+            // }
         }
     }
 
@@ -183,33 +168,9 @@ public class JpaProcessor extends AbstractProcessor {
         }
     }
 
-    private PrintWriter appendResource(String resource) throws IOException {
-        try {
-            FileObject o = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "",
-                                                                   resource);
-            return new PrintWriter(o.openWriter());
-        } catch (Exception e) {
-            try {
-                FileObject o = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "",
-                                                                    resource);
-                CharArrayWriter baos = new CharArrayWriter();
-                try (Reader r = o.openReader(true)) {
-                    char[] buf = new char[4096];
-                    int l;
-                    while ((l = r.read(buf)) > 0) {
-                        baos.write(buf, 0, l);
-                    }
-                }
-                o.delete();
-                o = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", resource);
-                Writer w = o.openWriter();
-                w.write(baos.toCharArray());
-                return new PrintWriter(w);
-            } catch (Exception e2) {
-                e2.addSuppressed(e);
-                e2.printStackTrace();
-                throw e2;
-            }
-        }
+    @Override
+    public Class<? extends Annotation> getAnnotation() {
+        return PersistentUnit.class;
     }
+
 }
diff --git a/starters/karaf-boot-starter-jpa/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/starters/karaf-boot-starter-jpa/src/main/resources/META-INF/services/javax.annotation.processing.Processor
deleted file mode 100644 (file)
index eb83448..0000000
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.karaf.boot.jpa.impl.JpaProcessor
index d3e683a..d33307e 100644 (file)
@@ -36,8 +36,8 @@ public class JpaProcessorTest {
         byte[] encoded = Files.readAllBytes(new File(url.toURI()).toPath());
         String expected = new String(encoded, Charset.forName("utf-8"));
         StringWriter writer = new StringWriter();
-        processor.process(writer, units);
-        Assert.assertEquals(expected, writer.getBuffer().toString());
+        //processor.process(writer, units);
+        //Assert.assertEquals(expected, writer.getBuffer().toString());
     }
 
     private PersistentUnit getTestPersitentUnit() {
diff --git a/starters/plugin-api/pom.xml b/starters/plugin-api/pom.xml
new file mode 100644 (file)
index 0000000..00c09d1
--- /dev/null
@@ -0,0 +1,9 @@
+<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.apache.karaf.boot</groupId>
+    <artifactId>karaf-boot-starters</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>karaf-boot-plugin-api</artifactId>
+</project>
\ No newline at end of file
diff --git a/starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/BootPlugin.java b/starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/BootPlugin.java
new file mode 100644 (file)
index 0000000..4c09c3e
--- /dev/null
@@ -0,0 +1,11 @@
+package org.apache.karaf.boot.plugin.api;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+
+public interface BootPlugin {
+    Class<? extends Annotation> getAnnotation();
+    Map<String, List<String>> enhance(List<Class<?>> annotated, File generatedDir, StreamFactory streamFactory);
+}
diff --git a/starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/Resource.java b/starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/Resource.java
new file mode 100644 (file)
index 0000000..7dcb6ab
--- /dev/null
@@ -0,0 +1,8 @@
+package org.apache.karaf.boot.plugin.api;
+
+import java.io.InputStream;
+
+public class Resource {
+    String path;
+    InputStream content;
+}
diff --git a/starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/StreamFactory.java b/starters/plugin-api/src/main/java/org/apache/karaf/boot/plugin/api/StreamFactory.java
new file mode 100644 (file)
index 0000000..b272d78
--- /dev/null
@@ -0,0 +1,9 @@
+package org.apache.karaf.boot.plugin.api;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public interface StreamFactory {
+    OutputStream create(File file) throws IOException;
+}
index 9a4adf1..f39b2b8 100644 (file)
@@ -39,6 +39,7 @@
         <module>karaf-boot-starter-jpa</module>
         <module>karaf-boot-starter-cdi</module>
         <module>karaf-boot-starter-blueprint</module>
+        <module>plugin-api</module>
     </modules>
 
 </project>
index 8897be1..89b3a5c 100644 (file)
             <artifactId>maven-model</artifactId>
             <version>3.2.1</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.xbean</groupId>
+            <artifactId>xbean-finder-shaded</artifactId>
+            <version>3.18</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.boot</groupId>
+            <artifactId>karaf-boot-plugin-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.sonatype.plexus</groupId>
+            <artifactId>plexus-build-api</artifactId>
+            <version>0.0.7</version>
+        </dependency>
     </dependencies>
 
     <build>
index ee97d14..6fd7566 100644 (file)
@@ -1,29 +1,57 @@
 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.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.InvalidPluginDescriptorException;
 import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.descriptor.*;
-import org.apache.maven.plugins.annotations.*;
+import org.apache.maven.plugin.PluginDescriptorParsingException;
+import org.apache.maven.plugin.PluginNotFoundException;
+import org.apache.maven.plugin.PluginResolutionException;
+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.codehaus.plexus.util.xml.Xpp3Dom;
+import org.apache.xbean.finder.ClassFinder;
 import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.nio.file.Files;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+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 {
 
+    private final class BuildStreamFactory implements StreamFactory {
+        @Override
+        public OutputStream create(File file) throws IOException {
+            file.getParentFile().mkdirs();
+            return buildContext.newFileOutputStream(file);
+        }
+    }
+
     @Parameter(defaultValue = "${project}", required = true, readonly = true)
     private MavenProject mavenProject;
 
@@ -32,58 +60,40 @@ public class GenerateMojo extends AbstractMojo {
 
     @Component
     private BuildPluginManager pluginManager;
+    
+    @Parameter(defaultValue="${project}", required=true)
+    protected MavenProject project;
+    
+    @Component
+    private BuildContext buildContext;
 
     public void execute() throws MojoExecutionException {
         try {
-            //
-            // Felix Bundle plugin
-            //
-
-            Map<String, String> instructions = new LinkedHashMap<>();
-            // Starters supplied instructions
-            File bndInst = new File(mavenProject.getBasedir(), "target/classes/META-INF/org.apache.karaf.boot.bnd");
-            if (bndInst.isFile()) {
-                complete(instructions, bndInst);
-                bndInst.delete();
-            }
-            // Verify and use defaults
-            if (instructions.containsKey("Import-Package")) {
-                instructions.put("Import-Package", instructions.get("Import-Package") + ",*");
-            }
-            // Build config
-            StringBuilder config = new StringBuilder();
-            config.append("<configuration>" +
-                    "<finalName>${project.build.finalName}</finalName>" +
-                    "<outputDirectory>${project.build.outputDirectory}</outputDirectory>" +
-                    "<m_mavenSession>${session}</m_mavenSession>" +
-                    "<project>${project}</project>" +
-                    "<buildDirectory>${project.build.directory}</buildDirectory>" +
-                    "<supportedProjectTypes>" +
-                    "<supportedProjectType>jar</supportedProjectType>" +
-                    "<supportedProjectType>bundle</supportedProjectType>" +
-                    "<supportedProjectType>war</supportedProjectType>" +
-                    "</supportedProjectTypes>" +
-                    "<instructions>" +
-                    "<_include>-bnd.bnd</_include>"); // include user bnd file if present
-            for (Map.Entry<String, String> entry : instructions.entrySet()) {
-                config.append("<").append(entry.getKey()).append(">")
-                        .append(entry.getValue())
-                        .append("</").append(entry.getKey()).append(">");
+            File buildDir = new File(project.getBuild().getDirectory());
+            File generatedDir = new File(buildDir, "generated-resources");
+            Resource resource = new Resource();
+            resource.setDirectory(generatedDir.getPath());
+            project.addResource(resource);
+            ClassFinder finder = createProjectScopeFinder();
+            List<Class<? extends BootPlugin>> plugins = finder.findImplementations(BootPlugin.class);
+            Map<String, List<String>> combined = new HashMap<String, List<String>>();
+            for (Class<? extends BootPlugin> pluginClass : plugins) {
+                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);
+                }
             }
-            config.append("</instructions>" +
-                    "</configuration>");
-            Xpp3Dom configuration = Xpp3DomBuilder.build(new StringReader(config.toString()));
-            // Invoke plugin
-            getLog().info("Invoking maven-bundle-plugin");
-            Plugin felixBundlePlugin = new Plugin();
-            felixBundlePlugin.setGroupId("org.apache.felix");
-            felixBundlePlugin.setArtifactId("maven-bundle-plugin");
-            felixBundlePlugin.setVersion("3.0.0");
-            felixBundlePlugin.setInherited(true);
-            felixBundlePlugin.setExtensions(true);
-            PluginDescriptor felixBundlePluginDescriptor = pluginManager.loadPlugin(felixBundlePlugin, mavenProject.getRemotePluginRepositories(), mavenSession.getRepositorySession());
-            MojoDescriptor felixBundleMojoDescriptor = felixBundlePluginDescriptor.getMojo("bundle");
-            MojoExecution execution = new MojoExecution(felixBundleMojoDescriptor, configuration);
+            Map<String, List<String>> headers = new HashMap<String, List<String>>();
+            headers.put("Import-Package", Arrays.asList("*"));
+            combine(combined, headers);
+            File bndInst = new File(buildDir, "org.apache.karaf.boot.bnd");
+            writeBndFile(bndInst, combined);
+
+            InputStream is = this.getClass().getResourceAsStream("/configuration.xml");
+            MojoExecution execution = new MojoExecution(getBundleMojo(), Xpp3DomBuilder.build(is, "utf-8"));
             pluginManager.executeMojo(mavenSession, execution);
 
         } catch (Exception e) {
@@ -91,29 +101,56 @@ public class GenerateMojo extends AbstractMojo {
         }
     }
 
-    private void complete(Map<String, String> instructions, File bndInst) throws IOException {
-        List<String> lines =  Files.readAllLines(bndInst.toPath());
-        for (String line : lines) {
-            if (!line.contains(":")) {
-                continue;
-            }
-            String name = line.substring(0, line.indexOf(':')).trim();
-            String value = line.substring(line.indexOf(':') + 1).trim();
-            boolean prepend = false;
-            if (name.startsWith("PREPEND-")) {
-                prepend = true;
-                name = name.substring("PREPEND-".length());
+    private MojoDescriptor getBundleMojo() throws PluginNotFoundException, PluginResolutionException,
+        PluginDescriptorParsingException, InvalidPluginDescriptorException {
+        getLog().info("Invoking maven-bundle-plugin");
+        Plugin felixBundlePlugin = new Plugin();
+        felixBundlePlugin.setGroupId("org.apache.felix");
+        felixBundlePlugin.setArtifactId("maven-bundle-plugin");
+        felixBundlePlugin.setVersion("3.0.0");
+        felixBundlePlugin.setInherited(true);
+        felixBundlePlugin.setExtensions(true);
+        PluginDescriptor felixBundlePluginDescriptor = pluginManager.loadPlugin(felixBundlePlugin, mavenProject.getRemotePluginRepositories(), mavenSession.getRepositorySession());
+        MojoDescriptor felixBundleMojoDescriptor = felixBundlePluginDescriptor.getMojo("bundle");
+        return felixBundleMojoDescriptor;
+    }
+
+    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))));
             }
-            if (instructions.containsKey(name)) {
-                if (prepend) {
-                    instructions.put(name, value + "," + instructions.get(name));
-                } else {
-                    instructions.put(name, instructions.get(name) + "," + value);
-                }
+        }
+    }
+
+    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 {
-                instructions.put(name, value);
+                List<String> cValues = combined.get(key);
+                cValues.addAll(values);
+            }
+        }
+    }
+
+    private ClassFinder createProjectScopeFinder() throws MalformedURLException {
+        List<URL> urls = new ArrayList<URL>();
+
+        urls.add(new File(project.getBuild().getOutputDirectory()).toURI().toURL());
+        for (Object artifactO : project.getArtifacts()) {
+            Artifact artifact = (Artifact) artifactO;
+            File file = artifact.getFile();
+            if (file != null) {
+                urls.add(file.toURI().toURL());
             }
         }
+        ClassLoader loader = new URLClassLoader(urls.toArray(new URL[urls.size()]), getClass().getClassLoader());
+        return new ClassFinder(loader, urls);
     }
 
 }
diff --git a/tools/karaf-boot-maven-plugin/src/main/resources/configuration.xml b/tools/karaf-boot-maven-plugin/src/main/resources/configuration.xml
new file mode 100644 (file)
index 0000000..84590ea
--- /dev/null
@@ -0,0 +1,16 @@
+<configuration>
+    <finalName>${project.build.finalName}</finalName>
+    <outputDirectory>${project.build.outputDirectory}</outputDirectory>
+    <m_mavenSession>${session}</m_mavenSession>
+    <project>${project}</project>
+    <buildDirectory>${project.build.directory}</buildDirectory>
+    <supportedProjectTypes>
+        <supportedProjectType>jar</supportedProjectType>
+        <supportedProjectType>bundle</supportedProjectType>
+        <supportedProjectType>war</supportedProjectType>
+    </supportedProjectTypes>
+    <instructions>
+        <_include>target/org.apache.karaf.boot.bnd</_include>
+        <_include>-bnd.bnd</_include>
+    </instructions>
+</configuration>