You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by da...@apache.org on 2018/04/27 09:51:30 UTC

[sling-org-apache-sling-feature] 10/22: SLING-7512 Order features based on their dependencies.

This is an automated email from the ASF dual-hosted git repository.

davidb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature.git

commit 1587e849822ffb784f459a0a8439e6ccd2dd932a
Author: David Bosschaert <bo...@adobe.com>
AuthorDate: Fri Feb 23 13:42:28 2018 +0000

    SLING-7512 Order features based on their dependencies.
    
    Very initial implementation which contains the refactoring of
    Requirements and Capabilities to use the OSGi ones.
---
 .../feature/AbstractCapabilityRequirement.java     |  96 +++++++++++++++++
 .../java/org/apache/sling/feature/Capability.java  | 104 -------------------
 .../java/org/apache/sling/feature/Feature.java     |  11 +-
 .../org/apache/sling/feature/OSGiCapability.java   |  59 +++++++++++
 .../org/apache/sling/feature/OSGiRequirement.java  |  59 +++++++++++
 .../java/org/apache/sling/feature/Requirement.java | 113 ---------------------
 .../sling/feature/process/ApplicationBuilder.java  |  14 +--
 .../apache/sling/feature/process/BuilderUtil.java  |   4 +-
 .../sling/feature/process/FeatureResolver.java     |  38 +++++++
 .../sling/feature/CapabilityRequirementTest.java   |  68 +++++++++++++
 .../sling/feature/process/FeatureBuilderTest.java  |  39 +++----
 11 files changed, 357 insertions(+), 248 deletions(-)

