Repository: karaf
Updated Branches:
  refs/heads/master 3f6bca010 -> e920dde01


http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/src/main/java/org/apache/karaf/profile/assembly/CustomSimpleDownloadTask.java
----------------------------------------------------------------------
diff --git 
a/profile/src/main/java/org/apache/karaf/profile/assembly/CustomSimpleDownloadTask.java
 
b/profile/src/main/java/org/apache/karaf/profile/assembly/CustomSimpleDownloadTask.java
new file mode 100644
index 0000000..94f5475
--- /dev/null
+++ 
b/profile/src/main/java/org/apache/karaf/profile/assembly/CustomSimpleDownloadTask.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.profile.assembly;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.apache.karaf.deployer.blueprint.BlueprintTransformer;
+import org.apache.karaf.deployer.spring.SpringTransformer;
+import 
org.apache.karaf.features.internal.download.impl.AbstractRetryableDownloadTask;
+import org.apache.karaf.profile.Profile;
+
+public class CustomSimpleDownloadTask extends AbstractRetryableDownloadTask {
+
+    private static final String WRAP_URI_PREFIX = "wrap";
+    private static final String SPRING_URI_PREFIX = "spring";
+    private static final String BLUEPRINT_URI_PREFIX = "blueprint";
+    private static final String WAR_URI_PREFIX = "war";
+    private static final String PROFILE_URI_PREFIX = "profile";
+
+    private final Profile profile;
+
+    public CustomSimpleDownloadTask(ScheduledExecutorService executorService, 
Profile profile, String url) {
+        super(executorService, url);
+        this.profile = profile;
+    }
+
+    @Override
+    protected File download() throws Exception {
+        URL url = createUrl(getUrl());
+        Path path = Files.createTempFile("download-", null);
+        try (InputStream is = url.openStream()) {
+            Files.copy(is, path, StandardCopyOption.REPLACE_EXISTING);
+        }
+        return path.toFile();
+    }
+
+    private URL createUrl(String url) throws MalformedURLException, 
URISyntaxException {
+        URLStreamHandler handler = getUrlStreamHandler(url);
+        if (handler != null) {
+            return new URL(null, url, handler);
+        } else {
+            return new URL(url);
+        }
+    }
+
+    private URLStreamHandler getUrlStreamHandler(String url) throws 
URISyntaxException {
+        String scheme = new URI(url).getScheme();
+        switch (scheme) {
+        case WRAP_URI_PREFIX:
+            return new org.ops4j.pax.url.wrap.Handler();
+        case WAR_URI_PREFIX:
+            return new org.ops4j.pax.url.war.Handler();
+        case SPRING_URI_PREFIX:
+            return new SpringURLHandler();
+        case BLUEPRINT_URI_PREFIX:
+            return new BlueprintURLHandler();
+        case PROFILE_URI_PREFIX:
+            if (profile != null) {
+                return new ProfileURLHandler();
+            }
+        default:
+            return null;
+        }
+    }
+
+    public class SpringURLHandler extends URLStreamHandler {
+        @Override
+        protected URLConnection openConnection(URL u) throws IOException {
+            return new URLConnection(u) {
+                @Override
+                public void connect() throws IOException {
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    try {
+                        ByteArrayOutputStream os = new ByteArrayOutputStream();
+                        SpringTransformer.transform(createUrl(url.getPath()), 
os);
+                        os.close();
+                        return new ByteArrayInputStream(os.toByteArray());
+                    } catch (Exception e) {
+                        throw new IOException("Error opening spring xml url", 
e);
+                    }
+                }
+            };
+        }
+    }
+
+    public class BlueprintURLHandler extends URLStreamHandler {
+        @Override
+        protected URLConnection openConnection(URL u) throws IOException {
+            return new URLConnection(u) {
+                @Override
+                public void connect() throws IOException {
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    try {
+                        ByteArrayOutputStream os = new ByteArrayOutputStream();
+                        
BlueprintTransformer.transform(createUrl(url.getPath()), os);
+                        os.close();
+                        return new ByteArrayInputStream(os.toByteArray());
+                    } catch (Exception e) {
+                        throw new IOException("Error opening blueprint xml 
url", e);
+                    }
+                }
+            };
+        }
+    }
+
+    public class ProfileURLHandler extends URLStreamHandler {
+        @Override
+        protected URLConnection openConnection(URL u) throws IOException {
+            return new URLConnection(u) {
+                @Override
+                public void connect() throws IOException {
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    String path = url.getPath();
+                    byte[] data = profile.getFileConfiguration(path);
+                    if (data == null) {
+                        throw new FileNotFoundException(url.toExternalForm());
+                    }
+                    return new ByteArrayInputStream(data);
+                }
+            };
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/src/main/java/org/apache/karaf/profile/assembly/FakeBundleRevision.java
----------------------------------------------------------------------
diff --git 
a/profile/src/main/java/org/apache/karaf/profile/assembly/FakeBundleRevision.java
 
b/profile/src/main/java/org/apache/karaf/profile/assembly/FakeBundleRevision.java
new file mode 100644
index 0000000..7b797d6
--- /dev/null
+++ 
b/profile/src/main/java/org/apache/karaf/profile/assembly/FakeBundleRevision.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.profile.assembly;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.apache.karaf.features.internal.resolver.ResourceImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+
+/**
+ * Fake bundle revision implementation for resolution simulations without OSGi.
+ */
+public class FakeBundleRevision extends ResourceImpl implements 
BundleRevision, BundleStartLevel {
+
+    private final Bundle bundle;
+    private int startLevel;
+
+    public FakeBundleRevision(final Hashtable<String, String> headers, final 
String location, final long bundleId) throws BundleException {
+        ResourceBuilder.build(this, location, headers);
+        this.bundle = (Bundle) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class[]{Bundle.class},
+                new BundleRevisionInvocationHandler(headers, location, 
bundleId));
+    }
+
+    @Override
+    public int getStartLevel() {
+        return startLevel;
+    }
+
+    @Override
+    public void setStartLevel(int startLevel) {
+        this.startLevel = startLevel;
+    }
+
+    @Override
+    public boolean isPersistentlyStarted() {
+        return true;
+    }
+
+    @Override
+    public boolean isActivationPolicyUsed() {
+        return false;
+    }
+
+    @Override
+    public String getSymbolicName() {
+        return bundle.getSymbolicName();
+    }
+
+    @Override
+    public Version getVersion() {
+        return bundle.getVersion();
+    }
+
+    @Override
+    public List<BundleCapability> getDeclaredCapabilities(String namespace) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<BundleRequirement> getDeclaredRequirements(String namespace) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getTypes() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public BundleWiring getWiring() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Bundle getBundle() {
+        return bundle;
+    }
+
+    private class BundleRevisionInvocationHandler implements InvocationHandler 
{
+
+        private final Hashtable<String, String> headers;
+        private final String location;
+        private final long bundleId;
+
+        public BundleRevisionInvocationHandler(Hashtable<String, String> 
headers, String location, long bundleId) {
+            this.headers = headers;
+            this.location = location;
+            this.bundleId = bundleId;
+        }
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+            if (method.getName().equals("hashCode")) {
+                return FakeBundleRevision.this.hashCode();
+            } else if (method.getName().equals("equals")) {
+                return proxy == args[0];
+            } else if (method.getName().equals("toString")) {
+                return bundle.getSymbolicName() + "/" + bundle.getVersion();
+            } else if (method.getName().equals("adapt")) {
+                if (args.length == 1 && args[0] == BundleRevision.class) {
+                    return FakeBundleRevision.this;
+                } else if (args.length == 1 && args[0] == 
BundleStartLevel.class) {
+                    return FakeBundleRevision.this;
+                }
+            } else if (method.getName().equals("getHeaders")) {
+                return headers;
+            } else if (method.getName().equals("getBundleId")) {
+                return bundleId;
+            } else if (method.getName().equals("getLocation")) {
+                return location;
+            } else if (method.getName().equals("getSymbolicName")) {
+                String name = headers.get(Constants.BUNDLE_SYMBOLICNAME);
+                int idx = name.indexOf(';');
+                if (idx > 0) {
+                    name = name.substring(0, idx).trim();
+                }
+                return name;
+            } else if (method.getName().equals("getVersion")) {
+                return new Version(headers.get(Constants.BUNDLE_VERSION));
+            } else if (method.getName().equals("getState")) {
+                return Bundle.ACTIVE;
+            } else if (method.getName().equals("getLastModified")) {
+                return 0l;
+            }
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/src/main/java/org/apache/karaf/profile/impl/ProfileImpl.java
----------------------------------------------------------------------
diff --git 
a/profile/src/main/java/org/apache/karaf/profile/impl/ProfileImpl.java 
b/profile/src/main/java/org/apache/karaf/profile/impl/ProfileImpl.java
index d2111fd..48b064e 100644
--- a/profile/src/main/java/org/apache/karaf/profile/impl/ProfileImpl.java
+++ b/profile/src/main/java/org/apache/karaf/profile/impl/ProfileImpl.java
@@ -40,7 +40,7 @@ final class ProfileImpl implements Profile {
     private static final Pattern ALLOWED_PROFILE_NAMES_PATTERN = 
Pattern.compile("^[A-Za-z0-9]+[\\.A-Za-z0-9_-]*$");
 
     private final String profileId;
-    private final Map<String, String> attributes = new HashMap<>();
+    private final Map<String, String> attributes;
     private final List<String> parents = new ArrayList<>();
     private final Map<String, byte[]> fileConfigurations = new HashMap<>();
     private final Map<String, Map<String, String>> configurations = new 
HashMap<>();
@@ -72,17 +72,8 @@ final class ProfileImpl implements Profile {
             }
         }
 
-        // Attributes are agent configuration with prefix 'attribute.'  
-        Map<String, String> agentConfig = 
configurations.get(Profile.INTERNAL_PID);
-        if (agentConfig != null) {
-            int prefixLength = Profile.ATTRIBUTE_PREFIX.length();
-            for (Entry<String, String> entry : agentConfig.entrySet()) {
-                String key = entry.getKey();
-                if (key.startsWith(Profile.ATTRIBUTE_PREFIX)) {
-                    attributes.put(key.substring(prefixLength), 
entry.getValue());
-                }
-            }
-        }
+        // Attributes are agent configuration with prefix 'attribute.'
+        attributes = getPrefixedMap(ATTRIBUTE_PREFIX);
     }
 
     public String getId() {
@@ -95,6 +86,31 @@ final class ProfileImpl implements Profile {
     }
 
     @Override
+    public Map<String, String> getConfig() {
+        return getPrefixedMap(CONFIG_PREFIX);
+    }
+
+    @Override
+    public Map<String, String> getSystem() {
+        return getPrefixedMap(SYSTEM_PREFIX);
+    }
+
+    private Map<String, String> getPrefixedMap(String prefix) {
+        Map<String, String> map = new HashMap<>();
+        Map<String, String> agentConfig = 
configurations.get(Profile.INTERNAL_PID);
+        if (agentConfig != null) {
+            int prefixLength = prefix.length();
+            for (Entry<String, String> entry : agentConfig.entrySet()) {
+                String key = entry.getKey();
+                if (key.startsWith(prefix)) {
+                    map.put(key.substring(prefixLength), entry.getValue());
+                }
+            }
+        }
+        return map;
+    }
+
+    @Override
     public List<String> getLibraries() {
         return getContainerConfigList(ConfigListType.LIBRARIES);
     }

http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/src/test/java/org/apache/karaf/profile/assembly/BuilderTest.java
----------------------------------------------------------------------
diff --git 
a/profile/src/test/java/org/apache/karaf/profile/assembly/BuilderTest.java 
b/profile/src/test/java/org/apache/karaf/profile/assembly/BuilderTest.java
new file mode 100644
index 0000000..dc7fbcd
--- /dev/null
+++ b/profile/src/test/java/org/apache/karaf/profile/assembly/BuilderTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.profile.assembly;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.Test;
+
+public class BuilderTest {
+
+    @Test
+    public void testBuilder() throws Exception {
+
+        Path workDir = Paths.get("target/distrib");
+        recursiveDelete(workDir);
+
+        Builder builder = Builder.newInstance()
+                .staticFramework()
+                
.profilesUris("jar:mvn:org.apache.karaf.demos.profiles/registry/4.0.0-SNAPSHOT!/")
+                .environment("static")
+                .profiles("karaf",
+                          "example-loanbroker-bank1",
+                          "example-loanbroker-bank2",
+                          "example-loanbroker-bank3",
+                          "example-loanbroker-broker",
+                          "activemq-broker")
+                .homeDirectory(workDir);
+
+        try {
+            builder.generateAssembly();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw e;
+        }
+    }
+
+    private static void recursiveDelete(Path path) throws IOException {
+        if (Files.exists(path)) {
+            if (Files.isDirectory(path)) {
+                try (DirectoryStream<Path> children = 
Files.newDirectoryStream(path)) {
+                    for (Path child : children) {
+                        recursiveDelete(child);
+                    }
+                }
+            }
+            Files.delete(path);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
index e36e0ed..c4b451d 100644
--- 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
+++ 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
@@ -18,52 +18,11 @@
  */
 package org.apache.karaf.tooling.features;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URL;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystemNotFoundException;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
-
-import org.apache.felix.utils.properties.InterpolationHelper;
-import org.apache.felix.utils.properties.Properties;
-import org.apache.karaf.features.internal.download.impl.DownloadManagerHelper;
-import org.apache.karaf.features.internal.model.Bundle;
-import org.apache.karaf.features.internal.model.Conditional;
-import org.apache.karaf.features.internal.model.Config;
-import org.apache.karaf.features.internal.model.ConfigFile;
-import org.apache.karaf.features.internal.model.Dependency;
-import org.apache.karaf.features.internal.model.Feature;
-import org.apache.karaf.features.internal.model.Features;
-import org.apache.karaf.features.internal.model.JaxbUtil;
-import org.apache.karaf.features.internal.util.MapUtils;
-import org.apache.karaf.kar.internal.Kar;
-import org.apache.karaf.profile.Profile;
-import org.apache.karaf.profile.ProfileBuilder;
-import org.apache.karaf.profile.impl.Profiles;
-import org.apache.karaf.tooling.url.CustomBundleURLStreamHandlerFactory;
-import org.apache.karaf.tooling.utils.InternalMavenResolver;
+
+import org.apache.karaf.profile.assembly.Builder;
 import org.apache.karaf.tooling.utils.IoUtils;
 import org.apache.karaf.tooling.utils.MojoSupport;
 import org.apache.maven.artifact.Artifact;
@@ -73,7 +32,6 @@ 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.ops4j.pax.url.mvn.MavenResolver;
 
 /**
  * Installs kar dependencies into a server-under-construction in 
target/assembly
@@ -182,16 +140,9 @@ public class InstallKarsMojo extends MojoSupport {
     // an access layer for available Aether implementation
     protected DependencyHelper dependencyHelper;
 
-    private static final String FEATURES_REPOSITORIES = "featuresRepositories";
-    private static final String FEATURES_BOOT = "featuresBoot";
-
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException {
-
         this.dependencyHelper = 
DependencyHelperFactory.createDependencyHelper(this.container, this.project, 
this.mavenSession, getLog());
-        MavenResolver resolver = new InternalMavenResolver(dependencyHelper, 
getLog());
-        CustomBundleURLStreamHandlerFactory.install(resolver);
-
         try {
             doExecute();
         }
@@ -201,20 +152,9 @@ public class InstallKarsMojo extends MojoSupport {
         catch (Exception e) {
             throw new MojoExecutionException("Unable to build assembly", e);
         }
-        finally {
-            CustomBundleURLStreamHandlerFactory.uninstall();
-        }
     }
 
     protected void doExecute() throws Exception {
-        this.dependencyHelper = 
DependencyHelperFactory.createDependencyHelper(this.container, this.project, 
this.mavenSession, getLog());
-
-        // creating system directory
-        getLog().info("Creating system directory");
-        systemDirectory.mkdirs();
-
-        IoUtils.deleteRecursive(new File(systemDirectory, "generated"));
-
         startupRepositories = nonNullList(startupRepositories);
         bootRepositories = nonNullList(bootRepositories);
         installedRepositories = nonNullList(installedRepositories);
@@ -241,576 +181,97 @@ public class InstallKarsMojo extends MojoSupport {
             installedRepositories.addAll(featureRepositories);
         }
 
-        // Build optional features and known prerequisites
-        Map<String, List<String>> prereqs = new HashMap<>();
-        prereqs.put("blueprint:", Arrays.asList("deployer", 
"aries-blueprint"));
-        prereqs.put("spring:", Arrays.asList("deployer", "spring"));
-        prereqs.put("wrap:", Arrays.asList("wrap"));
-        prereqs.put("war:", Arrays.asList("war"));
+        Builder builder = Builder.newInstance();
+
+        // creating system directory
+        getLog().info("Creating work directory");
+        builder.homeDirectory(workDirectory.toPath());
+        IoUtils.deleteRecursive(workDirectory);
+        workDirectory.mkdirs();
 
+        List<String> startupKars = new ArrayList<>();
+        List<String> bootKars = new ArrayList<>();
+        List<String> installedKars = new ArrayList<>();
 
         // Loading kars and features repositories
         getLog().info("Loading kar and features repositories dependencies");
         for (Artifact artifact : project.getDependencyArtifacts()) {
+            Builder.Stage stage;
+            switch (artifact.getScope()) {
+            case "compile":
+                stage = Builder.Stage.Startup;
+                break;
+            case "runtime":
+                stage = Builder.Stage.Boot;
+                break;
+            case "provided":
+                stage = Builder.Stage.Installed;
+                break;
+            default:
+                continue;
+            }
             if ("kar".equals(artifact.getType())) {
-                File karFile = artifact.getFile();
-                getLog().info("Extracting " + artifact.toString() + " kar");
-                try {
-                    Kar kar = new Kar(karFile.toURI());
-                    kar.extract(systemDirectory, workDirectory);
-                    for (URI repositoryUri : kar.getFeatureRepos()) {
-                        switch (artifact.getScope()) {
-                        case "compile":
-                            startupRepositories.add(repositoryUri.getPath());
-                            break;
-                        case "runtime":
-                            bootRepositories.add(repositoryUri.getPath());
-                            break;
-                        case "provided":
-                            installedRepositories.add(repositoryUri.getPath());
-                            break;
-                        }
-                    }
-                } catch (Exception e) {
-                    throw new RuntimeException("Can not install " + 
artifact.toString() + " kar", e);
+                String uri = dependencyHelper.artifactToMvn(artifact);
+                switch (stage) {
+                case Startup:   startupKars.add(uri); break;
+                case Boot:      bootKars.add(uri); break;
+                case Installed: installedKars.add(uri); break;
                 }
             } else if ("features".equals(artifact.getClassifier())) {
-                String repositoryUri = 
dependencyHelper.artifactToMvn(artifact);
-                switch (artifact.getScope()) {
-                case "compile":
-                    startupRepositories.add(repositoryUri);
-                    break;
-                case "runtime":
-                    bootRepositories.add(repositoryUri);
-                    break;
-                case "provided":
-                    installedRepositories.add(repositoryUri);
-                    break;
+                String uri = dependencyHelper.artifactToMvn(artifact);
+                switch (stage) {
+                case Startup:   startupRepositories.add(uri); break;
+                case Boot:      bootRepositories.add(uri); break;
+                case Installed: installedRepositories.add(uri); break;
                 }
             } else if ("jar".equals(artifact.getType()) || 
"bundle".equals(artifact.getType())) {
-                String bundleUri = dependencyHelper.artifactToMvn(artifact);
-                switch (artifact.getScope()) {
-                case "compile":
-                    startupBundles.add(bundleUri);
-                    break;
-                case "runtime":
-                    bootBundles.add(bundleUri);
-                    break;
-                case "provided":
-                    installedBundles.add(bundleUri);
-                    break;
+                String uri = dependencyHelper.artifactToMvn(artifact);
+                switch (stage) {
+                case Startup:   startupBundles.add(uri); break;
+                case Boot:      bootBundles.add(uri); break;
+                case Installed: installedBundles.add(uri); break;
                 }
             }
         }
 
-        // Load profiles
-        Map<String, Profile> allProfiles;
+        builder.use24SyntaxForStartup(use24SyntaxForStartup)
+               .useReferenceUrls(useReferenceUrls)
+               .defaultAddAll(installAllFeaturesByDefault)
+               .ignoreDependencyFlag(ignoreDependencyFlag);
         if (profilesUri != null) {
-            URI profileURI = URI.create(profilesUri);
-            Path profilePath;
-            try {
-                profilePath = Paths.get(profileURI);
-            } catch (FileSystemNotFoundException e) {
-                // file system does not exist, try to create it
-                FileSystem fs = FileSystems.newFileSystem(profileURI, new 
HashMap<String, Object>(), InstallKarsMojo.class.getClassLoader());
-                profilePath = fs.provider().getPath(profileURI);
-            }
-            allProfiles = Profiles.loadProfiles(profilePath);
-        } else {
-            allProfiles = new HashMap<>();
-        }
-        // Generate profiles
-        Profile startupProfile = generateProfile(allProfiles, startupProfiles, 
startupRepositories, startupFeatures, startupBundles);
-        Profile bootProfile = generateProfile(allProfiles, bootProfiles, 
bootRepositories, bootFeatures, bootBundles);
-        Profile installedProfile = generateProfile(allProfiles, 
installedProfiles, installedRepositories, installedFeatures, installedBundles);
-
-        //
-        // Compute overall profile
-        //
-        Profile overallProfile = 
ProfileBuilder.Factory.create(UUID.randomUUID().toString())
-                .setParents(Arrays.asList(startupProfile.getId(), 
bootProfile.getId(), installedProfile.getId()))
-                .getProfile();
-        Profile overallOverlay = Profiles.getOverlay(overallProfile, 
allProfiles);
-        Profile overallEffective = Profiles.getEffective(overallOverlay, 
false);
-
-        Hashtable<String, String> agentProps = new 
Hashtable<>(overallEffective.getConfiguration("org.ops4j.pax.url.mvn"));
-        final Map<String, String> properties = new HashMap<>();
-        properties.put("karaf.default.repository", "system");
-        InterpolationHelper.performSubstitution(agentProps, new 
InterpolationHelper.SubstitutionCallback() {
-            @Override
-            public String getValue(String key) {
-                return properties.get(key);
-            }
-        }, false, false, true);
-
-        //
-        // Write all configuration files
-        //
-        for (Map.Entry<String, byte[]> config : 
overallEffective.getFileConfigurations().entrySet()) {
-            Path configFile = workDirectory.toPath().resolve("etc/" + 
config.getKey());
-            Files.createDirectories(configFile.getParent());
-            Files.write(configFile, config.getValue());
-        }
-
-        //
-        // Compute startup
-        //
-        Profile startupOverlay = Profiles.getOverlay(startupProfile, 
allProfiles);
-        Profile startupEffective = Profiles.getEffective(startupOverlay, 
false);
-        // Load startup repositories
-        Map<String, Features> startupRepositories = 
loadRepositories(startupEffective.getRepositories());
-        // Compute startup feature dependencies
-        Set<Feature> allStartupFeatures = new HashSet<>();
-        for (Features repo : startupRepositories.values()) {
-            allStartupFeatures.addAll(repo.getFeature());
-        }
-        Set<Feature> startupFeatures = new LinkedHashSet<>();
-        if (startupEffective.getFeatures().isEmpty() && 
installAllFeaturesByDefault) {
-            startupFeatures.addAll(allStartupFeatures);
-        } else {
-            for (String feature : startupEffective.getFeatures()) {
-                addFeatures(startupFeatures, allStartupFeatures, feature);
-            }
-        }
-        // Compute all bundles
-        Map<String, Integer> allStartupBundles = new LinkedHashMap<>();
-        for (Feature feature : startupFeatures) {
-            for (Bundle bundleInfo : feature.getBundle()) {
-                String bundleLocation = bundleInfo.getLocation().trim();
-                int bundleStartLevel = bundleInfo.getStartLevel() == 0 ? 
defaultStartLevel : bundleInfo.getStartLevel();
-                if (allStartupBundles.containsKey(bundleLocation)) {
-                    bundleStartLevel = Math.min(bundleStartLevel, 
allStartupBundles.get(bundleLocation));
-                }
-                allStartupBundles.put(bundleLocation, bundleStartLevel);
-            }
-            // Install config
-            for (Config config : feature.getConfig()) {
-                installConfig(config);
-            }
-            for (Conditional cond : feature.getConditional()) {
-                boolean doInstall = true;
-                for (Dependency dep : cond.getFeature()) {
-                    if (!startupFeatures.contains(dep.getName())) {
-                        doInstall = false;
-                        break;
-                    }
-                }
-                if (doInstall) {
-                    for (Bundle bundleInfo : cond.getBundle()) {
-                        String bundleLocation = 
bundleInfo.getLocation().trim();
-                        int bundleStartLevel = bundleInfo.getStartLevel() == 0 
? defaultStartLevel : bundleInfo.getStartLevel();
-                        if (allStartupBundles.containsKey(bundleLocation)) {
-                            bundleStartLevel = Math.min(bundleStartLevel, 
allStartupBundles.get(bundleLocation));
-                        }
-                        allStartupBundles.put(bundleLocation, 
bundleStartLevel);
-                    }
-                }
-                for (Config config : cond.getConfig()) {
-                    installConfig(config);
-                }
-            }
-            // Install config files
-            for (ConfigFile configFile : feature.getConfigfile()) {
-                try (InputStream is = new 
URL(configFile.getLocation().trim()).openStream()) {
-                    String path = configFile.getFinalname();
-                    if (path.startsWith("/")) {
-                        path = path.substring(1);
-                    }
-                    Path output = workDirectory.toPath().resolve(path);
-                    Files.copy(is, output, 
StandardCopyOption.REPLACE_EXISTING);  // TODO: be smarter about overwrites
-                }
-            }
-            for (Conditional cond : feature.getConditional()) {
-                for (ConfigFile configFile : cond.getConfigfile()) {
-                    try (InputStream is = new 
URL(configFile.getLocation().trim()).openStream()) {
-                        Path output = 
workDirectory.toPath().resolve(configFile.getFinalname());
-                        Files.copy(is, output, 
StandardCopyOption.REPLACE_EXISTING); // TODO: be smarter about overwrites
-                    }
-                }
-            }
-        }
-        for (String bundleLocation : startupEffective.getBundles()) {
-            int bundleStartLevel = defaultStartLevel;
-            if (allStartupBundles.containsKey(bundleLocation)) {
-                bundleStartLevel = Math.min(bundleStartLevel, 
allStartupBundles.get(bundleLocation));
-            }
-            allStartupBundles.put(bundleLocation, bundleStartLevel);
-        }
-        // Load startup.properties
-        startupPropertiesFile.getParentFile().mkdirs();
-        Properties startupProperties = new Properties(startupPropertiesFile);
-        if (!startupPropertiesFile.exists()) {
-            startupProperties.setHeader(Collections.singletonList("# Bundles 
to be started on startup, with startlevel"));
-        }
-        // Install bundles and update startup.properties
-        Map<Integer, Set<String>> invertedStartupBundles = 
MapUtils.invert(allStartupBundles);
-        for (Map.Entry<Integer, Set<String>> entry : 
invertedStartupBundles.entrySet()) {
-            String startLevel = Integer.toString(entry.getKey());
-            for (String location : new TreeSet<>(entry.getValue())) {
-                location = installStartupArtifact(location, useReferenceUrls 
|| use24SyntaxForStartup);
-                if (location.startsWith("file:") && useReferenceUrls) {
-                    location = "reference:" + location;
-                }
-                if (location.startsWith("file:") && use24SyntaxForStartup) {
-                    location = location.substring("file:".length());
-                }
-                startupProperties.put(location, startLevel);
-            }
-        }
-        // generate the startup.properties file
-        startupProperties.save();
-
-
-        //
-        // Handle boot profiles
-        //
-        Profile bootOverlay = Profiles.getOverlay(bootProfile, allProfiles);
-        Profile bootEffective = Profiles.getEffective(bootOverlay, false);
-        // Load startup repositories
-        Map<String, Features> bootRepositories = 
loadRepositories(bootEffective.getRepositories());
-        // Compute startup feature dependencies
-        Set<Feature> allBootFeatures = new HashSet<>();
-        for (Features repo : bootRepositories.values()) {
-            allBootFeatures.addAll(repo.getFeature());
-        }
-        // Install all repositories
-        for (String repository : bootRepositories.keySet()) {
-            installArtifact(repository);
-        }
-        // Generate a global feature
-        Map<String, Dependency> generatedDep = new HashMap<>();
-        Feature generated = new Feature();
-        generated.setName(UUID.randomUUID().toString());
-        // Add feature dependencies
-        if (bootEffective.getFeatures().isEmpty()) {
-            if (installAllFeaturesByDefault) {
-                for (Features repo : bootRepositories.values()) {
-                    for (Feature feature : repo.getFeature()) {
-                        Dependency dep = generatedDep.get(feature.getName());
-                        if (dep == null) {
-                            dep = new Dependency();
-                            dep.setName(feature.getName());
-                            generated.getFeature().add(dep);
-                            generatedDep.put(dep.getName(), dep);
-                        }
-                        dep.setDependency(false);
-                    }
-                }
-            }
-        } else {
-            for (String dependency : bootEffective.getFeatures()) {
-                Dependency dep = generatedDep.get(dependency);
-                if (dep == null) {
-                    dep = new Dependency();
-                    dep.setName(dependency);
-                    generated.getFeature().add(dep);
-                    generatedDep.put(dep.getName(), dep);
-                }
-                dep.setDependency(false);
-            }
-        }
-        // Add bundles
-        for (String location : bootEffective.getBundles()) {
-            location = location.replace("profile:", "file:etc/");
-            Bundle bun = new Bundle();
-            bun.setLocation(location);
-            generated.getBundle().add(bun);
-        }
-        Features rep = new Features();
-        rep.setName(UUID.randomUUID().toString());
-        rep.getRepository().addAll(bootEffective.getRepositories());
-        rep.getFeature().add(generated);
-        allBootFeatures.add(generated);
-
-        // Compute startup feature dependencies
-        Set<Feature> bootFeatures = new HashSet<>();
-        addFeatures(bootFeatures, allBootFeatures, generated.getName());
-        for (Feature feature : bootFeatures) {
-            // the feature is a startup feature, updating startup.properties 
file
-            getLog().info("Feature " + feature.getName() + " is defined as a 
boot feature");
-            // add the feature in the system folder
-            Set<String> locations = new HashSet<>();
-            for (Bundle bundle : feature.getBundle()) {
-                if (!ignoreDependencyFlag || !bundle.isDependency()) {
-                    locations.add(bundle.getLocation().trim());
-                }
-            }
-            for (Conditional cond : feature.getConditional()) {
-                for (Bundle bundle : cond.getBundle()) {
-                    if (!ignoreDependencyFlag || !bundle.isDependency()) {
-                        locations.add(bundle.getLocation().trim());
-                    }
-                }
-            }
-            for (String location : locations) {
-                installArtifact(location);
-                for (Map.Entry<String, List<String>> entry : 
prereqs.entrySet()) {
-                    if (location.startsWith(entry.getKey())) {
-                        for (String prereq : entry.getValue()) {
-                            Dependency dep = generatedDep.get(prereq);
-                            if (dep == null) {
-                                dep = new Dependency();
-                                dep.setName(prereq);
-                                generated.getFeature().add(dep);
-                                generatedDep.put(dep.getName(), dep);
-                            }
-                            dep.setPrerequisite(true);
-                        }
-                    }
-                }
-            }
-            // Install config files
-            for (ConfigFile configFile : feature.getConfigfile()) {
-                installArtifact(configFile.getLocation().trim());
-            }
-            for (Conditional cond : feature.getConditional()) {
-                for (ConfigFile configFile : cond.getConfigfile()) {
-                    installArtifact(configFile.getLocation().trim());
-                }
-            }
-        }
-
-        // If there are bundles to install, we can't use the boot features only
-        // so keep the generated feature
-        if (!generated.getBundle().isEmpty()) {
-            File output = new File(workDirectory, "etc/" + rep.getName() + 
".xml");
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            JaxbUtil.marshal(rep, baos);
-            ByteArrayInputStream bais;
-            String repoUrl;
-            if (use24SyntaxForStartup) {
-                String str = baos.toString();
-                str = 
str.replace("http://karaf.apache.org/xmlns/features/v1.3.0";, 
"http://karaf.apache.org/xmlns/features/v1.2.0";);
-                str = str.replaceAll(" dependency=\".*?\"", "");
-                str = str.replaceAll(" prerequisite=\".*?\"", "");
-                for (Feature f : rep.getFeature()) {
-                    for (Dependency d : f.getFeature()) {
-                        if (d.isPrerequisite()) {
-                            if 
(!startupEffective.getFeatures().contains(d.getName())) {
-                                getLog().warn("Feature " + d.getName() + " is 
a prerequisite and should be installed as a startup feature.");                }
-                        }
-                    }
-                }
-                bais = new ByteArrayInputStream(str.getBytes());
-                repoUrl = "file:etc/" + output.getName();
-            } else {
-                bais = new ByteArrayInputStream(baos.toByteArray());
-                repoUrl = "file:${karaf.home}/etc/" + output.getName();
-            }
-            Files.copy(bais, output.toPath());
-            Properties featuresProperties = new Properties(featuresCfgFile);
-            featuresProperties.put(FEATURES_REPOSITORIES, repoUrl);
-            featuresProperties.put(FEATURES_BOOT, generated.getName());
-            featuresProperties.save();
-        }
-        else {
-            String boot = "";
-            for (Dependency dep : generatedDep.values()) {
-                if (dep.isPrerequisite()) {
-                    if (boot.isEmpty()) {
-                        boot = "(";
-                    } else {
-                        boot = boot + ",";
-                    }
-                    boot = boot + dep.getName();
-                }
-            }
-            if (!boot.isEmpty()) {
-                boot = boot + ")";
-            }
-            // TODO: for dependencies, we'd need to resolve the features 
completely
-            for (Dependency dep : generatedDep.values()) {
-                if (!dep.isPrerequisite() && !dep.isDependency()) {
-                    if (!boot.isEmpty()) {
-                        boot = boot + ",";
-                    }
-                    boot = boot + dep.getName();
-                }
-            }
-            String repos = "";
-            for (String repo : new HashSet<>(rep.getRepository())) {
-                if (!repos.isEmpty()) {
-                    repos = repos + ",";
-                }
-                repos = repos + repo;
-            }
-
-            Properties featuresProperties = new Properties(featuresCfgFile);
-            featuresProperties.put(FEATURES_REPOSITORIES, repos);
-            featuresProperties.put(FEATURES_BOOT, boot);
-            featuresProperties.save();
-        }
-
-
-        //
-        // Handle installed profiles
-        //
-        Profile installedOverlay = Profiles.getOverlay(installedProfile, 
allProfiles);
-        Profile installedEffective = Profiles.getEffective(installedOverlay, 
false);
-
-        // Load startup repositories
-        Map<String, Features> installedRepositories = 
loadRepositories(installedEffective.getRepositories());
-        // Install all repositories
-        for (String repository : installedRepositories.keySet()) {
-            installArtifact(repository);
-        }
-        // Compute startup feature dependencies
-        Set<Feature> allInstalledFeatures = new HashSet<>();
-        for (Features repo : installedRepositories.values()) {
-            allInstalledFeatures.addAll(repo.getFeature());
-        }
-        Set<Feature> installedFeatures = new LinkedHashSet<>();
-        if (installedEffective.getFeatures().isEmpty() && 
installAllFeaturesByDefault) {
-            installedFeatures.addAll(allInstalledFeatures);
-        } else {
-            // Add boot features for search
-            allInstalledFeatures.addAll(allBootFeatures);
-            for (String feature : installedEffective.getFeatures()) {
-                addFeatures(installedFeatures, allInstalledFeatures, feature);
-            }
-        }
-        for (Feature feature : installedFeatures) {
-            for (Bundle bundle : feature.getBundle()) {
-                if (!ignoreDependencyFlag || !bundle.isDependency()) {
-                    installArtifact(bundle.getLocation().trim());
-                }
-            }
-            for (Conditional cond : feature.getConditional()) {
-                for (Bundle bundle : cond.getBundle()) {
-                    if (!ignoreDependencyFlag || !bundle.isDependency()) {
-                        installArtifact(bundle.getLocation().trim());
-                    }
-                }
-            }
-        }
-        for (String location : installedEffective.getBundles()) {
-            installArtifact(location);
-        }
-        // TODO: install config files
-    }
-
-    private Map<String, Features> loadRepositories(List<String> repositories) 
throws Exception {
-        Map<String, Features> loaded = new HashMap<>();
-        for (String repository : repositories) {
-            doLoadRepository(loaded, repository);
-        }
-        return loaded;
+            builder.profilesUris(profilesUri);
+        }
+        // Startup
+        builder.defaultStage(Builder.Stage.Startup)
+               .kars(toArray(startupKars))
+               .repositories(startupFeatures.isEmpty() && 
startupProfiles.isEmpty() && installAllFeaturesByDefault, 
toArray(startupRepositories))
+               .features(toArray(startupFeatures))
+               .bundles(toArray(startupBundles))
+               .profiles(toArray(startupProfiles));
+        // Boot
+        builder.defaultStage(Builder.Stage.Boot)
+                .kars(toArray(bootKars))
+                .repositories(bootFeatures.isEmpty() && bootProfiles.isEmpty() 
&& installAllFeaturesByDefault, toArray(bootRepositories))
+                .features(toArray(bootFeatures))
+                .bundles(toArray(bootBundles))
+                .profiles(toArray(bootProfiles));
+        // Installed
+        builder.defaultStage(Builder.Stage.Installed)
+                .kars(toArray(installedKars))
+                .repositories(installedFeatures.isEmpty() && 
installedProfiles.isEmpty() && installAllFeaturesByDefault, 
toArray(installedRepositories))
+                .features(toArray(installedFeatures))
+                .bundles(toArray(installedBundles))
+                .profiles(toArray(installedProfiles));
+
+        builder.generateAssembly();
     }
 
-    private void doLoadRepository(Map<String, Features> loaded, String 
repository) throws Exception {
-        if (!loaded.containsKey(repository)) {
-            Features featuresModel = JaxbUtil.unmarshal(repository, false);
-            loaded.put(repository, featuresModel);
-            // recursively process the inner repositories
-            for (String innerRepository : featuresModel.getRepository()) {
-                doLoadRepository(loaded, innerRepository);
-            }
-        }
-    }
-
-    private Profile generateProfile(Map<String, Profile> allProfiles, 
List<String> profiles, List<String> repositories, List<String> features, 
List<String> bundles) {
-        Profile profile = 
ProfileBuilder.Factory.create(UUID.randomUUID().toString())
-                .setParents(profiles)
-                .setRepositories(repositories)
-                .setFeatures(features)
-                .setBundles(bundles)
-                .getProfile();
-        allProfiles.put(profile.getId(), profile);
-        return profile;
+    private String[] toArray(List<String> strings) {
+        return strings.toArray(new String[strings.size()]);
     }
 
     private List<String> nonNullList(List<String> list) {
         return list == null ? new ArrayList<String>() : list;
     }
 
-    private void addFeatures(Set<Feature> startupFeatures, Set<Feature> 
features, String feature) {
-        int nbFound = 0;
-        for (Feature f : features) {
-            String[] split = feature.split("/");
-            if (split.length == 2) {
-                if (f.getName().equals(split[0]) && 
f.getVersion().equals(split[1])) {
-                    for (Dependency dep : f.getFeature()) {
-                        addFeatures(startupFeatures, features, dep.getName());
-                    }
-                    startupFeatures.add(f);
-                    nbFound++;
-                }
-            } else {
-                if (feature.equals(f.getName())) {
-                    for (Dependency dep : f.getFeature()) {
-                        addFeatures(startupFeatures, features, dep.getName());
-                    }
-                    startupFeatures.add(f);
-                    nbFound++;
-                }
-            }
-        }
-        if (nbFound == 0) {
-            throw new IllegalStateException("Could not find matching feature 
for " + feature);
-        }
-    }
-
-    private String installStartupArtifact(String location, boolean asFile) 
throws Exception {
-        getLog().info("== Installing artifact " + location);
-        String url;
-        String path;
-        if (location.startsWith("mvn:")) {
-            url = location;
-            path = dependencyHelper.pathFromMaven(location);
-            if (asFile) {
-                location = "file:" + path ;
-            }
-        } else {
-            url = location.replace("profile:", "file:" + 
workDirectory.getAbsolutePath() + "/etc/");
-            path = "generated/" + location.replaceAll("[^0-9a-zA-Z.\\-_]+", 
"_");
-            location = "file:" + path;
-        }
-        File bundleSystemFile = new File(systemDirectory, path);
-        if (!bundleSystemFile.exists()) {
-            bundleSystemFile.getParentFile().mkdirs();
-            try (InputStream is = new URL(url).openStream()) {
-                Files.copy(is, bundleSystemFile.toPath());
-            }
-        }
-        return location;
-    }
-
-    private void installArtifact(String location) throws Exception {
-        getLog().info("== Installing artifact " + location);
-        location = DownloadManagerHelper.stripUrl(location);
-        location = 
DownloadManagerHelper.removeInlinedMavenRepositoryUrl(location);
-        if (location.startsWith("mvn:")) {
-            if (location.endsWith("/")) {
-                // for bad formed URL (like in Camel for mustache-compiler), 
we remove the trailing /
-                location = location.substring(0, location.length() - 1);
-            }
-            File inputFile = dependencyHelper.resolveById(location, getLog());
-            File targetFile = new File(systemDirectory, 
dependencyHelper.pathFromMaven(location));
-            copy(inputFile, targetFile);
-            // add metadata for snapshot
-            Artifact artifact = dependencyHelper.mvnToArtifact(location);
-            if (artifact.isSnapshot()) {
-                File metadataTarget = new File(targetFile.getParentFile(), 
"maven-metadata-local.xml");
-                try {
-                    MavenUtil.generateMavenMetadata(artifact, metadataTarget);
-                } catch (Exception e) {
-                    getLog().warn("Could not create maven-metadata-local.xml", 
e);
-                    getLog().warn("It means that this SNAPSHOT could be 
overwritten by an older one present on remote repositories");
-                }
-            }
-        } else {
-            getLog().warn("Ignoring artifact " + location);
-        }
-    }
-
-    private void installConfig(Config config) throws Exception {
-        getLog().info("== Installing configuration " + config.getName());
-        Path configFile = workDirectory.toPath().resolve("etc/" + 
config.getName());
-        if (!Files.exists(configFile)) {
-            Files.write(configFile, config.getValue().getBytes());
-        } else if (config.isAppend()) {
-            // TODO
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
index d196c44..b3cb74a 100644
--- 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
+++ 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
@@ -29,14 +29,10 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.net.URI;
 import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-import java.net.URLStreamHandlerFactory;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Dictionary;
 import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -81,9 +77,8 @@ import org.apache.karaf.features.internal.util.MultiException;
 import org.apache.karaf.tooling.url.CustomBundleURLStreamHandlerFactory;
 import org.apache.karaf.tooling.utils.InternalMavenResolver;
 import org.apache.karaf.tooling.utils.MojoSupport;
-import org.apache.karaf.tooling.utils.PropertiesLoader;
+import org.apache.karaf.util.config.PropertiesLoader;
 import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugins.annotations.Mojo;
@@ -91,11 +86,6 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.project.MavenProject;
 import org.ops4j.pax.url.mvn.MavenResolver;
-import org.ops4j.pax.url.mvn.MavenResolvers;
-import org.ops4j.pax.url.mvn.ServiceConstants;
-import org.ops4j.pax.url.mvn.internal.AetherBasedResolver;
-import org.ops4j.pax.url.mvn.internal.Connection;
-import org.ops4j.pax.url.mvn.internal.config.MavenConfigurationImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
@@ -111,7 +101,6 @@ import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
 import org.osgi.resource.Wire;
 import org.osgi.service.resolver.ResolutionException;
-import shaded.org.ops4j.util.property.PropertiesPropertyResolver;
 
 import static java.util.jar.JarFile.MANIFEST_NAME;
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/PropertiesLoader.java
----------------------------------------------------------------------
diff --git 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/PropertiesLoader.java
 
b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/PropertiesLoader.java
deleted file mode 100644
index 6507b83..0000000
--- 
a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/PropertiesLoader.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.karaf.tooling.utils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Enumeration;
-import org.apache.felix.utils.properties.Properties;
-import java.util.StringTokenizer;
-
-import static org.apache.felix.utils.properties.InterpolationHelper.substVars;
-
-public class PropertiesLoader {
-
-    private static final String INCLUDES_PROPERTY = "${includes}"; // 
mandatory includes
-
-    private static final String OPTIONALS_PROPERTY = "${optionals}"; // 
optionals include
-
-    private static final String OVERRIDE_PREFIX = "karaf.override."; // prefix 
that marks that system property should override defaults.
-
-    /**
-     * <p>
-     * Loads the configuration properties in the configuration property file
-     * associated with the framework installation; these properties
-     * are accessible to the framework and to bundles and are intended
-     * for configuration purposes. By default, the configuration property
-     * file is located in the <tt>conf/</tt> directory of the Felix
-     * installation directory and is called "<tt>config.properties</tt>".
-     * The installation directory of Felix is assumed to be the parent
-     * directory of the <tt>felix.jar</tt> file as found on the system class
-     * path property. The precise file from which to load configuration
-     * properties can be set by initializing the 
"<tt>felix.config.properties</tt>"
-     * system property to an arbitrary URL.
-     * </p>
-     *
-     * @return A <tt>Properties</tt> instance or <tt>null</tt> if there was an 
error.
-     * @throws Exception if something wrong occurs
-     */
-    public static Properties loadConfigProperties(File file) throws Exception {
-        // See if the property URL was specified as a property.
-        URL configPropURL;
-        try {
-            configPropURL = file.toURI().toURL();
-        }
-        catch (MalformedURLException ex) {
-            System.err.print("Main: " + ex);
-            return null;
-        }
-
-        Properties configProps = loadPropertiesFile(configPropURL, false);
-        copySystemProperties(configProps);
-        configProps.substitute();
-
-        // Perform variable substitution for system properties.
-//        for (Enumeration<?> e = configProps.propertyNames(); 
e.hasMoreElements();) {
-//            String name = (String) e.nextElement();
-//            configProps.setProperty(name,
-//                    SubstHelper.substVars(configProps.getProperty(name), 
name, null, configProps));
-//        }
-
-        return configProps;
-    }
-
-    /**
-     * <p>
-     * Loads the properties in the system property file associated with the
-     * framework installation into <tt>System.setProperty()</tt>. These 
properties
-     * are not directly used by the framework in anyway. By default, the system
-     * property file is located in the <tt>conf/</tt> directory of the Felix
-     * installation directory and is called "<tt>system.properties</tt>". The
-     * installation directory of Felix is assumed to be the parent directory of
-     * the <tt>felix.jar</tt> file as found on the system class path property.
-     * The precise file from which to load system properties can be set by
-     * initializing the "<tt>felix.system.properties</tt>" system property to 
an
-     * arbitrary URL.
-     * </p>
-     *
-     * @param karafBase the karaf base folder
-     * @throws IOException
-     */
-    public static void loadSystemProperties(File file) throws IOException {
-        Properties props = new Properties(false);
-        try {
-            InputStream is = new FileInputStream(file);
-            props.load(is);
-            is.close();
-        } catch (Exception e1) {
-            // Ignore
-        }
-
-        for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
-            String name = (String) e.nextElement();
-            if (name.startsWith(OVERRIDE_PREFIX)) {
-                String overrideName = name.substring(OVERRIDE_PREFIX.length());
-                String value = props.getProperty(name);
-                System.setProperty(overrideName, substVars(value, name, null, 
props));
-            } else {
-                String value = System.getProperty(name, 
props.getProperty(name));
-                System.setProperty(name, substVars(value, name, null, props));
-            }
-        }
-    }
-
-    public static void copySystemProperties(Properties configProps) {
-        for (Enumeration<?> e = System.getProperties().propertyNames();
-             e.hasMoreElements();) {
-            String key = (String) e.nextElement();
-            if (key.startsWith("felix.") ||
-                    key.startsWith("karaf.") ||
-                    key.startsWith("org.osgi.framework.")) {
-                configProps.setProperty(key, System.getProperty(key));
-            }
-        }
-    }
-
-    public static Properties loadPropertiesOrFail(File configFile) {
-        try {
-            URL configPropURL = configFile.toURI().toURL();
-            return loadPropertiesFile(configPropURL, true);
-        } catch (Exception e) {
-            throw new RuntimeException("Error loading properties from " + 
configFile, e);
-        }
-    }
-
-    public static Properties loadPropertiesFile(URL configPropURL, boolean 
failIfNotFound) throws Exception {
-        Properties configProps = new Properties(null, false);
-        InputStream is = null;
-        try {
-            is = configPropURL.openConnection().getInputStream();
-            configProps.load(is);
-            is.close();
-        } catch (FileNotFoundException ex) {
-            if (failIfNotFound) {
-                throw ex;
-            } else {
-                System.err.println("WARN: " + configPropURL + " is not found, 
so not loaded");
-            }
-        } catch (Exception ex) {
-            System.err.println("Error loading config properties from " + 
configPropURL);
-            System.err.println("Main: " + ex);
-            return configProps;
-        } finally {
-            try {
-                if (is != null) {
-                    is.close();
-                }
-            }
-            catch (IOException ex2) {
-                // Nothing we can do.
-            }
-        }
-        loadIncludes(INCLUDES_PROPERTY, true, configPropURL, configProps);
-        loadIncludes(OPTIONALS_PROPERTY, false, configPropURL, configProps);
-        trimValues(configProps);
-        return configProps;
-    }
-
-    private static void loadIncludes(String propertyName, boolean mandatory, 
URL configPropURL, Properties configProps)
-            throws MalformedURLException, Exception {
-        String includes = (String) configProps.get(propertyName);
-        if (includes != null) {
-            StringTokenizer st = new StringTokenizer(includes, "\" ", true);
-            if (st.countTokens() > 0) {
-                String location;
-                do {
-                    location = nextLocation(st);
-                    if (location != null) {
-                        URL url = new URL(configPropURL, location);
-                        Properties props = loadPropertiesFile(url, mandatory);
-                        configProps.putAll(props);
-                    }
-                }
-                while (location != null);
-            }
-        }
-        configProps.remove(propertyName);
-    }
-
-    private static void trimValues(Properties configProps) {
-        for (String key : configProps.keySet()) {
-            configProps.put(key, configProps.get(key).trim());
-        }
-    }
-
-    private static String nextLocation(StringTokenizer st) {
-        String retVal = null;
-
-        if (st.countTokens() > 0) {
-            String tokenList = "\" ";
-            StringBuffer tokBuf = new StringBuffer(10);
-            String tok;
-            boolean inQuote = false;
-            boolean tokStarted = false;
-            boolean exit = false;
-            while ((st.hasMoreTokens()) && (!exit)) {
-                tok = st.nextToken(tokenList);
-                if (tok.equals("\"")) {
-                    inQuote = !inQuote;
-                    if (inQuote) {
-                        tokenList = "\"";
-                    } else {
-                        tokenList = "\" ";
-                    }
-
-                } else if (tok.equals(" ")) {
-                    if (tokStarted) {
-                        retVal = tokBuf.toString();
-                        tokStarted = false;
-                        tokBuf = new StringBuffer(10);
-                        exit = true;
-                    }
-                } else {
-                    tokStarted = true;
-                    tokBuf.append(tok.trim());
-                }
-            }
-
-            // Handle case where end of token stream and
-            // still got data
-            if ((!exit) && (tokStarted)) {
-                retVal = tokBuf.toString();
-            }
-        }
-
-        return retVal;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/util/src/main/java/org/apache/karaf/util/config/PropertiesLoader.java
----------------------------------------------------------------------
diff --git 
a/util/src/main/java/org/apache/karaf/util/config/PropertiesLoader.java 
b/util/src/main/java/org/apache/karaf/util/config/PropertiesLoader.java
new file mode 100644
index 0000000..609957b
--- /dev/null
+++ b/util/src/main/java/org/apache/karaf/util/config/PropertiesLoader.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.util.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import org.apache.felix.utils.properties.Properties;
+import java.util.StringTokenizer;
+
+import static org.apache.felix.utils.properties.InterpolationHelper.substVars;
+
+public class PropertiesLoader {
+
+    private static final String INCLUDES_PROPERTY = "${includes}"; // 
mandatory includes
+
+    private static final String OPTIONALS_PROPERTY = "${optionals}"; // 
optionals include
+
+    private static final String OVERRIDE_PREFIX = "karaf.override."; // prefix 
that marks that system property should override defaults.
+
+    /**
+     * <p>
+     * Loads the configuration properties in the configuration property file
+     * associated with the framework installation; these properties
+     * are accessible to the framework and to bundles and are intended
+     * for configuration purposes. By default, the configuration property
+     * file is located in the <tt>conf/</tt> directory of the Felix
+     * installation directory and is called "<tt>config.properties</tt>".
+     * The installation directory of Felix is assumed to be the parent
+     * directory of the <tt>felix.jar</tt> file as found on the system class
+     * path property. The precise file from which to load configuration
+     * properties can be set by initializing the 
"<tt>felix.config.properties</tt>"
+     * system property to an arbitrary URL.
+     * </p>
+     *
+     * @return A <tt>Properties</tt> instance or <tt>null</tt> if there was an 
error.
+     * @throws Exception if something wrong occurs
+     */
+    public static Properties loadConfigProperties(File file) throws Exception {
+        // See if the property URL was specified as a property.
+        URL configPropURL;
+        try {
+            configPropURL = file.toURI().toURL();
+        }
+        catch (MalformedURLException ex) {
+            System.err.print("Main: " + ex);
+            return null;
+        }
+
+        Properties configProps = loadPropertiesFile(configPropURL, false);
+        copySystemProperties(configProps);
+        configProps.substitute();
+
+        // Perform variable substitution for system properties.
+//        for (Enumeration<?> e = configProps.propertyNames(); 
e.hasMoreElements();) {
+//            String name = (String) e.nextElement();
+//            configProps.setProperty(name,
+//                    SubstHelper.substVars(configProps.getProperty(name), 
name, null, configProps));
+//        }
+
+        return configProps;
+    }
+
+    /**
+     * <p>
+     * Loads the properties in the system property file associated with the
+     * framework installation into <tt>System.setProperty()</tt>. These 
properties
+     * are not directly used by the framework in anyway. By default, the system
+     * property file is located in the <tt>conf/</tt> directory of the Felix
+     * installation directory and is called "<tt>system.properties</tt>". The
+     * installation directory of Felix is assumed to be the parent directory of
+     * the <tt>felix.jar</tt> file as found on the system class path property.
+     * The precise file from which to load system properties can be set by
+     * initializing the "<tt>felix.system.properties</tt>" system property to 
an
+     * arbitrary URL.
+     * </p>
+     *
+     * @param karafBase the karaf base folder
+     * @throws IOException
+     */
+    public static void loadSystemProperties(File file) throws IOException {
+        Properties props = new Properties(false);
+        try {
+            InputStream is = new FileInputStream(file);
+            props.load(is);
+            is.close();
+        } catch (Exception e1) {
+            // Ignore
+        }
+
+        for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
+            String name = (String) e.nextElement();
+            if (name.startsWith(OVERRIDE_PREFIX)) {
+                String overrideName = name.substring(OVERRIDE_PREFIX.length());
+                String value = props.getProperty(name);
+                System.setProperty(overrideName, substVars(value, name, null, 
props));
+            } else {
+                String value = System.getProperty(name, 
props.getProperty(name));
+                System.setProperty(name, substVars(value, name, null, props));
+            }
+        }
+    }
+
+    public static void copySystemProperties(Properties configProps) {
+        for (Enumeration<?> e = System.getProperties().propertyNames();
+             e.hasMoreElements();) {
+            String key = (String) e.nextElement();
+            if (key.startsWith("felix.") ||
+                    key.startsWith("karaf.") ||
+                    key.startsWith("org.osgi.framework.")) {
+                configProps.setProperty(key, System.getProperty(key));
+            }
+        }
+    }
+
+    public static Properties loadPropertiesOrFail(File configFile) {
+        try {
+            URL configPropURL = configFile.toURI().toURL();
+            return loadPropertiesFile(configPropURL, true);
+        } catch (Exception e) {
+            throw new RuntimeException("Error loading properties from " + 
configFile, e);
+        }
+    }
+
+    public static Properties loadPropertiesFile(URL configPropURL, boolean 
failIfNotFound) throws Exception {
+        Properties configProps = new Properties(null, false);
+        InputStream is = null;
+        try {
+            is = configPropURL.openConnection().getInputStream();
+            configProps.load(is);
+            is.close();
+        } catch (FileNotFoundException ex) {
+            if (failIfNotFound) {
+                throw ex;
+            } else {
+                System.err.println("WARN: " + configPropURL + " is not found, 
so not loaded");
+            }
+        } catch (Exception ex) {
+            System.err.println("Error loading config properties from " + 
configPropURL);
+            System.err.println("Main: " + ex);
+            return configProps;
+        } finally {
+            try {
+                if (is != null) {
+                    is.close();
+                }
+            }
+            catch (IOException ex2) {
+                // Nothing we can do.
+            }
+        }
+        loadIncludes(INCLUDES_PROPERTY, true, configPropURL, configProps);
+        loadIncludes(OPTIONALS_PROPERTY, false, configPropURL, configProps);
+        trimValues(configProps);
+        return configProps;
+    }
+
+    private static void loadIncludes(String propertyName, boolean mandatory, 
URL configPropURL, Properties configProps)
+            throws MalformedURLException, Exception {
+        String includes = (String) configProps.get(propertyName);
+        if (includes != null) {
+            StringTokenizer st = new StringTokenizer(includes, "\" ", true);
+            if (st.countTokens() > 0) {
+                String location;
+                do {
+                    location = nextLocation(st);
+                    if (location != null) {
+                        URL url = new URL(configPropURL, location);
+                        Properties props = loadPropertiesFile(url, mandatory);
+                        configProps.putAll(props);
+                    }
+                }
+                while (location != null);
+            }
+        }
+        configProps.remove(propertyName);
+    }
+
+    private static void trimValues(Properties configProps) {
+        for (String key : configProps.keySet()) {
+            configProps.put(key, configProps.get(key).trim());
+        }
+    }
+
+    private static String nextLocation(StringTokenizer st) {
+        String retVal = null;
+
+        if (st.countTokens() > 0) {
+            String tokenList = "\" ";
+            StringBuffer tokBuf = new StringBuffer(10);
+            String tok;
+            boolean inQuote = false;
+            boolean tokStarted = false;
+            boolean exit = false;
+            while ((st.hasMoreTokens()) && (!exit)) {
+                tok = st.nextToken(tokenList);
+                if (tok.equals("\"")) {
+                    inQuote = !inQuote;
+                    if (inQuote) {
+                        tokenList = "\"";
+                    } else {
+                        tokenList = "\" ";
+                    }
+
+                } else if (tok.equals(" ")) {
+                    if (tokStarted) {
+                        retVal = tokBuf.toString();
+                        tokStarted = false;
+                        tokBuf = new StringBuffer(10);
+                        exit = true;
+                    }
+                } else {
+                    tokStarted = true;
+                    tokBuf.append(tok.trim());
+                }
+            }
+
+            // Handle case where end of token stream and
+            // still got data
+            if ((!exit) && (tokStarted)) {
+                retVal = tokBuf.toString();
+            }
+        }
+
+        return retVal;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/util/src/main/java/org/apache/karaf/util/maven/Parser.java
----------------------------------------------------------------------
diff --git a/util/src/main/java/org/apache/karaf/util/maven/Parser.java 
b/util/src/main/java/org/apache/karaf/util/maven/Parser.java
index 232942e..8f6920f 100644
--- a/util/src/main/java/org/apache/karaf/util/maven/Parser.java
+++ b/util/src/main/java/org/apache/karaf/util/maven/Parser.java
@@ -150,6 +150,19 @@ public class Parser
     }
 
     /**
+     * Returns the artifact path from the given maven uri.
+     * @param uri the maven uri
+     * @return the artifact path
+     * @throws MalformedURLException
+     */
+    public static String pathFromMaven(String uri) throws 
MalformedURLException {
+        if (!uri.startsWith("mvn:")) {
+            return uri;
+        }
+        return new Parser(uri.substring("mvn:".length())).getArtifactPath();
+    }
+
+    /**
      * Parses the artifact part of the url ( without the repository).
      *
      * @param part url part without protocol and repository.

Reply via email to