[KARAF-4553] Allow karaf boot starters to define maven plugins to call
diff --git a/samples/blueprint/service-provider/pom.xml b/samples/blueprint/service-provider/pom.xml
index 08b0912..3f0cbf2 100644
--- a/samples/blueprint/service-provider/pom.xml
+++ b/samples/blueprint/service-provider/pom.xml
@@ -18,6 +18,9 @@
     <groupId>org.apache.karaf.boot</groupId>
     <artifactId>karaf-boot-sample-service-provider-blueprint</artifactId>
     <version>1.0.0-SNAPSHOT</version>
+    <properties>
+        <scanPath>sample</scanPath>
+    </properties>
 
     <dependencies>
         <dependency>
@@ -36,24 +39,6 @@
                 <extensions>true</extensions>
             </plugin>
             <plugin>
-                <groupId>org.apache.aries.blueprint</groupId>
-                <artifactId>blueprint-maven-plugin</artifactId>
-                <version>1.3.0</version>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>blueprint-generate</goal>
-                        </goals>
-                        <phase>process-classes</phase>
-                    </execution>
-                </executions>
-                <configuration>
-                    <scanPaths>
-                        <scanPath>sample</scanPath>
-                    </scanPaths>
-                </configuration>
-            </plugin>
-            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>3.3</version>
diff --git a/starters/karaf-boot-starter-blueprint/src/main/resources/karaf-boot/plugins.xml b/starters/karaf-boot-starter-blueprint/src/main/resources/karaf-boot/plugins.xml
new file mode 100644
index 0000000..75a2f8a
--- /dev/null
+++ b/starters/karaf-boot-starter-blueprint/src/main/resources/karaf-boot/plugins.xml
@@ -0,0 +1,19 @@
+<plugin>
+    <groupId>org.apache.aries.blueprint</groupId>
+    <artifactId>blueprint-maven-plugin</artifactId>
+    <version>1.5.0-SNAPSHOT</version>
+    <executions>
+        <execution>
+            <goals>
+                <goal>blueprint-generate</goal>
+            </goals>
+        </execution>
+    </executions>
+    <configuration>
+        <project>${project}</project>
+        <generatedFileName>autowire.xml</generatedFileName>
+        <scanPaths>
+            <scanPath>${scanPath}</scanPath>
+        </scanPaths>
+    </configuration>
+</plugin>
diff --git a/tools/karaf-boot-maven-plugin/pom.xml b/tools/karaf-boot-maven-plugin/pom.xml
index 89b3a5c..bed2067 100644
--- a/tools/karaf-boot-maven-plugin/pom.xml
+++ b/tools/karaf-boot-maven-plugin/pom.xml
@@ -67,6 +67,13 @@
             <artifactId>plexus-build-api</artifactId>
             <version>0.0.7</version>
         </dependency>
+        
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/tools/karaf-boot-maven-plugin/src/main/java/org/apache/karaf/boot/maven/GenerateMojo.java b/tools/karaf-boot-maven-plugin/src/main/java/org/apache/karaf/boot/maven/GenerateMojo.java
index b6573f5..64a63f6 100644
--- a/tools/karaf-boot-maven-plugin/src/main/java/org/apache/karaf/boot/maven/GenerateMojo.java
+++ b/tools/karaf-boot-maven-plugin/src/main/java/org/apache/karaf/boot/maven/GenerateMojo.java
@@ -11,6 +11,7 @@
 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;
@@ -23,12 +24,8 @@
 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.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;
@@ -38,24 +35,13 @@
 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 {
 
-    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);
-            }
-        }
-    }
-
     @Parameter(defaultValue = "${project}", required = true, readonly = true)
     private MavenProject mavenProject;
 
@@ -74,51 +60,97 @@
     public void execute() throws MojoExecutionException {
         try {
             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);
-                }
-            }
-            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);
+            File generatedDir = createGeneratedDir(buildDir);
+            
+            URLClassLoader loader = projectClassLoader();
+            handleBootPlugins(loader, buildDir, generatedDir);
+            handleMavenPlugins(loader);
 
-            InputStream is = this.getClass().getResourceAsStream("/configuration.xml");
-            MojoExecution execution = new MojoExecution(getBundleMojo(), Xpp3DomBuilder.build(is, "utf-8"));
-            pluginManager.executeMojo(mavenSession, execution);
+            runMavenBundlePlugin();
 
         } catch (Exception e) {
             throw new MojoExecutionException("karaf-boot-maven-plugin failed", e);
         }
     }
 
-    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 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);
@@ -142,19 +174,47 @@
         }
     }
 
-    private ClassFinder createProjectScopeFinder() throws MalformedURLException {
+    private URLClassLoader projectClassLoader() 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;
+        for (Artifact artifact : project.getArtifacts()) {
             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);
+        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);
+            }
+        }
+    }
 }
diff --git a/tools/karaf-boot-maven-plugin/src/test/java/org/apache/karaf/boot/maven/PluginLoaderTest.java b/tools/karaf-boot-maven-plugin/src/test/java/org/apache/karaf/boot/maven/PluginLoaderTest.java
new file mode 100644
index 0000000..ab362d0
--- /dev/null
+++ b/tools/karaf-boot-maven-plugin/src/test/java/org/apache/karaf/boot/maven/PluginLoaderTest.java
@@ -0,0 +1,25 @@
+package org.apache.karaf.boot.maven;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.maven.model.Plugin;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PluginLoaderTest {
+
+    @Test
+    public void testLoadXml() throws XmlPullParserException, IOException {
+        InputStream is = this.getClass().getResourceAsStream("/plugins.xml");
+        Xpp3Dom pluginDef = Xpp3DomBuilder.build(is, "utf-8");
+        Plugin plugin = new GenerateMojo().loadPlugin(pluginDef);
+        Assert.assertEquals("org.apache.aries.blueprint", plugin.getGroupId());
+        Assert.assertEquals("blueprint-maven-plugin", plugin.getArtifactId());
+        Assert.assertEquals("1.4.0", plugin.getVersion());
+    }
+
+}
diff --git a/tools/karaf-boot-maven-plugin/src/test/resources/plugins.xml b/tools/karaf-boot-maven-plugin/src/test/resources/plugins.xml
new file mode 100644
index 0000000..b03a2f2
--- /dev/null
+++ b/tools/karaf-boot-maven-plugin/src/test/resources/plugins.xml
@@ -0,0 +1,17 @@
+<plugin>
+    <groupId>org.apache.aries.blueprint</groupId>
+    <artifactId>blueprint-maven-plugin</artifactId>
+    <version>1.4.0</version>
+    <executions>
+        <execution>
+            <goals>
+                <goal>blueprint-generate</goal>
+            </goals>
+        </execution>
+    </executions>
+    <configuration>
+        <scanPaths>
+            <scanPath>${scanPath}</scanPath>
+        </scanPaths>
+    </configuration>
+</plugin>