You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/04/11 19:20:52 UTC
[30/33] Revert "[KARAF-2852] Merge features/core and features/command"
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/ConfigFile.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/ConfigFile.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/ConfigFile.java
new file mode 100644
index 0000000..e5d9d94
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/ConfigFile.java
@@ -0,0 +1,136 @@
+/*
+ * 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.features.internal.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+import org.apache.karaf.features.ConfigFileInfo;
+
+
+/**
+ *
+ * Additional configuration files which should be created during feature installation.
+ *
+ *
+ * <p>Java class for configFile complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * <complexType name="configFile">
+ * <simpleContent>
+ * <extension base="<http://www.w3.org/2001/XMLSchema>string">
+ * <attribute name="finalname" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ * <attribute name="override" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ * </extension>
+ * </simpleContent>
+ * </complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "configFile", propOrder = {
+ "value"
+})
+public class ConfigFile implements ConfigFileInfo {
+
+ @XmlValue
+ protected String value;
+ @XmlAttribute(required = true)
+ protected String finalname;
+ @XmlAttribute
+ protected Boolean override;
+
+ /**
+ * Gets the value of the value property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getLocation() {
+ return value;
+ }
+
+ /**
+ * Sets the value of the value property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setLocation(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the value of the finalname property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getFinalname() {
+ return finalname;
+ }
+
+ /**
+ * Sets the value of the finalname property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setFinalname(String value) {
+ this.finalname = value;
+ }
+
+ /**
+ * Gets the value of the override property.
+ *
+ * @return
+ * possible object is
+ * {@link Boolean }
+ *
+ */
+ public boolean isOverride() {
+ return override == null? false: override;
+ }
+
+ /**
+ * Sets the value of the override property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Boolean }
+ *
+ */
+ public void setOverride(Boolean value) {
+ this.override = value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java
new file mode 100644
index 0000000..756e4c1
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Content.java
@@ -0,0 +1,199 @@
+/*
+ * 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.features.internal.model;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.bind.annotation.XmlTransient;
+import org.apache.karaf.features.BundleInfo;
+import org.apache.karaf.features.ConfigFileInfo;
+
+@XmlTransient
+public class Content {
+
+ protected List<Config> config;
+ protected List<ConfigFile> configfile;
+ protected List<Dependency> feature;
+ protected List<Bundle> bundle;
+
+ /**
+ * Gets the value of the config property.
+ * <p/>
+ * <p/>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the config property.
+ * <p/>
+ * <p/>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getConfig().add(newItem);
+ * </pre>
+ * <p/>
+ * <p/>
+ * <p/>
+ * Objects of the following type(s) are allowed in the list
+ * {@link Config }
+ */
+ public List<Config> getConfig() {
+ if (config == null) {
+ config = new ArrayList<Config>();
+ }
+ return this.config;
+ }
+
+ /**
+ * Gets the value of the configfile property.
+ * <p/>
+ * <p/>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the configfile property.
+ * <p/>
+ * <p/>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getConfigfile().add(newItem);
+ * </pre>
+ * <p/>
+ * <p/>
+ * <p/>
+ * Objects of the following type(s) are allowed in the list
+ * {@link ConfigFile }
+ */
+ public List<ConfigFile> getConfigfile() {
+ if (configfile == null) {
+ configfile = new ArrayList<ConfigFile>();
+ }
+ return this.configfile;
+ }
+
+ /**
+ * Gets the value of the feature property.
+ * <p/>
+ * <p/>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the feature property.
+ * <p/>
+ * <p/>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getFeature().add(newItem);
+ * </pre>
+ * <p/>
+ * <p/>
+ * <p/>
+ * Objects of the following type(s) are allowed in the list
+ * {@link Dependency }
+ */
+ public List<Dependency> getFeature() {
+ if (feature == null) {
+ feature = new ArrayList<Dependency>();
+ }
+ return this.feature;
+ }
+
+ /**
+ * Gets the value of the bundle property.
+ * <p/>
+ * <p/>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the bundle property.
+ * <p/>
+ * <p/>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getBundle().add(newItem);
+ * </pre>
+ * <p/>
+ * <p/>
+ * <p/>
+ * Objects of the following type(s) are allowed in the list
+ * {@link Bundle }
+ */
+ public List<Bundle> getBundle() {
+ if (bundle == null) {
+ bundle = new ArrayList<Bundle>();
+ }
+ return this.bundle;
+ }
+
+ public List<org.apache.karaf.features.Dependency> getDependencies() {
+ return Collections.<org.apache.karaf.features.Dependency>unmodifiableList(getFeature());
+ }
+
+ public List<BundleInfo> getBundles() {
+ return Collections.<BundleInfo>unmodifiableList(getBundle());
+ }
+
+ public Map<String, Map<String, String>> getConfigurations() {
+ Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
+ for (Config config : getConfig()) {
+ String name = config.getName();
+ StringReader propStream = new StringReader(config.getValue());
+ Properties props = new Properties();
+ try {
+ props.load(propStream);
+ } catch (IOException e) {
+ //ignore??
+ }
+ interpolation(props);
+ Map<String, String> propMap = new HashMap<String, String>();
+ for (Map.Entry<Object, Object> entry : props.entrySet()) {
+ propMap.put((String) entry.getKey(), (String) entry.getValue());
+ }
+ result.put(name, propMap);
+ }
+ return result;
+ }
+
+ public List<ConfigFileInfo> getConfigurationFiles() {
+ return Collections.<ConfigFileInfo>unmodifiableList(getConfigfile());
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected void interpolation(Properties properties) {
+ for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) {
+ String key = (String) e.nextElement();
+ String val = properties.getProperty(key);
+ Matcher matcher = Pattern.compile("\\$\\{([^}]+)\\}").matcher(val);
+ while (matcher.find()) {
+ String rep = System.getProperty(matcher.group(1));
+ if (rep != null) {
+ val = val.replace(matcher.group(0), rep);
+ matcher.reset(val);
+ }
+ }
+ properties.put(key, val);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
new file mode 100644
index 0000000..9c92a93
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
@@ -0,0 +1,121 @@
+/*
+ * 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.features.internal.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+
+
+/**
+ *
+ * Dependency of feature.
+ *
+ *
+ * <p>Java class for dependency complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * <complexType name="dependency">
+ * <simpleContent>
+ * <extension base="<http://karaf.apache.org/xmlns/features/v1.0.0>featureName">
+ * <attribute name="version" type="{http://www.w3.org/2001/XMLSchema}string" default="0.0.0" />
+ * </extension>
+ * </simpleContent>
+ * </complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "dependency", propOrder = {
+ "value"
+})
+public class Dependency implements org.apache.karaf.features.Dependency {
+
+ @XmlValue
+ protected String value;
+ @XmlAttribute
+ protected String version;
+
+ /**
+ *
+ * Feature name should be non empty string.
+ *
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return value;
+ }
+
+ /**
+ * Sets the value of the value property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the value of the version property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getVersion() {
+ if (version == null) {
+ return "0.0.0";
+ } else {
+ return version;
+ }
+ }
+
+ /**
+ * Sets the value of the version property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setVersion(String value) {
+ this.version = value;
+ }
+
+ public String toString() {
+ String ret = getName() + Feature.SPLIT_FOR_NAME_AND_VERSION + getVersion();
+ return ret;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
new file mode 100644
index 0000000..46580da
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
@@ -0,0 +1,374 @@
+/*
+ * 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.features.internal.model;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ *
+ * Definition of the Feature.
+ *
+ *
+ * <p>Java class for feature complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * <complexType name="feature">
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="details" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ * <element name="config" type="{http://karaf.apache.org/xmlns/features/v1.0.0}config" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="configfile" type="{http://karaf.apache.org/xmlns/features/v1.0.0}configFile" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="feature" type="{http://karaf.apache.org/xmlns/features/v1.0.0}dependency" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="bundle" type="{http://karaf.apache.org/xmlns/features/v1.0.0}bundle" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="conditional" type="{http://karaf.apache.org/xmlns/features/v1.0.0}conditional" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="capability" type="{http://karaf.apache.org/xmlns/features/v1.0.0}capability" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="requirement" type="{http://karaf.apache.org/xmlns/features/v1.0.0}requirement" maxOccurs="unbounded" minOccurs="0"/>
+ * </sequence>
+ * <attribute name="name" use="required" type="{http://karaf.apache.org/xmlns/features/v1.0.0}featureName" />
+ * <attribute name="version" type="{http://www.w3.org/2001/XMLSchema}string" default="0.0.0" />
+ * <attribute name="description" type="{http://www.w3.org/2001/XMLSchema}string" />
+ * <attribute name="resolver" type="{http://karaf.apache.org/xmlns/features/v1.0.0}resolver" />
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "feature", propOrder = {
+ "details",
+ "config",
+ "configfile",
+ "feature",
+ "bundle",
+ "conditional",
+ "capability",
+ "requirement"
+})
+public class Feature extends Content implements org.apache.karaf.features.Feature {
+ public static String SPLIT_FOR_NAME_AND_VERSION = "/";
+ public static String DEFAULT_VERSION = "0.0.0";
+
+
+ protected String details;
+ @XmlAttribute(required = true)
+ protected String name;
+ @XmlAttribute
+ protected String version;
+ @XmlAttribute
+ protected String description;
+ @XmlAttribute
+ protected String resolver;
+ @XmlAttribute
+ protected String install;
+ @XmlAttribute(name = "start-level")
+ protected Integer startLevel;
+ @XmlAttribute
+ protected String region;
+ protected List<Conditional> conditional;
+ protected List<Capability> capability;
+ protected List<Requirement> requirement;
+
+ public Feature() {
+ }
+
+ public Feature(String name) {
+ this.name = name;
+ }
+
+ public Feature(String name, String version) {
+ this.name = name;
+ this.version = version;
+ }
+
+
+ public static org.apache.karaf.features.Feature valueOf(String str) {
+ if (str.contains(SPLIT_FOR_NAME_AND_VERSION)) {
+ String strName = str.substring(0, str.indexOf(SPLIT_FOR_NAME_AND_VERSION));
+ String strVersion = str.substring(str.indexOf(SPLIT_FOR_NAME_AND_VERSION)
+ + SPLIT_FOR_NAME_AND_VERSION.length(), str.length());
+ return new Feature(strName, strVersion);
+ } else {
+ return new Feature(str);
+ }
+
+
+ }
+
+
+ public String getId() {
+ return getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
+ }
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+ /**
+ * Gets the value of the version property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getVersion() {
+ if (version == null) {
+ return DEFAULT_VERSION;
+ } else {
+ return version;
+ }
+ }
+
+ /**
+ * Sets the value of the version property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setVersion(String value) {
+ this.version = value;
+ }
+
+ /**
+ * Since version has a default value ("0.0.0"), returns
+ * whether or not the version has been set.
+ */
+ public boolean hasVersion() {
+ return this.version != null;
+ }
+
+ /**
+ * Gets the value of the description property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the value of the description property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setDescription(String value) {
+ this.description = value;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+ public void setDetails(String details) {
+ this.details = details;
+ }
+
+ /**
+ * Gets the value of the resolver property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getResolver() {
+ return resolver;
+ }
+
+ public String getInstall() {
+ return install;
+ }
+
+ public void setInstall(String install) {
+ this.install = install;
+ }
+
+ /**
+ * Sets the value of the resolver property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setResolver(String value) {
+ this.resolver = value;
+ }
+
+ /**
+ * Gets the value of the startLevel property.
+ *
+ * @return
+ * possible object is
+ * {@link Integer }
+ *
+ */
+ public int getStartLevel() {
+ return startLevel == null? 0: startLevel;
+ }
+
+ /**
+ * Sets the value of the startLevel property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Integer }
+ *
+ */
+ public void setStartLevel(Integer value) {
+ this.startLevel = value;
+ }
+
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ /**
+ * Gets the value of the conditional property.
+ * <p/>
+ * <p/>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the feature property.
+ * <p/>
+ * <p/>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getConditionals().add(newItem);
+ * </pre>
+ * <p/>
+ * <p/>
+ * <p/>
+ * Objects of the following type(s) are allowed in the list
+ * {@link Conditional }
+ */
+ public List<Conditional> getConditional() {
+ if (conditional == null) {
+ conditional = new ArrayList<Conditional>();
+ }
+ return this.conditional;
+ }
+
+ public List<Capability> getCapabilities() {
+ if (capability == null) {
+ capability = new ArrayList<Capability>();
+ }
+ return this.capability;
+ }
+
+ public List<Requirement> getRequirements() {
+ if (requirement == null) {
+ requirement = new ArrayList<Requirement>();
+ }
+ return this.requirement;
+ }
+
+ public String toString() {
+ return getId();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Feature feature = (Feature) o;
+
+ if (name != null ? !name.equals(feature.name) : feature.name != null) return false;
+ if (version != null ? !version.equals(feature.version) : feature.version != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (version != null ? version.hashCode() : 0);
+ return result;
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected void interpolation(Properties properties) {
+ for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ String val = properties.getProperty(key);
+ Matcher matcher = Pattern.compile("\\$\\{([^}]+)\\}").matcher(val);
+ while (matcher.find()) {
+ String rep = System.getProperty(matcher.group(1));
+ if (rep != null) {
+ val = val.replace(matcher.group(0), rep);
+ matcher.reset(val);
+ }
+ }
+ properties.put(key, val);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/Features.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Features.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Features.java
new file mode 100644
index 0000000..e116f31
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Features.java
@@ -0,0 +1,155 @@
+/*
+ * 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.features.internal.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ *
+ * Root element of Feature definition. It contains optional attribute which allow
+ * name of repository. This name will be used in shell to display source repository
+ * of given feature.
+ *
+ *
+ * <p>Java class for featuresRoot complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * <complexType name="features">
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="repository" type="{http://www.w3.org/2001/XMLSchema}anyURI" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="feature" type="{http://karaf.apache.org/xmlns/features/v1.0.0}feature" maxOccurs="unbounded" minOccurs="0"/>
+ * </sequence>
+ * <attribute name="name" type="{http://www.w3.org/2001/XMLSchema}string" />
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlRootElement(name = "features")
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "features", propOrder = {
+ "repository",
+ "feature"
+})
+public class Features {
+
+ @XmlSchemaType(name = "anyURI")
+ protected List<String> repository;
+ protected List<Feature> feature;
+ @XmlAttribute
+ protected String name;
+
+ /**
+ * Gets the value of the repository property.
+ *
+ * <p>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the repository property.
+ *
+ * <p>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getRepository().add(newItem);
+ * </pre>
+ *
+ *
+ * <p>
+ * Objects of the following type(s) are allowed in the list
+ * {@link String }
+ *
+ *
+ */
+ public List<String> getRepository() {
+ if (repository == null) {
+ repository = new ArrayList<String>();
+ }
+ return this.repository;
+ }
+
+ /**
+ * Gets the value of the feature property.
+ *
+ * <p>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the feature property.
+ *
+ * <p>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getFeature().add(newItem);
+ * </pre>
+ *
+ *
+ * <p>
+ * Objects of the following type(s) are allowed in the list
+ * {@link Feature }
+ *
+ *
+ */
+ public List<Feature> getFeature() {
+ if (feature == null) {
+ feature = new ArrayList<Feature>();
+ }
+ return this.feature;
+ }
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java
new file mode 100644
index 0000000..2036452
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/JaxbUtil.java
@@ -0,0 +1,224 @@
+/*
+ * 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.features.internal.model;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.net.URL;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.namespace.QName;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.karaf.features.FeaturesNamespaces;
+import org.apache.karaf.util.XmlUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+public class JaxbUtil {
+
+ private static final JAXBContext FEATURES_CONTEXT;
+ static {
+ try {
+ FEATURES_CONTEXT = JAXBContext.newInstance(Features.class);
+ } catch (JAXBException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void marshal(Features features, OutputStream out) throws JAXBException {
+ Marshaller marshaller = FEATURES_CONTEXT.createMarshaller();
+
+ marshaller.setProperty("jaxb.formatted.output", true);
+
+ marshaller.marshal(features, out);
+ }
+
+ public static void marshal(Features features, Writer out) throws JAXBException {
+ Marshaller marshaller = FEATURES_CONTEXT.createMarshaller();
+
+ marshaller.setProperty("jaxb.formatted.output", true);
+
+ marshaller.marshal(features, out);
+ }
+
+
+ /**
+ * Read in a Features from the input stream.
+ *
+ * @param uri uri to read
+ * @param validate whether to validate the input.
+ * @return a Features read from the input stream
+ */
+ public static Features unmarshal(String uri, boolean validate) {
+ if (validate) {
+ return unmarshalValidate(uri, null);
+ } else {
+ return unmarshalNoValidate(uri, null);
+ }
+ }
+
+ public static Features unmarshal(String uri, InputStream stream, boolean validate) {
+ if (validate) {
+ return unmarshalValidate(uri, stream);
+ } else {
+ return unmarshalNoValidate(uri, stream);
+ }
+ }
+
+ private static Features unmarshalValidate(String uri, InputStream stream) {
+ try {
+ Document doc;
+ if (stream != null) {
+ doc = XmlUtils.parse(stream);
+ doc.setDocumentURI(uri);
+ } else {
+ doc = XmlUtils.parse(uri);
+ }
+
+ Schema schema = getSchema(doc.getDocumentElement().getNamespaceURI());
+ try {
+ schema.newValidator().validate(new DOMSource(doc));
+ } catch (SAXException e) {
+ throw new IllegalArgumentException("Unable to validate " + uri, e);
+ }
+
+ fixDom(doc, doc.getDocumentElement());
+ Unmarshaller unmarshaller = FEATURES_CONTEXT.createUnmarshaller();
+ return (Features) unmarshaller.unmarshal(new DOMSource(doc));
+
+
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to load " + uri, e);
+ }
+ }
+
+ private static Map<String, Schema> schemas = new ConcurrentHashMap<String, Schema>();
+ private static Schema getSchema(String namespace) throws SAXException {
+ Schema schema = schemas.get(namespace);
+ if (schema == null) {
+ String schemaLocation;
+ if (FeaturesNamespaces.URI_1_0_0.equals(namespace)) {
+ schemaLocation = "/org/apache/karaf/features/karaf-features-1.0.0.xsd";
+ } else if (FeaturesNamespaces.URI_1_1_0.equals(namespace)) {
+ schemaLocation = "/org/apache/karaf/features/karaf-features-1.1.0.xsd";
+ } else if (FeaturesNamespaces.URI_1_2_0.equals(namespace)) {
+ schemaLocation = "/org/apache/karaf/features/karaf-features-1.2.0.xsd";
+ } else if (FeaturesNamespaces.URI_1_3_0.equals(namespace)) {
+ schemaLocation = "/org/apache/karaf/features/karaf-features-1.3.0.xsd";
+ } else {
+ throw new IllegalArgumentException("Unsupported namespace: " + namespace);
+ }
+
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ // root element has namespace - we can use schema validation
+ URL url = JaxbUtil.class.getResource(schemaLocation);
+ if (url == null) {
+ throw new IllegalStateException("Could not find resource: " + schemaLocation);
+ }
+ schema = factory.newSchema(new StreamSource(url.toExternalForm()));
+ schemas.put(namespace, schema);
+ }
+ return schema;
+ }
+
+
+ private static void fixDom(Document doc, Node node) {
+ if (node.getNamespaceURI() != null && !FeaturesNamespaces.URI_CURRENT.equals(node.getNamespaceURI())) {
+ doc.renameNode(node, FeaturesNamespaces.URI_CURRENT, node.getLocalName());
+ }
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ fixDom(doc, children.item(i));
+ }
+ }
+
+ private static Features unmarshalNoValidate(String uri, InputStream stream) {
+ try {
+ Unmarshaller unmarshaller = FEATURES_CONTEXT.createUnmarshaller();
+ XMLFilter xmlFilter = new NoSourceAndNamespaceFilter(XmlUtils.xmlReader());
+ xmlFilter.setContentHandler(unmarshaller.getUnmarshallerHandler());
+
+
+ InputSource is = new InputSource(uri);
+ if (stream != null) {
+ is.setByteStream(stream);
+ }
+ SAXSource source = new SAXSource(xmlFilter, new InputSource(uri));
+ return (Features) unmarshaller.unmarshal(source);
+
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to load " + uri, e);
+ }
+ }
+
+ /**
+ * Provides an empty inputsource for the entity resolver.
+ * Converts all elements to the features namespace to make old feature files
+ * compatible to the new format
+ */
+ public static class NoSourceAndNamespaceFilter extends XMLFilterImpl {
+ private static final InputSource EMPTY_INPUT_SOURCE = new InputSource(new ByteArrayInputStream(new byte[0]));
+
+ public NoSourceAndNamespaceFilter(XMLReader xmlReader) {
+ super(xmlReader);
+ }
+
+ @Override
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+ return EMPTY_INPUT_SOURCE;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+ super.startElement(FeaturesNamespaces.URI_CURRENT, localName, qName, atts);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ super.endElement(FeaturesNamespaces.URI_CURRENT, localName, qName);
+ }
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/ObjectFactory.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/ObjectFactory.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/ObjectFactory.java
new file mode 100644
index 0000000..96fbb0f
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/ObjectFactory.java
@@ -0,0 +1,111 @@
+/*
+ * 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.features.internal.model;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.annotation.XmlElementDecl;
+import javax.xml.bind.annotation.XmlRegistry;
+import javax.xml.namespace.QName;
+
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the org.apache.karaf.features.wrapper package.
+ * <p>An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+ private final static QName _Features_QNAME = new QName("http://karaf.apache.org/xmlns/features/v1.0.0", "features");
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.apache.karaf.features.wrapper
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link ConfigFile }
+ *
+ */
+ public ConfigFile createConfigFile() {
+ return new ConfigFile();
+ }
+
+ /**
+ * Create an instance of {@link Dependency }
+ *
+ */
+ public Dependency createDependency() {
+ return new Dependency();
+ }
+
+ /**
+ * Create an instance of {@link Bundle }
+ *
+ */
+ public Bundle createBundle() {
+ return new Bundle();
+ }
+
+ /**
+ * Create an instance of {@link Features }
+ *
+ */
+ public Features createFeaturesRoot() {
+ return new Features();
+ }
+
+ /**
+ * Create an instance of {@link Config }
+ *
+ */
+ public Config createConfig() {
+ return new Config();
+ }
+
+ /**
+ * Create an instance of {@link Feature }
+ *
+ */
+ public Feature createFeature() {
+ return new Feature();
+ }
+
+ /**
+ * Create an instance of {@link JAXBElement }{@code <}{@link Features }{@code >}}
+ *
+ */
+ @XmlElementDecl(namespace = "http://karaf.apache.org/xmlns/features/v1.0.0", name = "features")
+ public JAXBElement<Features> createFeatures(Features value) {
+ return new JAXBElement<Features>(_Features_QNAME, Features.class, null, value);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/Requirement.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Requirement.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Requirement.java
new file mode 100644
index 0000000..f7b5775
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Requirement.java
@@ -0,0 +1,87 @@
+/*
+ * 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.features.internal.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+
+
+/**
+ *
+ * Additional requirement for a feature.
+ *
+ *
+ * <p>Java class for bundle complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * <complexType name="capability">
+ * <simpleContent>
+ * <extension base="<http://www.w3.org/2001/XMLSchema>string">
+ * </extension>
+ * </simpleContent>
+ * </complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "requirement", propOrder = {
+ "value"
+})
+public class Requirement implements org.apache.karaf.features.Requirement {
+
+ @XmlValue
+ protected String value;
+
+
+ public Requirement() {
+ }
+
+ public Requirement(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Requirement bundle = (Requirement) o;
+
+ if (value != null ? !value.equals(bundle.value) : bundle.value != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = value != null ? value.hashCode() : 0;
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java
new file mode 100644
index 0000000..c86a58c
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+@javax.xml.bind.annotation.XmlSchema(namespace = org.apache.karaf.features.FeaturesNamespaces.URI_CURRENT, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
+package org.apache.karaf.features.internal.model;
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java b/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
new file mode 100644
index 0000000..be0da05
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
@@ -0,0 +1,208 @@
+/*
+ * 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.features.internal.osgi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.apache.karaf.features.FeaturesListener;
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.internal.service.EventAdminListener;
+import org.apache.karaf.features.internal.service.FeatureConfigInstaller;
+import org.apache.karaf.features.internal.service.FeatureFinder;
+import org.apache.karaf.features.internal.service.BootFeaturesInstaller;
+import org.apache.karaf.features.internal.service.FeaturesServiceImpl;
+import org.apache.karaf.features.internal.service.StateStorage;
+import org.apache.karaf.features.internal.management.FeaturesServiceMBeanImpl;
+import org.apache.karaf.features.RegionsPersistence;
+import org.apache.karaf.util.tracker.BaseActivator;
+import org.apache.karaf.util.tracker.SingleServiceTracker;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.url.URLStreamHandlerService;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public class Activator extends BaseActivator {
+
+ public static final String FEATURES_REPOS_PID = "org.apache.karaf.features.repos";
+ public static final String FEATURES_SERVICE_CONFIG_FILE = "org.apache.karaf.features.cfg";
+
+ private ServiceTracker<FeaturesListener, FeaturesListener> featuresListenerTracker;
+ private FeaturesServiceImpl featuresService;
+ private SingleServiceTracker<RegionsPersistence> regionsTracker;
+
+ public Activator() {
+ // Special case here, as we don't want the activator to wait for current job to finish,
+ // else it would forbid the features service to refresh itself
+ setSchedulerStopTimeout(0);
+ }
+
+ @Override
+ protected void doOpen() throws Exception {
+ trackService(URLStreamHandlerService.class, "(url.handler.protocol=mvn)");
+ trackService(ConfigurationAdmin.class);
+
+ Properties configuration = new Properties();
+ File configFile = new File(System.getProperty("karaf.etc"), FEATURES_SERVICE_CONFIG_FILE);
+ if (configFile.isFile() && configFile.canRead()) {
+ try {
+ configuration.load(new FileReader(configFile));
+ } catch (IOException e) {
+ logger.warn("Error reading configuration file " + configFile.toString(), e);
+ }
+ }
+ updated((Dictionary) configuration);
+ }
+
+ protected void doStart() throws Exception {
+ ConfigurationAdmin configurationAdmin = getTrackedService(ConfigurationAdmin.class);
+ URLStreamHandlerService mvnUrlHandler = getTrackedService(URLStreamHandlerService.class);
+
+ if (configurationAdmin == null || mvnUrlHandler == null) {
+ return;
+ }
+
+ FeatureFinder featureFinder = new FeatureFinder();
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put(Constants.SERVICE_PID, FEATURES_REPOS_PID);
+ register(ManagedService.class, featureFinder, props);
+
+ // TODO: region support
+// final BundleManager bundleManager = new BundleManager(bundleContext);
+// regionsTracker = new SingleServiceTracker<RegionsPersistence>(bundleContext, RegionsPersistence.class,
+// new SingleServiceTracker.SingleServiceListener() {
+// @Override
+// public void serviceFound() {
+// bundleManager.setRegionsPersistence(regionsTracker.getService());
+// }
+// @Override
+// public void serviceLost() {
+// serviceFound();
+// }
+// @Override
+// public void serviceReplaced() {
+// serviceFound();
+// }
+// });
+// regionsTracker.open();
+
+
+ FeatureConfigInstaller configInstaller = new FeatureConfigInstaller(configurationAdmin);
+ // TODO: honor respectStartLvlDuringFeatureStartup and respectStartLvlDuringFeatureUninstall
+// boolean respectStartLvlDuringFeatureStartup = getBoolean("respectStartLvlDuringFeatureStartup", true);
+// boolean respectStartLvlDuringFeatureUninstall = getBoolean("respectStartLvlDuringFeatureUninstall", true);
+ String overrides = getString("overrides", new File(System.getProperty("karaf.etc"), "overrides.properties").toURI().toString());
+ String featureResolutionRange = getString("featureResolutionRange", FeaturesServiceImpl.DEFAULT_FEATURE_RESOLUTION_RANGE);
+ String bundleUpdateRange = getString("bundleUpdateRange", FeaturesServiceImpl.DEFAULT_BUNDLE_UPDATE_RANGE);
+ String updateSnapshots = getString("updateSnapshots", FeaturesServiceImpl.DEFAULT_UPDATE_SNAPSHOTS);
+ StateStorage stateStorage = new StateStorage() {
+ @Override
+ protected InputStream getInputStream() throws IOException {
+ File file = bundleContext.getDataFile("FeaturesServiceState.properties");
+ if (file.exists()) {
+ return new FileInputStream(file);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected OutputStream getOutputStream() throws IOException {
+ File file = bundleContext.getDataFile("FeaturesServiceState.properties");
+ return new FileOutputStream(file);
+ }
+ };
+ EventAdminListener eventAdminListener;
+ try {
+ eventAdminListener = new EventAdminListener(bundleContext);
+ } catch (Throwable t) {
+ eventAdminListener = null;
+ }
+ featuresService = new FeaturesServiceImpl(
+ bundleContext.getBundle(),
+ bundleContext.getBundle(0).getBundleContext(),
+ stateStorage,
+ featureFinder,
+ eventAdminListener,
+ configInstaller,
+ overrides,
+ featureResolutionRange,
+ bundleUpdateRange,
+ updateSnapshots);
+ register(FeaturesService.class, featuresService);
+
+ featuresListenerTracker = new ServiceTracker<FeaturesListener, FeaturesListener>(
+ bundleContext, FeaturesListener.class, new ServiceTrackerCustomizer<FeaturesListener, FeaturesListener>() {
+ @Override
+ public FeaturesListener addingService(ServiceReference<FeaturesListener> reference) {
+ FeaturesListener service = bundleContext.getService(reference);
+ featuresService.registerListener(service);
+ return service;
+ }
+ @Override
+ public void modifiedService(ServiceReference<FeaturesListener> reference, FeaturesListener service) {
+ }
+ @Override
+ public void removedService(ServiceReference<FeaturesListener> reference, FeaturesListener service) {
+ featuresService.unregisterListener(service);
+ bundleContext.ungetService(reference);
+ }
+ }
+ );
+ featuresListenerTracker.open();
+
+ String featuresRepositories = getString("featuresRepositories", "");
+ String featuresBoot = getString("featuresBoot", "");
+ boolean featuresBootAsynchronous = getBoolean("featuresBootAsynchronous", false);
+ BootFeaturesInstaller bootFeaturesInstaller = new BootFeaturesInstaller(
+ bundleContext, featuresService,
+ featuresRepositories, featuresBoot, featuresBootAsynchronous);
+ bootFeaturesInstaller.start();
+
+ FeaturesServiceMBeanImpl featuresServiceMBean = new FeaturesServiceMBeanImpl();
+ featuresServiceMBean.setBundleContext(bundleContext);
+ featuresServiceMBean.setFeaturesService(featuresService);
+ registerMBean(featuresServiceMBean, "type=feature");
+ }
+
+ protected void doStop() {
+ if (regionsTracker != null) {
+ regionsTracker.close();
+ regionsTracker = null;
+ }
+ if (featuresListenerTracker != null) {
+ featuresListenerTracker.close();
+ featuresListenerTracker = null;
+ }
+ super.doStop();
+ if (featuresService != null) {
+ featuresService = null;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/repository/AggregateRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/AggregateRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/AggregateRepository.java
new file mode 100644
index 0000000..0d5e83d
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/AggregateRepository.java
@@ -0,0 +1,55 @@
+/*
+ * 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.features.internal.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.service.repository.Repository;
+
+public class AggregateRepository implements Repository {
+
+ private final Collection<Repository> repositories;
+
+ public AggregateRepository(Collection<Repository> repositories) {
+ this.repositories = repositories;
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>();
+ for (Requirement requirement : requirements) {
+ List<Capability> caps = new ArrayList<Capability>();
+ for (Repository repository : repositories) {
+ Map<Requirement, Collection<Capability>> resMap =
+ repository.findProviders(Collections.singleton(requirement));
+ Collection<Capability> res = resMap != null ? resMap.get(requirement) : null;
+ if (res != null) {
+ caps.addAll(res);
+ }
+ }
+ result.put(requirement, caps);
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
new file mode 100644
index 0000000..c4c0d16
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
@@ -0,0 +1,86 @@
+/*
+ * 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.features.internal.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.karaf.features.internal.resolver.CapabilitySet;
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
+import org.apache.karaf.features.internal.resolver.SimpleFilter;
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.service.repository.Repository;
+
+/**
+ */
+public class BaseRepository implements Repository {
+
+ protected final List<Resource> resources;
+ protected final Map<String, CapabilitySet> capSets;
+
+ public BaseRepository() {
+ this.resources = new ArrayList<Resource>();
+ this.capSets = new HashMap<String, CapabilitySet>();
+ }
+
+ protected void addResource(Resource resource) {
+ for (Capability cap : resource.getCapabilities(null)) {
+ String ns = cap.getNamespace();
+ CapabilitySet set = capSets.get(ns);
+ if (set == null) {
+ set = new CapabilitySet(Collections.singletonList(ns));
+ capSets.put(ns, set);
+ }
+ set.addCapability(cap);
+ }
+ resources.add(resource);
+ }
+
+ public List<Resource> getResources() {
+ return resources;
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>();
+ for (Requirement requirement : requirements) {
+ CapabilitySet set = capSets.get(requirement.getNamespace());
+ if (set != null) {
+ SimpleFilter sf;
+ if (requirement instanceof RequirementImpl) {
+ sf = ((RequirementImpl) requirement).getFilter();
+ } else {
+ String filter = requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
+ sf = (filter != null)
+ ? SimpleFilter.parse(filter)
+ : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ }
+ result.put(requirement, set.match(sf, true));
+ } else {
+ result.put(requirement, Collections.<Capability>emptyList());
+ }
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java
new file mode 100644
index 0000000..7916821
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.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.karaf.features.internal.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.service.repository.Repository;
+
+public class CacheRepository implements Repository {
+
+ private final Repository repository;
+ private final Map<Requirement, Collection<Capability>> cache =
+ new ConcurrentHashMap<Requirement, Collection<Capability>>();
+
+ public CacheRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ List<Requirement> missing = new ArrayList<Requirement>();
+ Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>();
+ for (Requirement requirement : requirements) {
+ Collection<Capability> caps = cache.get(requirement);
+ if (caps == null) {
+ missing.add(requirement);
+ } else {
+ result.put(requirement, caps);
+ }
+ }
+ Map<Requirement, Collection<Capability>> newCache = repository.findProviders(missing);
+ for (Requirement requirement : newCache.keySet()) {
+ cache.put(requirement, newCache.get(requirement));
+ result.put(requirement, newCache.get(requirement));
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
new file mode 100644
index 0000000..1aecef1
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
@@ -0,0 +1,88 @@
+/*
+ * 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.features.internal.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.karaf.features.internal.util.JsonReader;
+
+/**
+ */
+public class HttpMetadataProvider implements MetadataProvider {
+
+ public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
+ public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
+ public static final String GZIP = "gzip";
+
+ private final String url;
+ private long lastModified;
+ private Map<String, Map<String, String>> metadatas;
+
+ public HttpMetadataProvider(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ @Override
+ public Map<String, Map<String, String>> getMetadatas() {
+ try {
+ HttpURLConnection.setFollowRedirects(false);
+ HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
+ if (lastModified > 0) {
+ con.setIfModifiedSince(lastModified);
+ }
+ con.setRequestProperty(HEADER_ACCEPT_ENCODING, GZIP);
+ if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ lastModified = con.getLastModified();
+ InputStream is = con.getInputStream();
+ if (GZIP.equals(con.getHeaderField(HEADER_CONTENT_ENCODING))) {
+ is = new GZIPInputStream(is);
+ }
+ metadatas = verify(JsonReader.read(is));
+ } else if (con.getResponseCode() != HttpURLConnection.HTTP_NOT_MODIFIED) {
+ throw new IOException("Unexpected http response: "
+ + con.getResponseCode() + " " + con.getResponseMessage());
+ }
+ return metadatas;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Map<String, Map<String, String>> verify(Object value) {
+ Map<?,?> obj = Map.class.cast(value);
+ for (Map.Entry<?,?> entry : obj.entrySet()) {
+ String.class.cast(entry.getKey());
+ Map<?,?> child = Map.class.cast(entry.getValue());
+ for (Map.Entry<?,?> ce : child.entrySet()) {
+ String.class.cast(ce.getKey());
+ String.class.cast(ce.getValue());
+ }
+ }
+ return (Map<String, Map<String, String>>) obj;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
new file mode 100644
index 0000000..9ac54a1
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
@@ -0,0 +1,29 @@
+/*
+ * 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.features.internal.repository;
+
+import java.util.Map;
+
+/**
+ */
+public interface MetadataProvider {
+
+ long getLastModified();
+
+ Map<String, Map<String, String>> getMetadatas();
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
new file mode 100644
index 0000000..2d4fbba
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.features.internal.repository;
+
+import java.util.Map;
+
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.osgi.resource.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ */
+public class MetadataRepository extends BaseRepository {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MetadataRepository.class);
+
+ public MetadataRepository(MetadataProvider provider) {
+ Map<String, Map<String, String>> metadatas = provider.getMetadatas();
+ for (Map.Entry<String, Map<String, String>> metadata : metadatas.entrySet()) {
+ try {
+ Resource resource = ResourceBuilder.build(metadata.getKey(), metadata.getValue());
+ addResource(resource);
+ } catch (Exception e) {
+ LOGGER.info("Unable to build resource for " + metadata.getKey(), e);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
new file mode 100644
index 0000000..f289c8d
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
@@ -0,0 +1,33 @@
+/*
+ * 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.features.internal.repository;
+
+import java.util.Collection;
+
+import org.osgi.resource.Resource;
+
+/**
+ */
+public class StaticRepository extends BaseRepository {
+
+ public StaticRepository(Collection<Resource> resources) {
+ for (Resource resource : resources) {
+ addResource(resource);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/0c8e8a81/features/core/src/main/java/org/apache/karaf/features/internal/resolver/BaseClause.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/BaseClause.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/BaseClause.java
new file mode 100644
index 0000000..0653398
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/BaseClause.java
@@ -0,0 +1,114 @@
+/*
+ * 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.features.internal.resolver;
+
+import java.util.Map;
+
+import org.osgi.framework.Version;
+import org.osgi.resource.Resource;
+
+/**
+ */
+public abstract class BaseClause {
+
+ public abstract Resource getResource();
+
+ public abstract String getNamespace();
+
+ public abstract Map<String, String> getDirectives();
+
+ public abstract Map<String, Object> getAttributes();
+
+ @Override
+ public String toString() {
+ return toString(getResource(), getNamespace(), getAttributes(), getDirectives());
+ }
+
+ public static String toString(Resource res, String namespace, Map<String, Object> attrs, Map<String, String> dirs) {
+ StringBuilder sb = new StringBuilder();
+ if (res != null) {
+ sb.append("[").append(res).append("] ");
+ }
+ sb.append(namespace);
+ for (String key : attrs.keySet()) {
+ sb.append("; ");
+ append(sb, key, attrs.get(key), true);
+ }
+ for (String key : dirs.keySet()) {
+ sb.append("; ");
+ append(sb, key, dirs.get(key), false);
+ }
+ return sb.toString();
+ }
+
+ private static void append(StringBuilder sb, String key, Object val, boolean attribute) {
+ sb.append(key);
+ if (val instanceof Version) {
+ sb.append(":Version=");
+ sb.append(val);
+ } else if (val instanceof Long) {
+ sb.append(":Long=");
+ sb.append(val);
+ } else if (val instanceof Double) {
+ sb.append(":Double=");
+ sb.append(val);
+ } else if (val instanceof Iterable) {
+ Iterable it = (Iterable) val;
+ String scalar = null;
+ for (Object o : it) {
+ String ts;
+ if (o instanceof String) {
+ ts = "String";
+ } else if (o instanceof Long) {
+ ts = "Long";
+ } else if (o instanceof Double) {
+ ts = "Double";
+ } else if (o instanceof Version) {
+ ts = "Version";
+ } else {
+ throw new IllegalArgumentException("Unsupported scalar type: " + o);
+ }
+ if (scalar == null) {
+ scalar = ts;
+ } else if (!scalar.equals(ts)) {
+ throw new IllegalArgumentException("Unconsistent list type for attribute " + key);
+ }
+ }
+ sb.append(":List<").append(scalar).append(">=");
+ sb.append("\"");
+ boolean first = true;
+ for (Object o : it) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(o.toString().replace("\"", "\\\"").replace(",", "\\,"));
+ }
+ sb.append("\"");
+ } else {
+ sb.append(attribute ? "=" : ":=");
+ String s = val.toString();
+ if (s.matches("[0-9a-zA-Z_\\-.]*")) {
+ sb.append(s);
+ } else {
+ sb.append("\"").append(s.replace("\"", "\\\\")).append("\"");
+ }
+ }
+ }
+
+}