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/06 11:35:32 UTC

[karaf] branch KARAF-5376-overrides_v2 updated: [KARAF-5339] Add blacklisted.[repository|feature|bundle].xx properties support to profiles

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


The following commit(s) were added to refs/heads/KARAF-5376-overrides_v2 by this push:
     new cb06b9e  [KARAF-5339] Add blacklisted.[repository|feature|bundle].xx properties support to profiles
cb06b9e is described below

commit cb06b9e55f0f7a6bb0f7e6e918412448d36a1aaf
Author: Grzegorz Grzybek <gr...@gmail.com>
AuthorDate: Wed Dec 6 12:35:23 2017 +0100

    [KARAF-5339] Add blacklisted.[repository|feature|bundle].xx properties support to profiles
---
 .../org/apache/karaf/features/LocationPattern.java |  14 +-
 .../model/processing/FeaturesProcessing.java       |   2 +-
 .../karaf/features/internal/service/Blacklist.java |   4 +-
 .../internal/service/LocationPatternTest.java      |   4 +-
 .../java/org/apache/karaf/profile/Profile.java     |  21 +++
 .../org/apache/karaf/profile/ProfileBuilder.java   |  12 ++
 .../org/apache/karaf/profile/ProfileConstants.java |  20 +++
 .../org/apache/karaf/profile/assembly/Builder.java |  80 +++++++----
 .../karaf/profile/impl/ProfileBuilderImpl.java     | 154 ++++++++++++++-------
 .../org/apache/karaf/profile/impl/ProfileImpl.java |  31 ++++-
 .../apache/karaf/profile/impl/ProfilesTest.java    |   3 +
 11 files changed, 251 insertions(+), 94 deletions(-)

