You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gg...@apache.org on 2017/12/09 18:46:49 UTC
[karaf] 06/19: [KARAF-5468] Cleaning up AssemblyMojo,
Profiles and profile Builder
This is an automated email from the ASF dual-hosted git repository.
ggrzybek pushed a commit to branch KARAF-5376-overrides_v2
in repository https://gitbox.apache.org/repos/asf/karaf.git
commit fa8f79690961398e0194a5c0c3b1cb7be7e4d937
Author: Grzegorz Grzybek <gr...@gmail.com>
AuthorDate: Wed Nov 15 15:43:01 2017 +0100
[KARAF-5468] Cleaning up AssemblyMojo, Profiles and profile Builder
* Added good amount of documentation for public API, Maven mojo
parameters and interfaces
* Removed unused karaf-maven-plugin:assembly parameters
* Renamed some of karaf-maven-plugin:assembly parameters
* Changed some of karaf-maven-plugin:assembly parameters from String to
List<String>
* Changed some string constants into enums (Builder.JavaVersion)
* Refactored AssemblyMojo
* Added new logging statements to AssemblyMojo and Builder
* Renamed "agent" to "profile" wherever possible
---
demos/profiles/dynamic/pom.xml | 6 +-
demos/profiles/static/pom.xml | 6 +-
profile/pom.xml | 1 +
.../java/org/apache/karaf/profile/Profile.java | 129 ++--
.../org/apache/karaf/profile/ProfileConstants.java | 128 ++++
.../org/apache/karaf/profile/assembly/Builder.java | 604 +++++++++++++++---
.../karaf/profile/command/ProfileDisplay.java | 14 +-
.../apache/karaf/profile/command/ProfileEdit.java | 28 +-
.../karaf/profile/impl/ProfileBuilderImpl.java | 28 +-
.../org/apache/karaf/profile/impl/ProfileImpl.java | 72 ++-
.../org/apache/karaf/profile/impl/Profiles.java | 99 ++-
.../apache/karaf/profile/impl/ProfilesTest.java | 142 +++++
tooling/karaf-maven-plugin/pom.xml | 4 -
.../org/apache/karaf/tooling/AssemblyMojo.java | 678 +++++++++++++--------
.../java/org/apache/karaf/tooling/VerifyMojo.java | 28 +-
.../org/apache/karaf/tooling/utils/MavenUtil.java | 32 +-
16 files changed, 1496 insertions(+), 503 deletions(-)
diff --git a/demos/profiles/dynamic/pom.xml b/demos/profiles/dynamic/pom.xml
index 9c2ec36..ce8f734 100644
--- a/demos/profiles/dynamic/pom.xml
+++ b/demos/profiles/dynamic/pom.xml
@@ -106,9 +106,9 @@
</execution>
</executions>
<configuration>
- <profilesUri>
- jar:mvn:org.apache.karaf.demos.profiles/registry/${project.version}!/
- </profilesUri>
+ <profilesUris>
+ <uri>jar:mvn:org.apache.karaf.demos.profiles/registry/${project.version}!/</uri>
+ </profilesUris>
<bootFeatures>
<feature>deployer</feature>
</bootFeatures>
diff --git a/demos/profiles/static/pom.xml b/demos/profiles/static/pom.xml
index 0542c6b..1250085 100644
--- a/demos/profiles/static/pom.xml
+++ b/demos/profiles/static/pom.xml
@@ -122,9 +122,9 @@
<configuration>
<useReferenceUrls>true</useReferenceUrls>
<environment>static</environment>
- <profilesUri>
- jar:mvn:org.apache.karaf.demos.profiles/registry/${project.version}!/
- </profilesUri>
+ <profilesUris>
+ <uri>jar:mvn:org.apache.karaf.demos.profiles/registry/${project.version}!/</uri>
+ </profilesUris>
<startupProfiles>
<profile>karaf</profile>
<profile>example-loanbroker-bank1</profile>
diff --git a/profile/pom.xml b/profile/pom.xml
index 223e522..118a14f 100644
--- a/profile/pom.xml
+++ b/profile/pom.xml
@@ -183,6 +183,7 @@
org.apache.karaf.profile.impl,
org.apache.karaf.profile.impl.osgi,
org.apache.karaf.profile.versioning,
+ org.apache.karaf.util,
org.apache.karaf.util.config,
org.apache.karaf.util.maven,
org.apache.felix.utils.manifest,
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 c9d4b3d..ca08bd2 100644
--- a/profile/src/main/java/org/apache/karaf/profile/Profile.java
+++ b/profile/src/main/java/org/apache/karaf/profile/Profile.java
@@ -21,88 +21,135 @@ import java.util.Map;
import java.util.Set;
/**
- * The immutable view of a profile
+ * <p>A <em>profile</em> is a container for configuration that can be applied to Karaf distribution.</p>
+ *
+ * <p>Profiles may inherit from other (single or multiple) profiles. An <em>overlay</em> profile is single
+ * profile with all the configurations, attributes and files from parent profiles, while configurations,
+ * attributes and files from <em>child</em> profile overwrites corresponding data from parent profiles.</p>
+ *
+ * <p>Configuration include:<ul>
+ * <li>Attributes</li>
+ * <li>ConfigAdmin configurations (PIDs) to put into <code>${karaf.etc}</code> directory</li>
+ * <li>Other resources to put into <code>${karaf.etc}</code> directory</li>
+ * </ul></p>
+ *
+ * <p>Attributes are properties in special file <code>profile.cfg</code> (<code>profile</code> PID) and may specify:<ul>
+ * <li>OSGi bundles to install (prefix: <code>bundle.</code>)</li>
+ * <li>Karaf features to install (prefix: <code>feature.</code>)</li>
+ * <li>Feature XML repositories to use to resolve bundles and features (prefix: <code>repository.</code>)</li>
+ * <li>Identifiers of parent profiles (property name: <code>attribute.parents</code>)</li>
+ * <li>Indication of abstract profile (property name: <code>abstract</code>)</li>
+ * <li>Indication of hidden profile (property name: <code>hidden</code>)</li>
+ * <li>Different attributes (prefix: <code>attribute.</code>)</li>
+ * <li>Properties to be added to <code>etc/config.properties</code> (prefix: <code>config.</code>)</li>
+ * <li>Properties to be added to <code>etc/system.properties</code> (prefix: <code>system.</code>)</li>
+ * <li>Additional libraries to be added to <code>lib</code> (prefix: <code>library.</code>)</li>
+ * <li>Additional libraries to be added to <code>lib/boot</code> (prefix: <code>boot.</code>)</li>
+ * <li>Additional libraries to be added to <code>lib/endorsed</code> (prefix: <code>endorsed.</code>)</li>
+ * <li>Additional libraries to be added to <code>lib/ext</code> (prefix: <code>ext.</code>)</li>
+ * <li>Bundle override definitions to be added to <code>etc/overrides.properties</code> (prefix: <code>override.</code>)</li>
+ * <li>Optional {@link org.osgi.resource.Resource resources} to be used during resolution (prefix: <code>optional.</code>)</li>
+ * </ul></p>
*/
-public interface Profile {
+public interface Profile extends ProfileConstants {
/**
- * The attribute key for the list of parents
+ * Returns an attribute map of this profile
+ * @return
*/
- String PARENTS = "parents";
+ Map<String, String> getAttributes();
/**
- * The attribute key for the description of the profile
+ * Returns a property map for additional properties to be added to <code>${karaf.etc}/config.properties</code>
+ * @return
*/
- String DESCRIPTION = "description";
+ Map<String, String> getConfig();
/**
- * The attribute key for the abstract flag
+ * Returns a property map for additional properties to be added to <code>${karaf.etc}/system.properties</code>
+ * @return
*/
- String ABSTRACT = "abstract";
+ Map<String, String> getSystem();
/**
- * The attribute key for the hidden flag
+ * Returns a unique identifier of this profile
+ * @return
*/
- String HIDDEN = "hidden";
+ String getId();
/**
- * Key indicating a deletion.
- * This value can appear as the value of a key in a configuration
- * or as a key itself. If used as a key, the whole configuration
- * is flagged has been deleted from its parent when computing the
- * overlay.
+ * Returns a list of parent profile identifiers for this profile
+ * @return
*/
- String DELETED = "#deleted#";
+ List<String> getParentIds();
/**
- * The pid of the configuration holding internal profile attributes
+ * Returns a list of bundles (bundle URIs) defined in this profile
+ * @return
*/
- String INTERNAL_PID = "profile";
+ List<String> getBundles();
/**
- * The file suffix for a configuration
+ * Returns a list of features (<code>feature-name[/feature-version]</code>) defined in this profile
+ * @return
*/
- String PROPERTIES_SUFFIX = ".cfg";
+ List<String> getFeatures();
/**
- * The attribute prefix for in the agent configuration
+ * Returns a list of features XML repositories (URIs) defined in this profile
+ * @return
*/
- String ATTRIBUTE_PREFIX = "attribute.";
+ List<String> getRepositories();
/**
- * The config prefix for in the agent configuration
+ * Returns a list of libraries (to be added to <code>${karaf.home}/lib</code>) defined in this profile
+ * @return
*/
- String CONFIG_PREFIX = "config.";
+ List<String> getLibraries();
/**
- * The config prefix for in the agent configuration
+ * Returns a list of boot libraries (to be added to <code>${karaf.home}/lib/boot</code>) defined in this profile
+ * @return
*/
- String SYSTEM_PREFIX = "system.";
+ List<String> getBootLibraries();
- Map<String, String> getAttributes();
- Map<String, String> getConfig();
- Map<String, String> getSystem();
+ /**
+ * Returns a list of endorsed libraries (to be added to <code>${karaf.home}/lib/endorsed</code>) defined in this profile
+ * @return
+ */
+ List<String> getEndorsedLibraries();
- List<String> getParentIds();
+ /**
+ * Returns a list of extension libraries (to be added to <code>${karaf.home}/lib/ext</code>) defined in this profile
+ * @return
+ */
+ List<String> getExtLibraries();
- List<String> getLibraries();
- List<String> getBundles();
- List<String> getFeatures();
- List<String> getRepositories();
+ /**
+ * Returns a list of bundle override definitions (to be added to <code>${karaf.etc}/overrides.properties</code>)
+ * defined in this profile
+ * @return
+ */
List<String> getOverrides();
- List<String> getOptionals();
- String getId();
+ /**
+ * Returns a list of optional {@link org.osgi.resource.Resource resources} (URIs) to be used during
+ * resolution
+ * @return
+ */
+ List<String> getOptionals();
/**
- * Get the configuration file names that are available on this profile.
+ * Get the configuration file names that are available on this profile. This list should contain at least
+ * <code>profile.cfg</code> file.
*
* @return The configuration file names in the profile.
*/
Set<String> getConfigurationFileNames();
/**
- * Get all file configurations.
+ * Get all file configurations. This list should contain at least
+ * <code>profile.cfg</code> file.
*
* @return The file configurations in the profile.
*/
@@ -117,7 +164,8 @@ public interface Profile {
byte[] getFileConfiguration(String fileName);
/**
- * Get all configuration properties.
+ * Get all configuration properties.This list should contain at least
+ * configuration from main profile file - <code>profile.cfg</code>.
*
* @return The configurations in the profile.
*/
@@ -132,7 +180,8 @@ public interface Profile {
Map<String, Object> getConfiguration(String pid);
/**
- * Indicate if this profile is an overlay or not.
+ * Indicate if this profile is an overlay or not. An <em>overlay</em> profile includes configurations and
+ * attributes of parent profiles, while descendant profiles always have priority over parent profiles.
*
* @return True if the profile is an overlay, false else.
*/
diff --git a/profile/src/main/java/org/apache/karaf/profile/ProfileConstants.java b/profile/src/main/java/org/apache/karaf/profile/ProfileConstants.java
new file mode 100644
index 0000000..fe9417d
--- /dev/null
+++ b/profile/src/main/java/org/apache/karaf/profile/ProfileConstants.java
@@ -0,0 +1,128 @@
+/*
+ * 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;
+
+public interface ProfileConstants {
+
+ /**
+ * The attribute prefix for the profile configuration (<code>profile.cfg</code>)
+ */
+ String ATTRIBUTE_PREFIX = "attribute.";
+
+ /**
+ * The attribute key for whitespace-separated list of parent profile IDs
+ */
+ String PARENTS = ATTRIBUTE_PREFIX + "parents";
+
+ /**
+ * The attribute key for the description of the profile
+ */
+ String DESCRIPTION = "description";
+
+ /**
+ * The attribute key for the <em>abstract</em> flag
+ */
+ String ABSTRACT = "abstract";
+
+ /**
+ * The attribute key for the <em>hidden</em> flag
+ */
+ String HIDDEN = "hidden";
+
+ /**
+ * <p>Key indicating a deletion.</p>
+ * <p>This value can appear as the value of a key in a configuration
+ * or as a key itself. If used as a key, the whole configuration
+ * is flagged as deleted from its parent when computing the overlay.</p>
+ */
+ String DELETED = "#deleted#";
+
+ /**
+ * The pid of the configuration holding internal profile attributes
+ */
+ String INTERNAL_PID = "profile";
+
+ /**
+ * The file suffix for a configuration
+ */
+ String PROPERTIES_SUFFIX = ".cfg";
+
+ /**
+ * The prefix for attributes that are targeted for <code>${karaf.etc}/config.properties</code> file
+ */
+ String CONFIG_PREFIX = "config.";
+
+ /**
+ * The prefix for attributes that are targeted for <code>${karaf.etc}/system.properties</code> file
+ */
+ String SYSTEM_PREFIX = "system.";
+
+ /**
+ * The prefix for attributes that specify URIs of features XML files
+ */
+ String REPOSITORY_PREFIX = "repository.";
+
+ /**
+ * The prefix for attributes that specify feature names (<code>name[/version]</code>) to install/use
+ */
+ String FEATURE_PREFIX = "feature.";
+
+ /**
+ * The prefix for attributes that specify bundle URIs to install
+ */
+ String BUNDLE_PREFIX = "bundle.";
+
+ /**
+ * The prefix for attributes that specify additional libraries to add to <code>${karaf.home}/lib</code>.
+ * These are native libraries only. JARs that should be available in app classpath should go to
+ * <code>${karaf.home}/lib/boot</code> and use {@link #BOOT_PREFIX}.
+ */
+ String LIB_PREFIX = "library.";
+
+ /**
+ * The prefix for attributes that specify additional endorsed libraries to add to
+ * <code>${karaf.home}/lib/endorsed</code>
+ */
+ String ENDORSED_PREFIX = "endorsed.";
+
+ /**
+ * The prefix for attributes that specify additional extension libraries to add to
+ * <code>${karaf.home}/lib/ext</code>
+ */
+ String EXT_PREFIX = "ext.";
+
+ /**
+ * The prefix for attributes that specify additional endorsed libraries to add to
+ * <code>${karaf.home}/lib/boot</code>
+ */
+ String BOOT_PREFIX = "boot.";
+
+ /**
+ * The prefix for attributes that specify bundle overrides
+ * (see {@link org.apache.karaf.features.internal.service.Overrides}). In version 4.2 it's better to use
+ * {@link org.apache.karaf.features.internal.service.FeaturesProcessor} configuration.
+ */
+ String OVERRIDE_PREFIX = "override.";
+
+ /**
+ * The prefix for attributes that specify optional resources
+ */
+ String OPTIONAL_PREFIX = "optional.";
+
+}
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
index f14c6ae..6df4243 100644
--- a/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
+++ b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java
@@ -39,6 +39,7 @@ import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
@@ -48,14 +49,17 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Function;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
+import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.felix.resolver.ResolverImpl;
import org.apache.felix.utils.manifest.Clause;
import org.apache.felix.utils.properties.Properties;
+import org.apache.karaf.features.FeaturePattern;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.features.Library;
+import org.apache.karaf.features.LocationPattern;
import org.apache.karaf.features.internal.download.DownloadCallback;
import org.apache.karaf.features.internal.download.DownloadManager;
import org.apache.karaf.features.internal.download.Downloader;
@@ -67,17 +71,21 @@ 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.model.processing.FeaturesProcessing;
import org.apache.karaf.features.internal.repository.BaseRepository;
import org.apache.karaf.features.internal.resolver.ResourceBuilder;
import org.apache.karaf.features.internal.service.Blacklist;
import org.apache.karaf.features.internal.service.Deployer;
import org.apache.karaf.features.internal.util.MapUtils;
+import org.apache.karaf.features.internal.util.MultiException;
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.tools.utils.KarafPropertiesEditor;
import org.apache.karaf.tools.utils.model.KarafPropertyEdits;
+import org.apache.karaf.util.ThreadUtils;
+import org.apache.karaf.util.Version;
import org.apache.karaf.util.config.PropertiesLoader;
import org.apache.karaf.util.maven.Parser;
import org.ops4j.pax.url.mvn.MavenResolver;
@@ -91,9 +99,11 @@ import org.slf4j.LoggerFactory;
import static java.util.Collections.singletonList;
import static java.util.jar.JarFile.MANIFEST_NAME;
-import static org.apache.karaf.features.internal.service.Blacklist.TYPE_REPOSITORY;
import static org.apache.karaf.profile.assembly.Builder.Stage.Startup;
+/**
+ * A builder-like class to create instances of {@link Profile profiles}.
+ */
public class Builder {
private static final String STATIC_FEATURES_KAR = "mvn:org.apache.karaf.features/static/%s/kar";
@@ -106,25 +116,105 @@ public class Builder {
private static final String LIBRARY_CLAUSE_TYPE = "type";
private static final String LIBRARY_CLAUSE_EXPORT = "export";
private static final String LIBRARY_CLAUSE_DELEGATE = "delegate";
+
public static final String ORG_OPS4J_PAX_URL_MVN_PID = "org.ops4j.pax.url.mvn";
+ /**
+ * <p>An indication of <em>stage</em> for bundles/features/repositories/kars/profiles.</p>
+ */
public enum Stage {
- Startup, Boot, Installed
+ /**
+ * Karaf runtime is in <em>startup</em> stage when it installs OSGi bundles into OSGi framework before
+ * passing this responsibility to {@link FeaturesService}. A list of bundles to install is defined
+ * in <code>${karaf.etc}/startup.properties</code>.
+ */
+ Startup,
+ /**
+ * Karaf runtime is in <em>boot</em> stage when it installs OSGi bundles using Karaf features. Features
+ * (and features XML repositories) are defined in <code>${karaf.etc}/org.apache.karaf.features.cfg</code>.
+ * Repositories and features available in startup stage should be <em>visible</em> in boot stage as well, as
+ * this is the stage where term <em>Karaf feature</em> gets its meaning.
+ */
+ Boot,
+ /**
+ * <em>Installed</em> stage is just a space where bundles and features may be installed after starting
+ * Karaf runtime (e.g., using Karaf shell commands, JMX or UI).
+ */
+ Installed;
+
+ /**
+ * Get a {@link Stage} corresponding to Maven scope.
+ * @param scope
+ * @return
+ */
+ public static Stage fromMavenScope(String scope) {
+ switch (scope) {
+ case "compile":
+ return Builder.Stage.Startup;
+ case "runtime":
+ return Builder.Stage.Boot;
+ case "provided":
+ return Builder.Stage.Installed;
+ default:
+ return null;
+ }
+ }
}
+ /**
+ * <p>An identifiier of Karaf version <em>family</em>. Each version family may have special methods
+ * or requirements for generating/preparing configuration.</p>
+ */
public enum KarafVersion {
v24, v3x, v4x
}
+ /**
+ * <p>An idenfifier for supported Java version. This version is used for example in
+ * <code>${karaf.etc}/jre.properties</code> to define system packages for given Java version. Only
+ * supported versions are defined.</p>
+ */
+ public enum JavaVersion {
+ Java16("1.6"), Java17("1.7"), Java18("1.8"), Java9("9");
+ private String version;
+
+ JavaVersion(String version) {
+ this.version = version;
+ }
+
+ public static JavaVersion from(String version) {
+ Optional<JavaVersion> v = Arrays.stream(values())
+ .filter(jv -> jv.version.equals(version))
+ .findFirst();
+
+ if (!v.isPresent()) {
+ throw new IllegalArgumentException("Java version \"" + version + "\" is not supported");
+ }
+ return v.get();
+ }
+ }
+
+ /**
+ * TODOCUMENT
+ */
public enum BlacklistPolicy {
Discard,
Fail
}
+ /**
+ * Configuration of features XML repository (standalone or inside KAR). <code>addAll</code> may configure
+ * given repository to install all defined features if no explicit feature is specified.
+ */
static class RepositoryInfo {
Stage stage;
boolean addAll;
+
+ public RepositoryInfo(Stage stage, boolean addAll) {
+ this.stage = stage;
+ this.addAll = addAll;
+ }
}
//
@@ -145,7 +235,7 @@ public class Builder {
List<String> blacklistedRepositories = new ArrayList<>();
BlacklistPolicy blacklistPolicy = BlacklistPolicy.Discard;
List<String> libraries = new ArrayList<>();
- String javase = "1.8";
+ JavaVersion javase = JavaVersion.Java18;
KarafVersion karafVersion = KarafVersion.v4x;
String environment = null;
boolean useReferenceUrls;
@@ -158,6 +248,7 @@ public class Builder {
Map<String, String> config = new LinkedHashMap<>();
Map<String, String> system = new LinkedHashMap<>();
List<String> pidsToExtract = new LinkedList<>();
+ boolean writeProfiles;
private ScheduledExecutorService executor;
private DownloadManager manager;
@@ -166,74 +257,139 @@ public class Builder {
private Path systemDirectory;
private Map<String, Profile> allProfiles;
private KarafPropertyEdits propertyEdits;
+ private FeaturesProcessing featuresProcessing = new FeaturesProcessing();
private Map<String, String> translatedUrls;
- private Function<MavenResolver, MavenResolver> resolverWrapper = null;
+ private Function<MavenResolver, MavenResolver> resolverWrapper = Function.identity();
public static Builder newInstance() {
return new Builder();
}
+ /**
+ * Sets the {@link Stage} used by next builder invocations.
+ * @param stage
+ * @return
+ */
public Builder defaultStage(Stage stage) {
this.defaultStage = stage;
return this;
}
+ /**
+ * Sets default <em>add all</em> flag for KARs and repositories.
+ * @param addAll
+ * @return
+ */
public Builder defaultAddAll(boolean addAll) {
this.defaultAddAll = addAll;
return this;
}
+ /**
+ * Configure a list of profile URIs to be used for profile import
+ * @param profilesUri
+ * @return
+ */
public Builder profilesUris(String... profilesUri) {
Collections.addAll(this.profilesUris, profilesUri);
return this;
}
+ /**
+ * Configure libraries to use. Each library may contain OSGi header-like directives: <code>type</code>,
+ * <code>url</code>, <code>export</code> and <code>delegate</code>.
+ * @param libraries
+ * @return
+ */
public Builder libraries(String... libraries) {
Collections.addAll(this.libraries, libraries);
return this;
}
+ /**
+ * Configure KARs to use at current {@link #defaultStage stage} with default <em>add all</em> flag
+ * @param kars
+ * @return
+ */
public Builder kars(String... kars) {
return kars(defaultStage, defaultAddAll, kars);
}
+ /**
+ * Configure KARs to use at current {@link #defaultStage stage} with given <em>add all</em> flag
+ * @param addAll
+ * @param kars
+ * @return
+ */
public Builder kars(boolean addAll, String... kars) {
return kars(defaultStage, addAll, kars);
}
+ /**
+ * Configure KARs to use at given stage with given <em>add all</em> flag
+ * @param stage
+ * @param addAll
+ * @param kars
+ * @return
+ */
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);
+ this.kars.put(kar, new RepositoryInfo(stage, addAll));
}
return this;
}
+ /**
+ * Configure features XML repositories to use at current {@link #defaultStage stage} with default <em>add all</em> flag
+ * @param repositories
+ * @return
+ */
public Builder repositories(String... repositories) {
return repositories(defaultStage, defaultAddAll, repositories);
}
+ /**
+ * Configure features XML repositories to use at current {@link #defaultStage stage} with given <em>add all</em> flag
+ * @param addAll
+ * @param repositories
+ * @return
+ */
public Builder repositories(boolean addAll, String... repositories) {
return repositories(defaultStage, addAll, repositories);
}
+ /**
+ * Configure features XML repositories to use at given stage with given <em>add all</em> flag
+ * @param stage
+ * @param addAll
+ * @param repositories
+ * @return
+ */
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);
+ this.repositories.put(repository, new RepositoryInfo(stage, addAll));
}
return this;
}
+ /**
+ * Configure features to use at current {@link #defaultStage stage}. Each feature may be specified as
+ * <code>name</code> or <code>name/version</code> (no version ranges allowed).
+ * @param features
+ * @return
+ */
public Builder features(String... features) {
return features(defaultStage, features);
}
+ /**
+ * Configure features to use at given stage. Each feature may be specified as <code>name</code> or
+ * <code>name/version</code> (no version ranges allowed).
+ * @param stage
+ * @param features
+ * @return
+ */
public Builder features(Stage stage, String... features) {
for (String feature : features) {
this.features.put(feature, stage);
@@ -241,10 +397,21 @@ public class Builder {
return this;
}
+ /**
+ * Configure bundle URIs to use at current {@link #defaultStage stage}.
+ * @param bundles
+ * @return
+ */
public Builder bundles(String... bundles) {
return bundles(defaultStage, bundles);
}
+ /**
+ * Configure bundle URIs to use at given stage.
+ * @param stage
+ * @param bundles
+ * @return
+ */
public Builder bundles(Stage stage, String... bundles) {
for (String bundle : bundles) {
this.bundles.put(bundle, stage);
@@ -252,10 +419,21 @@ public class Builder {
return this;
}
+ /**
+ * Configure profiles to use at current {@link #defaultStage stage}.
+ * @param profiles
+ * @return
+ */
public Builder profiles(String... profiles) {
return profiles(defaultStage, profiles);
}
+ /**
+ * Configure profiles to use at given stage.
+ * @param stage
+ * @param profiles
+ * @return
+ */
public Builder profiles(Stage stage, String... profiles) {
for (String profile : profiles) {
this.profiles.put(profile, stage);
@@ -263,6 +441,11 @@ public class Builder {
return this;
}
+ /**
+ * Configure target directory, where distribution is being assembled.
+ * @param homeDirectory
+ * @return
+ */
public Builder homeDirectory(Path homeDirectory) {
if (homeDirectory == null) {
throw new IllegalArgumentException("homeDirectory is null");
@@ -271,101 +454,213 @@ public class Builder {
return this;
}
+ /**
+ * Configure Java version to use. This version will be resolved in several property placeholders inside
+ * <code>${karaf.etc}/config.properties</code> and <code>${karaf.etc}/jre.properties</code>.
+ * @param javase
+ * @return
+ */
public Builder javase(String javase) {
if (javase == null) {
throw new IllegalArgumentException("javase is null");
}
- this.javase = javase;
+ this.javase = JavaVersion.from(javase);
return this;
}
+ /**
+ * Set environment to use that may be used to select different variant of PID configuration file, e.g.,
+ * <code>org.ops4j.pax.url.mvn.cfg#docker</code>.
+ * @param environment
+ * @return
+ */
public Builder environment(String environment) {
this.environment = environment;
return this;
}
+ /**
+ * Configure builder to generate <code>reference:</code>-like URIs in <code>${karaf.etc}/startup.properties</code>.
+ * Bundles declared in this way are not copied (by Felix) to <code>data/cache</code> directory, but are
+ * used from original location.
+ * @return
+ */
public Builder useReferenceUrls() {
return useReferenceUrls(true);
}
+ /**
+ * Configure builder to use (when <code>true</code>) <code>reference:</code>-like URIs in
+ * <code>${karaf.etc}/startup.properties</code>.
+ * @param useReferenceUrls
+ * @return
+ */
public Builder useReferenceUrls(boolean useReferenceUrls) {
this.useReferenceUrls = useReferenceUrls;
return this;
}
+ /**
+ * Configure builder to copy generated and configured profiles into <code>${karaf.etc}/profiles</code>
+ * directory.
+ * @param writeProfiles
+ */
+ public void writeProfiles(boolean writeProfiles) {
+ this.writeProfiles = writeProfiles;
+ }
+
+ /**
+ * Configure Karaf version to target. This impacts the way some configuration files are generated.
+ * @param karafVersion
+ * @return
+ */
public Builder karafVersion(KarafVersion karafVersion) {
this.karafVersion = karafVersion;
return this;
}
+ /**
+ * Sets default start level for bundles declared in <code>${karaf.etc}/startup.properties</code>.
+ * @param defaultStartLevel
+ * @return
+ */
public Builder defaultStartLevel(int defaultStartLevel) {
this.defaultStartLevel = defaultStartLevel;
return this;
}
+ /**
+ * Ignore the dependency attribute (dependency="[true|false]") on bundles, effectively forcing their
+ * installation.
+ */
public Builder ignoreDependencyFlag() {
return ignoreDependencyFlag(true);
}
+ /**
+ * Configures builder to ignore (or not) <code>dependency</code> flag on bundles declared
+ * in features XML file.
+ * @param ignoreDependencyFlag
+ * @return
+ */
public Builder ignoreDependencyFlag(boolean ignoreDependencyFlag) {
this.ignoreDependencyFlag = ignoreDependencyFlag;
return this;
}
+ /**
+ * Configures builder to use offline pax-url-aether resolver
+ * @return
+ */
+ public Builder offline() {
+ return offline(true);
+ }
+
+ /**
+ * Configures whether pax-url-aether resolver should work in offline mode
+ * @param offline
+ * @return
+ */
public Builder offline(boolean offline) {
this.offline = offline;
return this;
}
- public Builder offline() {
- return offline(true);
- }
-
+ /**
+ * Configures local Maven repository to use by pax-url-aether. By default, assembly mojo sets the value
+ * read from current Maven build.
+ * @param localRepository
+ * @return
+ */
public Builder localRepository(String localRepository) {
this.localRepository = localRepository;
return this;
}
+ /**
+ * Configures comma-separated list of remote Maven repositories to use by pax-url-aether.
+ * By default, assembly mojo sets the repositories from current Maven build.
+ * @param mavenRepositories
+ * @return
+ */
public Builder mavenRepositories(String mavenRepositories) {
this.mavenRepositories = mavenRepositories;
return this;
}
+ /**
+ * Configures a function that may alter/replace {@link MavenResolver} used to resolve <code>mvn:</code> URIs.
+ * @param wrapper
+ * @return
+ */
public Builder resolverWrapper(Function<MavenResolver, MavenResolver> wrapper) {
this.resolverWrapper = wrapper;
return this;
}
+ /**
+ * Short-hand builder configuration to use standard Karaf static KAR at current Karaf version
+ * @return
+ */
public Builder staticFramework() {
- // TODO: load this from resources
- return staticFramework("4.0.0-SNAPSHOT");
+ return staticFramework(Version.karafVersion());
}
+ /**
+ * Short-hand builder configuration to use standard Karaf static KAR at given Karaf version
+ * @param version
+ * @return
+ */
public Builder staticFramework(String version) {
String staticFeaturesKar = String.format(STATIC_FEATURES_KAR, version);
return this.defaultStage(Startup).useReferenceUrls().kars(Startup, true, staticFeaturesKar);
}
+ /**
+ * Configure a list of blacklisted profile names (possibly using <code>*</code> glob)
+ * @param profiles
+ * @return
+ */
public Builder blacklistProfiles(Collection<String> profiles) {
this.blacklistedProfiles.addAll(profiles);
return this;
}
+ /**
+ * Configure a list of blacklisted feature names (see {@link FeaturePattern})
+ * @param features
+ * @return
+ */
public Builder blacklistFeatures(Collection<String> features) {
this.blacklistedFeatures.addAll(features);
return this;
}
+ /**
+ * Configure a list of blacklisted bundle URIs (see {@link LocationPattern})
+ * @param bundles
+ * @return
+ */
public Builder blacklistBundles(Collection<String> bundles) {
this.blacklistedBundles.addAll(bundles);
return this;
}
+ /**
+ * Configure a list of blacklisted features XML repository URIs (see {@link LocationPattern})
+ * @param repositories
+ * @return
+ */
public Builder blacklistRepositories(Collection<String> repositories) {
this.blacklistedRepositories.addAll(repositories);
return this;
}
+ /**
+ * TODOCUMENT
+ * @param policy
+ * @return
+ */
public Builder blacklistPolicy(BlacklistPolicy policy) {
this.blacklistPolicy = policy;
return this;
@@ -381,6 +676,12 @@ public class Builder {
return this;
}
+ /**
+ * Configures a list of PIDs (or PID patterns) to copy to <code>${karaf.etc}</code> from features, when
+ * assembling a distribution
+ * @param pidsToExtract
+ * @return
+ */
public Builder pidsToExtract(List<String> pidsToExtract) {
if (pidsToExtract != null) {
for (String pid : pidsToExtract) {
@@ -401,11 +702,23 @@ public class Builder {
return this;
}
+ /**
+ * Configures additional properties to add to <code>${karaf.etc}/config.properties</code>
+ * @param key
+ * @param value
+ * @return
+ */
public Builder config(String key, String value) {
this.config.put(key, value);
return this;
}
+ /**
+ * Configures additional properties to add to <code>${karaf.etc}/system.properties</code>
+ * @param key
+ * @param value
+ * @return
+ */
public Builder system(String key, String value) {
this.system.put(key, value);
return this;
@@ -435,6 +748,10 @@ public class Builder {
return pidsToExtract;
}
+ /**
+ * Main method to generate custom Karaf distribution using configuration provided with builder-like methods.
+ * @throws Exception
+ */
public void generateAssembly() throws Exception {
if (javase == null) {
throw new IllegalArgumentException("javase is not set");
@@ -443,6 +760,11 @@ public class Builder {
throw new IllegalArgumentException("homeDirectory is not set");
}
try {
+ executor = Executors.newScheduledThreadPool(8, ThreadUtils.namedThreadFactory("builder"));
+
+ systemDirectory = homeDirectory.resolve("system");
+ etcDirectory = homeDirectory.resolve("etc");
+
doGenerateAssembly();
} finally {
if (executor != null) {
@@ -452,47 +774,35 @@ public class Builder {
}
private void doGenerateAssembly() throws Exception {
- systemDirectory = homeDirectory.resolve("system");
- etcDirectory = homeDirectory.resolve("etc");
-
- LOGGER.info("Generating karaf assembly: " + homeDirectory);
+ LOGGER.info("Generating Karaf assembly: " + homeDirectory);
//
- // Create download manager
+ // Create download manager - combination of pax-url-aether and a resolver wrapper that may
+ // alter the way pax-url-aether resolver works
//
- Dictionary<String, String> props = new Hashtable<>();
- if (offline) {
- props.put(ORG_OPS4J_PAX_URL_MVN_PID + "offline", "true");
- }
- if (localRepository != null) {
- props.put(Builder.ORG_OPS4J_PAX_URL_MVN_PID + ".localRepository", localRepository);
- }
- if (mavenRepositories != null) {
- props.put(Builder.ORG_OPS4J_PAX_URL_MVN_PID + ".repositories", mavenRepositories);
- }
- MavenResolver resolver = MavenResolvers.createMavenResolver(props, ORG_OPS4J_PAX_URL_MVN_PID);
- if (resolverWrapper != null) {
- resolver = resolverWrapper.apply(resolver);
- }
- executor = Executors.newScheduledThreadPool(8);
+ MavenResolver resolver = createMavenResolver();
manager = new CustomDownloadManager(resolver, executor, null, translatedUrls);
this.resolver = new ResolverImpl(new Slf4jResolverLog(LOGGER));
//
- // Unzip kars
+ // Unzip KARs
//
LOGGER.info("Unzipping kars");
- Map<String, RepositoryInfo> repositories = new LinkedHashMap<>(this.repositories);
+// Map<String, RepositoryInfo> repositories = new LinkedHashMap<>(this.repositories);
Downloader downloader = manager.createDownloader();
for (String kar : kars.keySet()) {
downloader.download(kar, null);
}
downloader.await();
+ // each KAR is extracted and all features XML repositories found there are added to the same
+ // stage as the KAR and with the same "add all" flag as the KAR itself
for (String karUri : kars.keySet()) {
+ LOGGER.info(" processing KAR: " + karUri);
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()) {
+ LOGGER.info(" found repository: " + repositoryUri);
repositories.put(repositoryUri.toString(), info);
}
}
@@ -500,12 +810,13 @@ public class Builder {
//
// Propagate feature installation from repositories
//
- LOGGER.info(" Loading repositories");
- Map<String, Stage> features = new LinkedHashMap<>(this.features);
+ LOGGER.info("Loading 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) {
+ LOGGER.info(" adding all features from repository: " + repo + " (stage: " + info.stage + ")");
for (Feature feature : karRepositories.get(repo).getFeature()) {
features.put(feature.getId(), info.stage);
}
@@ -515,72 +826,74 @@ public class Builder {
//
// 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<>(), Builder.class.getClassLoader());
- profilePath = fs.provider().getPath(profileURI);
- }
- allProfiles.putAll(Profiles.loadProfiles(profilePath));
- // Handle blacklisted profiles
- if (!blacklistedProfiles.isEmpty()) {
- if (blacklistPolicy == BlacklistPolicy.Discard) {
- // Override blacklisted profiles with empty ones
- for (String profile : blacklistedProfiles) {
- allProfiles.put(profile, ProfileBuilder.Factory.create(profile).getProfile());
- }
- } else {
- // Remove profiles completely
- allProfiles.keySet().removeAll(blacklistedProfiles);
- }
- }
+ LOGGER.info("Loading profiles from:");
+ profilesUris.forEach(p -> LOGGER.info(" " + p));
+ allProfiles = loadExternalProfiles(profilesUris);
+ if (allProfiles.size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ LOGGER.info(" Found profiles: " + allProfiles.keySet().stream().collect(Collectors.joining(", ")));
}
- // Generate profiles
+ //
+ // Generate profiles. If user has configured additional profiles, they'll be used as parents
+ // of the generated ones.
+ //
Profile startupProfile = generateProfile(Stage.Startup, profiles, repositories, features, bundles);
+ allProfiles.put(startupProfile.getId(), startupProfile);
+
+ // generated startup profile should be used (together with configured startup and boot profiles) as parent
+ // of the generated boot profile - similar visibility rule (boot stage requires startup stage) is applied
+ // for repositories and features
profiles.put(startupProfile.getId(), Stage.Boot);
Profile bootProfile = generateProfile(Stage.Boot, profiles, repositories, features, bundles);
+ allProfiles.put(bootProfile.getId(), bootProfile);
+
Profile installedProfile = generateProfile(Stage.Installed, profiles, repositories, features, bundles);
+ allProfiles.put(installedProfile.getId(), installedProfile);
//
- // Compute overall profile
+ // Compute "overlay" profile - a single profile with all parent profiles included (when there's the same
+ // file in both profiles, parent profile's version has lower priority)
//
ProfileBuilder builder = ProfileBuilder.Factory.create(UUID.randomUUID().toString())
.setParents(Arrays.asList(startupProfile.getId(), bootProfile.getId(), installedProfile.getId()));
config.forEach((k ,v) -> builder.addConfiguration(Profile.INTERNAL_PID, Profile.CONFIG_PREFIX + k, v));
system.forEach((k ,v) -> builder.addConfiguration(Profile.INTERNAL_PID, Profile.SYSTEM_PREFIX + k, v));
- Profile overallProfile = builder
- .getProfile();
+ // profile with all the parents configured
+ Profile overallProfile = builder.getProfile();
+
+ // profile with parents included and "flattened" using inheritance rules (child files overwrite parent
+ // files and child PIDs are merged with parent PIDs and same properties are taken from child profiles)
Profile overallOverlay = Profiles.getOverlay(overallProfile, allProfiles, environment);
+
+ // profile with property placeholders resolved or left unchanged (if there's no property value available,
+ // so property placeholders are preserved - like ${karaf.base})
Profile overallEffective = Profiles.getEffective(overallOverlay, false);
+ if (writeProfiles) {
+ Path profiles = etcDirectory.resolve("profiles");
+ LOGGER.info("Adding profiles to {}", homeDirectory.relativize(profiles));
+ allProfiles.forEach((id, profile) -> {
+ try {
+ Profiles.writeProfile(profiles, profile);
+ } catch (IOException e) {
+ LOGGER.warn("Problem writing profile {}: {}", id, e.getMessage());
+ }
+ });
+ }
+
manager = new CustomDownloadManager(resolver, executor, overallEffective, translatedUrls);
-// Hashtable<String, String> agentProps = new Hashtable<>(overallEffective.getConfiguration(ORG_OPS4J_PAX_URL_MVN_PID));
+// Hashtable<String, String> profileProps = new Hashtable<>(overallEffective.getConfiguration(ORG_OPS4J_PAX_URL_MVN_PID));
// final Map<String, String> properties = new HashMap<>();
// properties.put("karaf.default.repository", "system");
-// InterpolationHelper.performSubstitution(agentProps, properties::get, false, false, true);
+// InterpolationHelper.performSubstitution(profileProps, properties::get, false, false, true);
//
// Write config and system properties
//
+ LOGGER.info("Configuring etc/config.properties and etc/system.properties");
+
Path configPropertiesPath = etcDirectory.resolve("config.properties");
Properties configProperties = new Properties(configPropertiesPath.toFile());
configProperties.putAll(overallEffective.getConfig());
@@ -595,11 +908,13 @@ public class Builder {
// Download libraries
//
// TODO: handle karaf 2.x and 3.x libraries
- LOGGER.info("Downloading libraries");
downloader = manager.createDownloader();
+ LOGGER.info("Downloading libraries for generated profiles");
downloadLibraries(downloader, configProperties, overallEffective.getLibraries(), "");
+ LOGGER.info("Downloading additional libraries");
downloadLibraries(downloader, configProperties, libraries, "");
downloader.await();
+
// Reformat clauses
reformatClauses(configProperties, Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
reformatClauses(configProperties, Constants.FRAMEWORK_BOOTDELEGATION);
@@ -678,6 +993,65 @@ public class Builder {
installStage(installedProfile, allBootFeatures);
}
+ private MavenResolver createMavenResolver() {
+ Dictionary<String, String> props = new Hashtable<>();
+ if (offline) {
+ props.put(ORG_OPS4J_PAX_URL_MVN_PID + "offline", "true");
+ }
+ if (localRepository != null) {
+ props.put(ORG_OPS4J_PAX_URL_MVN_PID + ".localRepository", localRepository);
+ }
+ if (mavenRepositories != null) {
+ props.put(ORG_OPS4J_PAX_URL_MVN_PID + ".repositories", mavenRepositories);
+ }
+ MavenResolver resolver = MavenResolvers.createMavenResolver(props, ORG_OPS4J_PAX_URL_MVN_PID);
+ return resolverWrapper.apply(resolver);
+ }
+
+ /**
+ * Loads all profiles declared in profile URIs. These will be used in addition to generated
+ * <em>startup</em>, <em>boot</em> and <em>installed</em> profiles.
+ */
+ private Map<String, Profile> loadExternalProfiles(List<String> profilesUris) throws IOException, MultiException, InterruptedException {
+ Map<String, Profile> profiles = new LinkedHashMap<>();
+ 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 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<>(), Builder.class.getClassLoader());
+ profilePath = fs.provider().getPath(profileURI);
+ }
+ profiles.putAll(Profiles.loadProfiles(profilePath));
+ // Handle blacklisted profiles
+ if (!blacklistedProfiles.isEmpty()) {
+ if (blacklistPolicy == BlacklistPolicy.Discard) {
+ // Override blacklisted profiles with empty ones
+ for (String profile : blacklistedProfiles) {
+ profiles.put(profile, ProfileBuilder.Factory.create(profile).getProfile());
+ }
+ } else {
+ // Remove profiles completely
+ profiles.keySet().removeAll(blacklistedProfiles);
+ }
+ }
+ }
+ return profiles;
+ }
+
private void reformatClauses(Properties config, String key) {
String val = config.getProperty(key);
if (val != null && !val.isEmpty()) {
@@ -902,8 +1276,8 @@ public class Builder {
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"));
+ prereqs.put("wrap:", Collections.singletonList("wrap"));
+ prereqs.put("war:", Collections.singletonList("war"));
ArtifactInstaller installer = new ArtifactInstaller(systemDirectory, downloader, blacklistedBundles);
for (String location : locations) {
installer.installArtifact(location);
@@ -1104,6 +1478,12 @@ public class Builder {
return startupEffective;
}
+ /**
+ * Gets a list of objects (bundle URIs, profile IDs, feature IDs) configured for given stage
+ * @param stage
+ * @param data
+ * @return
+ */
private List<String> getStaged(Stage stage, Map<String, Stage> data) {
List<String> staged = new ArrayList<>();
for (String s : data.keySet()) {
@@ -1114,6 +1494,13 @@ public class Builder {
return staged;
}
+ /**
+ * Gets a list of features XML repository URIs configured for given stage. There's one special rule - startup
+ * repositories are added as boot repositories as well.
+ * @param stage
+ * @param data
+ * @return
+ */
private List<String> getStagedRepositories(Stage stage, Map<String, RepositoryInfo> data) {
List<String> staged = new ArrayList<>();
for (String s : data.keySet()) {
@@ -1132,8 +1519,7 @@ public class Builder {
final List<String> blacklist = new ArrayList<>();
blacklist.addAll(blacklistedBundles);
blacklist.addAll(blacklistedFeatures);
- final List<String> blacklistRepos = new ArrayList<>();
- blacklistRepos.addAll(blacklistedRepositories);
+ final List<String> blacklistRepos = new ArrayList<>(blacklistedRepositories);
final Blacklist blacklistOther = new Blacklist(blacklist);
final Blacklist repoBlacklist = new Blacklist(blacklistRepos);
for (String repository : repositories) {
@@ -1142,7 +1528,7 @@ public class Builder {
public void downloaded(final StreamProvider provider) throws Exception {
String url = provider.getUrl();
if (repoBlacklist.isRepositoryBlacklisted(url)) {
- LOGGER.info(" feature repository " + url + " is blacklisted");
+ LOGGER.info(" feature repository " + url + " is blacklisted");
return;
}
synchronized (loaded) {
@@ -1174,15 +1560,31 @@ public class Builder {
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))
+ /**
+ * Generate internal profile (for the purpose of custom assembly builder) for given <code>stage</code>.
+ * @param stage a {@link Stage} for which the profile is being generated
+ * @param parentProfiles all profiles for given stage will be used as parent profiles
+ * @param repositories repositories to use in generated profile
+ * @param features features to declare in generated profile
+ * @param bundles bundles to declare in generated profile
+ * @return
+ */
+ private Profile generateProfile(Stage stage, Map<String, Stage> parentProfiles, Map<String, RepositoryInfo> repositories, Map<String, Stage> features, Map<String, Stage> bundles) {
+ String name = "generated-" + stage.name().toLowerCase();
+ List<String> stagedParentProfiles = getStaged(stage, parentProfiles);
+
+ if (stagedParentProfiles.isEmpty()) {
+ LOGGER.info("Generating {} profile", name);
+ } else {
+ LOGGER.info("Generating {} profile with parents: {}", name, stagedParentProfiles.stream().collect(Collectors.joining(", ")));
+ }
+
+ return ProfileBuilder.Factory.create(name)
+ .setParents(stagedParentProfiles)
.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(
@@ -1254,7 +1656,7 @@ public class Builder {
private BundleRevision getSystemBundle() throws Exception {
Path configPropPath = etcDirectory.resolve("config.properties");
Properties configProps = PropertiesLoader.loadPropertiesOrFail(configPropPath.toFile());
- configProps.put("java.specification.version", javase);
+ configProps.put("java.specification.version", javase.version);
configProps.substitute();
Attributes attributes = new Attributes();
@@ -1262,14 +1664,18 @@ public class Builder {
attributes.putValue(Constants.BUNDLE_SYMBOLICNAME, "system.bundle");
attributes.putValue(Constants.BUNDLE_VERSION, "0.0.0");
- String exportPackages = configProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES);
+ String exportPackages = configProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES, "");
+ if ("".equals(exportPackages.trim())) {
+ throw new IllegalArgumentException("\"org.osgi.framework.system.packages\" property should specify system bundle" +
+ " packages. It can't be empty, please check etc/config.properties of the assembly.");
+ }
if (configProps.containsKey(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA)) {
exportPackages += "," + configProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
}
exportPackages = exportPackages.replaceAll(",\\s*,", ",");
attributes.putValue(Constants.EXPORT_PACKAGE, exportPackages);
- String systemCaps = configProps.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES);
+ String systemCaps = configProps.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES, "");
attributes.putValue(Constants.PROVIDE_CAPABILITY, systemCaps);
final Hashtable<String, String> headers = new Hashtable<>();
diff --git a/profile/src/main/java/org/apache/karaf/profile/command/ProfileDisplay.java b/profile/src/main/java/org/apache/karaf/profile/command/ProfileDisplay.java
index e6d4619..d92bf8a 100644
--- a/profile/src/main/java/org/apache/karaf/profile/command/ProfileDisplay.java
+++ b/profile/src/main/java/org/apache/karaf/profile/command/ProfileDisplay.java
@@ -54,7 +54,7 @@ public class ProfileDisplay implements Action {
private ProfileService profileService;
@Override
- public Object execute() throws Exception {
+ public Object execute() {
displayProfile(profileService.getRequiredProfile(profileId));
return null;
}
@@ -87,11 +87,11 @@ public class ProfileDisplay implements Action {
Map<String, Map<String, Object>> configuration = new HashMap<>(profile.getConfigurations());
Map<String, byte[]> resources = profile.getFileConfigurations();
- Map<String,Object> agentConfiguration = profile.getConfiguration(Profile.INTERNAL_PID);
- List<String> agentProperties = new ArrayList<>();
+ Map<String,Object> profileConfiguration = profile.getConfiguration(Profile.INTERNAL_PID);
+ List<String> profileProperties = new ArrayList<>();
List<String> systemProperties = new ArrayList<>();
List<String> configProperties = new ArrayList<>();
- for (Map.Entry<String, Object> entry : agentConfiguration.entrySet()) {
+ for (Map.Entry<String, Object> entry : profileConfiguration.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (value instanceof String && ((String) value).contains(",")) {
@@ -107,7 +107,7 @@ public class ProfileDisplay implements Action {
else if (!key.startsWith("feature.") && !key.startsWith("repository") &&
!key.startsWith("bundle.") && !key.startsWith("fab.") &&
!key.startsWith("override.") && !key.startsWith("attribute.")) {
- agentProperties.add(" " + key + " = " + value);
+ profileProperties.add(" " + key + " = " + value);
}
}
@@ -131,8 +131,8 @@ public class ProfileDisplay implements Action {
printConfigList("Overrides : ", output, profile.getOverrides());
}
- if (agentProperties.size() > 0) {
- printConfigList("Agent Properties : ", output, agentProperties);
+ if (profileProperties.size() > 0) {
+ printConfigList("Profile Properties : ", output, profileProperties);
}
if (systemProperties.size() > 0) {
diff --git a/profile/src/main/java/org/apache/karaf/profile/command/ProfileEdit.java b/profile/src/main/java/org/apache/karaf/profile/command/ProfileEdit.java
index 596731a..6e52086 100644
--- a/profile/src/main/java/org/apache/karaf/profile/command/ProfileEdit.java
+++ b/profile/src/main/java/org/apache/karaf/profile/command/ProfileEdit.java
@@ -26,6 +26,7 @@ import java.util.Map;
import org.apache.karaf.profile.Profile;
import org.apache.karaf.profile.ProfileBuilder;
+import org.apache.karaf.profile.ProfileConstants;
import org.apache.karaf.profile.ProfileService;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
@@ -48,15 +49,6 @@ public class ProfileEdit implements Action {
private static final Logger LOGGER = LoggerFactory.getLogger(ProfileEdit.class);
- static final String FEATURE_PREFIX = "feature.";
- static final String REPOSITORY_PREFIX = "repository.";
- static final String BUNDLE_PREFIX = "bundle.";
- static final String OVERRIDE_PREFIX = "override.";
- static final String CONFIG_PREFIX = "config.";
- static final String SYSTEM_PREFIX = "system.";
- static final String LIB_PREFIX = "lib.";
- static final String ENDORSED_PREFIX = "endorsed.";
- static final String EXT_PREFIX = "ext.";
static final String DELIMITER = ",";
static final String PID_KEY_SEPARATOR = "/";
@@ -156,15 +148,15 @@ public class ProfileEdit implements Action {
}
if (libs != null && libs.length > 0) {
editInLine = true;
- handleLibraries(builder, libs, profile, "lib", LIB_PREFIX);
+ handleLibraries(builder, libs, profile, "lib", ProfileConstants.LIB_PREFIX);
}
if (endorsed != null && endorsed.length > 0) {
editInLine = true;
- handleLibraries(builder, endorsed, profile, "endorsed lib", ENDORSED_PREFIX);
+ handleLibraries(builder, endorsed, profile, "endorsed lib", ProfileConstants.ENDORSED_PREFIX);
}
if (extension != null && extension.length > 0) {
editInLine = true;
- handleLibraries(builder, extension, profile, "extension lib", EXT_PREFIX);
+ handleLibraries(builder, extension, profile, "extension lib", ProfileConstants.EXT_PREFIX);
}
if (bundles != null && bundles.length > 0) {
editInLine = true;
@@ -215,7 +207,7 @@ public class ProfileEdit implements Action {
} else {
System.out.println("Adding feature:" + feature + " to profile:" + profile.getId());
}
- updateConfig(conf, FEATURE_PREFIX + feature.replace('/', '_'), feature, set, delete);
+ updateConfig(conf, ProfileConstants.FEATURE_PREFIX + feature.replace('/', '_'), feature, set, delete);
builder.addConfiguration(Profile.INTERNAL_PID, conf);
}
}
@@ -231,7 +223,7 @@ public class ProfileEdit implements Action {
} else if (delete) {
System.out.println("Deleting feature repository:" + repositoryURI + " from profile:" + profile.getId());
}
- updateConfig(conf, REPOSITORY_PREFIX + repositoryURI.replace('/', '_'), repositoryURI, set, delete);
+ updateConfig(conf, ProfileConstants.REPOSITORY_PREFIX + repositoryURI.replace('/', '_'), repositoryURI, set, delete);
}
builder.addConfiguration(Profile.INTERNAL_PID, conf);
}
@@ -269,7 +261,7 @@ public class ProfileEdit implements Action {
} else if (delete) {
System.out.println("Deleting bundle:" + bundle + " from profile:" + profile.getId());
}
- updateConfig(conf, BUNDLE_PREFIX + bundle.replace('/', '_'), bundle, set, delete);
+ updateConfig(conf, ProfileConstants.BUNDLE_PREFIX + bundle.replace('/', '_'), bundle, set, delete);
}
builder.addConfiguration(Profile.INTERNAL_PID, conf);
}
@@ -287,7 +279,7 @@ public class ProfileEdit implements Action {
} else if (delete) {
System.out.println("Deleting override:" + override + " from profile:" + profile.getId());
}
- updateConfig(conf, OVERRIDE_PREFIX + override.replace('/', '_'), override, set, delete);
+ updateConfig(conf, ProfileConstants.OVERRIDE_PREFIX + override.replace('/', '_'), override, set, delete);
}
builder.addConfiguration(Profile.INTERNAL_PID, conf);
}
@@ -375,7 +367,7 @@ public class ProfileEdit implements Action {
} else {
System.out.println("Removing value:" + value + " key:" + key + " from system properties and profile:" + profile.getId());
}
- updatedDelimitedList(conf, SYSTEM_PREFIX + key, value, delimiter, set, delete, append, remove);
+ updatedDelimitedList(conf, ProfileConstants.SYSTEM_PREFIX + key, value, delimiter, set, delete, append, remove);
}
}
builder.addConfiguration(Profile.INTERNAL_PID, conf);
@@ -400,7 +392,7 @@ public class ProfileEdit implements Action {
} else if (set) {
System.out.println("Setting value:" + value + " key:" + key + " from config properties and profile:" + profile.getId());
}
- updatedDelimitedList(conf, CONFIG_PREFIX + key, value, delimiter, set, delete, append, remove);
+ updatedDelimitedList(conf, ProfileConstants.CONFIG_PREFIX + key, value, delimiter, set, delete, append, remove);
}
}
builder.addConfiguration(Profile.INTERNAL_PID, conf);
diff --git a/profile/src/main/java/org/apache/karaf/profile/impl/ProfileBuilderImpl.java b/profile/src/main/java/org/apache/karaf/profile/impl/ProfileBuilderImpl.java
index 532caf6..10331bd 100644
--- a/profile/src/main/java/org/apache/karaf/profile/impl/ProfileBuilderImpl.java
+++ b/profile/src/main/java/org/apache/karaf/profile/impl/ProfileBuilderImpl.java
@@ -38,8 +38,6 @@ import static org.apache.karaf.profile.impl.ProfileImpl.ConfigListType;
*/
public final class ProfileBuilderImpl implements ProfileBuilder {
- private static final String PARENTS_ATTRIBUTE_KEY = Profile.ATTRIBUTE_PREFIX + Profile.PARENTS;
-
private String profileId;
private Map<String, byte[]> fileMapping = new HashMap<>();
private boolean isOverlay;
@@ -60,7 +58,7 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
@Override
public List<String> getParents() {
Map<String, Object> config = getConfigurationInternal(Profile.INTERNAL_PID);
- String pspec = (String) config.get(PARENTS_ATTRIBUTE_KEY);
+ String pspec = (String) config.get(Profile.PARENTS);
String[] parentIds = pspec != null ? pspec.split(" ") : new String[0];
return Arrays.asList(parentIds);
}
@@ -102,9 +100,9 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
private void updateParentsAttribute(Collection<String> parentIds) {
Map<String, Object> config = getConfigurationInternal(Profile.INTERNAL_PID);
- config.remove(PARENTS_ATTRIBUTE_KEY);
+ config.remove(Profile.PARENTS);
if (parentIds.size() > 0) {
- config.put(PARENTS_ATTRIBUTE_KEY, parentsAttributeValue(parentIds));
+ config.put(Profile.PARENTS, parentsAttributeValue(parentIds));
}
addConfiguration(Profile.INTERNAL_PID, config);
}
@@ -195,49 +193,49 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
@Override
public ProfileBuilder setBundles(List<String> values) {
- addAgentConfiguration(ConfigListType.BUNDLES, values);
+ addProfileConfiguration(ConfigListType.BUNDLES, values);
return this;
}
@Override
public ProfileBuilder addBundle(String value) {
- addAgentConfiguration(ConfigListType.BUNDLES, value);
+ addProfileConfiguration(ConfigListType.BUNDLES, value);
return this;
}
@Override
public ProfileBuilder setFeatures(List<String> values) {
- addAgentConfiguration(ConfigListType.FEATURES, values);
+ addProfileConfiguration(ConfigListType.FEATURES, values);
return this;
}
@Override
public ProfileBuilder addFeature(String value) {
- addAgentConfiguration(ConfigListType.FEATURES, value);
+ addProfileConfiguration(ConfigListType.FEATURES, value);
return this;
}
@Override
public ProfileBuilder setRepositories(List<String> values) {
- addAgentConfiguration(ConfigListType.REPOSITORIES, values);
+ addProfileConfiguration(ConfigListType.REPOSITORIES, values);
return this;
}
@Override
public ProfileBuilder addRepository(String value) {
- addAgentConfiguration(ConfigListType.REPOSITORIES, value);
+ addProfileConfiguration(ConfigListType.REPOSITORIES, value);
return this;
}
@Override
public ProfileBuilder setOverrides(List<String> values) {
- addAgentConfiguration(ConfigListType.OVERRIDES, values);
+ addProfileConfiguration(ConfigListType.OVERRIDES, values);
return this;
}
@Override
public ProfileBuilder setOptionals(List<String> values) {
- addAgentConfiguration(ConfigListType.OPTIONALS, values);
+ addProfileConfiguration(ConfigListType.OPTIONALS, values);
return this;
}
@@ -267,7 +265,7 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
return null;
}
- private void addAgentConfiguration(ConfigListType type, List<String> values) {
+ private void addProfileConfiguration(ConfigListType type, List<String> values) {
String prefix = type + ".";
Map<String, Object> config = getConfigurationInternal(Profile.INTERNAL_PID);
for (String key : new ArrayList<>(config.keySet())) {
@@ -281,7 +279,7 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
addConfiguration(Profile.INTERNAL_PID, config);
}
- private void addAgentConfiguration(ConfigListType type, String value) {
+ private void addProfileConfiguration(ConfigListType type, String value) {
String prefix = type + ".";
Map<String, Object> config = getConfigurationInternal(Profile.INTERNAL_PID);
config.put(prefix + value, value);
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 bcd1b03..c8707ea 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
@@ -37,7 +37,7 @@ import static org.apache.karaf.profile.impl.Utils.assertTrue;
*/
final class ProfileImpl implements Profile {
- private static final Pattern ALLOWED_PROFILE_NAMES_PATTERN = Pattern.compile("^[A-Za-z0-9]+[\\.A-Za-z0-9_-]*$");
+ 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;
@@ -53,7 +53,7 @@ final class ProfileImpl implements Profile {
assertNotNull(profileId, "profileId is null");
assertNotNull(parents, "parents is null");
assertNotNull(fileConfigs, "fileConfigs is null");
- assertTrue(ALLOWED_PROFILE_NAMES_PATTERN.matcher(profileId).matches(), "Profile id '" + profileId + "' is invalid. Profile id must be: lower-case letters, numbers, and . _ or - characters");
+ assertTrue(ALLOWED_PROFILE_NAMES_PATTERN.matcher(profileId).matches(), "Profile id '" + profileId + "' is invalid. Profile id must be: upper-case or lower-case letters, numbers, and . _ or - characters");
this.profileId = profileId;
this.isOverlay = isOverlay;
@@ -72,14 +72,10 @@ final class ProfileImpl implements Profile {
}
}
- // Attributes are agent configuration with prefix 'attribute.'
+ // Attributes are profile configuration properties with prefix "attribute." contained in "profile" PID
attributes = getPrefixedMap(ATTRIBUTE_PREFIX);
}
- public String getId() {
- return profileId;
- }
-
@Override
public Map<String, String> getAttributes() {
return Collections.unmodifiableMap(attributes);
@@ -95,12 +91,17 @@ final class ProfileImpl implements Profile {
return getPrefixedMap(SYSTEM_PREFIX);
}
+ @Override
+ public String getId() {
+ return profileId;
+ }
+
private Map<String, String> getPrefixedMap(String prefix) {
Map<String, String> map = new HashMap<>();
- Map<String, Object> agentConfig = configurations.get(Profile.INTERNAL_PID);
- if (agentConfig != null) {
+ Map<String, Object> profileConfig = configurations.get(Profile.INTERNAL_PID);
+ if (profileConfig != null) {
int prefixLength = prefix.length();
- for (Entry<String, Object> entry : agentConfig.entrySet()) {
+ for (Entry<String, Object> entry : profileConfig.entrySet()) {
String key = entry.getKey();
if (key.startsWith(prefix)) {
map.put(key.substring(prefixLength), entry.getValue().toString());
@@ -111,8 +112,8 @@ final class ProfileImpl implements Profile {
}
@Override
- public List<String> getLibraries() {
- return getContainerConfigList(ConfigListType.LIBRARIES);
+ public List<String> getParentIds() {
+ return Collections.unmodifiableList(parents);
}
@Override
@@ -131,6 +132,26 @@ final class ProfileImpl implements Profile {
}
@Override
+ public List<String> getLibraries() {
+ return getContainerConfigList(ConfigListType.LIBRARIES);
+ }
+
+ @Override
+ public List<String> getBootLibraries() {
+ return getContainerConfigList(ConfigListType.BOOT_LIBRARIES);
+ }
+
+ @Override
+ public List<String> getEndorsedLibraries() {
+ return getContainerConfigList(ConfigListType.ENDORSED_LIBRARIES);
+ }
+
+ @Override
+ public List<String> getExtLibraries() {
+ return getContainerConfigList(ConfigListType.EXT_LIBRARIES);
+ }
+
+ @Override
public List<String> getOverrides() {
return getContainerConfigList(ConfigListType.OVERRIDES);
}
@@ -141,26 +162,27 @@ final class ProfileImpl implements Profile {
}
@Override
- public List<String> getParentIds() {
- return Collections.unmodifiableList(parents);
+ public boolean isOverlay() {
+ return isOverlay;
}
@Override
public boolean isAbstract() {
- return parseBoolean(getAttributes().get(ABSTRACT));
+ return parseBoolean(attributes.get(ABSTRACT));
}
@Override
public boolean isHidden() {
- return parseBoolean(getAttributes().get(HIDDEN));
+ return parseBoolean(attributes.get(HIDDEN));
}
private Boolean parseBoolean(Object obj) {
- return obj instanceof Boolean ? (Boolean) obj : Boolean.parseBoolean(obj.toString());
+ return obj instanceof Boolean ? (Boolean) obj : obj != null && Boolean.parseBoolean(obj.toString());
}
- public boolean isOverlay() {
- return isOverlay;
+ @Override
+ public Set<String> getConfigurationFileNames() {
+ return Collections.unmodifiableSet(fileConfigurations.keySet());
}
@Override
@@ -169,15 +191,11 @@ final class ProfileImpl implements Profile {
}
@Override
- public Set<String> getConfigurationFileNames() {
- return Collections.unmodifiableSet(fileConfigurations.keySet());
- }
-
- @Override
public byte[] getFileConfiguration(String fileName) {
return fileConfigurations.get(fileName);
}
+ @Override
public Map<String, Map<String, Object>> getConfigurations() {
return Collections.unmodifiableMap(configurations);
}
@@ -230,13 +248,16 @@ final class ProfileImpl implements Profile {
@Override
public String toString() {
- return "Profile[id=" + profileId + ",attrs=" + getAttributes() + "]";
+ return "Profile[id=" + profileId + ", attrs=" + getAttributes() + "]";
}
enum ConfigListType {
BUNDLES("bundle"),
FEATURES("feature"),
LIBRARIES("library"),
+ BOOT_LIBRARIES("boot"),
+ ENDORSED_LIBRARIES("endorsed"),
+ EXT_LIBRARIES("ext"),
OPTIONALS("optional"),
OVERRIDES("override"),
REPOSITORIES("repository");
@@ -251,4 +272,5 @@ final class ProfileImpl implements Profile {
return value;
}
}
+
}
diff --git a/profile/src/main/java/org/apache/karaf/profile/impl/Profiles.java b/profile/src/main/java/org/apache/karaf/profile/impl/Profiles.java
index 9fbfdcc..c3843d6 100644
--- a/profile/src/main/java/org/apache/karaf/profile/impl/Profiles.java
+++ b/profile/src/main/java/org/apache/karaf/profile/impl/Profiles.java
@@ -38,16 +38,29 @@ import org.apache.karaf.profile.ProfileBuilder;
import static org.apache.karaf.profile.impl.Utils.assertNotNull;
+/**
+ * Static utilities to work with {@link Profile profiles}.
+ */
public final class Profiles {
public static final String PROFILE_FOLDER_SUFFIX = ".profile";
+ /**
+ * <p>Loads profiles from given directory path. A profile is represented as directory with <code>.profile</code>
+ * extension. Subdirectories constitute part of {@linl Profile#getId} - directory separators are changed to
+ * <code>-</code>.</p>
+ * <p>For example, profile contained in directory <code>mq/broker/standalone.profile</code> will have
+ * id = <code>mq-broker-standalone</code>.</p>
+ * @param root
+ * @return
+ * @throws IOException
+ */
public static Map<String, Profile> loadProfiles(final Path root) throws IOException {
final Map<String, Profile> profiles = new HashMap<>();
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
ProfileBuilder builder;
@Override
- public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
Path fileName = dir.getFileName();
if (fileName != null && (fileName.toString().endsWith(PROFILE_FOLDER_SUFFIX)
|| fileName.toString().endsWith(PROFILE_FOLDER_SUFFIX + "/"))) {
@@ -87,6 +100,12 @@ public final class Profiles {
return profiles;
}
+ /**
+ * Deletes profile by given {@link Profile#getId()} from <code>root</code> path.
+ * @param root
+ * @param id
+ * @throws IOException
+ */
public static void deleteProfile(Path root, String id) throws IOException {
Path path = root.resolve(id.replaceAll("-", root.getFileSystem().getSeparator()) + PROFILE_FOLDER_SUFFIX);
if (Files.isDirectory(path)) {
@@ -105,6 +124,13 @@ public final class Profiles {
}
}
+ /**
+ * Writes given {@link Profile} under a path specified as <code>root</code>. Directory name to store a profile is
+ * derived from {@link Profile#getId()}
+ * @param root
+ * @param profile
+ * @throws IOException
+ */
public static void writeProfile(Path root, Profile profile) throws IOException {
Path path = root.resolve(profile.getId().replaceAll("-", root.getFileSystem().getSeparator()) + PROFILE_FOLDER_SUFFIX);
Files.createDirectories(path);
@@ -113,10 +139,28 @@ public final class Profiles {
}
}
+ /**
+ * <p>Gets an <em>overlay</em> profile for given <code>profile</code>, where passed in map of additional profiles
+ * is searched for possible parent profiles of given <code>profile</code>.</p>
+ * @param profile
+ * @param profiles
+ * @return
+ */
public static Profile getOverlay(Profile profile, Map<String, Profile> profiles) {
return getOverlay(profile, profiles, null);
}
+ /**
+ * <p>Gets an <em>overlay</em> profile for given <code>profile</code>, where passed in map of additional profiles
+ * is searched for possible parent profiles of given <code>profile</code>.</p>
+ * <p><code>environment</code> may be used to select different <em>variants</em> of profile configuration files.
+ * For example, if <code>environment</code> is specified, configuration for <code>my.pid</code> PID will be read
+ * from <code>my.pid.cfg#<environment></code>.</p>
+ * @param profile
+ * @param profiles
+ * @param environment
+ * @return
+ */
public static Profile getOverlay(Profile profile, Map<String, Profile> profiles, String environment) {
assertNotNull(profile, "profile is null");
assertNotNull(profile, "profiles is null");
@@ -130,22 +174,49 @@ public final class Profiles {
}
}
+ /**
+ * Gets an <code>effective</code> profile with single property placeholder resolver for <code>${profile:xxx}</code>
+ * placeholders and with <code>finalSubstitution</code> set to <code>true</code>.
+ * @param profile
+ * @return
+ */
public static Profile getEffective(final Profile profile) {
- return getEffective(profile,
- true);
+ return getEffective(profile, true);
}
+ /**
+ * Gets an <code>effective</code> profile with single property placeholder resolver for <code>${profile:xxx}</code>
+ * placeholders.
+ * @param profile
+ * @param finalSubstitution
+ * @return
+ */
public static Profile getEffective(final Profile profile, boolean finalSubstitution) {
return getEffective(profile,
Collections.singleton(new PlaceholderResolvers.ProfilePlaceholderResolver()),
finalSubstitution);
}
+ /**
+ * Gets an <code>effective</code> profile with <code>finalSubstitution</code> set to <code>true</code>.
+ * @param profile
+ * @param resolvers
+ * @return
+ */
public static Profile getEffective(final Profile profile,
final Collection<PlaceholderResolver> resolvers) {
return getEffective(profile, resolvers, true);
}
+ /**
+ * <p>Gets an <em>effective</em> profile for given <code>profile</code>. Effective profile has all property
+ * placeholders resolved. When <code>finalSubstitution</code> is <code>true</code>, placeholders that can't
+ * be resolved are replaced with empty strings. When it's <code>false</code>, placeholders are left unchanged.</p>
+ * @param profile
+ * @param resolvers
+ * @param finalSubstitution
+ * @return
+ */
public static Profile getEffective(final Profile profile,
final Collection<PlaceholderResolver> resolvers,
boolean finalSubstitution) {
@@ -208,6 +279,28 @@ public final class Profiles {
return builder.getProfile();
}
+ /**
+ * <p>Helper internal class to configure {@link ProfileBuilder} used to create an <em>overlay</em> profile.</p>
+ * <p>There are strict rules built on a concept of profiles being <em>containers of file configurations</em>.
+ * Each profile may contain files with the same name. Profiles may be set in multi-parent - child relationship.
+ * Such graph of profiles is searched in depth-first fashion, while child (being a root of the graph) has
+ * highest priority.</p>
+ * <p>Files from higher-priority profile override files from parent profiles. Special case are PID files (with
+ * {@link Profile#PROPERTIES_SUFFIX} extension). These files are not simply taken from child profiles. Child
+ * profiles may have own version of given PID configuration file, but these files are overwritten at property
+ * level.</p>
+ * <p>For example, if parent profile specifies:<pre>
+ * property1 = v1
+ * property2 = v2
+ * </pre> and child profile specifies:<pre>
+ * property1 = v1a
+ * property3 = v3a
+ * </pre>an <em>overlay</em> profile for child profile uses:<pre>
+ * property1 = v1a
+ * property2 = v2
+ * property3 = v3a
+ * </pre></p>
+ */
static private class OverlayOptionsProvider {
private final Map<String, Profile> profiles;
diff --git a/profile/src/test/java/org/apache/karaf/profile/impl/ProfilesTest.java b/profile/src/test/java/org/apache/karaf/profile/impl/ProfilesTest.java
index 056332c..5a1e886 100644
--- a/profile/src/test/java/org/apache/karaf/profile/impl/ProfilesTest.java
+++ b/profile/src/test/java/org/apache/karaf/profile/impl/ProfilesTest.java
@@ -16,17 +16,65 @@
*/
package org.apache.karaf.profile.impl;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.UUID;
import org.apache.karaf.profile.Profile;
import org.apache.karaf.profile.ProfileBuilder;
import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
public class ProfilesTest {
+ public static Logger LOG = LoggerFactory.getLogger(ProfilesTest.class);
+
+ @Test
+ public void testProfilesApi() throws IOException {
+ ProfileBuilder builder = ProfileBuilder.Factory.create("my-simple-profile");
+ builder.addParents(Collections.emptyList());
+ builder.addAttribute("attr1", "val1");
+ builder.addBundle("mvn:commons-everything/commons-everything/42");
+ builder.addConfiguration("my.pid", "a1", "v1${profile:my.pid2/a2}");
+ builder.addConfiguration("my.pid", "a2", "v1${profile:my.pid2/a3}");
+ builder.addFeature("feature1");
+ builder.addFileConfiguration("my.pid2.txt", "hello!".getBytes("UTF-8"));
+ builder.addFileConfiguration("my.pid2.cfg", "a2=v2".getBytes("UTF-8"));
+ builder.addRepository("mvn:my/repository/1/xml/features");
+ builder.setOptionals(Arrays.asList("mvn:g/a/1", "mvn:g/a/2"));
+ builder.setOverrides(Arrays.asList("mvn:g/a/4", "mvn:g/a/3"));
+ Profile profile = builder.getProfile();
+ LOG.info("Profile: {}", profile.toString());
+ LOG.info("Config: {}", profile.getConfig());
+ LOG.info("Libraries: {}", profile.getLibraries());
+ LOG.info("System: {}", profile.getSystem());
+ LOG.info("Configurations: {}", profile.getConfigurations());
+ LOG.info("ConfigurationFileNames: {}", profile.getConfigurationFileNames());
+ LOG.info("FileConfigurations: {}", profile.getFileConfigurations().keySet());
+
+ Profile effectiveProfile1 = Profiles.getEffective(profile, false);
+ Profile effectiveProfile2 = Profiles.getEffective(profile, true);
+ Map<String, Profile> profiles = new HashMap<>();
+ profiles.put("x", profile);
+ Profile overlayProfile = Profiles.getOverlay(profile, profiles);
+ Profiles.writeProfile(Paths.get("target/p-" + UUID.randomUUID().toString()), profile);
+ Profiles.writeProfile(Paths.get("target/ep1-" + UUID.randomUUID().toString()), effectiveProfile1);
+ Profiles.writeProfile(Paths.get("target/ep2-" + UUID.randomUUID().toString()), effectiveProfile2);
+ Profiles.writeProfile(Paths.get("target/op-" + UUID.randomUUID().toString()), overlayProfile);
+ }
+
@Test
public void testProfilePlaceholderResolver() {
Profile profile = ProfileBuilder.Factory.create("test")
@@ -87,4 +135,98 @@ public class ProfilesTest {
String outPid1 = new String(overlay.getFileConfiguration("pid1.cfg"));
assertEquals(String.format("%1$s%n%2$s%n","# My comment","foo = bar2"), outPid1);
}
+
+ @Test
+ public void overlayProfiles() {
+ Profile p1 = ProfileBuilder.Factory.create("p1")
+ .addAttribute("p1a1", "p1v1")
+ .addConfiguration("p1p1", "p1p1p1", "p1p1v1")
+ .addConfiguration("pp1", "pp1p1", "p1p1v1")
+ .getProfile();
+ Profile p2 = ProfileBuilder.Factory.create("p2")
+ .addAttribute("p2a1", "p2v1")
+ .addConfiguration("p2p1", "p2p1p1", "p2p1v1")
+ .addConfiguration("pp1", "pp1p1", "p2p1v1")
+ .getProfile();
+
+ Profile c1 = ProfileBuilder.Factory.create("c2")
+ .addParents(Arrays.asList("p1", "p2"))
+ .getProfile();
+
+ assertThat(c1.getAttributes().get("p1a1"), nullValue());
+ assertThat(c1.getAttributes().get("p2a1"), nullValue());
+ assertThat(c1.getConfigurations().size(), equalTo(1));
+ assertTrue(c1.getConfigurations().containsKey("profile"));
+
+ Map<String, Profile> parents = new LinkedHashMap<>();
+ parents.put("p1", p1);
+ parents.put("p2", p2);
+ Profile oc1 = Profiles.getOverlay(c1, parents);
+ assertThat(oc1.getAttributes().get("p1a1"), equalTo("p1v1"));
+ assertThat(oc1.getAttributes().get("p2a1"), equalTo("p2v1"));
+ assertThat(oc1.getConfigurations().size(), equalTo(4));
+ assertTrue(oc1.getConfigurations().containsKey("p1p1"));
+ assertTrue(oc1.getConfigurations().containsKey("p2p1"));
+ assertTrue(oc1.getConfigurations().containsKey("pp1"));
+ assertTrue(oc1.getConfigurations().containsKey("profile"));
+ }
+
+ @Test
+ public void inheritanceOrder() {
+ Profile gp1 = ProfileBuilder.Factory.create("gp1")
+ .addAttribute("a", "1")
+ .addFileConfiguration("f", new byte[] { 0x01 })
+ .addAttribute("b", "1")
+ .addAttribute("c", "1")
+ .addConfiguration("p", "p", "1")
+ .addConfiguration("p", "px", "1")
+ .getProfile();
+ Profile gp2 = ProfileBuilder.Factory.create("gp2")
+ .addAttribute("a", "2")
+ .addAttribute("c", "2")
+ .addFileConfiguration("f", new byte[] { 0x02 })
+ .addConfiguration("p", "p", "2")
+ .getProfile();
+ Profile p1 = ProfileBuilder.Factory.create("p1")
+ .addParents(Arrays.asList("gp1", "gp2"))
+ .addAttribute("a", "3")
+ .addFileConfiguration("f", new byte[] { 0x03 })
+ .addConfiguration("p", "p", "3")
+ .getProfile();
+ Profile p2 = ProfileBuilder.Factory.create("p2")
+ .addAttribute("a", "4")
+ .addAttribute("b", "4")
+ .addFileConfiguration("f", new byte[] { 0x04 })
+ .addConfiguration("p", "p", "4")
+ .getProfile();
+ Profile c = ProfileBuilder.Factory.create("p2")
+ .addParents(Arrays.asList("p1", "p2"))
+ .addAttribute("a", "5")
+ .addFileConfiguration("f", new byte[] { 0x05 })
+ .addConfiguration("p", "p", "5")
+ .getProfile();
+
+ Map<String, Profile> parents = new LinkedHashMap<>();
+ parents.put("gp1", gp1);
+ parents.put("gp2", gp2);
+ parents.put("p1", p1);
+ parents.put("p2", p2);
+
+ assertThat(Profiles.getOverlay(c, parents).getAttributes().get("a"), equalTo("5"));
+ assertThat(Profiles.getOverlay(c, parents).getAttributes().get("b"), equalTo("4"));
+ assertThat(Profiles.getOverlay(c, parents).getAttributes().get("c"), equalTo("2"));
+ assertThat(Profiles.getOverlay(c, parents).getConfiguration("p").get("p"), equalTo("5"));
+ assertThat(Profiles.getOverlay(c, parents).getConfiguration("p").get("px"), equalTo("1"));
+ assertThat(Profiles.getOverlay(c, parents).getFileConfiguration("f"), equalTo(new byte[] { 0x05 }));
+ }
+
+ @Test
+ public void overrides() {
+ Profile p = ProfileBuilder.Factory.create("p")
+ .setOverrides(Arrays.asList("a", "b"))
+ .getProfile();
+
+ assertThat(p.getConfiguration("profile").size(), equalTo(2));
+ }
+
}
diff --git a/tooling/karaf-maven-plugin/pom.xml b/tooling/karaf-maven-plugin/pom.xml
index c957c63..67feb65 100644
--- a/tooling/karaf-maven-plugin/pom.xml
+++ b/tooling/karaf-maven-plugin/pom.xml
@@ -226,10 +226,6 @@
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
- <resource>
- <directory>${project.basedir}/src/main/filtered-resources</directory>
- <filtering>true</filtering>
- </resource>
</resources>
<testResources>
<testResource>
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java
index 6671614..f8d78b9 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/AssemblyMojo.java
@@ -20,6 +20,7 @@ package org.apache.karaf.tooling;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.File;
import java.io.FileInputStream;
@@ -30,10 +31,11 @@ import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Properties;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
@@ -46,6 +48,7 @@ import org.apache.karaf.tooling.utils.MojoSupport;
import org.apache.karaf.tooling.utils.ReactorMavenResolver;
import org.apache.karaf.tools.utils.model.KarafPropertyEdits;
import org.apache.karaf.tools.utils.model.io.stax.KarafPropertyInstructionsModelStaxReader;
+import org.apache.karaf.util.Version;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@@ -54,17 +57,21 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
import org.eclipse.aether.repository.WorkspaceReader;
import org.osgi.framework.Constants;
+import org.osgi.framework.launch.FrameworkFactory;
/**
- * Creates a customized Karaf distribution by installing features and setting up
- * configuration files. The plugin gets features from feature.xml files and KAR
+ * <p>Creates a customized Karaf distribution by installing features and setting up
+ * configuration files.</p>
+ *
+ * <p>The plugin gets features from feature.xml files and KAR
* archives declared as dependencies or as files configured with the
- * featureRespositories parameter. It picks up other files, such as config files,
+ * [startup|boot|installed]Respositories parameters. It picks up other files, such as config files,
* from ${project.build.directory}/classes. Thus, a file in src/main/resources/etc
* will be copied by the resource plugin to ${project.build.directory}/classes/etc,
- * and then added to the assembly by this goal.
+ * and then added to the assembly by this goal.</p>
*/
@Mojo(name = "assembly", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
public class AssemblyMojo extends MojoSupport {
@@ -81,130 +88,260 @@ public class AssemblyMojo extends MojoSupport {
@Parameter(defaultValue = "${project.build.directory}/assembly")
protected File workDirectory;
- /**
- * Features configuration file (etc/org.apache.karaf.features.cfg).
+ /*
+ * There are three builder stages related to maven dependency scopes:
+ * - Stage.Startup : scope=compile
+ * - Stage.Boot : scope=runtime
+ * - Stage.Installed : scope=provided
+ * There's special category not related to stage - Blacklisted
+ *
+ * There are five kinds of artifacts/dependencies that may go into any of the above stages/categories/scopes:
+ * - kars: maven artifacts with "kar" type
+ * - repositories: maven artifacts with "features" classifier
+ * - features: Karaf feature names (name[/version])
+ * - bundles: maven artifacts with "jar" or "bundle" type
+ * - profiles: directories with Karaf 4 profiles
+ * (Not all artifacts/dependencies may be connected with every stage/category/scope.)
+ *
+ * Blacklisting:
+ * - kars: there are no blacklisted kars
+ * - repositories: won't be processed at all (also affects transitive repositories)
+ * - features: will be removed from JAXB model of features XML after loading
+ * - bundles: will be removed from features of JAXB model after loading
+ * - profiles: will be removed
+ *
+ * Stage.Startup:
+ * - bundles: will be put to etc/startup.properties
+ * - features: their bundles will be put to etc/startup.properties
+ * - repositories: will be used to resolve startup bundles/feature before adding them to etc/startup.properties
+ * - kars: unpacked to assembly, detected features XML repositories added as Stage.Startup repositories
+ *
+ * Stage.Boot:
+ * - bundles: special etc/<UUID>.xml features XML file will be created with <UUID> feature.
+ * etc/org.apacha.karaf.features.cfg will have this features XML file in featuresRepositories property and
+ * the feature itself in featuresBoot property
+ * - features: will be added to etc/org.apacha.karaf.features.cfg file, featuresBoot property
+ * also features from Stage.Startup will be used here.
+ * - repositories: will be added to etc/org.apacha.karaf.features.cfg file, featuresRepositories property
+ * also repositories from Stage.Startup will be used here.
+ * - kars: unpacked to assembly, detected features XML repositories added as Stage.Boot repositories
+ *
+ * Stage.Installed:
+ * - bundles: will be copied to system/
+ * - features: their bundles and config files will be copied to system/
+ * - repositories: will be used to find Stage.Installed features
+ * also repositories from Stage.Boot will be searched for Stage.Installed features
+ * - kars: unpacked to assembly, detected features XML repositories added as Stage.Installed repositories
*/
- @Parameter(defaultValue = "${project.build.directory}/assembly/etc/org.apache.karaf.features.cfg")
- protected File featuresCfgFile;
/**
- * startup.properties file.
+ * For given stage (startup, boot, install) if there are no stage-specific features and profiles, all features
+ * from stage-specific repositories will be used.
*/
- @Parameter(defaultValue = "${project.build.directory}/assembly/etc/startup.properties")
- protected File startupPropertiesFile;
+ @Parameter(defaultValue = "true")
+ protected boolean installAllFeaturesByDefault = true;
/**
- * Directory used during build to construction the Karaf system repository.
+ * An environment identifier that may be used to select different variant of PID configuration file, e.g.,
+ * <code>org.ops4j.pax.url.mvn.cfg#docker</code>.
*/
- @Parameter(defaultValue="${project.build.directory}/assembly/system")
- protected File systemDirectory;
+ @Parameter
+ private String environment;
/**
- * default start level for bundles in features that don't specify it.
+ * List of compile-scope features XML files to be used in startup stage (etc/startup.properties)
*/
@Parameter
- protected int defaultStartLevel = 30;
-
- @Parameter
private List<String> startupRepositories;
+ /**
+ * List of runtime-scope features XML files to be used in boot stage (etc/org.apache.karaf.features.cfg)
+ */
@Parameter
private List<String> bootRepositories;
+ /**
+ * List of provided-scope features XML files to be used in install stage
+ */
@Parameter
private List<String> installedRepositories;
-
+ /**
+ * List of blacklisted repository URIs. Blacklisted URI may use globs and version ranges. See
+ * {@link org.apache.karaf.features.LocationPattern}.
+ */
@Parameter
private List<String> blacklistedRepositories;
/**
- * List of features from runtime-scope features xml and kars to be installed into system and listed in startup.properties.
+ * List of features from compile-scope features XML files and KARs to be installed into system repo
+ * and listed in etc/startup.properties.
*/
@Parameter
private List<String> startupFeatures;
-
/**
- * List of features from runtime-scope features xml and kars to be installed into system repo and listed in features service boot features.
+ * List of features from runtime-scope features XML files and KARs to be installed into system repo
+ * and listed in featuresBoot property in etc/org.apache.karaf.features.cfg
*/
@Parameter
private List<String> bootFeatures;
-
/**
- * List of features from runtime-scope features xml and kars to be installed into system repo and not mentioned elsewhere.
+ * List of features from provided-scope features XML files and KARs to be installed into system repo
+ * and not mentioned elsewhere.
*/
@Parameter
private List<String> installedFeatures;
-
+ /**
+ * <p>List of feature blacklisting clauses. Each clause is in one of the formats ({@link org.apache.karaf.features.FeaturePattern}):<ul>
+ * <li><code>feature-name</code></li>
+ * <li><code>feature-name;range=version-or-range</code></li>
+ * <li><code>feature-name/version-or-range</code></li>
+ * </ul></p>
+ */
@Parameter
private List<String> blacklistedFeatures;
+ /**
+ * List of compile-scope bundles added to etc/startup.properties
+ */
@Parameter
private List<String> startupBundles;
+ /**
+ * List of runtime-scope bundles wrapped in special feature added to featuresBoot property
+ * in etc/org.apache.karaf.features.cfg
+ */
@Parameter
private List<String> bootBundles;
+ /**
+ * List of provided-scope bundles added to system repo
+ */
@Parameter
private List<String> installedBundles;
+ /**
+ * List of blacklisted bundle URIs. Blacklisted URI may use globs and version ranges. See
+ * {@link org.apache.karaf.features.LocationPattern}.
+ */
@Parameter
private List<String> blacklistedBundles;
-
- @Parameter
- private String profilesUri;
+ /**
+ * List of profile URIs to use
+ */
@Parameter
- private List<String> bootProfiles;
+ private List<String> profilesUris;
+ /**
+ * List of profiles names to load from configured <code>profilesUris</code> and use as startup profiles.
+ */
@Parameter
private List<String> startupProfiles;
-
+ /**
+ * List of profiles names to load from configured <code>profilesUris</code> and use as boot profiles.
+ */
+ @Parameter
+ private List<String> bootProfiles;
+ /**
+ * List of profiles names to load from configured <code>profilesUris</code> and use as installed profiles.
+ */
@Parameter
private List<String> installedProfiles;
-
+ /**
+ * List of blacklisted profile names (possibly using <code>*</code> glob)
+ */
@Parameter
private List<String> blacklistedProfiles;
+ /**
+ * When assembly custom distribution, we can include generated and added profiles in the distribution itself,
+ * in <code>${karaf.etc}/profiles</code> directory.
+ */
+ @Parameter(defaultValue = "false")
+ private boolean writeProfiles;
+
+ /*
+ * KARs are not configured using Maven plugin configuration, but rather detected from dependencies.
+ * All KARs are just unzipped into the assembly being constructed, but additionally KAR's embedded
+ * features XML repositories are added to relevant stage.
+ */
+
+ private List<String> startupKars = new ArrayList<>();
+ private List<String> bootKars = new ArrayList<>();
+ private List<String> installedKars = new ArrayList<>();
+
+ /**
+ * TODOCUMENT
+ */
@Parameter
private Builder.BlacklistPolicy blacklistPolicy = Builder.BlacklistPolicy.Discard;
/**
- * Ignore the dependency attribute (dependency="[true|false]") on bundle
+ * Ignore the dependency attribute (dependency="[true|false]") on bundles, effectively forcing their
+ * installation.
*/
@Parameter(defaultValue = "false")
protected boolean ignoreDependencyFlag;
/**
- * Additional feature repositories
+ * <p>Additional libraries to add into assembled distribution. Libraries are specified using
+ * <code>name[;url:=<url>][;type:=<type>][;export:=true|false][;delegate:=true|false]</code>
+ * syntax. If there's no <code>url</code> header directive, <code>name</code> is used as URI. Otherwise
+ * <code>name</code> is used as target file name to use.</p>
+ *
+ * <p><code>type</code> may be:<ul>
+ * <li>endorsed - library will be added to <code>${karaf.home}/lib/endorsed</code></li>
+ * <li>extension - library will be added to <code>${karaf.home}/lib/ext</code></li>
+ * <li>boot - library will be added to <code>${karaf.home}/lib/boot</code></li>
+ * <li>by default, library is put directly into <code>${karaf.home}/lib</code> - these libraries will
+ * be used in default classloader for OSGi framework which will load {@link FrameworkFactory} implementation.</li>
+ * </ul></p>
+ *
+ * <p><code>export</code> flag determines whether packages from <code>Export-Package</code> manifest
+ * header of the library will be added to <code>org.osgi.framework.system.packages.extra</code> property in
+ * <code>${karaf.etc}/config.properties</code>.</p>
+ *
+ * <p><code>delegate</code> flag determines whether packages from <code>Export-Pavkage</code> manifest
+ * header of the library will be added to <code>org.osgi.framework.bootdelegation</code> property in
+ * <code>${karaf.etc}/config.properties</code>.</p>
*/
@Parameter
- protected List<String> featureRepositories;
-
- @Parameter
protected List<String> libraries;
/**
- * Use reference: style urls in startup.properties
+ * Use <code>reference:file:gr/oup/Id/artifactId/version/artifactId-version-classifier.type</code> style
+ * urls in <code>etc/startup.properties</code>.
*/
+ // see:
+ // - org.apache.felix.framework.cache.BundleArchive.createRevisionFromLocation()
+ // - org.apache.karaf.main.Main.installAndStartBundles()
@Parameter(defaultValue = "false")
protected boolean useReferenceUrls;
/**
- * Include project build output directory in the assembly
+ * Include project build output directory in the assembly. This allows (filtered or unfiltered) Maven
+ * resources directories to be used to provide additional resources in the assembly.
*/
@Parameter(defaultValue = "true")
protected boolean includeBuildOutputDirectory;
- @Parameter
- protected boolean installAllFeaturesByDefault = true;
-
+ /**
+ * Karaf version changes the way some configuration files are prepared (to adjust to given Karaf version
+ * requirements).
+ */
@Parameter
protected Builder.KarafVersion karafVersion = Builder.KarafVersion.v4x;
/**
- * Specify the version of Java SE to be assumed for osgi.ee.
+ * <p>Specify the version of Java SE to be assumed for osgi.ee. The value will be used in
+ * <code>etc/config.properties</code> file, in <code>java.specification.version</code> placeholder used in
+ * several properties:<ul>
+ * <li><code>org.osgi.framework.system.packages</code></li>
+ * <li><code>org.osgi.framework.system.capabilities</code></li>
+ * </ul></p>
+ * <p>Valid values are: 1.6, 1.7, 1.8, 9</p>
*/
@Parameter(defaultValue = "1.8")
protected String javase;
/**
* Specify which framework to use
- * (one of framework, framework-logback, static-framework, static-framework-logback).
+ * (one of framework, framework-logback, static-framework, static-framework-logback, custom).
*/
@Parameter
protected String framework;
@@ -248,8 +385,8 @@ public class AssemblyMojo extends MojoSupport {
protected String propertyFileEdits;
/**
- * Glob specifying which configuration pids in the selected boot features
- * should be extracted to the etc directory.
+ * Glob specifying which configuration PIDs in the selected boot features
+ * should be extracted to <code>${karaf.etc}</code> directory. By default all PIDs are extracted.
*/
@Parameter
protected List<String> pidsToExtract = Collections.singletonList("*");
@@ -262,9 +399,15 @@ public class AssemblyMojo extends MojoSupport {
@Parameter
protected Map<String, String> translatedUrls;
+ /**
+ * Specify a list of additional properties that should be added to <code>${karaf.etc}/config.properties</code>
+ */
@Parameter
protected Map<String, String> config;
+ /**
+ * Specify a list of additional properties that should be added to <code>${karaf.etc}/system.properties</code>
+ */
@Parameter
protected Map<String, String> system;
@@ -274,77 +417,56 @@ public class AssemblyMojo extends MojoSupport {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
+ setNullListsToEmpty();
+ setNullMapsToEmpty();
+
doExecute();
- }
- catch (MojoExecutionException | MojoFailureException e) {
+ } catch (MojoExecutionException | MojoFailureException e) {
throw e;
- }
- catch (Exception e) {
+ } catch (Exception e) {
throw new MojoExecutionException("Unable to build assembly", e);
}
}
+ /**
+ * Main processing method. Most of the work involves configuring and invoking {@link Builder a profile builder}.
+ * @throws Exception
+ */
protected void doExecute() throws Exception {
- startupRepositories = nonNullList(startupRepositories);
- bootRepositories = nonNullList(bootRepositories);
- installedRepositories = nonNullList(installedRepositories);
- startupBundles = nonNullList(startupBundles);
- bootBundles = nonNullList(bootBundles);
- installedBundles = nonNullList(installedBundles);
- blacklistedBundles = nonNullList(blacklistedBundles);
- startupFeatures = nonNullList(startupFeatures);
- bootFeatures = nonNullList(bootFeatures);
- installedFeatures = nonNullList(installedFeatures);
- blacklistedFeatures = nonNullList(blacklistedFeatures);
- startupProfiles = nonNullList(startupProfiles);
- bootProfiles = nonNullList(bootProfiles);
- installedProfiles = nonNullList(installedProfiles);
- blacklistedProfiles = nonNullList(blacklistedProfiles);
- blacklistedRepositories = nonNullList(blacklistedRepositories);
-
if (!startupProfiles.isEmpty() || !bootProfiles.isEmpty() || !installedProfiles.isEmpty()) {
- if (profilesUri == null) {
- throw new IllegalArgumentException("profilesDirectory must be specified");
- }
- }
-
- if (featureRepositories != null && !featureRepositories.isEmpty()) {
- getLog().warn("Use of featureRepositories is deprecated, use startupRepositories, bootRepositories or installedRepositories instead");
- startupRepositories.addAll(featureRepositories);
- bootRepositories.addAll(featureRepositories);
- installedRepositories.addAll(featureRepositories);
- }
-
- StringBuilder remote = new StringBuilder();
- for (Object obj : project.getRemoteProjectRepositories()) {
- if (remote.length() > 0) {
- remote.append(",");
- }
- remote.append(invoke(obj, "getUrl"));
- remote.append("@id=").append(invoke(obj, "getId"));
- if (!((Boolean) invoke(getPolicy(obj, false), "isEnabled"))) {
- remote.append("@noreleases");
- }
- if ((Boolean) invoke(getPolicy(obj, true), "isEnabled")) {
- remote.append("@snapshots");
+ if (profilesUris.size() == 0) {
+ throw new IllegalArgumentException("profilesUris option must be specified");
}
}
- getLog().info("Using repositories: " + remote.toString());
Builder builder = Builder.newInstance();
+
+ // Set up miscellaneous options
builder.offline(mavenSession.isOffline());
builder.localRepository(localRepo.getBasedir());
- builder.mavenRepositories(remote.toString());
builder.resolverWrapper((resolver) -> new ReactorMavenResolver(reactor, resolver));
builder.javase(javase);
-
- // Set up config and system props
- if (config != null) {
- config.forEach(builder::config);
- }
- if (system != null) {
- system.forEach(builder::system);
+ builder.karafVersion(karafVersion);
+ builder.useReferenceUrls(useReferenceUrls);
+ builder.defaultAddAll(installAllFeaturesByDefault);
+ builder.ignoreDependencyFlag(ignoreDependencyFlag);
+ builder.propertyEdits(configurePropertyEdits());
+ builder.translatedUrls(configureTranslatedUrls());
+ builder.pidsToExtract(pidsToExtract);
+ builder.writeProfiles(writeProfiles);
+ builder.environment(environment);
+
+ // Set up remote repositories from Maven build, to be used by pax-url-aether resolver
+ String remoteRepositories = MavenUtil.remoteRepositoryList(project.getRemoteProjectRepositories());
+ getLog().info("Using repositories:");
+ for (String r : remoteRepositories.split(",")) {
+ getLog().info(" " + r);
}
+ builder.mavenRepositories(remoteRepositories);
+
+ // Set up config and system properties
+ config.forEach(builder::config);
+ system.forEach(builder::system);
// Set up blacklisted items
builder.blacklistBundles(blacklistedBundles);
@@ -353,163 +475,37 @@ public class AssemblyMojo extends MojoSupport {
builder.blacklistRepositories(blacklistedRepositories);
builder.blacklistPolicy(blacklistPolicy);
- if (propertyFileEdits != null) {
- File file = new File(propertyFileEdits);
- if (file.exists()) {
- KarafPropertyEdits edits;
- try (InputStream editsStream = new FileInputStream(propertyFileEdits)) {
- KarafPropertyInstructionsModelStaxReader kipmsr = new KarafPropertyInstructionsModelStaxReader();
- edits = kipmsr.read(editsStream, true);
- }
- builder.propertyEdits(edits);
- }
- }
- builder.pidsToExtract(pidsToExtract);
-
- Map<String, String> urls = new HashMap<>();
- List<Artifact> artifacts = new ArrayList<>(project.getAttachedArtifacts());
- artifacts.add(project.getArtifact());
- for (Artifact artifact : artifacts) {
- if (artifact.getFile() != null && artifact.getFile().exists()) {
- String mvnUrl = "mvn:" + artifact.getGroupId() + "/" + artifact.getArtifactId()
- + "/" + artifact.getVersion();
- String type = artifact.getType();
- if ("bundle".equals(type)) {
- type = "jar";
- }
- if (!"jar".equals(type) || artifact.getClassifier() != null) {
- mvnUrl += "/" + type;
- if (artifact.getClassifier() != null) {
- mvnUrl += "/" + artifact.getClassifier();
- }
- }
- urls.put(mvnUrl, artifact.getFile().toURI().toString());
- }
- }
- if (translatedUrls != null) {
- urls.putAll(translatedUrls);
- }
- builder.translatedUrls(urls);
-
- // creating system directory
- getLog().info("Creating work directory");
+ // Creating system directory
+ configureWorkDirectory();
+ getLog().info("Creating work directory: " + workDirectory);
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 direct KAR and features XML dependencies");
+ processDirectMavenDependencies();
- // 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;
- }
- String uri = artifactToMvn(artifact);
- String type = getType(artifact);
- if ("kar".equals(type)) {
- switch (stage) {
- case Startup: startupKars.add(uri); break;
- case Boot: bootKars.add(uri); break;
- case Installed: installedKars.add(uri); break;
- }
- } else if ("features".equals(type)) {
- switch (stage) {
- case Startup: startupRepositories.add(uri); break;
- case Boot: bootRepositories.add(uri); break;
- case Installed: installedRepositories.add(uri); break;
- }
- } else if ("bundle".equals(type)) {
- switch (stage) {
- case Startup: startupBundles.add(uri); break;
- case Boot: bootBundles.add(uri); break;
- case Installed: installedBundles.add(uri); break;
- }
- }
- }
+ // Set up profiles and libraries
+ profilesUris.forEach(builder::profilesUris);
+ libraries.forEach(builder::libraries);
- builder.karafVersion(karafVersion)
- .useReferenceUrls(useReferenceUrls)
- .defaultAddAll(installAllFeaturesByDefault)
- .ignoreDependencyFlag(ignoreDependencyFlag);
- if (profilesUri != null) {
- builder.profilesUris(profilesUri);
- }
- if (libraries != null) {
- builder.libraries(libraries.toArray(new String[libraries.size()]));
- }
- // Startup
- boolean hasFrameworkKar = false;
- for (String kar : startupKars) {
- if (kar.startsWith("mvn:org.apache.karaf.features/framework/")
- || kar.startsWith("mvn:org.apache.karaf.features/static/")) {
- hasFrameworkKar = true;
- startupKars.remove(kar);
- if (framework == null) {
- framework = kar.startsWith("mvn:org.apache.karaf.features/framework/")
- ? "framework" : "static-framework";
- }
- builder.kars(Builder.Stage.Startup, false, kar);
- break;
- }
- }
- if (!hasFrameworkKar) {
- Properties versions = new Properties();
- try (InputStream is = getClass().getResourceAsStream("versions.properties")) {
- versions.load(is);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- String realKarafVersion = versions.getProperty("karaf-version");
- String kar;
- switch (framework) {
- case "framework":
- kar = "mvn:org.apache.karaf.features/framework/" + realKarafVersion + "/xml/features";
- break;
- case "framework-logback":
- kar = "mvn:org.apache.karaf.features/framework/" + realKarafVersion + "/xml/features";
- break;
- case "static-framework":
- kar = "mvn:org.apache.karaf.features/static/" + realKarafVersion + "/xml/features";
- break;
- case "static-framework-logback":
- kar = "mvn:org.apache.karaf.features/static/" + realKarafVersion + "/xml/features";
- break;
- default:
- throw new IllegalArgumentException("Unsupported framework: " + framework);
- }
- builder.kars(Builder.Stage.Startup, false, kar);
- }
- if (!startupFeatures.contains(framework)) {
- builder.features(Builder.Stage.Startup, framework);
- }
+ // Startup stage
+ detectStartupKarsAndFeatures(builder);
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
+
+ // Boot stage
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
+
+ // Installed stage
builder.defaultStage(Builder.Stage.Installed)
.kars(toArray(installedKars))
.repositories(installedFeatures.isEmpty() && installedProfiles.isEmpty() && installAllFeaturesByDefault, toArray(installedRepositories))
@@ -520,47 +516,180 @@ public class AssemblyMojo extends MojoSupport {
// Generate the assembly
builder.generateAssembly();
- // Include project classes content
+ // Include project classes content if not specified otherwise
if (includeBuildOutputDirectory)
IoUtils.copyDirectory(new File(project.getBuild().getOutputDirectory()), workDirectory);
- // Overwrite assembly dir contents
+ // Overwrite assembly dir contents with source directory (not filtered) when directory exists
if (sourceDirectory.exists())
IoUtils.copyDirectory(sourceDirectory, workDirectory);
// Chmod the bin/* scripts
File[] files = new File(workDirectory, "bin").listFiles();
- if( files!=null ) {
+ if (files != null) {
for (File file : files) {
- if( !file.getName().endsWith(".bat") ) {
+ if (!file.getName().endsWith(".bat")) {
try {
Files.setPosixFilePermissions(file.toPath(), PosixFilePermissions.fromString("rwxr-xr-x"));
} catch (Throwable ignore) {
- // we tried our best, perhaps the OS does not support posix file perms.
+ // we tried our best, perhaps the OS does not support POSIX file perms.
}
}
}
}
}
- private Object invoke(Object object, String getter) throws MojoExecutionException {
- try {
- return object.getClass().getMethod(getter).invoke(object);
- } catch (Exception e) {
- throw new MojoExecutionException("Unable to build remote repository from " + object.toString(), e);
+ private void configureWorkDirectory() {
+ IoUtils.deleteRecursive(workDirectory);
+ workDirectory.mkdirs();
+ new File(workDirectory, "etc").mkdirs();
+ new File(workDirectory, "system").mkdirs();
+ }
+
+ /**
+ * <p>Turns direct maven dependencies into startup/boot/installed artifacts.</p>
+ * <p>{@link MavenProject#getDependencyArtifacts()} is deprecated, but we don't want (?) transitive
+ * dependencies given by {@link MavenProject#getArtifacts()}.</p>
+ */
+ @SuppressWarnings("deprecation")
+ private void processDirectMavenDependencies() {
+ for (Artifact artifact : project.getDependencyArtifacts()) {
+ Builder.Stage stage = Builder.Stage.fromMavenScope(artifact.getScope());
+ if (stage == null) {
+ continue;
+ }
+ String uri = artifactToMvn(artifact);
+ switch (getType(artifact)) {
+ case "kar":
+ addUris(stage, uri, startupKars, bootKars, installedKars);
+ break;
+ case "features":
+ addUris(stage, uri, startupRepositories, bootRepositories, installedRepositories);
+ break;
+ case "bundle":
+ addUris(stage, uri, startupBundles, bootBundles, installedBundles);
+ break;
+ }
}
}
- private Object getPolicy(Object object, boolean snapshots) throws MojoExecutionException {
- return invoke(object, "getPolicy", new Class[] { Boolean.TYPE }, new Object[] { snapshots });
+ private void addUris(Builder.Stage stage, String uri, List<String> startup, List<String> boot, List<String> installed) {
+ switch (stage) {
+ case Startup:
+ startup.add(uri);
+ break;
+ case Boot:
+ boot.add(uri);
+ break;
+ case Installed:
+ installed.add(uri);
+ break;
+ }
}
- private Object invoke(Object object, String getter, Class[] types, Object[] params) throws MojoExecutionException {
- try {
- return object.getClass().getMethod(getter, types).invoke(object, params);
- } catch (Exception e) {
- throw new MojoExecutionException("Unable to build remote repository from " + object.toString(), e);
+ /**
+ * <p>Custom distribution is created from at least one <em>startup KAR</em> and one <em>startup</em>
+ * feature. Such startup KAR + feature is called <em>framework</em>.</p>
+ *
+ * <p>We can specify one of 5 <em>frameworks</em>:<ul>
+ * <li>framework: <code>mvn:org.apache.karaf.features/framework/VERSION/kar</code> and <code>framework</code> feature</li>
+ * <li>framework-logback: <code>mvn:org.apache.karaf.features/framework/VERSION/kar</code> and <code>framework-logback</code> feature</li>
+ * <li>static-framework: <code>mvn:org.apache.karaf.features/static/VERSION/kar</code> and <code>static-framework</code> feature</li>
+ * <li>static-framework-logback: <code>mvn:org.apache.karaf.features/static/VERSION/kar</code> and <code>static-framework-logback</code> feature</li>
+ * <li>custom: both startup KAR and startup feature has to be specified explicitly</li>
+ * </ul></p>
+ * @param builder
+ */
+ private void detectStartupKarsAndFeatures(Builder builder) {
+ boolean hasStandardKarafFrameworkKar = false;
+ boolean hasCustomFrameworkKar = false;
+ for (Iterator<String> iterator = startupKars.iterator(); iterator.hasNext(); ) {
+ String kar = iterator.next();
+ if (kar.startsWith("mvn:org.apache.karaf.features/framework/")
+ || kar.startsWith("mvn:org.apache.karaf.features/static/")) {
+ hasStandardKarafFrameworkKar = true;
+ iterator.remove();
+ if (framework == null) {
+ framework = kar.startsWith("mvn:org.apache.karaf.features/framework/")
+ ? "framework" : "static-framework";
+ }
+ getLog().info(" Standard startup Karaf KAR found: " + kar);
+ builder.kars(Builder.Stage.Startup, false, kar);
+ break;
+ }
+ }
+
+ if (!hasStandardKarafFrameworkKar) {
+ if ("custom".equals(framework)) {
+ // we didn't detect standard Karaf KAR (framework or static), so we expect at least one
+ // other KAR dependency with compile scope and at least one startup feature
+ if (startupKars.isEmpty()) {
+ throw new IllegalArgumentException("Custom KAR was declared, but there's no Maven dependency with type=kar and scope=compile." +
+ " Please specify at least one KAR for custom assembly.");
+ }
+ if (startupFeatures.isEmpty()) {
+ throw new IllegalArgumentException("Custom KAR was declared, but there's no startup feature declared." +
+ " Please specify at least one startup feature defined in features XML repository inside custom startup KAR or startup repository.");
+ }
+ hasCustomFrameworkKar = true;
+ for (String startupKar : startupKars) {
+ getLog().info(" Custom startup KAR found: " + startupKar);
+ }
+ } else if (framework == null) {
+ throw new IllegalArgumentException("Can't determine framework to use (framework, framework-logback, static-framework, static-framework-logback, custom)." +
+ " Please specify valid \"framework\" option or add Maven dependency with \"kar\" type and \"compile\" scope for one of standard Karaf KARs.");
+ } else {
+ String realKarafVersion = Version.karafVersion();
+ String kar;
+ switch (framework) {
+ case "framework":
+ case "framework-logback":
+ kar = "mvn:org.apache.karaf.features/framework/" + realKarafVersion + "/kar";
+ break;
+ case "static-framework":
+ case "static-framework-logback":
+ kar = "mvn:org.apache.karaf.features/static/" + realKarafVersion + "/kar";
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported framework: " + framework);
+ }
+ getLog().info(" Standard startup KAR implied from framework (" + framework + "): " + kar);
+ builder.kars(Builder.Stage.Startup, false, kar);
+ }
+ }
+
+ if (hasStandardKarafFrameworkKar && !startupFeatures.contains(framework)) {
+ getLog().info(" Feature " + framework + " will be added as a startup feature");
+ builder.features(Builder.Stage.Startup, framework);
+ }
+ }
+
+ private KarafPropertyEdits configurePropertyEdits() throws IOException, XMLStreamException {
+ KarafPropertyEdits edits = null;
+ if (propertyFileEdits != null) {
+ File file = new File(propertyFileEdits);
+ if (file.exists()) {
+ try (InputStream editsStream = new FileInputStream(propertyFileEdits)) {
+ KarafPropertyInstructionsModelStaxReader kipmsr = new KarafPropertyInstructionsModelStaxReader();
+ edits = kipmsr.read(editsStream, true);
+ }
+ }
+ }
+ return edits;
+ }
+
+ private Map<String,String> configureTranslatedUrls() {
+ Map<String, String> urls = new HashMap<>();
+ List<Artifact> artifacts = new ArrayList<>(project.getAttachedArtifacts());
+ artifacts.add(project.getArtifact());
+ for (Artifact artifact : artifacts) {
+ if (artifact.getFile() != null && artifact.getFile().exists()) {
+ String mvnUrl = artifactToMvn(artifact);
+ urls.put(mvnUrl, artifact.getFile().toURI().toString());
+ }
}
+ urls.putAll(translatedUrls);
+ return urls;
}
private String getType(Artifact artifact) {
@@ -616,7 +745,7 @@ public class AssemblyMojo extends MojoSupport {
return "unknown";
}
- private String artifactToMvn(Artifact artifact) throws MojoExecutionException {
+ private String artifactToMvn(Artifact artifact) {
String uri;
String groupId = artifact.getGroupId();
@@ -641,9 +770,40 @@ public class AssemblyMojo extends MojoSupport {
return strings.toArray(new String[strings.size()]);
}
+ private void setNullListsToEmpty() {
+ startupRepositories = nonNullList(startupRepositories);
+ bootRepositories = nonNullList(bootRepositories);
+ installedRepositories = nonNullList(installedRepositories);
+ blacklistedRepositories = nonNullList(blacklistedRepositories);
+ startupBundles = nonNullList(startupBundles);
+ bootBundles = nonNullList(bootBundles);
+ installedBundles = nonNullList(installedBundles);
+ blacklistedBundles = nonNullList(blacklistedBundles);
+ startupFeatures = nonNullList(startupFeatures);
+ bootFeatures = nonNullList(bootFeatures);
+ installedFeatures = nonNullList(installedFeatures);
+ blacklistedFeatures = nonNullList(blacklistedFeatures);
+ startupProfiles = nonNullList(startupProfiles);
+ bootProfiles = nonNullList(bootProfiles);
+ installedProfiles = nonNullList(installedProfiles);
+ blacklistedProfiles = nonNullList(blacklistedProfiles);
+ libraries = nonNullList(libraries);
+ profilesUris = nonNullList(profilesUris);
+ }
+
+ private void setNullMapsToEmpty() {
+ config = nonNullMap(config);
+ system = nonNullMap(system);
+ translatedUrls = nonNullMap(translatedUrls);
+ }
+
private List<String> nonNullList(List<String> list) {
final List<String> nonNullList = list == null ? new ArrayList<>() : list;
return nonNullList.stream().filter(Objects::nonNull).collect(Collectors.toList());
}
+ private Map<String, String> nonNullMap(Map<String, String> map) {
+ return map == null ? new LinkedHashMap<>() : map;
+ }
+
}
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/VerifyMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/VerifyMojo.java
index 2ff0a53..d13e786 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/VerifyMojo.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/VerifyMojo.java
@@ -80,6 +80,7 @@ import org.apache.karaf.features.internal.service.StaticInstallSupport;
import org.apache.karaf.features.internal.util.MapUtils;
import org.apache.karaf.features.internal.util.MultiException;
import org.apache.karaf.profile.assembly.CustomDownloadManager;
+import org.apache.karaf.tooling.utils.MavenUtil;
import org.apache.karaf.tooling.utils.MojoSupport;
import org.apache.karaf.tooling.utils.ReactorMavenResolver;
import org.apache.karaf.util.config.PropertiesLoader;
@@ -167,32 +168,13 @@ public class VerifyMojo extends MojoSupport {
}
if (karafVersion == null) {
- Properties versions = new Properties();
- try (InputStream is = getClass().getResourceAsStream("versions.properties")) {
- versions.load(is);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- karafVersion = versions.getProperty("karaf-version");
+ karafVersion = org.apache.karaf.util.Version.karafVersion();
}
Hashtable<String, String> config = new Hashtable<>();
- StringBuilder remote = new StringBuilder();
- for (Object obj : project.getRemoteProjectRepositories()) {
- if (remote.length() > 0) {
- remote.append(",");
- }
- remote.append(invoke(obj, "getUrl"));
- remote.append("@id=").append(invoke(obj, "getId"));
- if (!((Boolean) invoke(getPolicy(obj, false), "isEnabled"))) {
- remote.append("@noreleases");
- }
- if ((Boolean) invoke(getPolicy(obj, true), "isEnabled")) {
- remote.append("@snapshots");
- }
- }
- getLog().info("Using repositories: " + remote.toString());
- config.put("maven.repositories", remote.toString());
+ String remoteRepositories = MavenUtil.remoteRepositoryList(project.getRemoteProjectRepositories());
+ getLog().info("Using repositories: " + remoteRepositories);
+ config.put("maven.repositories", remoteRepositories);
config.put("maven.localRepository", localRepo.getBasedir());
config.put("maven.settings", mavenSession.getRequest().getUserSettingsFile().toString());
// TODO: add more configuration bits ?
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/MavenUtil.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/MavenUtil.java
index 4d11dd5..13fcd80 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/MavenUtil.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/utils/MavenUtil.java
@@ -23,6 +23,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Date;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -33,6 +34,7 @@ import org.apache.maven.artifact.repository.metadata.Snapshot;
import org.apache.maven.artifact.repository.metadata.SnapshotVersion;
import org.apache.maven.artifact.repository.metadata.Versioning;
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
+import org.eclipse.aether.repository.RemoteRepository;
/**
* Util method for Maven manipulation (URL convert, metadata generation, etc).
@@ -41,7 +43,7 @@ public class MavenUtil {
static final DefaultRepositoryLayout layout = new DefaultRepositoryLayout();
private static final Pattern aetherPattern = Pattern.compile("([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)");
- private static final Pattern mvnPattern = Pattern.compile("(?:(?:wrap:)|(?:blueprint:))?mvn:([^/ ]+)/([^/ ]+)/([^/\\$ ]*)(/([^/\\$ ]+)(/([^/\\$ ]+))?)?(/\\$.+)?");
+ private static final Pattern mvnPattern = Pattern.compile("(?:(?:wrap:)|(?:blueprint:))?mvn:([^/ ]+)/([^/ ]+)/([^/$ ]*)(/([^/$ ]+)(/([^/$ ]+))?)?(/\\$.+)?");
/**
* Convert PAX URL mvn format to aether coordinate format.
@@ -153,13 +155,35 @@ public class MavenUtil {
}
public static String getFileName(Artifact artifact) {
- String name = artifact.getArtifactId() + "-" + artifact.getBaseVersion()
+ return artifact.getArtifactId() + "-" + artifact.getBaseVersion()
+ (artifact.getClassifier() != null ? "-" + artifact.getClassifier() : "") + "." + artifact.getType();
- return name;
}
-
+
public static String getDir(Artifact artifact) {
return artifact.getGroupId().replace('.', '/') + "/" + artifact.getArtifactId() + "/" + artifact.getBaseVersion() + "/";
}
+ /**
+ * Changes maven configuration of remote repositories to a list of repositories for pax-url-aether
+ * @param remoteRepositories
+ * @return
+ */
+ public static String remoteRepositoryList(List<RemoteRepository> remoteRepositories) {
+ StringBuilder remotes = new StringBuilder();
+ for (RemoteRepository rr : remoteRepositories) {
+ if (remotes.length() > 0) {
+ remotes.append(",");
+ }
+ remotes.append(rr.getUrl());
+ remotes.append("@id=").append(rr.getId());
+ if (!rr.getPolicy(false).isEnabled()) {
+ remotes.append("@noreleases");
+ }
+ if (rr.getPolicy(true).isEnabled()) {
+ remotes.append("@snapshots");
+ }
+ }
+ return remotes.toString();
+ }
+
}
--
To stop receiving notification emails like this one, please contact
"commits@karaf.apache.org" <co...@karaf.apache.org>.