You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2015/02/27 12:54:57 UTC
[2/2] karaf git commit: [KARAF-3546] Provide a programmatic way to
build custom karaf distributions outside of maven
[KARAF-3546] Provide a programmatic way to build custom karaf distributions outside of maven
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/e920dde0
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/e920dde0
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/e920dde0
Branch: refs/heads/master
Commit: e920dde01f7824e646e3d1b083141546a0502e5f
Parents: 3f6bca0
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Tue Feb 24 14:35:15 2015 +0100
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Fri Feb 27 12:54:44 2015 +0100
----------------------------------------------------------------------
demos/profiles/dynamic/pom.xml | 7 +-
.../activemq/broker.profile/profile.cfg | 2 +-
.../main/resources/default.profile/profile.cfg | 2 +
.../org.ops4j.pax.logging.cfg#static | 54 ++
demos/profiles/static/pom.xml | 8 +-
.../download/impl/MavenDownloadManager.java | 14 +-
main/pom.xml | 2 +
.../org/apache/karaf/main/ConfigProperties.java | 1 +
.../main/java/org/apache/karaf/main/Main.java | 1 +
.../org/apache/karaf/main/PropertiesLoader.java | 207 ----
.../karaf/main/util/SimpleMavenResolver.java | 61 +-
.../main/util/SimpleMavenResolverTest.java | 6 +-
pom.xml | 5 +
profile/pom.xml | 61 +-
.../java/org/apache/karaf/profile/Profile.java | 12 +
.../assembly/AssemblyDeployCallback.java | 227 +++++
.../apache/karaf/profile/assembly/Builder.java | 969 +++++++++++++++++++
.../profile/assembly/CustomDownloadManager.java | 43 +
.../assembly/CustomSimpleDownloadTask.java | 161 +++
.../profile/assembly/FakeBundleRevision.java | 157 +++
.../apache/karaf/profile/impl/ProfileImpl.java | 40 +-
.../karaf/profile/assembly/BuilderTest.java | 68 ++
.../karaf/tooling/features/InstallKarsMojo.java | 683 ++-----------
.../features/VerifyFeatureResolutionMojo.java | 13 +-
.../karaf/tooling/utils/PropertiesLoader.java | 249 -----
.../karaf/util/config/PropertiesLoader.java | 249 +++++
.../org/apache/karaf/util/maven/Parser.java | 13 +
27 files changed, 2154 insertions(+), 1161 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/demos/profiles/dynamic/pom.xml
----------------------------------------------------------------------
diff --git a/demos/profiles/dynamic/pom.xml b/demos/profiles/dynamic/pom.xml
index 87990c7..6db6a5d 100644
--- a/demos/profiles/dynamic/pom.xml
+++ b/demos/profiles/dynamic/pom.xml
@@ -106,13 +106,8 @@
</execution>
</executions>
<configuration>
- <!--
- <profilesArtifact>
- org.apache.karaf.demo.profiles:org.apache.karaf.demo.profiles.registry
- </profilesArtifact>
- -->
<profilesUri>
- file://${basedir}/../registry/src/main/resources
+ jar:mvn:org.apache.karaf.demos.profiles/registry/${project.version}!/
</profilesUri>
<bootFeatures>
<feature>deployer</feature>
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/demos/profiles/registry/src/main/resources/activemq/broker.profile/profile.cfg
----------------------------------------------------------------------
diff --git a/demos/profiles/registry/src/main/resources/activemq/broker.profile/profile.cfg b/demos/profiles/registry/src/main/resources/activemq/broker.profile/profile.cfg
index 269c1f9..65a0806 100644
--- a/demos/profiles/registry/src/main/resources/activemq/broker.profile/profile.cfg
+++ b/demos/profiles/registry/src/main/resources/activemq/broker.profile/profile.cfg
@@ -18,4 +18,4 @@
attribute.parents = karaf
feature.aries-bluerint = aries-blueprint
-feature.activemq-broker = activemq-broker
+feature.activemq-broker = activemq-broker-noweb
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/demos/profiles/registry/src/main/resources/default.profile/profile.cfg
----------------------------------------------------------------------
diff --git a/demos/profiles/registry/src/main/resources/default.profile/profile.cfg b/demos/profiles/registry/src/main/resources/default.profile/profile.cfg
index 08f0801..a4cc652 100644
--- a/demos/profiles/registry/src/main/resources/default.profile/profile.cfg
+++ b/demos/profiles/registry/src/main/resources/default.profile/profile.cfg
@@ -18,6 +18,8 @@
#framework=mvn\:org.apache.felix/org.apache.felix.framework/${felix.framework.version}
repository.karaf-standard=mvn\:org.apache.karaf.assemblies.features/standard/${profile:version/karaf}/xml/features
+library.jolokia-agent=mvn\:org.jolokia/jolokia-jvm/1.2.2/jar/agent
+
org.ops4j.pax.url.mvn.repositories= \
file:${runtime.home}/${karaf.default.repository}@snapshots@id=karaf-default, \
http://repo1.maven.org/maven2@id=central, \
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/demos/profiles/registry/src/main/resources/karaf.profile/org.ops4j.pax.logging.cfg#static
----------------------------------------------------------------------
diff --git a/demos/profiles/registry/src/main/resources/karaf.profile/org.ops4j.pax.logging.cfg#static b/demos/profiles/registry/src/main/resources/karaf.profile/org.ops4j.pax.logging.cfg#static
new file mode 100644
index 0000000..8b8184d
--- /dev/null
+++ b/demos/profiles/registry/src/main/resources/karaf.profile/org.ops4j.pax.logging.cfg#static
@@ -0,0 +1,54 @@
+#
+# 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.
+#
+
+# Root logger
+log4j.rootLogger=INFO, stdout, osgi:*
+log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
+# use this for source code lines enabled in the logs (beware it impacts performance)
+#log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.RollingFileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
+# use this for source code lines enabled in the logs (beware it impacts performance)
+#log4j.appender.out.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
+log4j.appender.out.file=${karaf.data}/log/karaf.log
+log4j.appender.out.append=true
+log4j.appender.out.maxFileSize=100MB
+log4j.appender.out.maxBackupIndex=10
+
+# Sift appender
+log4j.appender.sift=org.apache.log4j.sift.MDCSiftingAppender
+log4j.appender.sift.key=bundle.name
+log4j.appender.sift.default=karaf
+log4j.appender.sift.appender=org.apache.log4j.FileAppender
+log4j.appender.sift.appender.layout=org.apache.log4j.PatternLayout
+log4j.appender.sift.appender.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %m%n
+# use this for source code lines enabled in the logs (beware it impacts performance)
+#log4j.appender.sift.appender.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.sift.appender.file=${karaf.data}/log/$\\{bundle.name\\}.log
+log4j.appender.sift.appender.append=true
+
+# To avoid flooding the log when using DEBUG level on an ssh connection and doing log:tail
+log4j.logger.org.apache.sshd.server.channel.ChannelSession = INFO
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/demos/profiles/static/pom.xml
----------------------------------------------------------------------
diff --git a/demos/profiles/static/pom.xml b/demos/profiles/static/pom.xml
index 4f12134..8db3ef1 100644
--- a/demos/profiles/static/pom.xml
+++ b/demos/profiles/static/pom.xml
@@ -121,13 +121,9 @@
</executions>
<configuration>
<useReferenceUrls>true</useReferenceUrls>
- <!--
- <profilesArtifact>
- org.apache.karaf.demo.profiles:org.apache.karaf.demo.profiles.registry
- </profilesArtifact>
- -->
+ <environment>static</environment>
<profilesUri>
- file://${basedir}/../registry/src/main/resources
+ jar:mvn:org.apache.karaf.demos.profiles/registry/${project.version}!/
</profilesUri>
<startupProfiles>
<profile>karaf</profile>
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/features/core/src/main/java/org/apache/karaf/features/internal/download/impl/MavenDownloadManager.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/download/impl/MavenDownloadManager.java b/features/core/src/main/java/org/apache/karaf/features/internal/download/impl/MavenDownloadManager.java
index 82f3eea..99dec02 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/download/impl/MavenDownloadManager.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/download/impl/MavenDownloadManager.java
@@ -33,11 +33,11 @@ import org.ops4j.pax.url.mvn.MavenResolver;
public class MavenDownloadManager implements DownloadManager {
- private final MavenResolver mavenResolver;
+ protected final MavenResolver mavenResolver;
- private final ScheduledExecutorService executorService;
+ protected final ScheduledExecutorService executorService;
- private File tmpPath;
+ protected File tmpPath;
private final Map<String, AbstractDownloadTask> downloaded = new HashMap<>();
@@ -143,7 +143,7 @@ public class MavenDownloadManager implements DownloadManager {
});
}
- private AbstractDownloadTask createDownloadTask(final String url) {
+ protected AbstractDownloadTask createDownloadTask(final String url) {
final String mvnUrl = DownloadManagerHelper.stripUrl(url);
if (mvnUrl.startsWith("mvn:")) {
if (!mvnUrl.equals(url)) {
@@ -152,7 +152,7 @@ public class MavenDownloadManager implements DownloadManager {
return new MavenDownloadTask(executorService, mavenResolver, mvnUrl);
}
} else {
- return new SimpleDownloadTask(executorService, url, tmpPath);
+ return createCustomDownloadTask(url);
}
}
@@ -205,4 +205,8 @@ public class MavenDownloadManager implements DownloadManager {
}
+ protected AbstractDownloadTask createCustomDownloadTask(final String url) {
+ return new SimpleDownloadTask(executorService, url, tmpPath);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/main/pom.xml
----------------------------------------------------------------------
diff --git a/main/pom.xml b/main/pom.xml
index e3783b4..f47f1b3 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -94,6 +94,8 @@
<Private-Package>
org.apache.karaf.main*,
org.apache.felix.utils.properties;-split-package:=merge-first,
+ org.apache.karaf.util.config,
+ org.apache.karaf.util.maven,
org.apache.karaf.util.locks;-split-package:=merge-first,
META-INF;-split-package:=merge-first
</Private-Package>
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/main/src/main/java/org/apache/karaf/main/ConfigProperties.java
----------------------------------------------------------------------
diff --git a/main/src/main/java/org/apache/karaf/main/ConfigProperties.java b/main/src/main/java/org/apache/karaf/main/ConfigProperties.java
index 32947d9..d2803af 100644
--- a/main/src/main/java/org/apache/karaf/main/ConfigProperties.java
+++ b/main/src/main/java/org/apache/karaf/main/ConfigProperties.java
@@ -29,6 +29,7 @@ import org.apache.felix.utils.properties.Properties;
import org.apache.karaf.main.lock.SimpleFileLock;
import org.apache.karaf.main.util.Utils;
+import org.apache.karaf.util.config.PropertiesLoader;
import org.osgi.framework.Constants;
public class ConfigProperties {
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/main/src/main/java/org/apache/karaf/main/Main.java
----------------------------------------------------------------------
diff --git a/main/src/main/java/org/apache/karaf/main/Main.java b/main/src/main/java/org/apache/karaf/main/Main.java
index b35a055..169e31a 100644
--- a/main/src/main/java/org/apache/karaf/main/Main.java
+++ b/main/src/main/java/org/apache/karaf/main/Main.java
@@ -46,6 +46,7 @@ import org.apache.karaf.main.util.ArtifactResolver;
import org.apache.karaf.main.util.BootstrapLogManager;
import org.apache.karaf.main.util.SimpleMavenResolver;
import org.apache.karaf.main.util.Utils;
+import org.apache.karaf.util.config.PropertiesLoader;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/main/src/main/java/org/apache/karaf/main/PropertiesLoader.java
----------------------------------------------------------------------
diff --git a/main/src/main/java/org/apache/karaf/main/PropertiesLoader.java b/main/src/main/java/org/apache/karaf/main/PropertiesLoader.java
deleted file mode 100644
index 7fe2057..0000000
--- a/main/src/main/java/org/apache/karaf/main/PropertiesLoader.java
+++ /dev/null
@@ -1,207 +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.main;
-
-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 org.apache.karaf.main.util.Utils;
-
-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
- */
- 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
- */
- 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));
- }
- }
- }
-
- 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));
- }
- }
- }
-
- 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);
- }
- }
-
- private 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 = Utils.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());
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/main/src/main/java/org/apache/karaf/main/util/SimpleMavenResolver.java
----------------------------------------------------------------------
diff --git a/main/src/main/java/org/apache/karaf/main/util/SimpleMavenResolver.java b/main/src/main/java/org/apache/karaf/main/util/SimpleMavenResolver.java
index 6ca10c1..b703c58 100644
--- a/main/src/main/java/org/apache/karaf/main/util/SimpleMavenResolver.java
+++ b/main/src/main/java/org/apache/karaf/main/util/SimpleMavenResolver.java
@@ -19,16 +19,16 @@
package org.apache.karaf.main.util;
import java.io.File;
+import java.net.MalformedURLException;
import java.net.URI;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+
+import org.apache.karaf.util.maven.Parser;
/**
* Resolves local maven artifacts and raw file paths
*/
public class SimpleMavenResolver implements ArtifactResolver {
- private static final Pattern mvnPattern = Pattern.compile("mvn:([^/ ]+)/([^/ ]+)/([^/ ]*)(/([^/ ]+)(/([^/ ]+))?)?");
private final List<File> mavenRepos;
/**
@@ -60,54 +60,17 @@ public class SimpleMavenResolver implements ArtifactResolver {
}
private static File findFile(File dir, URI mvnUri) {
- String path = fromMaven(mvnUri);
- File theFile = new File(dir, path);
-
- if (theFile.exists() && !theFile.isDirectory()) {
- return theFile;
- }
- return null;
- }
-
-
+ try {
+ String path = Parser.pathFromMaven(mvnUri.toString());
+ File theFile = new File(dir, path);
- /**
- * Returns a path for an srtifact.
- * Input: path (no ':') returns path
- * Input: converts to default repo location path
- * type and classifier are optional.
- *
- *
- * @param name input artifact info
- * @return path as supplied or a default maven repo path
- */
- static String fromMaven(URI name) {
- Matcher m = mvnPattern.matcher(name.toString());
- if (!m.matches()) {
- return name.toString();
- }
- StringBuilder path = new StringBuilder();
- path.append(m.group(1).replace(".", "/"));
- path.append("/");//groupId
- String artifactId = m.group(2);
- String version = m.group(3);
- String extension = m.group(5);
- String classifier = m.group(7);
- path.append(artifactId).append("/");//artifactId
- path.append(version).append("/");//version
- path.append(artifactId).append("-").append(version);
- if (present(classifier)) {
- path.append("-").append(classifier);
- }
- if (present(extension)) {
- path.append(".").append(extension);
- } else {
- path.append(".jar");
+ if (theFile.exists() && !theFile.isDirectory()) {
+ return theFile;
+ }
+ return null;
+ } catch (MalformedURLException e) {
+ return null;
}
- return path.toString();
}
- private static boolean present(String part) {
- return part != null && !part.isEmpty();
- }
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/main/src/test/java/org/apache/karaf/main/util/SimpleMavenResolverTest.java
----------------------------------------------------------------------
diff --git a/main/src/test/java/org/apache/karaf/main/util/SimpleMavenResolverTest.java b/main/src/test/java/org/apache/karaf/main/util/SimpleMavenResolverTest.java
index b914e33..541f0d4 100644
--- a/main/src/test/java/org/apache/karaf/main/util/SimpleMavenResolverTest.java
+++ b/main/src/test/java/org/apache/karaf/main/util/SimpleMavenResolverTest.java
@@ -19,20 +19,22 @@
package org.apache.karaf.main.util;
import java.io.File;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import junit.framework.Assert;
+import org.apache.karaf.util.maven.Parser;
import org.junit.Test;
public class SimpleMavenResolverTest {
private static final String ARTIFACT_COORDS = "mvn:org.apache.karaf.features/framework/1.0.0/xml/features";
@Test
- public void mavenToPath() throws URISyntaxException {
- String resolvedPath = SimpleMavenResolver.fromMaven(new URI(ARTIFACT_COORDS));
+ public void mavenToPath() throws MalformedURLException {
+ String resolvedPath = Parser.pathFromMaven(ARTIFACT_COORDS);
Assert.assertEquals("org/apache/karaf/features/framework/1.0.0/framework-1.0.0-features.xml", resolvedPath);
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index ddba0e1..0b555c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -532,6 +532,11 @@
<artifactId>demos</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.karaf.demos.profiles</groupId>
+ <artifactId>registry</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.karaf.features</groupId>
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/pom.xml
----------------------------------------------------------------------
diff --git a/profile/pom.xml b/profile/pom.xml
index 086355b..b2fab7d 100644
--- a/profile/pom.xml
+++ b/profile/pom.xml
@@ -77,6 +77,56 @@
</dependency>
<dependency>
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>org.apache.karaf.features.core</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.equinox</groupId>
+ <artifactId>org.eclipse.equinox.region</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.resolver</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.kar</groupId>
+ <artifactId>org.apache.karaf.kar.core</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.deployer</groupId>
+ <artifactId>org.apache.karaf.deployer.blueprint</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.deployer</groupId>
+ <artifactId>org.apache.karaf.deployer.spring</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-wrap</artifactId>
+ <classifier>uber</classifier>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-war</artifactId>
+ <classifier>uber</classifier>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
<groupId>org.jledit</groupId>
<artifactId>core</artifactId>
<optional>true</optional>
@@ -87,6 +137,12 @@
<artifactId>slf4j-jdk14</artifactId>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.karaf.demos.profiles</groupId>
+ <artifactId>registry</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@@ -123,10 +179,13 @@
<Private-Package>
org.apache.karaf.profile.command,
org.apache.karaf.profile.command.completers,
+ org.apache.karaf.profile.assembly,
org.apache.karaf.profile.impl,
org.apache.karaf.profile.impl.osgi,
+ org.apache.karaf.util.config,
+ org.apache.karaf.util.maven,
org.apache.karaf.util.tracker,
- org.apache.karaf.util.properties,
+ org.apache.felix.utils.version,
org.apache.felix.utils.properties,
</Private-Package>
<Provide-Capability>
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/src/main/java/org/apache/karaf/profile/Profile.java
----------------------------------------------------------------------
diff --git a/profile/src/main/java/org/apache/karaf/profile/Profile.java b/profile/src/main/java/org/apache/karaf/profile/Profile.java
index f924b7a..6f4d9fd 100644
--- a/profile/src/main/java/org/apache/karaf/profile/Profile.java
+++ b/profile/src/main/java/org/apache/karaf/profile/Profile.java
@@ -69,7 +69,19 @@ public interface Profile {
*/
String ATTRIBUTE_PREFIX = "attribute.";
+ /**
+ * The config prefix for in the agent configuration
+ */
+ String CONFIG_PREFIX = "config.";
+
+ /**
+ * The config prefix for in the agent configuration
+ */
+ String SYSTEM_PREFIX = "system.";
+
Map<String, String> getAttributes();
+ Map<String, String> getConfig();
+ Map<String, String> getSystem();
List<String> getParentIds();
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/src/main/java/org/apache/karaf/profile/assembly/AssemblyDeployCallback.java
----------------------------------------------------------------------
diff --git a/profile/src/main/java/org/apache/karaf/profile/assembly/AssemblyDeployCallback.java b/profile/src/main/java/org/apache/karaf/profile/assembly/AssemblyDeployCallback.java
new file mode 100644
index 0000000..826aa7e
--- /dev/null
+++ b/profile/src/main/java/org/apache/karaf/profile/assembly/AssemblyDeployCallback.java
@@ -0,0 +1,227 @@
+/*
+ * 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.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+
+import org.apache.karaf.features.FeatureEvent;
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.internal.download.DownloadCallback;
+import org.apache.karaf.features.internal.download.DownloadManager;
+import org.apache.karaf.features.internal.download.Downloader;
+import org.apache.karaf.features.internal.download.StreamProvider;
+import org.apache.karaf.features.internal.model.Config;
+import org.apache.karaf.features.internal.model.ConfigFile;
+import org.apache.karaf.features.internal.model.Feature;
+import org.apache.karaf.features.internal.model.Features;
+import org.apache.karaf.features.internal.service.Deployer;
+import org.apache.karaf.features.internal.service.State;
+import org.apache.karaf.features.internal.util.MapUtils;
+import org.apache.karaf.util.maven.Parser;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AssemblyDeployCallback implements Deployer.DeployCallback {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Builder.class);
+
+ private final DownloadManager manager;
+ private final Path homeDirectory;
+ private final int defaultStartLevel;
+ private final Path etcDirectory;
+ private final Path systemDirectory;
+ private final Deployer.DeploymentState dstate;
+ private final AtomicLong nextBundleId = new AtomicLong(0);
+
+ private final Map<String, Bundle> bundles = new HashMap<>();
+
+ public AssemblyDeployCallback(DownloadManager manager, Path homeDirectory, int defaultStartLevel, BundleRevision systemBundle, Collection<Features> repositories) throws Exception {
+ this.manager = manager;
+ this.homeDirectory = homeDirectory;
+ this.etcDirectory = homeDirectory.resolve("etc");
+ this.systemDirectory = homeDirectory.resolve("system");
+ this.defaultStartLevel = defaultStartLevel;
+ dstate = new Deployer.DeploymentState();
+ dstate.bundles = new HashMap<>();
+ dstate.features = new HashMap<>();
+ dstate.bundlesPerRegion = new HashMap<>();
+ dstate.filtersPerRegion = new HashMap<>();
+ dstate.state = new State();
+
+ MapUtils.addToMapSet(dstate.bundlesPerRegion, FeaturesService.ROOT_REGION, 0l);
+ dstate.bundles.put(0l, systemBundle.getBundle());
+ for (Features repo : repositories) {
+ for (Feature f : repo.getFeature()) {
+ dstate.features.put(f.getId(), f);
+ }
+ }
+ }
+
+ public Map<String, Integer> getStartupBundles() {
+ Map<String, Integer> startup = new HashMap<>();
+ for (Map.Entry<String, Bundle> bundle : bundles.entrySet()) {
+ int level = bundle.getValue().adapt(BundleStartLevel.class).getStartLevel();
+ if (level <= 0) {
+ level = defaultStartLevel;
+ }
+ startup.put(bundle.getKey(), level);
+ }
+ return startup;
+ }
+
+ public Deployer.DeploymentState getDeploymentState() {
+ return dstate;
+ }
+
+ @Override
+ public void print(String message, boolean verbose) {
+ }
+
+ @Override
+ public void saveState(State state) {
+ dstate.state.replace(state);
+ }
+
+ @Override
+ public void persistResolveRequest(Deployer.DeploymentRequest request) throws IOException {
+ }
+
+ @Override
+ public void installFeatureConfigs(org.apache.karaf.features.Feature feature) throws IOException, InvalidSyntaxException {
+ LOGGER.info("Installing feature config for " + feature.getId());
+ for (Config config : ((Feature) feature).getConfig()) {
+ Path configFile = etcDirectory.resolve(config.getName());
+ if (!Files.exists(configFile)) {
+ Files.write(configFile, config.getValue().getBytes());
+ } else if (config.isAppend()) {
+ Files.write(configFile, config.getValue().getBytes(), StandardOpenOption.APPEND);
+ }
+ }
+ Downloader downloader = manager.createDownloader();
+ for (final ConfigFile configFile : ((Feature) feature).getConfigfile()) {
+ downloader.download(configFile.getLocation(), new DownloadCallback() {
+ @Override
+ public void downloaded(StreamProvider provider) throws Exception {
+ Path input = provider.getFile().toPath();
+ String path = configFile.getFinalname();
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ Path output = homeDirectory.resolve(path);
+ Files.copy(input, output, StandardCopyOption.REPLACE_EXISTING);
+ }
+ });
+ }
+ try {
+ downloader.await();
+ } catch (Exception e) {
+ throw new IOException("Error downloading configuration files", e);
+ }
+ }
+
+ @Override
+ public void callListeners(FeatureEvent featureEvent) {
+ }
+
+ @Override
+ public Bundle installBundle(String region, String uri, InputStream is) throws BundleException {
+ LOGGER.info("Installing bundle " + uri);
+ try {
+ String path;
+ if (uri.startsWith("mvn:")) {
+ path = Parser.pathFromMaven(uri);
+ } else {
+ path = "generated/" + uri.replaceAll("[^0-9a-zA-Z.\\-_]+", "_");
+ }
+ final Path bundleSystemFile = systemDirectory.resolve(path);
+ Files.createDirectories(bundleSystemFile.getParent());
+ Files.copy(is, bundleSystemFile, StandardCopyOption.REPLACE_EXISTING);
+
+ Hashtable<String, String> headers = new Hashtable<>();
+ JarFile jar = new JarFile(bundleSystemFile.toFile());
+ Attributes attributes = jar.getManifest().getMainAttributes();
+ for (Map.Entry attr : attributes.entrySet()) {
+ headers.put(attr.getKey().toString(), attr.getValue().toString());
+ }
+ BundleRevision revision = new FakeBundleRevision(headers, uri, nextBundleId.incrementAndGet());
+ Bundle bundle = revision.getBundle();
+ MapUtils.addToMapSet(dstate.bundlesPerRegion, region, bundle.getBundleId());
+ dstate.bundles.put(bundle.getBundleId(), bundle);
+
+ bundles.put(path, bundle);
+ return bundle;
+ } catch (IOException e) {
+ throw new BundleException("Unable to install bundle", e);
+ }
+ }
+
+ @Override
+ public void updateBundle(Bundle bundle, String uri, InputStream is) throws BundleException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void uninstall(Bundle bundle) throws BundleException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void startBundle(Bundle bundle) throws BundleException {
+ }
+
+ @Override
+ public void stopBundle(Bundle bundle, int options) throws BundleException {
+ }
+
+ @Override
+ public void setBundleStartLevel(Bundle bundle, int startLevel) {
+ bundle.adapt(BundleStartLevel.class).setStartLevel(startLevel);
+ }
+
+ @Override
+ public void refreshPackages(Collection<Bundle> bundles) throws InterruptedException {
+ }
+
+ @Override
+ public void resolveBundles(Set<Bundle> bundles, Map<Resource, List<Wire>> wiring, Map<Resource, Bundle> resToBnd) {
+ }
+
+ @Override
+ public void replaceDigraph(Map<String, Map<String, Map<String, Set<String>>>> policies, Map<String, Set<Long>> bundles) throws BundleException, InvalidSyntaxException {
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
----------------------------------------------------------------------
diff --git a/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
new file mode 100644
index 0000000..e0f3ce3
--- /dev/null
+++ b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
@@ -0,0 +1,969 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+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.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.EnumSet;
+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 java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.felix.utils.properties.InterpolationHelper;
+import org.apache.felix.utils.properties.Properties;
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.internal.download.DownloadCallback;
+import org.apache.karaf.features.internal.download.DownloadManager;
+import org.apache.karaf.features.internal.download.Downloader;
+import org.apache.karaf.features.internal.download.StreamProvider;
+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.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.repository.BaseRepository;
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.apache.karaf.features.internal.service.Deployer;
+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.util.config.PropertiesLoader;
+import org.apache.karaf.util.maven.Parser;
+import org.ops4j.pax.url.mvn.MavenResolver;
+import org.ops4j.pax.url.mvn.MavenResolvers;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.jar.JarFile.MANIFEST_NAME;
+
+public class Builder {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Builder.class);
+
+ private static final String FEATURES_REPOSITORIES = "featuresRepositories";
+ private static final String FEATURES_BOOT = "featuresBoot";
+
+ public static enum Stage {
+ Startup, Boot, Installed
+ }
+
+ static class RepositoryInfo {
+ Stage stage;
+ boolean addAll;
+ }
+
+ //
+ // Input parameters
+ //
+
+ List<String> profilesUris = new ArrayList<>();
+ boolean defaultAddAll = true;
+ Stage defaultStage = Stage.Startup;
+ Map<String, RepositoryInfo> kars = new LinkedHashMap<>();
+ Map<String, Stage> profiles = new LinkedHashMap<>();
+ Map<String, RepositoryInfo> repositories = new LinkedHashMap<>();
+ Map<String, Stage> features = new LinkedHashMap<>();
+ Map<String, Stage> bundles = new LinkedHashMap<>();
+ String javase = "1.7";
+ String environment = null;
+ boolean useReferenceUrls;
+ boolean use24SyntaxForStartup;
+ boolean ignoreDependencyFlag;
+ int defaultStartLevel = 50;
+ Path homeDirectory;
+
+ private ScheduledExecutorService executor;
+ private DownloadManager manager;
+ private Path etcDirectory;
+ private Path systemDirectory;
+ private Map<String, Profile> allProfiles;
+
+ public static Builder newInstance() {
+ return new Builder();
+ }
+
+ public Builder defaultStage(Stage stage) {
+ this.defaultStage = stage;
+ return this;
+ }
+
+ public Builder defaultAddAll(boolean addAll) {
+ this.defaultAddAll = addAll;
+ return this;
+ }
+
+ public Builder profilesUris(String... profilesUri) {
+ Collections.addAll(this.profilesUris, profilesUri);
+ return this;
+ }
+
+ public Builder kars(String... kars) {
+ return kars(defaultStage, defaultAddAll, kars);
+ }
+
+ public Builder kars(boolean addAll, String... kars) {
+ return kars(defaultStage, addAll, kars);
+ }
+
+ public Builder kars(Stage stage, boolean addAll, String... kars) {
+ for (String kar : kars) {
+ RepositoryInfo info = new RepositoryInfo();
+ info.stage = stage;
+ info.addAll = addAll;
+ this.kars.put(kar, info);
+ }
+ return this;
+ }
+
+ public Builder repositories(String... repositories) {
+ return repositories(defaultStage, defaultAddAll, repositories);
+ }
+
+ public Builder repositories(boolean addAll, String... repositories) {
+ return repositories(defaultStage, addAll, repositories);
+ }
+
+ public Builder repositories(Stage stage, boolean addAll, String... repositories) {
+ for (String repository : repositories) {
+ RepositoryInfo info = new RepositoryInfo();
+ info.stage = stage;
+ info.addAll = addAll;
+ this.repositories.put(repository, info);
+ }
+ return this;
+ }
+
+ public Builder features(String... features) {
+ return features(defaultStage, features);
+ }
+
+ public Builder features(Stage stage, String... features) {
+ for (String feature : features) {
+ this.features.put(feature, stage);
+ }
+ return this;
+ }
+
+ public Builder bundles(String... bundles) {
+ return bundles(defaultStage, bundles);
+ }
+
+ public Builder bundles(Stage stage, String... bundles) {
+ for (String bundle : bundles) {
+ this.bundles.put(bundle, stage);
+ }
+ return this;
+ }
+
+ public Builder profiles(String... profiles) {
+ return profiles(defaultStage, profiles);
+ }
+
+ public Builder profiles(Stage stage, String... profiles) {
+ for (String profile : profiles) {
+ this.profiles.put(profile, stage);
+ }
+ return this;
+ }
+
+ public Builder homeDirectory(Path homeDirectory) {
+ if (homeDirectory == null) {
+ throw new IllegalArgumentException("homeDirectory is null");
+ }
+ this.homeDirectory = homeDirectory;
+ return this;
+ }
+
+ public Builder javase(String javase) {
+ if (javase == null) {
+ throw new IllegalArgumentException("javase is null");
+ }
+ this.javase = javase;
+ return this;
+ }
+
+ public Builder environment(String environment) {
+ this.environment = environment;
+ return this;
+ }
+
+ public Builder useReferenceUrls() {
+ return useReferenceUrls(true);
+ }
+
+ public Builder useReferenceUrls(boolean useReferenceUrls) {
+ this.useReferenceUrls = useReferenceUrls;
+ return this;
+ }
+
+ public Builder use24SyntaxForStartup() {
+ return use24SyntaxForStartup(true);
+ }
+
+ public Builder use24SyntaxForStartup(boolean use24SyntaxForStartup) {
+ this.use24SyntaxForStartup = use24SyntaxForStartup;
+ return this;
+ }
+
+ public Builder defaultStartLevel(int defaultStartLevel) {
+ this.defaultStartLevel = defaultStartLevel;
+ return this;
+ }
+
+ public Builder ignoreDependencyFlag() {
+ return ignoreDependencyFlag(true);
+ }
+
+ public Builder ignoreDependencyFlag(boolean ignoreDependencyFlag) {
+ this.ignoreDependencyFlag = ignoreDependencyFlag;
+ return this;
+ }
+
+ public Builder staticFramework() {
+ // TODO: load this from resources
+ return staticFramework("4.0.0-SNAPSHOT");
+ }
+
+ public Builder staticFramework(String version) {
+ return this.defaultStage(Stage.Startup)
+ .useReferenceUrls()
+ .kars(Stage.Startup, true, "mvn:org.apache.karaf.features/static/" + version + "/kar");
+ }
+
+ public void generateAssembly() throws Exception {
+ if (javase == null) {
+ throw new IllegalArgumentException("javase is not set");
+ }
+ if (homeDirectory == null) {
+ throw new IllegalArgumentException("homeDirectory is not set");
+ }
+ try {
+ doGenerateAssembly();
+ } finally {
+ if (executor != null) {
+ executor.shutdownNow();
+ }
+ }
+ }
+
+ private void doGenerateAssembly() throws Exception {
+ systemDirectory = homeDirectory.resolve("system");
+ etcDirectory = homeDirectory.resolve("etc");
+
+ LOGGER.info("Generating karaf assembly: " + homeDirectory);
+
+ //
+ // Create download manager
+ //
+ Dictionary<String, String> props = new Hashtable<>();
+ MavenResolver resolver = MavenResolvers.createMavenResolver(props, "org.ops4j.pax.url.mvn");
+ executor = Executors.newScheduledThreadPool(8);
+ manager = new CustomDownloadManager(resolver, executor);
+
+ //
+ // Unzip kars
+ //
+ LOGGER.info("Unzipping kars");
+ Map<String, RepositoryInfo> repositories = new LinkedHashMap<>(this.repositories);
+ Downloader downloader = manager.createDownloader();
+ for (String kar : kars.keySet()) {
+ downloader.download(kar, null);
+ }
+ downloader.await();
+ for (String karUri : kars.keySet()) {
+ Kar kar = new Kar(manager.getProviders().get(karUri).getFile().toURI());
+ kar.extract(systemDirectory.toFile(), homeDirectory.toFile());
+ RepositoryInfo info = kars.get(karUri);
+ for (URI repositoryUri : kar.getFeatureRepos()) {
+ repositories.put(repositoryUri.toString(), info);
+ }
+ }
+
+ //
+ // Propagate feature installation from repositories
+ //
+ Map<String, Stage> features = new LinkedHashMap<>(this.features);
+ Map<String, Features> karRepositories = loadRepositories(manager, repositories.keySet(), false);
+ for (String repo : repositories.keySet()) {
+ RepositoryInfo info = repositories.get(repo);
+ if (info.addAll) {
+ for (Feature feature : karRepositories.get(repo).getFeature()) {
+ features.put(feature.getId(), info.stage);
+ }
+ }
+ }
+
+ //
+ // Load profiles
+ //
+ LOGGER.info("Loading profiles");
+ allProfiles = new HashMap<>();
+ for (String profilesUri : profilesUris) {
+ String uri = profilesUri;
+ if (uri.startsWith("jar:") && uri.contains("!/")) {
+ uri = uri.substring("jar:".length(), uri.indexOf("!/"));
+ }
+ if (!uri.startsWith("file:")) {
+ downloader = manager.createDownloader();
+ downloader.download(uri, null);
+ downloader.await();
+ StreamProvider provider = manager.getProviders().get(uri);
+ profilesUri = profilesUri.replace(uri, provider.getFile().toURI().toString());
+ }
+ 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>(), Builder.class.getClassLoader());
+ profilePath = fs.provider().getPath(profileURI);
+ }
+ allProfiles.putAll(Profiles.loadProfiles(profilePath));
+ }
+
+ // Generate profiles
+ Profile startupProfile = generateProfile(Stage.Startup, profiles, repositories, features, bundles);
+ Profile bootProfile = generateProfile(Stage.Boot, profiles, repositories, features, bundles);
+ Profile installedProfile = generateProfile(Stage.Installed, profiles, repositories, features, bundles);
+
+ //
+ // 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, environment);
+ Profile overallEffective = Profiles.getEffective(overallOverlay, false);
+
+ manager = new CustomDownloadManager(resolver, executor, overallEffective);
+
+ 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 config and system properties
+ //
+ Path configPropertiesPath = etcDirectory.resolve("config.properties");
+ Properties configProperties = new Properties(configPropertiesPath.toFile());
+ configProperties.putAll(overallEffective.getConfig());
+ configProperties.save();
+
+ Path systemPropertiesPath = etcDirectory.resolve("system.properties");
+ Properties systemProperties = new Properties(systemPropertiesPath.toFile());
+ systemProperties.putAll(overallEffective.getSystem());
+ systemProperties.save();
+
+ //
+ // Download libraries
+ //
+ // TODO: handle karaf 2.x and 3.x libraries
+ LOGGER.info("Downloading libraries");
+ downloader = manager.createDownloader();
+ downloadLibraries(downloader, overallEffective.getLibraries(), "lib");
+ downloadLibraries(downloader, overallEffective.getEndorsedLibraries(), "lib/endorsed");
+ downloadLibraries(downloader, overallEffective.getExtensionLibraries(), "lib/ext");
+ downloadLibraries(downloader, overallEffective.getBootLibraries(), "lib/boot");
+ downloader.await();
+
+ //
+ // Write all configuration files
+ //
+ for (Map.Entry<String, byte[]> config : overallEffective.getFileConfigurations().entrySet()) {
+ Path configFile = etcDirectory.resolve(config.getKey());
+ Files.createDirectories(configFile.getParent());
+ Files.write(configFile, config.getValue());
+ }
+
+ //
+ // Startup stage
+ //
+ Profile startupEffective = startupStage(startupProfile);
+
+ //
+ // Boot stage
+ //
+ Set<Feature> allBootFeatures = bootStage(bootProfile, startupEffective);
+
+ //
+ // Installed stage
+ //
+ installStage(installedProfile, allBootFeatures);
+ }
+
+ private void downloadLibraries(Downloader downloader, List<String> libraries, final String path) throws MalformedURLException {
+ for (String library : libraries) {
+ downloader.download(library, new DownloadCallback() {
+ @Override
+ public void downloaded(final StreamProvider provider) throws Exception {
+ synchronized (provider) {
+ Path input = provider.getFile().toPath();
+ Path output = homeDirectory.resolve(path).resolve(input.getFileName().toString());
+ Files.copy(input, output);
+ }
+ }
+ });
+ }
+ }
+
+ private void installStage(Profile installedProfile, Set<Feature> allBootFeatures) throws Exception {
+ Downloader downloader;//
+ // Handle installed profiles
+ //
+ Profile installedOverlay = Profiles.getOverlay(installedProfile, allProfiles, environment);
+ Profile installedEffective = Profiles.getEffective(installedOverlay, false);
+
+ downloader = manager.createDownloader();
+
+ // Load startup repositories
+ Map<String, Features> installedRepositories = loadRepositories(manager, installedEffective.getRepositories(), true);
+ // Compute startup feature dependencies
+ Set<Feature> allInstalledFeatures = new HashSet<>();
+ for (Features repo : installedRepositories.values()) {
+ allInstalledFeatures.addAll(repo.getFeature());
+ }
+ Set<Feature> installedFeatures = new LinkedHashSet<>();
+ // 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(downloader, bundle.getLocation().trim());
+ }
+ }
+ for (Conditional cond : feature.getConditional()) {
+ for (Bundle bundle : cond.getBundle()) {
+ if (!ignoreDependencyFlag || !bundle.isDependency()) {
+ installArtifact(downloader, bundle.getLocation().trim());
+ }
+ }
+ }
+ }
+ for (String location : installedEffective.getBundles()) {
+ installArtifact(downloader, location);
+ }
+ downloader.await();
+ }
+
+ private Set<Feature> bootStage(Profile bootProfile, Profile startupEffective) throws Exception {
+ //
+ // Handle boot profiles
+ //
+ Profile bootOverlay = Profiles.getOverlay(bootProfile, allProfiles, environment);
+ Profile bootEffective = Profiles.getEffective(bootOverlay, false);
+ // Load startup repositories
+ Map<String, Features> bootRepositories = loadRepositories(manager, bootEffective.getRepositories(), true);
+ // Compute startup feature dependencies
+ Set<Feature> allBootFeatures = new HashSet<>();
+ for (Features repo : bootRepositories.values()) {
+ allBootFeatures.addAll(repo.getFeature());
+ }
+ // Generate a global feature
+ Map<String, Dependency> generatedDep = new HashMap<>();
+ Feature generated = new Feature();
+ generated.setName(UUID.randomUUID().toString());
+ // Add feature dependencies
+ 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);
+
+ Downloader downloader = manager.createDownloader();
+
+ // 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
+ LOGGER.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());
+ }
+ }
+ }
+
+ // 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"));
+ for (String location : locations) {
+ installArtifact(downloader, 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(downloader, configFile.getLocation().trim());
+ }
+ for (Conditional cond : feature.getConditional()) {
+ for (ConfigFile configFile : cond.getConfigfile()) {
+ installArtifact(downloader, configFile.getLocation().trim());
+ }
+ }
+ }
+
+ // If there are bundles to install, we can't use the boot features only
+ // so keep the generated feature
+ Path featuresCfgFile = etcDirectory.resolve("org.apache.karaf.features.cfg");
+ if (!generated.getBundle().isEmpty()) {
+ File output = etcDirectory.resolve(rep.getName() + ".xml").toFile();
+ 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())) {
+ LOGGER.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.toFile());
+ 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.toFile());
+ featuresProperties.put(FEATURES_REPOSITORIES, repos);
+ featuresProperties.put(FEATURES_BOOT, boot);
+ // TODO: reformat to multiline values
+ featuresProperties.save();
+ }
+ downloader.await();
+ return allBootFeatures;
+ }
+
+ private Profile startupStage(Profile startupProfile) throws Exception {
+ //
+ // Compute startup
+ //
+ Profile startupOverlay = Profiles.getOverlay(startupProfile, allProfiles, environment);
+ Profile startupEffective = Profiles.getEffective(startupOverlay, false);
+ // Load startup repositories
+ LOGGER.info("Loading repositories");
+ Map<String, Features> startupRepositories = loadRepositories(manager, startupEffective.getRepositories(), false);
+
+ //
+ // Resolve
+ //
+ LOGGER.info("Resolving features");
+ Map<String, Integer> bundles =
+ resolve(manager,
+ startupRepositories.values(),
+ startupEffective.getFeatures(),
+ startupEffective.getBundles(),
+ startupEffective.getOverrides(),
+ startupEffective.getOptionals());
+
+ //
+ // Generate startup.properties
+ //
+ Properties startup = new Properties();
+ startup.setHeader(Collections.singletonList("# Bundles to be started on startup, with startlevel"));
+ Map<Integer, Set<String>> invertedStartupBundles = MapUtils.invert(bundles);
+ for (Map.Entry<Integer, Set<String>> entry : invertedStartupBundles.entrySet()) {
+ String startLevel = Integer.toString(entry.getKey());
+ for (String location : new TreeSet<>(entry.getValue())) {
+ if (location.startsWith("file:") && useReferenceUrls) {
+ location = "reference:" + location;
+ }
+ if (location.startsWith("file:") && use24SyntaxForStartup) {
+ location = location.substring("file:".length());
+ }
+ startup.put(location, startLevel);
+ }
+ }
+ Path startupProperties = etcDirectory.resolve("startup.properties");
+ startup.save(startupProperties.toFile());
+ return startupEffective;
+ }
+
+ private void installArtifact(Downloader downloader, String location) throws Exception {
+ LOGGER.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);
+ }
+ downloader.download(location, new DownloadCallback() {
+ @Override
+ public void downloaded(final StreamProvider provider) throws Exception {
+ Path path = systemDirectory.resolve(Parser.pathFromMaven(provider.getUrl()));
+ synchronized (provider) {
+ Files.createDirectories(path.getParent());
+ Files.copy(provider.getFile().toPath(), path, StandardCopyOption.REPLACE_EXISTING);
+ }
+ }
+ });
+ // 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 {
+ LOGGER.warn("Ignoring artifact " + location);
+ }
+ }
+
+ 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 List<String> getStaged(Stage stage, Map<String, Stage> data) {
+ List<String> staged = new ArrayList<>();
+ for (String s : data.keySet()) {
+ if (data.get(s) == stage) {
+ staged.add(s);
+ }
+ }
+ return staged;
+ }
+
+ private List<String> getStagedRepositories(Stage stage, Map<String, RepositoryInfo> data) {
+ List<String> staged = new ArrayList<>();
+ for (String s : data.keySet()) {
+ if (data.get(s).stage == stage) {
+ staged.add(s);
+ }
+ }
+ return staged;
+ }
+
+ private Map<String, Features> loadRepositories(DownloadManager manager, Collection<String> repositories, final boolean install) throws Exception {
+ final Map<String, Features> loaded = new HashMap<>();
+ final Downloader downloader = manager.createDownloader();
+ for (String repository : repositories) {
+ downloader.download(repository, new DownloadCallback() {
+ @Override
+ public void downloaded(final StreamProvider provider) throws Exception {
+ if (install) {
+ synchronized (provider) {
+ Path path = systemDirectory.resolve(Parser.pathFromMaven(provider.getUrl()));
+ Files.createDirectories(path.getParent());
+ Files.copy(provider.getFile().toPath(), path, StandardCopyOption.REPLACE_EXISTING);
+ }
+ }
+ try (InputStream is = provider.open()) {
+ Features featuresModel = JaxbUtil.unmarshal(provider.getUrl(), is, false);
+ synchronized (loaded) {
+ loaded.put(provider.getUrl(), featuresModel);
+ for (String innerRepository : featuresModel.getRepository()) {
+ downloader.download(innerRepository, this);
+ }
+ }
+ }
+ }
+ });
+ }
+ downloader.await();
+ return loaded;
+ }
+
+ private Profile generateProfile(Stage stage, Map<String, Stage> profiles, Map<String, RepositoryInfo> repositories, Map<String, Stage> features, Map<String, Stage> bundles) {
+ Profile profile = ProfileBuilder.Factory.create(UUID.randomUUID().toString())
+ .setParents(getStaged(stage, profiles))
+ .setRepositories(getStagedRepositories(stage, repositories))
+ .setFeatures(getStaged(stage, features))
+ .setBundles(getStaged(stage, bundles))
+ .getProfile();
+ allProfiles.put(profile.getId(), profile);
+ return profile;
+ }
+
+ private Map<String, Integer> resolve(
+ DownloadManager manager,
+ Collection<Features> repositories,
+ Collection<String> features,
+ Collection<String> bundles,
+ Collection<String> overrides,
+ Collection<String> optionals) throws Exception {
+ BundleRevision systemBundle = getSystemBundle();
+ AssemblyDeployCallback callback = new AssemblyDeployCallback(manager, homeDirectory, defaultStartLevel, systemBundle, repositories);
+ Deployer deployer = new Deployer(manager, callback);
+
+ // Install framework
+ Deployer.DeploymentRequest request = createDeploymentRequest();
+ // Add overrides
+ request.overrides.addAll(overrides);
+ // Add optional resources
+ final List<Resource> resources = new ArrayList<>();
+ Downloader downloader = manager.createDownloader();
+ for (String optional : optionals) {
+ downloader.download(optional, new DownloadCallback() {
+ @Override
+ public void downloaded(StreamProvider provider) throws Exception {
+ Resource resource = ResourceBuilder.build(provider.getUrl(), getHeaders(provider));
+ synchronized (resources) {
+ resources.add(resource);
+ }
+ }
+ });
+ }
+ downloader.await();
+ request.globalRepository = new BaseRepository(resources);
+ // Install features
+ for (String feature : features) {
+ MapUtils.addToMapSet(request.requirements, FeaturesService.ROOT_REGION, feature);
+ }
+ for (String bundle : bundles) {
+ MapUtils.addToMapSet(request.requirements, FeaturesService.ROOT_REGION, "bundle:" + bundle);
+ }
+ Set<String> prereqs = new HashSet<>();
+ while (true) {
+ try {
+ deployer.deploy(callback.getDeploymentState(), request);
+ break;
+ } catch (Deployer.PartialDeploymentException e) {
+ if (!prereqs.containsAll(e.getMissing())) {
+ prereqs.addAll(e.getMissing());
+ } else {
+ throw new Exception("Deployment aborted due to loop in missing prerequisites: " + e.getMissing());
+ }
+ }
+ }
+
+ return callback.getStartupBundles();
+ }
+
+ private Deployer.DeploymentRequest createDeploymentRequest() {
+ Deployer.DeploymentRequest request = new Deployer.DeploymentRequest();
+ request.bundleUpdateRange = FeaturesService.DEFAULT_BUNDLE_UPDATE_RANGE;
+ request.featureResolutionRange = FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE;
+ request.overrides = new HashSet<>();
+ request.requirements = new HashMap<>();
+ request.stateChanges = new HashMap<>();
+ request.options = EnumSet.noneOf(FeaturesService.Option.class);
+ return request;
+ }
+
+ private BundleRevision getSystemBundle() throws Exception {
+ Path configPropPath = etcDirectory.resolve("config.properties");
+ Properties configProps = PropertiesLoader.loadPropertiesOrFail(configPropPath.toFile());
+ configProps.put("java.specification.version", javase);
+ configProps.substitute();
+
+ Attributes attributes = new Attributes();
+ attributes.putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+ attributes.putValue(Constants.BUNDLE_SYMBOLICNAME, "system.bundle");
+ attributes.putValue(Constants.BUNDLE_VERSION, "0.0.0");
+
+ String exportPackages = configProps.getProperty("org.osgi.framework.system.packages");
+ if (configProps.containsKey("org.osgi.framework.system.packages.extra")) {
+ exportPackages += "," + configProps.getProperty("org.osgi.framework.system.packages.extra");
+ }
+ exportPackages = exportPackages.replaceAll(",\\s*,", ",");
+ attributes.putValue(Constants.EXPORT_PACKAGE, exportPackages);
+
+ String systemCaps = configProps.getProperty("org.osgi.framework.system.capabilities");
+ attributes.putValue(Constants.PROVIDE_CAPABILITY, systemCaps);
+
+ final Hashtable<String, String> headers = new Hashtable<>();
+ for (Map.Entry attr : attributes.entrySet()) {
+ headers.put(attr.getKey().toString(), attr.getValue().toString());
+ }
+
+ return new FakeBundleRevision(headers, "system-bundle", 0l);
+ }
+
+ Map<String, String> getHeaders(StreamProvider provider) throws IOException {
+ try (
+ InputStream is = provider.open()
+ ) {
+ ZipInputStream zis = new ZipInputStream(is);
+ ZipEntry entry;
+ while ((entry = zis.getNextEntry()) != null) {
+ if (MANIFEST_NAME.equals(entry.getName())) {
+ Attributes attributes = new Manifest(zis).getMainAttributes();
+ Map<String, String> headers = new HashMap<>();
+ for (Map.Entry attr : attributes.entrySet()) {
+ headers.put(attr.getKey().toString(), attr.getValue().toString());
+ }
+ return headers;
+ }
+ }
+ }
+ throw new IllegalArgumentException("Resource " + provider.getUrl() + " does not contain a manifest");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e920dde0/profile/src/main/java/org/apache/karaf/profile/assembly/CustomDownloadManager.java
----------------------------------------------------------------------
diff --git a/profile/src/main/java/org/apache/karaf/profile/assembly/CustomDownloadManager.java b/profile/src/main/java/org/apache/karaf/profile/assembly/CustomDownloadManager.java
new file mode 100644
index 0000000..824d435
--- /dev/null
+++ b/profile/src/main/java/org/apache/karaf/profile/assembly/CustomDownloadManager.java
@@ -0,0 +1,43 @@
+/*
+ * 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.util.concurrent.ScheduledExecutorService;
+
+import org.apache.karaf.features.internal.download.impl.AbstractDownloadTask;
+import org.apache.karaf.features.internal.download.impl.MavenDownloadManager;
+import org.apache.karaf.profile.Profile;
+import org.ops4j.pax.url.mvn.MavenResolver;
+
+public class CustomDownloadManager extends MavenDownloadManager {
+
+ private final Profile profile;
+
+ public CustomDownloadManager(MavenResolver resolver, ScheduledExecutorService executor) {
+ this(resolver, executor, null);
+ }
+
+ public CustomDownloadManager(MavenResolver resolver, ScheduledExecutorService executor, Profile profile) {
+ super(resolver, executor);
+ this.profile = profile;
+ }
+
+ @Override
+ protected AbstractDownloadTask createCustomDownloadTask(String url) {
+ return new CustomSimpleDownloadTask(executorService, profile, url);
+ }
+}