diff --git a/features/core/src/main/java/org/apache/karaf/features/LocationPattern.java b/features/core/src/main/java/org/apache/karaf/features/LocationPattern.java
index cf6f01d..f155b78 100644
--- a/features/core/src/main/java/org/apache/karaf/features/LocationPattern.java
+++ b/features/core/src/main/java/org/apache/karaf/features/LocationPattern.java
@@ -60,7 +60,7 @@ public class LocationPattern {
     private String classifier;
     private Pattern classifierPattern;
 
-    public LocationPattern(String uri) throws MalformedURLException {
+    public LocationPattern(String uri) throws IllegalArgumentException {
         if (uri == null) {
             throw new IllegalArgumentException("URI to match should not be null");
         }
@@ -69,7 +69,12 @@ public class LocationPattern {
             originalPattern = toRegExp(originalUri);
         } else {
             uri = uri.substring(4);
-            Parser parser = new Parser(uri);
+            Parser parser = null;
+            try {
+                parser = new Parser(uri);
+            } catch (MalformedURLException e) {
+                throw new IllegalArgumentException(e.getMessage(), e);
+            }
             if (Parser.VERSION_LATEST.equals(parser.getVersion())) {
                 parser.setVersion(null);
             }
@@ -92,8 +97,7 @@ public class LocationPattern {
                         version = new Version(VersionCleaner.clean(versionString));
                     }
                 } catch (IllegalArgumentException e) {
-                    MalformedURLException mue = new MalformedURLException("Can't parse version \"" + versionString + "\" as OSGi version object.");
-                    mue.initCause(e);
+                    IllegalArgumentException mue = new IllegalArgumentException("Can't parse version \"" + versionString + "\" as OSGi version object.", e);
                     throw mue;
                 }
             }
@@ -144,7 +148,7 @@ public class LocationPattern {
         LocationPattern other;
         try {
             other = new LocationPattern(otherUri);
-        } catch (MalformedURLException e) {
+        } catch (IllegalArgumentException e) {
             LOG.debug("Can't parse \"" + otherUri + "\" as Maven URI. Ignoring.");
             return false;
         }
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/processing/FeaturesProcessing.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/processing/FeaturesProcessing.java
index 537057e..4b37362 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/model/processing/FeaturesProcessing.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/processing/FeaturesProcessing.java
@@ -160,7 +160,7 @@ public class FeaturesProcessing {
             try {
                 blacklistedRepositoryLocationPatterns.add(new LocationPattern(repositoryURI));
                 blacklisted.add(repositoryURI + ";" + Blacklist.BLACKLIST_TYPE + "=" + Blacklist.TYPE_REPOSITORY);
-            } catch (MalformedURLException e) {
+            } catch (IllegalArgumentException e) {
                 LOG.warn("Can't parse blacklisted repository location pattern: " + repositoryURI + ". Ignoring.");
             }
         }
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/Blacklist.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/Blacklist.java
index 9792afa..fe3d86e 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/Blacklist.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/Blacklist.java
@@ -116,7 +116,7 @@ public class Blacklist {
                     } else {
                         try {
                             repositoryBlacklist.add(new LocationPattern(location));
-                        } catch (MalformedURLException e) {
+                        } catch (IllegalArgumentException e) {
                             LOG.warn("Problem parsing repository blacklist URI \"" + location + "\": " + e.getMessage() + ". Ignoring.");
                         }
                     }
@@ -139,7 +139,7 @@ public class Blacklist {
                     } else {
                         try {
                             bundleBlacklist.add(new LocationPattern(location));
-                        } catch (MalformedURLException e) {
+                        } catch (IllegalArgumentException e) {
                             LOG.warn("Problem parsing bundle blacklist URI \"" + location + "\": " + e.getMessage() + ". Ignoring.");
                         }
                     }
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/LocationPatternTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/LocationPatternTest.java
index 3c0a808..27abdd7 100644
--- a/features/core/src/test/java/org/apache/karaf/features/internal/service/LocationPatternTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/LocationPatternTest.java
@@ -58,7 +58,7 @@ public class LocationPatternTest {
         }) {
             try {
                 new LocationPattern(p);
-            } catch (MalformedURLException ignored) {
+            } catch (IllegalArgumentException ignored) {
                 exception |= true;
             }
         }
@@ -94,7 +94,7 @@ public class LocationPatternTest {
             try {
                 new LocationPattern(p);
                 exception &= false;
-            } catch (MalformedURLException ignored) {
+            } catch (IllegalArgumentException ignored) {
             }
         }
         assertTrue("We should fail for all broken mvn: URIs", exception);
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 ca08bd2..94f160d 100644
--- a/profile/src/main/java/org/apache/karaf/profile/Profile.java
+++ b/profile/src/main/java/org/apache/karaf/profile/Profile.java
@@ -20,6 +20,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.karaf.features.FeaturePattern;
+import org.apache.karaf.features.LocationPattern;
+
 /**
  * <p>A <em>profile</em> is a container for configuration that can be applied to Karaf distribution.</p>
  *
@@ -102,6 +105,24 @@ public interface Profile extends ProfileConstants {
     List<String> getRepositories();
 
     /**
+     * Returns a list of blacklisted bundles (URIs) (as {@link LocationPattern location patterns}
+     * @return
+     */
+    List<LocationPattern> getBlacklistedBundles();
+
+    /**
+     * Returns a list of blacklisted {@link FeaturePattern feature patterns}
+     * @return
+     */
+    List<FeaturePattern> getBlacklistedFeatures();
+
+    /**
+     * Returns a list of blacklisted features XML repositories (URIs) (as {@link LocationPattern location patterns}
+     * @return
+     */
+    List<LocationPattern> getBlacklistedRepositories();
+
+    /**
      * Returns a list of libraries (to be added to <code>${karaf.home}/lib</code>) defined in this profile
      * @return
      */
diff --git a/profile/src/main/java/org/apache/karaf/profile/ProfileBuilder.java b/profile/src/main/java/org/apache/karaf/profile/ProfileBuilder.java
index b87f9a7..552d6bb 100644
--- a/profile/src/main/java/org/apache/karaf/profile/ProfileBuilder.java
+++ b/profile/src/main/java/org/apache/karaf/profile/ProfileBuilder.java
@@ -89,6 +89,18 @@ public interface ProfileBuilder {
 
     ProfileBuilder addRepository(String value);
 
+    ProfileBuilder setBlacklistedBundles(List<String> values);
+
+    ProfileBuilder addBlacklistedBundle(String value);
+
+    ProfileBuilder setBlacklistedFeatures(List<String> values);
+
+    ProfileBuilder addBlacklistedFeature(String value);
+
+    ProfileBuilder setBlacklistedRepositories(List<String> values);
+
+    ProfileBuilder addBlacklistedRepository(String value);
+
     ProfileBuilder setOverrides(List<String> values);
     
     ProfileBuilder setOptionals(List<String> values);
diff --git a/profile/src/main/java/org/apache/karaf/profile/ProfileConstants.java b/profile/src/main/java/org/apache/karaf/profile/ProfileConstants.java
index 8df688b..2ca1a5b 100644
--- a/profile/src/main/java/org/apache/karaf/profile/ProfileConstants.java
+++ b/profile/src/main/java/org/apache/karaf/profile/ProfileConstants.java
@@ -26,6 +26,11 @@ public interface ProfileConstants {
     String ATTRIBUTE_PREFIX = "attribute.";
 
     /**
+     * Prefix for blacklisting attributes
+     */
+    String BLACKLISTED_PREFIX = "blacklisted.";
+
+    /**
      * The attribute key for whitespace-separated list of parent profile IDs
      */
     String PARENTS = ATTRIBUTE_PREFIX + "parents";
@@ -94,6 +99,21 @@ public interface ProfileConstants {
     String BUNDLE_PREFIX = "bundle.";
 
     /**
+     * The prefix for attributes that specify URIs of blacklisted features XML files
+     */
+    String BLACKLISTED_REPOSITORY_PREFIX = BLACKLISTED_PREFIX + "repository.";
+
+    /**
+     * The prefix for attributes that specify blacklisted feature names (<code>name[/version]</code>)
+     */
+    String BLACKLISTED_FEATURE_PREFIX = BLACKLISTED_PREFIX + "feature.";
+
+    /**
+     * The prefix for attributes that specify blacklisted bundle URIs
+     */
+    String BLACKLISTED_BUNDLE_PREFIX = BLACKLISTED_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}.
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 4a5603c..413eaec 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
@@ -875,13 +875,31 @@ public class Builder {
         }
 
         //
-        // Handle blacklist - we'll use SINGLE instance iof Blacklist for all further downloads
+        // Load profiles
+        //
+        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 initial profile to collect overrides and blacklisting instructions
+        Profile initialProfile = ProfileBuilder.Factory.create("initial")
+                .setParents(new ArrayList<>(profiles.keySet()))
+                .getProfile();
+        Profile initialOverlay = Profiles.getOverlay(initialProfile, allProfiles, environment);
+        Profile initialEffective = Profiles.getEffective(initialOverlay, false);
+
         //
-        blacklist = processBlacklist();
+        // Handle blacklist - we'll use SINGLE instance of Blacklist for all further downloads
+        //
+        blacklist = processBlacklist(initialEffective);
 
-        // we can't yet have full feature processor, because some overrides may be defined in profiles and
-        // profiles are generated after reading features from repositories
-        // so for now, we can only configure blacklisting features processor
+        //
+        // Configure blacklisting and overriding features processor
+        //
 
         boolean needFeaturesProcessorFileCopy = false;
         String existingProcessorDefinitionURI = null;
@@ -905,9 +923,12 @@ public class Builder {
 
         // now we can configure blacklisting features processor which may have already defined (in XML)
         // configuration for bundle replacements or feature overrides.
-        // we'll add overrides from profiles later.
         FeaturesProcessorImpl processor = new FeaturesProcessorImpl(existingProcessorDefinitionURI, null, blacklist, new HashSet<>());
 
+        // add overrides from initialProfile
+        Set<String> overrides = processOverrides(initialEffective.getOverrides());
+        processor.addOverrides(overrides);
+
         //
         // Propagate feature installation from repositories
         //
@@ -928,17 +949,6 @@ public class Builder {
         }
 
         //
-        // Load profiles
-        //
-        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. If user has configured additional profiles, they'll be used as parents
         // of the generated ones.
         //
@@ -963,7 +973,11 @@ public class Builder {
                 .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 with all the parents configured
+        // profile with all the parents configured and stage-agnostic blacklisting configuration added
+        blacklistedRepositoryURIs.forEach(builder::addBlacklistedRepository);
+        blacklistedFeatureIdentifiers.forEach(builder::addBlacklistedFeature);
+        blacklistedBundleURIs.forEach(builder::addBlacklistedBundle);
+        // final profilep
         Profile overallProfile = builder.getProfile();
 
         // profile with parents included and "flattened" using inheritance rules (child files overwrite parent
@@ -974,14 +988,6 @@ public class Builder {
         // so property placeholders are preserved - like ${karaf.base})
         Profile overallEffective = Profiles.getEffective(overallOverlay, false);
 
-        //
-        // Handle overrides - existing (unzipped from KAR) and defined in profile
-        //
-        Set<String> overrides = processOverrides(overallEffective.getOverrides());
-
-        // we can now add overrides from profiles.
-        processor.addOverrides(overrides);
-
         if (writeProfiles) {
             Path profiles = etcDirectory.resolve("profiles");
             LOGGER.info("Adding profiles to {}", homeDirectory.relativize(profiles));
@@ -1116,10 +1122,11 @@ public class Builder {
 
     /**
      * Checks existing and configured blacklisting definitions
+     * @param initialProfile
      * @return
      * @throws IOException
      */
-    private Blacklist processBlacklist() throws IOException {
+    private Blacklist processBlacklist(Profile initialProfile) throws IOException {
         Blacklist existingBlacklist = null;
         Blacklist blacklist = new Blacklist();
         Path existingBLacklistedLocation = etcDirectory.resolve("blacklisted.properties");
@@ -1128,22 +1135,37 @@ public class Builder {
             existingBlacklist = new Blacklist(Files.readAllLines(existingBLacklistedLocation));
         }
         for (String br : blacklistedRepositoryURIs) {
+            // from Maven/Builder configuration
             try {
                 blacklist.blacklistRepository(new LocationPattern(br));
-            } catch (MalformedURLException e) {
+            } catch (IllegalArgumentException e) {
                 LOGGER.warn("Blacklisted features XML repository URI is invalid: {}, ignoring", br);
             }
         }
+        for (LocationPattern br : initialProfile.getBlacklistedRepositories()) {
+            // from profile configuration
+            blacklist.blacklistRepository(br);
+        }
         for (String bf : blacklistedFeatureIdentifiers) {
+            // from Maven/Builder configuration
             blacklist.blacklistFeature(new FeaturePattern(bf));
         }
+        for (FeaturePattern bf : initialProfile.getBlacklistedFeatures()) {
+            // from profile configuration
+            blacklist.blacklistFeature(bf);
+        }
         for (String bb : blacklistedBundleURIs) {
+            // from Maven/Builder configuration
             try {
                 blacklist.blacklistBundle(new LocationPattern(bb));
-            } catch (MalformedURLException e) {
+            } catch (IllegalArgumentException e) {
                 LOGGER.warn("Blacklisted bundle URI is invalid: {}, ignoring", bb);
             }
         }
+        for (LocationPattern bb : initialProfile.getBlacklistedBundles()) {
+            // from profile configuration
+            blacklist.blacklistBundle(bb);
+        }
         if (existingBlacklist != null) {
             blacklist.merge(existingBlacklist);
         }
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 9ea1722..9c363c5 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
@@ -41,24 +41,24 @@ import static org.apache.karaf.profile.impl.ProfileImpl.ConfigListType;
  */
 public final class ProfileBuilderImpl implements ProfileBuilder {
 
-	private String profileId;
-	private Map<String, FileContent> fileMapping = new HashMap<>();
-	private boolean isOverlay;
-	
-	@Override
-	public ProfileBuilder from(Profile profile) {
-		profileId = profile.getId();
-		setFileConfigurations(profile.getFileConfigurations());
+    private String profileId;
+    private Map<String, FileContent> fileMapping = new HashMap<>();
+    private boolean isOverlay;
+
+    @Override
+    public ProfileBuilder from(Profile profile) {
+        profileId = profile.getId();
+        setFileConfigurations(profile.getFileConfigurations());
         return this;
-	}
+    }
 
-	@Override
-	public ProfileBuilder identity(String profileId) {
-		this.profileId = profileId;
-		return this;
-	}
+    @Override
+    public ProfileBuilder identity(String profileId) {
+        this.profileId = profileId;
+        return this;
+    }
 
-	@Override
+    @Override
     public List<String> getParents() {
         Map<String, Object> config = getConfigurationInternal(Profile.INTERNAL_PID);
         String pspec = (String) config.get(PARENTS);
@@ -67,14 +67,14 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
     }
 
     @Override
-	public ProfileBuilder addParent(String parentId) {
+    public ProfileBuilder addParent(String parentId) {
         return addParentsInternal(Collections.singletonList(parentId), false);
-	}
+    }
 
-	@Override
-	public ProfileBuilder addParents(List<String> parentIds) {
-		return addParentsInternal(parentIds, false);
-	}
+    @Override
+    public ProfileBuilder addParents(List<String> parentIds) {
+        return addParentsInternal(parentIds, false);
+    }
 
     @Override
     public ProfileBuilder setParents(List<String> parentIds) {
@@ -94,12 +94,12 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
     }
     
     @Override
-	public ProfileBuilder removeParent(String profileId) {
+    public ProfileBuilder removeParent(String profileId) {
         Set<String> currentIds = new LinkedHashSet<>(getParents());
         currentIds.remove(profileId);
         updateParentsAttribute(currentIds);
-		return this;
-	}
+        return this;
+    }
 
     private void updateParentsAttribute(Collection<String> parentIds) {
         Map<String, Object> config = getConfigurationInternal(Profile.INTERNAL_PID);
@@ -111,7 +111,7 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
     }
 
     private String parentsAttributeValue(Collection<String> parentIds) {
-	    return parentIds.isEmpty() ? "" : String.join(" ", parentIds);
+        return parentIds.isEmpty() ? "" : String.join(" ", parentIds);
     }
     
     @Override
@@ -143,16 +143,16 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
         return this;
     }
 
-	@Override
-	public ProfileBuilder setConfigurations(Map<String, Map<String, Object>> configs) {
-	    for (String pid : getConfigurationKeys()) {
-	        deleteConfiguration(pid);
-	    }
-		for (Entry<String, Map<String, Object>> entry : configs.entrySet()) {
-		    addConfiguration(entry.getKey(), new HashMap<>(entry.getValue()));
-		}
-		return this;
-	}
+    @Override
+    public ProfileBuilder setConfigurations(Map<String, Map<String, Object>> configs) {
+        for (String pid : getConfigurationKeys()) {
+            deleteConfiguration(pid);
+        }
+        for (Entry<String, Map<String, Object>> entry : configs.entrySet()) {
+            addConfiguration(entry.getKey(), new HashMap<>(entry.getValue()));
+        }
+        return this;
+    }
 
     @Override
     public ProfileBuilder addConfiguration(String pid, Map<String, Object> config) {
@@ -195,11 +195,11 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
         return this;
     }
     
-	@Override
-	public ProfileBuilder setBundles(List<String> values) {
-		addProfileConfiguration(ConfigListType.BUNDLES, values);
-		return this;
-	}
+    @Override
+    public ProfileBuilder setBundles(List<String> values) {
+        addProfileConfiguration(ConfigListType.BUNDLES, values);
+        return this;
+    }
 
     @Override
     public ProfileBuilder addBundle(String value) {
@@ -208,10 +208,10 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
     }
 
     @Override
-	public ProfileBuilder setFeatures(List<String> values) {
-		addProfileConfiguration(ConfigListType.FEATURES, values);
-		return this;
-	}
+    public ProfileBuilder setFeatures(List<String> values) {
+        addProfileConfiguration(ConfigListType.FEATURES, values);
+        return this;
+    }
 
     @Override
     public ProfileBuilder addFeature(String value) {
@@ -220,10 +220,10 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
     }
 
     @Override
-	public ProfileBuilder setRepositories(List<String> values) {
-		addProfileConfiguration(ConfigListType.REPOSITORIES, values);
-		return this;
-	}
+    public ProfileBuilder setRepositories(List<String> values) {
+        addProfileConfiguration(ConfigListType.REPOSITORIES, values);
+        return this;
+    }
 
     @Override
     public ProfileBuilder addRepository(String value) {
@@ -232,10 +232,46 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
     }
 
     @Override
-	public ProfileBuilder setOverrides(List<String> values) {
-		addProfileConfiguration(ConfigListType.OVERRIDES, values);
-		return this;
-	}
+    public ProfileBuilder setBlacklistedBundles(List<String> values) {
+        addProfileConfiguration(ConfigListType.BLACKLISTED_BUNDLES, values);
+        return null;
+    }
+
+    @Override
+    public ProfileBuilder addBlacklistedBundle(String value) {
+        addProfileConfiguration(ConfigListType.BLACKLISTED_BUNDLES, value);
+        return null;
+    }
+
+    @Override
+    public ProfileBuilder setBlacklistedFeatures(List<String> values) {
+        addProfileConfiguration(ConfigListType.BLACKLISTED_FEATURES, values);
+        return null;
+    }
+
+    @Override
+    public ProfileBuilder addBlacklistedFeature(String value) {
+        addProfileConfiguration(ConfigListType.BLACKLISTED_FEATURES, value);
+        return null;
+    }
+
+    @Override
+    public ProfileBuilder setBlacklistedRepositories(List<String> values) {
+        addProfileConfiguration(ConfigListType.BLACKLISTED_REPOSITORIES, values);
+        return null;
+    }
+
+    @Override
+    public ProfileBuilder addBlacklistedRepository(String value) {
+        addProfileConfiguration(ConfigListType.BLACKLISTED_REPOSITORIES, value);
+        return null;
+    }
+
+    @Override
+    public ProfileBuilder setOverrides(List<String> values) {
+        addProfileConfiguration(ConfigListType.OVERRIDES, values);
+        return this;
+    }
 
     @Override
     public ProfileBuilder setOptionals(List<String> values) {
@@ -249,7 +285,7 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
         return this;
     }
 
-	@Override
+    @Override
     public ProfileBuilder addAttribute(String key, String value) {
         addConfiguration(Profile.INTERNAL_PID, Profile.ATTRIBUTE_PREFIX + key, value);
         return this;
@@ -322,6 +358,9 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
         Map<String, Object> repositories = new LinkedHashMap<>();
         Map<String, Object> features = new LinkedHashMap<>();
         Map<String, Object> bundles = new LinkedHashMap<>();
+        Map<String, Object> blacklistedRepositories = new LinkedHashMap<>();
+        Map<String, Object> blacklistedFeatures = new LinkedHashMap<>();
+        Map<String, Object> blacklistedBundles = new LinkedHashMap<>();
         Map<String, Object> libraries = new LinkedHashMap<>();
         Map<String, Object> bootLibraries = new LinkedHashMap<>();
         Map<String, Object> endorsedLibraries = new LinkedHashMap<>();
@@ -342,6 +381,12 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
                 features.put(key, v);
             } else if (key.startsWith(BUNDLE_PREFIX)) {
                 bundles.put(key, v);
+            } else if (key.startsWith(BLACKLISTED_REPOSITORY_PREFIX)) {
+                blacklistedRepositories.put(key, v);
+            } else if (key.startsWith(BLACKLISTED_FEATURE_PREFIX)) {
+                blacklistedFeatures.put(key, v);
+            } else if (key.startsWith(BLACKLISTED_BUNDLE_PREFIX)) {
+                blacklistedBundles.put(key, v);
             } else if (key.startsWith(LIB_PREFIX)) {
                 libraries.put(key, v);
             } else if (key.startsWith(BOOT_PREFIX)) {
@@ -377,6 +422,9 @@ public final class ProfileBuilderImpl implements ProfileBuilder {
         addGroupOfProperties("Configuration properties for etc/system.properties", result, system);
         addGroupOfProperties("Bundle overrides (deprecated)", result, overrides);
         addGroupOfProperties("Optional resources for resolution", result, optionals);
+        addGroupOfProperties("Blacklisted repositories", result, blacklistedRepositories);
+        addGroupOfProperties("Blacklisted features", result, blacklistedFeatures);
+        addGroupOfProperties("Blacklisted bundles", result, blacklistedBundles);
 
         return Utils.toBytes(result);
     }
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 c8707ea..bfc4d72 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
@@ -24,8 +24,11 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import java.util.zip.CRC32;
 
+import org.apache.karaf.features.FeaturePattern;
+import org.apache.karaf.features.LocationPattern;
 import org.apache.karaf.profile.Profile;
 
 import static org.apache.karaf.profile.impl.Utils.assertNotNull;
@@ -132,6 +135,27 @@ final class ProfileImpl implements Profile {
     }
 
     @Override
+    public List<LocationPattern> getBlacklistedBundles() {
+        return getContainerConfigList(ConfigListType.BLACKLISTED_BUNDLES).stream()
+                .map(LocationPattern::new)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<FeaturePattern> getBlacklistedFeatures() {
+        return getContainerConfigList(ConfigListType.BLACKLISTED_FEATURES).stream()
+                .map(FeaturePattern::new)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<LocationPattern> getBlacklistedRepositories() {
+        return getContainerConfigList(ConfigListType.BLACKLISTED_REPOSITORIES).stream()
+                .map(LocationPattern::new)
+                .collect(Collectors.toList());
+    }
+
+    @Override
     public List<String> getLibraries() {
         return getContainerConfigList(ConfigListType.LIBRARIES);
     }
@@ -210,7 +234,7 @@ final class ProfileImpl implements Profile {
     private List<String> getContainerConfigList(ConfigListType type) {
         Map<String, Object> containerProps = getConfiguration(Profile.INTERNAL_PID);
         List<String> rc = new ArrayList<>();
-        String prefix = type + ".";
+        String prefix = type.value + ".";
         for (Map.Entry<String, Object> e : containerProps.entrySet()) {
             if ((e.getKey()).startsWith(prefix)) {
                 rc.add(e.getValue().toString());
@@ -253,14 +277,17 @@ final class ProfileImpl implements Profile {
 
     enum ConfigListType {
         BUNDLES("bundle"),
+        BLACKLISTED_BUNDLES("blacklisted.bundle"),
         FEATURES("feature"),
+        BLACKLISTED_FEATURES("blacklisted.feature"),
         LIBRARIES("library"),
         BOOT_LIBRARIES("boot"),
         ENDORSED_LIBRARIES("endorsed"),
         EXT_LIBRARIES("ext"),
         OPTIONALS("optional"),
         OVERRIDES("override"),
-        REPOSITORIES("repository");
+        REPOSITORIES("repository"),
+        BLACKLISTED_REPOSITORIES("blacklisted.repository");
 
         private String value;
 
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 6369e38..482c62f 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
@@ -47,12 +47,15 @@ public class ProfilesTest {
         builder.addParents(Collections.emptyList());
         builder.addAttribute("attr1", "val1");
         builder.addBundle("mvn:commons-everything/commons-everything/42");
+        builder.addBlacklistedBundle("mvn:commons-banned/commons-banned/[42,52)");
         builder.addConfiguration("my.pid", "a1", "v1${profile:my.pid2/a2}");
         builder.addConfiguration("my.pid", "a2", "v1${profile:my.pid2/a3}");
         builder.addFeature("feature1");
+        builder.addBlacklistedFeature("f34tu4e");
         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.addBlacklistedRepository("mvn:my/repository/[0,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();

-- 
To stop receiving notification emails like this one, please contact
['"commits@karaf.apache.org" <co...@karaf.apache.org>'].