diff --git a/src/main/java/org/apache/sling/feature/AbstractCapabilityRequirement.java b/src/main/java/org/apache/sling/feature/AbstractCapabilityRequirement.java
new file mode 100644
index 0000000..200cb92
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/AbstractCapabilityRequirement.java
@@ -0,0 +1,96 @@
+/*
+ * 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.sling.feature;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.resource.Resource;
+
+abstract class AbstractCapabilityRequirement {
+    private final Resource resource;
+    private final String namespace;
+    private final Map<String, Object> attributes;
+    private final Map<String, String> directives;
+
+    AbstractCapabilityRequirement(Resource res, String ns, Map<String, Object> attrs, Map<String, String> dirs) {
+        resource = res;
+        namespace = ns;
+        attributes = Collections.unmodifiableMap(new HashMap<>(attrs));
+        directives = Collections.unmodifiableMap(new HashMap<>(dirs));
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    public Map<String, String> getDirectives() {
+        return directives;
+    }
+
+    public Resource getResource() {
+        return resource;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
+        result = prime * result + ((directives == null) ? 0 : directives.hashCode());
+        result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        AbstractCapabilityRequirement other = (AbstractCapabilityRequirement) obj;
+        if (attributes == null) {
+            if (other.attributes != null)
+                return false;
+        } else if (!attributes.equals(other.attributes))
+            return false;
+        if (directives == null) {
+            if (other.directives != null)
+                return false;
+        } else if (!directives.equals(other.directives))
+            return false;
+        if (namespace == null) {
+            if (other.namespace != null)
+                return false;
+        } else if (!namespace.equals(other.namespace))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " [resource=" + resource + ", namespace=" + namespace + ", attributes=" + attributes
+                + ", directives=" + directives + "]";
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/Capability.java b/src/main/java/org/apache/sling/feature/Capability.java
deleted file mode 100644
index 53c9c3b..0000000
--- a/src/main/java/org/apache/sling/feature/Capability.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * A capability of a feature.
- * The capability is modeled after an OSGi capability: it
- * belongs to a namespace and might have attributes and / or
- * directives.
- */
-public class Capability {
-
-    /** The namspace. */
-    private final String namespace;
-
-    /** Map of attributes. */
-    private final Map<String, Object> attributes = new ConcurrentHashMap<>();
-
-    /** Map of directives. */
-    private final Map<String, Object> directives = new ConcurrentHashMap<>();
-
-    /**
-     * Create a new Capability.
-     * @param namespace The namespace
-     * @throws IllegalArgumentException If namespace is {@code null}.
-     */
-    public Capability(final String namespace) {
-        if ( namespace == null ) {
-            throw new IllegalArgumentException("namespace must not be null.");
-        }
-        this.namespace = namespace;
-    }
-
-    /**
-     * The namespace
-     * @return The namespace
-     */
-    public String getNamespace() {
-        return namespace;
-    }
-
-    /**
-     * Get the map of attributes.
-     * The map is modifiable.
-     * @return The map of attributes.
-     */
-    public Map<String, Object> getAttributes() {
-        return attributes;
-    }
-
-    /**
-     * Get the map of directives.
-     * The map is modifiable.
-     * @return The map of directives.
-     */
-    public Map<String, Object> getDirectives() {
-        return directives;
-    }
-
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + attributes.hashCode();
-        result = prime * result + directives.hashCode();
-        result = prime * result + namespace.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        final Capability other = (Capability) obj;
-        if (!attributes.equals(other.attributes)
-            || !directives.equals(other.directives)
-            || !namespace.equals(other.namespace)) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/Feature.java b/src/main/java/org/apache/sling/feature/Feature.java
index 64c2488..874790d 100644
--- a/src/main/java/org/apache/sling/feature/Feature.java
+++ b/src/main/java/org/apache/sling/feature/Feature.java
@@ -20,6 +20,9 @@ import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
 
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+
 /**
  * A feature consists of
  * <ul>
@@ -296,17 +299,13 @@ public class Feature implements Comparable<Feature> {
 
         // requirements
         for(final Requirement r : this.getRequirements()) {
-            final Requirement c = new Requirement(r.getNamespace());
-            c.getAttributes().putAll(r.getAttributes());
-            c.getDirectives().putAll(r.getDirectives());
+            final Requirement c = new OSGiRequirement(r.getNamespace(), r.getAttributes(), r.getDirectives());
             result.getRequirements().add(c);
         }
 
         // capabilities
         for(final Capability r : this.getCapabilities()) {
-            final Capability c = new Capability(r.getNamespace());
-            c.getAttributes().putAll(r.getAttributes());
-            c.getDirectives().putAll(r.getDirectives());
+            final Capability c = new OSGiCapability(r.getNamespace(), r.getAttributes(), r.getDirectives());
             result.getCapabilities().add(c);
         }
 
diff --git a/src/main/java/org/apache/sling/feature/OSGiCapability.java b/src/main/java/org/apache/sling/feature/OSGiCapability.java
new file mode 100644
index 0000000..5c2000d
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/OSGiCapability.java
@@ -0,0 +1,59 @@
+/*
+ * 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.sling.feature;
+
+import java.util.Map;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+/**
+ * Implementation of the OSGi Capability interface.
+ */
+public class OSGiCapability extends AbstractCapabilityRequirement implements Capability {
+    /**
+     * Create a capability that is not associated with a resource.
+     * @param res The resource associated with the capability. May be null.
+     * @param ns The namespace of the capability.
+     * @param attrs The attributes of the capability.
+     * @param dirs The directives of the capability.
+     */
+    public OSGiCapability(String ns, Map<String, Object> attrs, Map<String, String> dirs) {
+        this(null, ns, attrs, dirs);
+    }
+
+    /**
+     * Create a capability.
+     * @param res The resource associated with the capability. May be null.
+     * @param ns The namespace of the capability.
+     * @param attrs The attributes of the capability.
+     * @param dirs The directives of the capability.
+     */
+    public OSGiCapability(Resource res, String ns, Map<String, Object> attrs, Map<String, String> dirs) {
+        super(res, ns, attrs, dirs);
+    }
+
+    /**
+     * Create a capability based on an existing capability, providing the resource.
+     * The namespace, attributes and directives are copied from the provided capability.
+     * @param resource The resource to be associated with the capability
+     * @param capability The capability to base the new requirement on.
+     */
+    public OSGiCapability(Resource resource, Capability capability) {
+        this(resource, capability.getNamespace(), capability.getAttributes(), capability.getDirectives());
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/OSGiRequirement.java b/src/main/java/org/apache/sling/feature/OSGiRequirement.java
new file mode 100644
index 0000000..2727379
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/OSGiRequirement.java
@@ -0,0 +1,59 @@
+/*
+ * 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.sling.feature;
+
+import java.util.Map;
+
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+/**
+ * Implementation of the OSGi Requirement interface.
+ */
+public class OSGiRequirement extends AbstractCapabilityRequirement implements Requirement {
+    /**
+     * Create a requirement that is not associated with a resource.
+     * @param res The resource associated with the requirement.
+     * @param ns The namespace of the requirement.
+     * @param attrs The attributes of the requirement.
+     * @param dirs The directives of the requirement.
+     */
+    public OSGiRequirement(String ns, Map<String, Object> attrs, Map<String, String> dirs) {
+        this(null, ns, attrs, dirs);
+    }
+
+    /**
+     * Create a requirement.
+     * @param res The resource associated with the requirement.
+     * @param ns The namespace of the requirement.
+     * @param attrs The attributes of the requirement.
+     * @param dirs The directives of the requirement.
+     */
+    public OSGiRequirement(Resource res, String ns, Map<String, Object> attrs, Map<String, String> dirs) {
+        super(res, ns, attrs, dirs);
+    }
+
+    /**
+     * Create a requirement based on an existing requirement, providing the resource.
+     * The namespace, attributes and directives are copied from the provided requirement.
+     * @param resource The resource to be associated with the requirement
+     * @param requirement The requirement to base the new requirement on.
+     */
+    public OSGiRequirement(Resource resource, Requirement requirement) {
+        this(resource, requirement.getNamespace(), requirement.getAttributes(), requirement.getDirectives());
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/Requirement.java b/src/main/java/org/apache/sling/feature/Requirement.java
deleted file mode 100644
index 3409e51..0000000
--- a/src/main/java/org/apache/sling/feature/Requirement.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.feature;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * A requirement for a feature.
- * The requirement is modeled after an OSGi requirement: it
- * belongs to a namespace and might have attributes and / or
- * directives.
- */
-public class Requirement {
-
-    /*public static final String RESOLUTION_OPTIONAL = "optional";
-
-    public static final String	RESOLUTION_DIRECTIVE = "resolution";*/
-
-    /** The namspace. */
-    private final String namespace;
-
-    /** Map of attributes. */
-    private final Map<String, Object> attributes = new ConcurrentHashMap<>();
-
-    /** Map of directives. */
-    private final Map<String, Object> directives = new ConcurrentHashMap<>();
-
-    /**
-     * Create a new Requirement.
-     * @param namespace The namespace
-     * @throws IllegalArgumentException If namespace is {@code null}.
-     */
-    public Requirement(final String namespace) {
-        if ( namespace == null ) {
-            throw new IllegalArgumentException("namespace must not be null.");
-        }
-        this.namespace = namespace;
-    }
-
-    /**
-     * The namespace
-     * @return The namespace
-     */
-    public String getNamespace() {
-        return namespace;
-    }
-
-    /**
-     * Get the map of attributes.
-     * The map is modifiable.
-     * @return The map of attributes.
-     */
-    public Map<String,Object> getAttributes() {
-        return attributes;
-    }
-
-    /**
-     * Get the map of directives.
-     * The map is modifiable.
-     * @return The map of directives.
-     */
-    public Map<String,Object> getDirectives() {
-        return directives;
-    }
-
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + attributes.hashCode();
-        result = prime * result + directives.hashCode();
-        result = prime * result + namespace.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        final Requirement other = (Requirement) obj;
-        if (!attributes.equals(other.attributes)
-            || !directives.equals(other.directives)
-            || !namespace.equals(other.namespace)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return "[Requirement namespace='" + namespace + "' attributes=" + attributes + " directives=" + directives + "]";
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java b/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
index 1a555e1..49c17b8 100644
--- a/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
+++ b/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
@@ -17,7 +17,6 @@
 package org.apache.sling.feature.process;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 import org.apache.sling.feature.Application;
@@ -38,6 +37,7 @@ public class ApplicationBuilder {
      *
      * @param app The optional application to use as a base.
      * @param context The builder context
+     * @param resolver The Feature Resolver to use
      * @param featureIds The feature ids
      * @return The application
      * throws IllegalArgumentException If context or featureIds is {@code null}
@@ -45,6 +45,7 @@ public class ApplicationBuilder {
      */
     public static Application assemble(final Application app,
             final BuilderContext context,
+            final FeatureResolver resolver,
             final String... featureIds) {
         if ( featureIds == null || context == null ) {
             throw new IllegalArgumentException("Features and/or context must not be null");
@@ -59,7 +60,7 @@ public class ApplicationBuilder {
             }
             index++;
         }
-        return assemble(app, context, features);
+        return assemble(app, context, resolver, features);
     }
 
     /**
@@ -70,6 +71,7 @@ public class ApplicationBuilder {
      *
      * @param app The optional application to use as a base.
      * @param context The builder context
+     * @param resolver The Feature Resolver to use
      * @param features The features
      * @return The application
      * throws IllegalArgumentException If context or featureIds is {@code null}
@@ -78,7 +80,7 @@ public class ApplicationBuilder {
     public static Application assemble(
             Application app,
             final BuilderContext context,
-            final Feature... features) {
+            final FeatureResolver resolver, final Feature... features) {
         if ( features == null || context == null ) {
             throw new IllegalArgumentException("Features and/or context must not be null");
         }
@@ -89,7 +91,7 @@ public class ApplicationBuilder {
 
         // Created sorted feature list
         // Remove duplicate features by selecting the one with the highest version
-        final List<Feature> sortedFeatureList = new ArrayList<>();
+        List<Feature> sortedFeatureList = new ArrayList<>();
         for(final Feature f : features) {
             Feature found = null;
             for(final Feature s : sortedFeatureList) {
@@ -116,8 +118,8 @@ public class ApplicationBuilder {
             }
         }
 
-        // sort
-        Collections.sort(sortedFeatureList);
+        // order by dependency chain
+        sortedFeatureList = resolver.orderFeatures(sortedFeatureList);
 
         // assemble
         for(final Feature f : sortedFeatureList) {
diff --git a/src/main/java/org/apache/sling/feature/process/BuilderUtil.java b/src/main/java/org/apache/sling/feature/process/BuilderUtil.java
index 377a5be..90da88e 100644
--- a/src/main/java/org/apache/sling/feature/process/BuilderUtil.java
+++ b/src/main/java/org/apache/sling/feature/process/BuilderUtil.java
@@ -31,13 +31,13 @@ import javax.json.JsonValue.ValueType;
 import org.apache.sling.feature.Application;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.Bundles;
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.KeyValueMap;
-import org.apache.sling.feature.Requirement;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 /**
  * Utility methods for the builders
diff --git a/src/main/java/org/apache/sling/feature/process/FeatureResolver.java b/src/main/java/org/apache/sling/feature/process/FeatureResolver.java
new file mode 100644
index 0000000..b3eaa35
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/process/FeatureResolver.java
@@ -0,0 +1,38 @@
+/*
+ * 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.sling.feature.process;
+
+import java.util.List;
+
+import org.apache.sling.feature.Feature;
+
+/**
+ * A resolver that can perform operations on the feature model.
+ */
+public interface FeatureResolver extends AutoCloseable {
+    /**
+     * Order the features by their dependency chain. Each feature and its
+     * components are resolved and each other feature providing the capabilities
+     * needed by the feature is placed before the requiring feature in the
+     * result.
+     *
+     * @param features
+     *            The features to order.
+     * @return The ordered features.
+     */
+    List<Feature> orderFeatures(List<Feature> features);
+}
diff --git a/src/test/java/org/apache/sling/feature/CapabilityRequirementTest.java b/src/test/java/org/apache/sling/feature/CapabilityRequirementTest.java
new file mode 100644
index 0000000..e834bad
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/CapabilityRequirementTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.feature;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class CapabilityRequirementTest {
+    @Test
+    public void testCapability() {
+        Map<String, Object> attrs = new HashMap<>();
+        attrs.put("org.foo", "1234");
+        attrs.put("bar", 456);
+        Map<String, String> dirs = new HashMap<>();
+        dirs.put("my_dir", "my_value");
+        Capability c = new OSGiCapability("org.foo", attrs, dirs);
+        assertEquals("org.foo", c.getNamespace());
+        assertEquals(attrs, c.getAttributes());
+        assertEquals(dirs, c.getDirectives());
+        assertNull(c.getResource());
+    }
+
+    @Test
+    public void testRequirement() {
+        Resource tr = new TestResource();
+        Requirement r = new OSGiRequirement(tr, "testing",
+                Collections.emptyMap(), Collections.emptyMap());
+        assertEquals(tr, r.getResource());
+        assertEquals(0, r.getAttributes().size());
+        assertEquals(0, r.getDirectives().size());
+    }
+
+    private static class TestResource implements Resource {
+        @Override
+        public List<Capability> getCapabilities(String namespace) {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public List<Requirement> getRequirements(String namespace) {
+            return Collections.emptyList();
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/feature/process/FeatureBuilderTest.java b/src/test/java/org/apache/sling/feature/process/FeatureBuilderTest.java
index f990aec..ca27cf5 100644
--- a/src/test/java/org/apache/sling/feature/process/FeatureBuilderTest.java
+++ b/src/test/java/org/apache/sling/feature/process/FeatureBuilderTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -29,13 +30,15 @@ import java.util.Map;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.BundlesTest;
-import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.Include;
-import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.OSGiCapability;
+import org.apache.sling.feature.OSGiRequirement;
 import org.junit.Test;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 public class FeatureBuilderTest {
 
@@ -193,18 +196,19 @@ public class FeatureBuilderTest {
     @Test public void testNoIncludesNoUpgrade() throws Exception {
         final Feature base = new Feature(ArtifactId.parse("org.apache.sling/test-feature/1.1"));
 
-        final Requirement r1 = new Requirement("osgi.contract");
-        r1.getDirectives().put("filter", "(&(osgi.contract=JavaServlet)(version=3.1))");
+        final Requirement r1 = new OSGiRequirement("osgi.contract",
+                Collections.emptyMap(), Collections.singletonMap("filter", "(&(osgi.contract=JavaServlet)(version=3.1))"));
         base.getRequirements().add(r1);
 
-        final Capability c1 = new Capability("osgi.implementation");
-        c1.getAttributes().put("osgi.implementation", "osgi.http");
-        c1.getAttributes().put("version:Version", "1.1");
-        c1.getDirectives().put("uses", "javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard");
+        Map<String, Object> attrs = new HashMap<>();
+        attrs.put("osgi.implementation", "osgi.http");
+        attrs.put("version:Version", "1.1");
+        final Capability c1 = new OSGiCapability("osgi.implementation", attrs,
+                Collections.singletonMap("uses", "javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard"));
         base.getCapabilities().add(c1);
-        final Capability c2 = new Capability("osgi.service");
-        c2.getAttributes().put("objectClass:List<String>", "org.osgi.service.http.runtime.HttpServiceRuntime");
-        c2.getDirectives().put("uses", "org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto");
+        final Capability c2 = new OSGiCapability("osgi.service",
+                Collections.singletonMap("objectClass:List<String>", "org.osgi.service.http.runtime.HttpServiceRuntime"),
+                Collections.singletonMap("uses", "org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto"));
         base.getCapabilities().add(c2);
 
         base.getFrameworkProperties().put("foo", "1");
@@ -241,14 +245,15 @@ public class FeatureBuilderTest {
         final Include i1 = new Include(ArtifactId.parse("g/a/1"));
         base.getIncludes().add(i1);
 
-        final Requirement r1 = new Requirement("osgi.contract");
-        r1.getDirectives().put("filter", "(&(osgi.contract=JavaServlet)(version=3.1))");
+        final Requirement r1 = new OSGiRequirement("osgi.contract",
+                Collections.emptyMap(), Collections.singletonMap("filter", "(&(osgi.contract=JavaServlet)(version=3.1))"));
         base.getRequirements().add(r1);
 
-        final Capability c1 = new Capability("osgi.implementation");
-        c1.getAttributes().put("osgi.implementation", "osgi.http");
-        c1.getAttributes().put("version:Version", "1.1");
-        c1.getDirectives().put("uses", "javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard");
+        Map<String, Object> attrs = new HashMap<>();
+        attrs.put("osgi.implementation", "osgi.http");
+        attrs.put("version:Version", "1.1");
+        final Capability c1 = new OSGiCapability("osgi.implementation", attrs,
+                Collections.singletonMap("uses", "javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard"));
         base.getCapabilities().add(c1);
 
         base.getFrameworkProperties().put("foo", "1");

-- 
To stop receiving notification emails like this one, please contact
davidb@apache.org.