karaf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gno...@apache.org
Subject [1/2] [KARAF-1718] Add support for support for conditional bundle and feature definition inside features
Date Mon, 21 Jul 2014 16:32:22 GMT
Repository: karaf
Updated Branches:
  refs/heads/karaf-2.x afb7ecf67 -> d380c4d0c


http://git-wip-us.apache.org/repos/asf/karaf/blob/d380c4d0/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
index 982de6e..c0fc354 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
@@ -157,72 +157,28 @@ public class RepositoryImpl implements Repository {
                     if (details != null && details.length() > 0)
                         f.setDetails(details);
 
-                    NodeList featureNodes = e.getElementsByTagName("feature");
-                    for (int j = 0; j < featureNodes.getLength(); j++) {
-                        Element b = (Element) featureNodes.item(j);
-                        String dependencyFeatureVersion = b.getAttribute("version");
-                        if (dependencyFeatureVersion != null && dependencyFeatureVersion.length() > 0) {
-                            f.addDependency(new FeatureImpl(b.getTextContent(), dependencyFeatureVersion));
-                        } else {
-                            f.addDependency(new FeatureImpl(b.getTextContent()));
-                        }
-                    }
-                    NodeList configNodes = e.getElementsByTagName("config");
-                    for (int j = 0; j < configNodes.getLength(); j++) {
-                        Element c = (Element) configNodes.item(j);
-                        String cfgName = c.getAttribute("name");
-                        String data = c.getTextContent();
-                        Properties properties = new Properties();
-                        properties.load(new ByteArrayInputStream(data.getBytes()));
-                        interpolation(properties);
-                        Map<String, String> hashtable = new Hashtable<String, String>();
-                        for (Object key : properties.keySet()) {
-                            String n = key.toString();
-                            hashtable.put(n, properties.getProperty(n));
-                        }
-                        f.addConfig(cfgName, hashtable);
-                    }
-                    NodeList configurationFiles = e.getElementsByTagName("configfile");
-                    for (int j = 0; j < configurationFiles.getLength(); j++) {
-                    	Element cf = (Element) configurationFiles.item(j);
-                    	String finalname = cf.getAttribute("finalname");
-                    	String location = cf.getTextContent().trim();
-                    	String override = cf.getAttribute("override");
-                    	boolean finalnameOverride = false;
-                    	// Check the value of the "override" attribute
-                        if (override != null && override.length() > 0) {
-                        	finalnameOverride = Boolean.parseBoolean(override);
-                        }
-                    	f.addConfigurationFile(new ConfigFileInfoImpl(location, finalname, finalnameOverride));
-                    }
-                    NodeList bundleNodes = e.getElementsByTagName("bundle");
-                    for (int j = 0; j < bundleNodes.getLength(); j++) {
-                        Element b = (Element) bundleNodes.item(j);
-                        String bStartLevel = b.getAttribute("start-level");
-                        String bStart = b.getAttribute("start");
-                        String bDependency = b.getAttribute("dependency");
-                        boolean bs = true;
-                        boolean bd = false;
-                        int bsl = absl;
+                    parseContent(e, f, absl);
 
-                        // Check the value of the "start" attribute
-                        if (bStart != null && bStart.length() > 0) {
-                            bs = Boolean.parseBoolean(bStart);
-                        }
-                        // Check the value of the "dependency" attribute
-                        if (bDependency != null && bDependency.length() > 0) {
-                            bd = Boolean.parseBoolean(bDependency);
-                        }
-                        // Check start level
-                        if (bStartLevel != null && bStartLevel.length() > 0) {
-                            try {
-                                bsl = Integer.parseInt(bStartLevel);
-                            } catch (Exception ex) {
-                                LOGGER.error("The start-level is not an int value for the bundle : " + b.getTextContent());
+                    NodeList conditionalNodes = e.getElementsByTagName("conditional");
+                    for (int j = 0; j < conditionalNodes.getLength(); j++) {
+                        Element c = (Element) conditionalNodes.item(j);
+
+                        ConditionalImpl conditional = new ConditionalImpl();
+                        parseContent(c, conditional, absl);
+
+                        NodeList conditionNodes = c.getElementsByTagName("condition");
+                        for (int k = 0; k < conditionNodes.getLength(); k++) {
+                            Element b = (Element) conditionNodes.item(k);
+                            String dependencyFeatureVersion = b.getAttribute("version");
+                            if (dependencyFeatureVersion != null && dependencyFeatureVersion.length() > 0) {
+                                conditional.addCondition(new FeatureImpl(b.getTextContent(), dependencyFeatureVersion));
+                            } else {
+                                conditional.addCondition(new FeatureImpl(b.getTextContent()));
                             }
                         }
-                        f.addBundle(new BundleInfoImpl(b.getTextContent().trim(), bsl, bs, bd));
+                        f.addConditional(conditional);
                     }
+
                     features.add(f);
                 }
             }
@@ -241,6 +197,79 @@ public class RepositoryImpl implements Repository {
         }
     }
 
+    private void parseContent(Element e, ContentImpl content, int absl) throws IOException {
+        NodeList featureNodes = e.getElementsByTagName("feature");
+        for (int j = 0; j < featureNodes.getLength(); j++) {
+            Element b = (Element) featureNodes.item(j);
+            if (b.getParentNode() != e) continue;
+            String dependencyFeatureVersion = b.getAttribute("version");
+            if (dependencyFeatureVersion != null && dependencyFeatureVersion.length() > 0) {
+                content.addDependency(new FeatureImpl(b.getTextContent(), dependencyFeatureVersion));
+            } else {
+                content.addDependency(new FeatureImpl(b.getTextContent()));
+            }
+        }
+        NodeList configNodes = e.getElementsByTagName("config");
+        for (int j = 0; j < configNodes.getLength(); j++) {
+            Element c = (Element) configNodes.item(j);
+            if (c.getParentNode() != e) continue;
+            String cfgName = c.getAttribute("name");
+            String data = c.getTextContent();
+            Properties properties = new Properties();
+            properties.load(new ByteArrayInputStream(data.getBytes()));
+            interpolation(properties);
+            Map<String, String> hashtable = new Hashtable<String, String>();
+            for (Object key : properties.keySet()) {
+                String n = key.toString();
+                hashtable.put(n, properties.getProperty(n));
+            }
+            content.addConfig(cfgName, hashtable);
+        }
+        NodeList configurationFiles = e.getElementsByTagName("configfile");
+        for (int j = 0; j < configurationFiles.getLength(); j++) {
+            Element cf = (Element) configurationFiles.item(j);
+            if (cf.getParentNode() != e) continue;
+            String finalname = cf.getAttribute("finalname");
+            String location = cf.getTextContent().trim();
+            String override = cf.getAttribute("override");
+            boolean finalnameOverride = false;
+            // Check the value of the "override" attribute
+            if (override != null && override.length() > 0) {
+                finalnameOverride = Boolean.parseBoolean(override);
+            }
+            content.addConfigurationFile(new ConfigFileInfoImpl(location, finalname, finalnameOverride));
+        }
+        NodeList bundleNodes = e.getElementsByTagName("bundle");
+        for (int j = 0; j < bundleNodes.getLength(); j++) {
+            Element b = (Element) bundleNodes.item(j);
+            if (b.getParentNode() != e) continue;
+            String bStartLevel = b.getAttribute("start-level");
+            String bStart = b.getAttribute("start");
+            String bDependency = b.getAttribute("dependency");
+            boolean bs = true;
+            boolean bd = false;
+            int bsl = absl;
+
+            // Check the value of the "start" attribute
+            if (bStart != null && bStart.length() > 0) {
+                bs = Boolean.parseBoolean(bStart);
+            }
+            // Check the value of the "dependency" attribute
+            if (bDependency != null && bDependency.length() > 0) {
+                bd = Boolean.parseBoolean(bDependency);
+            }
+            // Check start level
+            if (bStartLevel != null && bStartLevel.length() > 0) {
+                try {
+                    bsl = Integer.parseInt(bStartLevel);
+                } catch (Exception ex) {
+                    LOGGER.error("The start-level is not an int value for the bundle : " + b.getTextContent());
+                }
+            }
+            content.addBundle(new BundleInfoImpl(b.getTextContent().trim(), bsl, bs, bd));
+        }
+    }
+
     protected void interpolation(Properties properties) {
         for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) {
             String key = (String) e.nextElement();
@@ -262,3 +291,4 @@ public class RepositoryImpl implements Repository {
     }
 
 }
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/d380c4d0/features/core/src/main/java/org/apache/karaf/features/osgi/Activator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/osgi/Activator.java b/features/core/src/main/java/org/apache/karaf/features/osgi/Activator.java
new file mode 100644
index 0000000..4defdd7
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/osgi/Activator.java
@@ -0,0 +1,173 @@
+/*
+ * 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.osgi;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.karaf.features.FeaturesListener;
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.features.internal.FeaturesServiceImpl;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator implements BundleActivator {
+
+    public static final String FEATURES_SERVICE_CONFIG_FILE = "org.apache.karaf.features.cfg";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(Activator.class);
+
+    private BundleContext bundleContext;
+    private ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> configurationAdminServiceTracker;
+    private ServiceTracker<FeaturesListener, FeaturesListener> featuresListenerTracker;
+    private FeaturesServiceImpl featuresService;
+    private ServiceRegistration<FeaturesService> featuresServiceRegistration;
+
+    public Activator() {
+    }
+
+    public void start(final BundleContext bundleContext) throws Exception {
+        this.bundleContext = bundleContext;
+
+        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);
+            }
+        }
+        featuresService = new FeaturesServiceImpl();
+        featuresService.setUrls(getString(configuration, "featuresRepositories", ""));
+        featuresService.setBoot(getString(configuration, "featuresBoot", ""));
+        featuresService.setBootFeaturesAsynchronous(getBool(configuration, "bootFeaturesAsynchronous", false));
+        featuresService.setRespectStartLvlDuringFeatureStartup(getBool(configuration, "respectStartLvlDuringFeatureStartup", false));
+        featuresService.setResolverTimeout(getLong(configuration, "resolverTimeout", 5000));
+        featuresService.setBundleContext(bundleContext);
+
+        featuresListenerTracker = new ServiceTracker<FeaturesListener, FeaturesListener>(
+                bundleContext,
+                FeaturesListener.class,
+                new ServiceTrackerCustomizer<FeaturesListener, FeaturesListener>() {
+                    public FeaturesListener addingService(ServiceReference<FeaturesListener> reference) {
+                        FeaturesListener service = bundleContext.getService(reference);
+                        featuresService.registerListener(service);
+                        return service;
+                    }
+                    public void modifiedService(ServiceReference<FeaturesListener> reference, FeaturesListener service) {
+                    }
+                    public void removedService(ServiceReference<FeaturesListener> reference, FeaturesListener service) {
+                        featuresService.unregisterListener(service);
+                        bundleContext.ungetService(reference);
+                    }
+                }
+        );
+
+        configurationAdminServiceTracker = new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>(
+                bundleContext,
+                ConfigurationAdmin.class,
+                new ServiceTrackerCustomizer<ConfigurationAdmin, ConfigurationAdmin>() {
+                    public ConfigurationAdmin addingService(ServiceReference<ConfigurationAdmin> reference) {
+                        ConfigurationAdmin configurationAdmin = bundleContext.getService(reference);
+                        doStart(configurationAdmin);
+                        return configurationAdmin;
+                    }
+
+                    public void modifiedService(ServiceReference<ConfigurationAdmin> reference, ConfigurationAdmin service) {
+                    }
+
+                    public void removedService(ServiceReference<ConfigurationAdmin> reference, ConfigurationAdmin service) {
+                        doStop();
+                    }
+                }
+        );
+
+        featuresListenerTracker.open();
+
+        configurationAdminServiceTracker.open();
+    }
+
+    public void stop(BundleContext bundleContext) throws Exception {
+        if (configurationAdminServiceTracker != null) {
+            configurationAdminServiceTracker.close();
+            configurationAdminServiceTracker = null;
+        }
+    }
+
+    protected void doStart(ConfigurationAdmin configurationAdmin) {
+        doStop();
+        try {
+            featuresService.setConfigAdmin(configurationAdmin);
+            featuresListenerTracker.open();
+            featuresService.start();
+            featuresServiceRegistration = bundleContext.registerService(FeaturesService.class, featuresService, null);
+        } catch (Exception e) {
+            LOGGER.error("Error starting FeaturesService", e);
+        }
+    }
+
+    protected void doStop() {
+        if (featuresServiceRegistration != null) {
+            try {
+                featuresServiceRegistration.unregister();
+            } catch (IllegalStateException e) {
+                // Ignore
+            }
+        }
+        featuresListenerTracker.close();
+        try {
+            featuresService.stop();
+        } catch (Exception e) {
+            LOGGER.warn("Error stopping FeaturesService", e);
+        }
+    }
+
+    private static String getString(Properties configuration, String key, String def) {
+        if (configuration.containsKey(key)) {
+            return configuration.getProperty(key);
+        } else {
+            return def;
+        }
+    }
+
+    private static boolean getBool(Properties configuration, String key, boolean def) {
+        if (configuration.containsKey(key)) {
+            return Boolean.parseBoolean(configuration.getProperty(key));
+        } else {
+            return def;
+        }
+    }
+
+    private static long getLong(Properties configuration, String key, long def) {
+        if (configuration.containsKey(key)) {
+            return Long.parseLong(configuration.getProperty(key));
+        } else {
+            return def;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/d380c4d0/features/core/src/main/resources/OSGI-INF/blueprint/gshell-features.xml
----------------------------------------------------------------------
diff --git a/features/core/src/main/resources/OSGI-INF/blueprint/gshell-features.xml b/features/core/src/main/resources/OSGI-INF/blueprint/gshell-features.xml
deleted file mode 100644
index fe3f81d..0000000
--- a/features/core/src/main/resources/OSGI-INF/blueprint/gshell-features.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    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.
-
--->
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">
-
-    <ext:property-placeholder placeholder-prefix="$(" placeholder-suffix=")"/>
-
-    <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]" ignore-missing-locations="true">
-        <ext:default-properties>
-            <ext:property name="featuresRepositories" value=""/>
-            <ext:property name="featuresBoot" value=""/>
-            <ext:property name="resolverTimeout" value="5000"/>
-            <ext:property name="respectStartLvlDuringFeatureStartup" value="false"/>
-            <ext:property name="featuresBootAsynchronous" value="false"/>
-        </ext:default-properties>
-        <ext:location>file:$(karaf.etc)/org.apache.karaf.features.cfg</ext:location>
-    </ext:property-placeholder>
-
-    <bean id="featuresService" class="org.apache.karaf.features.internal.FeaturesServiceImpl" init-method="start" destroy-method="stop">
-        <property name="urls" value="$[featuresRepositories]" />
-        <property name="boot" value="$[featuresBoot]" />
-        <property name="bootFeaturesAsynchronous" value="$[featuresBootAsynchronous]"/>
-        <property name="respectStartLvlDuringFeatureStartup" value="$[respectStartLvlDuringFeatureStartup]" />
-        <property name="resolverTimeout" value="$[resolverTimeout]" />
-        <property name="configAdmin" ref="configAdmin" />
-        <property name="packageAdmin" ref="packageAdmin" />
-        <property name="startLevel" ref="startLevel" />
-        <property name="bundleContext" ref="blueprintBundleContext" />
-    </bean>
-
-    <reference-list id="featuresListeners" interface="org.apache.karaf.features.FeaturesListener" availability="optional">
-        <reference-listener ref="featuresService"
-                            bind-method="registerListener"
-                            unbind-method="unregisterListener" />
-    </reference-list>
-
-    <reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin" />
-
-    <reference id="packageAdmin" interface="org.osgi.service.packageadmin.PackageAdmin" />
-
-    <reference id="startLevel" interface="org.osgi.service.startlevel.StartLevel" />
-    
-    <service ref="featuresService" interface="org.apache.karaf.features.FeaturesService" />
-
-</blueprint>

http://git-wip-us.apache.org/repos/asf/karaf/blob/d380c4d0/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd
----------------------------------------------------------------------
diff --git a/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd
new file mode 100644
index 0000000..dbc4bfa
--- /dev/null
+++ b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.2.0.xsd
@@ -0,0 +1,254 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+
+-->
+<xs:schema elementFormDefault="qualified"
+    targetNamespace="http://karaf.apache.org/xmlns/features/v1.2.0"
+    xmlns:tns="http://karaf.apache.org/xmlns/features/v1.2.0"
+    xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+    <xs:annotation>
+        <xs:documentation><![CDATA[
+Karaf features mechanism. For documentation please visit the
+<a href="http://karaf.apache.org/">Karaf website</a>.
+        ]]></xs:documentation>
+    </xs:annotation>
+
+    <xs:complexType name="features">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Root element of Feature definition. It contains an required attribute for
+designating from which repository this feature should be loaded. The Karaf
+shell will show the repository name when displaying information about the feature.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="repository" type="xs:anyURI">
+                <xs:annotation>
+                    <xs:documentation><![CDATA[
+Additional repositories where dependencies are stored.
+                    ]]></xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="feature" type="tns:feature">
+                <xs:annotation>
+                    <xs:documentation><![CDATA[
+Feature definition.
+                    ]]></xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="feature">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Definition of the Feature.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="details" minOccurs="0" type="xs:string">
+                <xs:annotation>
+                    <xs:documentation><![CDATA[
+The help text shown for this feature when using feature:info console command.
+                    ]]>
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="config" type="tns:config" />
+            <xs:element name="configfile" type="tns:configFile" />
+            <xs:element name="feature" type="tns:dependency" />
+            <xs:element name="bundle" type="tns:bundle" />
+            <xs:element name="conditional" type="tns:conditional" />
+        </xs:choice>
+        <xs:attribute name="name" type="tns:featureName" use="required" />
+        <xs:attribute name="version" type="xs:string" default="0.0.0" />
+        <xs:attribute name="description" type="xs:string" />
+        <xs:attribute name="resolver" type="tns:resolver">
+            <xs:annotation>
+                <xs:documentation><![CDATA[
+Optional alternative resolver to use for determining the list of bundles to install for a given feature.
+                ]]>
+                </xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="install" type="tns:install">
+            <xs:annotation>
+                <xs:documentation><![CDATA[
+Marks if the feaute will be automatically started when thrown to the deploy folder.
+                ]]>
+                </xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="start-level" type="xs:int">
+             <xs:annotation>
+                <xs:documentation><![CDATA[
+Set this attribute to have an OSGi start level for this feature different
+from the default start level defined in Karaf's config.properties.
+                ]]>
+                </xs:documentation>
+             </xs:annotation>
+         </xs:attribute>
+    </xs:complexType>
+
+    <xs:complexType name="conditional">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Definition of the Conditional.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="config" type="tns:config" />
+            <xs:element name="configfile" type="tns:configFile" />
+            <xs:element name="feature" type="tns:dependency" />
+            <xs:element name="bundle" type="tns:bundle" />
+            <xs:element name="condition" type="tns:dependency" minOccurs="0" maxOccurs="1" />
+        </xs:choice>
+    </xs:complexType>
+
+
+    <xs:complexType name="bundle">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Deployable element to install.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:simpleContent>
+            <xs:extension base="xs:anyURI">
+                <xs:attribute name="start-level" type="xs:int">
+                    <xs:annotation>
+                        <xs:documentation><![CDATA[
+Set this attribute to have an OSGi start level for this bundle different
+from the default start level defined in the Karaf's config.properties.
+                        ]]>
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:attribute>
+                <xs:attribute name="start" type="xs:boolean" default="true">
+                    <xs:annotation>
+                        <xs:documentation><![CDATA[
+If false, leaves bundle in resolved state rather than the default active state.
+                        ]]>
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:attribute>
+                <xs:attribute name="dependency" type="xs:boolean">
+                    <xs:annotation>
+                        <xs:documentation><![CDATA[
+Mark this bundle as a dependency for the resolver.
+                        ]]>
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:attribute>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:complexType name="dependency">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Dependency of feature.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:simpleContent>
+            <xs:extension base="tns:featureName">
+                <xs:attribute name="version" type="xs:string" default="0.0.0" />
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:complexType name="config">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Configuration entries which should be created during feature installation. This
+configuration may be used with OSGi Configuration Admin. The element content is
+read in as a properties file.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="name" type="xs:string" use="required" />
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:complexType name="configFile">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Additional configuration files which should be created during feature installation.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:simpleContent>
+            <xs:extension base="xs:anyURI">
+                <xs:attribute name="finalname" type="xs:string" use="required">
+                    <xs:annotation>
+                        <xs:documentation><![CDATA[
+The final destination path and name for the configuration file.
+                        ]]></xs:documentation>
+                    </xs:annotation>
+                </xs:attribute>
+                <xs:attribute name="override" type="xs:boolean">
+                    <xs:annotation>
+                        <xs:documentation><![CDATA[
+If the configFile already exists at the finalname location, whether or not to replace it.
+                        ]]></xs:documentation>
+                    </xs:annotation>
+                </xs:attribute>
+
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:simpleType name="featureName">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Feature name should be non empty string.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:minLength value="1" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="resolver">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Resolver to use. Karaf will look for OSGi service which have following properties:
+objectClass: org.apache.karaf.features.Resolver
+name: the value
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:minLength value="1" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="install">
+        <xs:annotation>
+            <xs:documentation><![CDATA[
+Installation mode. Can be either manual or auto. Specifies whether the feature should be automatically installed when
+dropped inside the deploy folder. Note: This attribute doesn't affect feature descriptors that are installed from the
+command line or as part of the org.apache.karaf.features.cfg.
+            ]]></xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:minLength value="1" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:element name="features" type="tns:features" />
+
+</xs:schema>

http://git-wip-us.apache.org/repos/asf/karaf/blob/d380c4d0/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java b/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
index aaf88ca..2ab2761 100644
--- a/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
@@ -33,6 +33,7 @@ import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Dictionary;
 import java.util.EnumSet;
@@ -46,10 +47,15 @@ import junit.framework.TestCase;
 
 import org.apache.karaf.features.internal.FeatureImpl;
 import org.apache.karaf.features.internal.FeaturesServiceImpl;
+import org.easymock.Capture;
 import org.easymock.EasyMock;
+import org.easymock.IAnswer;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.wiring.FrameworkWiring;
 import org.osgi.service.log.LogService;
 import org.osgi.service.packageadmin.PackageAdmin;
 
@@ -81,6 +87,9 @@ public class FeaturesServiceTest extends TestCase {
         BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
         Bundle installedBundle = EasyMock.createMock(Bundle.class);
 
+        // required since the sorted set uses it
+        expect(installedBundle.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
 
         replay(bundleContext, installedBundle);
@@ -109,6 +118,9 @@ public class FeaturesServiceTest extends TestCase {
 
         reset(bundleContext, installedBundle);
 
+        // required since the sorted set uses it
+        expect(installedBundle.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
         expect(bundleContext.installBundle(isA(String.class),
                                            isA(InputStream.class))).andReturn(installedBundle);
@@ -149,6 +161,8 @@ public class FeaturesServiceTest extends TestCase {
         URI uri = tmp.toURI();
         
         BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+        final Bundle sysBundle = EasyMock.createMock(Bundle.class);
+        FrameworkWiring frameworkWiring = EasyMock.createMock(FrameworkWiring.class);
         Bundle installedBundle = EasyMock.createMock(Bundle.class);
 
         expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
@@ -163,6 +177,9 @@ public class FeaturesServiceTest extends TestCase {
 
         reset(bundleContext, installedBundle);
 
+        // required since the sorted set uses it
+        expect(installedBundle.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         // Installs f1 and 0.1
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
         expect(bundleContext.installBundle(isA(String.class),
@@ -195,7 +212,21 @@ public class FeaturesServiceTest extends TestCase {
 
         expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
 
-        replay(bundleContext, installedBundle);
+        // refresh
+        expect(bundleContext.getBundle(0)).andStubReturn(sysBundle);
+        expect(sysBundle.adapt(FrameworkWiring.class)).andStubReturn(frameworkWiring);
+        final Capture<FrameworkListener> listeners =  new Capture<FrameworkListener>();
+        frameworkWiring.refreshBundles(EasyMock.<Collection<Bundle>>eq(null), capture(listeners));
+        expectLastCall().andStubAnswer(new IAnswer<Object>() {
+            public Object answer() throws Throwable {
+                for (FrameworkListener listener : listeners.getValues()) {
+                    listener.frameworkEvent(new FrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, sysBundle, null));
+                }
+                return null;
+            }
+        });
+
+        replay(bundleContext, installedBundle, sysBundle, frameworkWiring);
 
         try {
             svc.uninstallFeature("f1");
@@ -281,8 +312,13 @@ public class FeaturesServiceTest extends TestCase {
         URI uri = tmp.toURI();
 
         BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+        final Bundle sysBundle = EasyMock.createMock(Bundle.class);
+        FrameworkWiring frameworkWiring = EasyMock.createMock(FrameworkWiring.class);
         Bundle installedBundle = EasyMock.createMock(Bundle.class);
 
+        // required since the sorted set uses it
+        expect(installedBundle.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         // Installs feature f1 with dependency on f2
         // so will install f2 first
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
@@ -316,7 +352,21 @@ public class FeaturesServiceTest extends TestCase {
 
         expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
 
-        replay(bundleContext, installedBundle);
+        // refresh
+        expect(bundleContext.getBundle(0)).andStubReturn(sysBundle);
+        expect(sysBundle.adapt(FrameworkWiring.class)).andStubReturn(frameworkWiring);
+        final Capture<FrameworkListener> listeners =  new Capture<FrameworkListener>();
+        frameworkWiring.refreshBundles(EasyMock.<Collection<Bundle>>eq(null), capture(listeners));
+        expectLastCall().andStubAnswer(new IAnswer<Object>() {
+            public Object answer() throws Throwable {
+                for (FrameworkListener listener : listeners.getValues()) {
+                    listener.frameworkEvent(new FrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, sysBundle, null));
+                }
+                return null;
+            }
+        });
+
+        replay(bundleContext, installedBundle, sysBundle, frameworkWiring);
 
         FeaturesServiceImpl svc = new FeaturesServiceImpl();
         svc.setBundleContext(bundleContext);
@@ -456,8 +506,13 @@ public class FeaturesServiceTest extends TestCase {
         URI uri = tmp.toURI();
 
         BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+        final Bundle sysBundle = EasyMock.createMock(Bundle.class);
+        FrameworkWiring frameworkWiring = EasyMock.createMock(FrameworkWiring.class);
         Bundle installedBundle = EasyMock.createMock(Bundle.class);
 
+        // required since the sorted set uses it
+        expect(installedBundle.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         // Installs feature f1 with dependency on f2
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
         expect(bundleContext.installBundle(isA(String.class),
@@ -473,12 +528,29 @@ public class FeaturesServiceTest extends TestCase {
                                            isA(InputStream.class))).andReturn(installedBundle);
         installedBundle.start();
 
+        expect(bundleContext.getBundle(0)).andReturn(sysBundle);
+        expect(sysBundle.adapt(FrameworkWiring.class)).andReturn(frameworkWiring);
+
         // uninstalls first feature name = f2, version = 0.1
         installedBundle.uninstall();
 
         expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
 
-        replay(bundleContext, installedBundle);
+        // refresh
+        expect(bundleContext.getBundle(0)).andStubReturn(sysBundle);
+        expect(sysBundle.adapt(FrameworkWiring.class)).andStubReturn(frameworkWiring);
+        final Capture<FrameworkListener> listeners =  new Capture<FrameworkListener>();
+        frameworkWiring.refreshBundles(EasyMock.<Collection<Bundle>>eq(null), capture(listeners));
+        expectLastCall().andStubAnswer(new IAnswer<Object>() {
+            public Object answer() throws Throwable {
+                for (FrameworkListener listener : listeners.getValues()) {
+                    listener.frameworkEvent(new FrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, sysBundle, null));
+                }
+                return null;
+            }
+        });
+
+        replay(bundleContext, installedBundle, sysBundle, frameworkWiring);
 
         FeaturesServiceImpl svc = new FeaturesServiceImpl();
         svc.setBundleContext(bundleContext);
@@ -535,6 +607,11 @@ public class FeaturesServiceTest extends TestCase {
     private BundleContext prepareBundleContextForInstallUninstall() throws Exception {
         BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
         Bundle installedBundle = EasyMock.createMock(Bundle.class);
+        final Bundle sysBundle = EasyMock.createMock(Bundle.class);
+        FrameworkWiring frameworkWiring = EasyMock.createMock(FrameworkWiring.class);
+
+        // required since the sorted set uses it
+        expect(installedBundle.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
 
         // Installs feature f1 with dependency on f2
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
@@ -553,7 +630,22 @@ public class FeaturesServiceTest extends TestCase {
 
         expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
 
-        replay(bundleContext, installedBundle);
+        // refresh
+        expect(bundleContext.getBundle(0)).andStubReturn(sysBundle);
+        expect(sysBundle.adapt(FrameworkWiring.class)).andStubReturn(frameworkWiring);
+
+        final Capture<FrameworkListener> listeners =  new Capture<FrameworkListener>();
+        frameworkWiring.refreshBundles(EasyMock.<Collection<Bundle>>eq(null), capture(listeners));
+        expectLastCall().andStubAnswer(new IAnswer<Object>() {
+            public Object answer() throws Throwable {
+                for (FrameworkListener listener : listeners.getValues()) {
+                    listener.frameworkEvent(new FrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, sysBundle, null));
+                }
+                return null;
+            }
+        });
+
+        replay(bundleContext, installedBundle, sysBundle, frameworkWiring);
         return bundleContext;
     }
 
@@ -580,6 +672,10 @@ public class FeaturesServiceTest extends TestCase {
         Bundle installedBundle1 = EasyMock.createMock(Bundle.class);
         Bundle installedBundle2 = EasyMock.createMock(Bundle.class);
 
+        // required since the sorted set uses it
+        expect(installedBundle1.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+        expect(installedBundle2.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         // Installs feature f1 and f2
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
         expect(bundleContext.installBundle(eq(bundle1), isA(InputStream.class))).andReturn(installedBundle1);
@@ -632,6 +728,10 @@ public class FeaturesServiceTest extends TestCase {
         Bundle installedBundle1 = EasyMock.createMock(Bundle.class);
         Bundle installedBundle2 = EasyMock.createMock(Bundle.class);
 
+        // required since the sorted set uses it
+        expect(installedBundle1.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+        expect(installedBundle2.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         // Installs feature f1 and f2
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
         expect(bundleContext.installBundle(eq(bundle1), isA(InputStream.class))).andReturn(installedBundle1);
@@ -685,6 +785,10 @@ public class FeaturesServiceTest extends TestCase {
         Bundle installedBundle1 = EasyMock.createMock(Bundle.class);
         Bundle installedBundle2 = EasyMock.createMock(Bundle.class);
 
+        // required since the sorted set uses it
+        expect(installedBundle1.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+        expect(installedBundle2.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         // Installs feature f1 and f2
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
         expect(bundleContext.installBundle(eq(bundle1), isA(InputStream.class))).andReturn(installedBundle1);
@@ -742,6 +846,10 @@ public class FeaturesServiceTest extends TestCase {
         Bundle installedBundle1 = EasyMock.createMock(Bundle.class);
         Bundle installedBundle2 = EasyMock.createMock(Bundle.class);
 
+        // required since the sorted set uses it
+        expect(installedBundle1.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+        expect(installedBundle2.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         // Installs feature f1 and f2
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
         expect(bundleContext.installBundle(eq(bundle1), isA(InputStream.class))).andReturn(installedBundle1);
@@ -800,11 +908,16 @@ public class FeaturesServiceTest extends TestCase {
         }
 
         // loads the state
-        PackageAdmin packageAdmin = EasyMock.createMock(PackageAdmin.class);
         BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+        final Bundle sysBundle = EasyMock.createMock(Bundle.class);
+        FrameworkWiring frameworkWiring = EasyMock.createMock(FrameworkWiring.class);
         Bundle installedBundle1 = EasyMock.createMock(Bundle.class);
         Bundle installedBundle2 = EasyMock.createMock(Bundle.class);
 
+        // required since the sorted set uses it
+        expect(installedBundle1.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+        expect(installedBundle2.compareTo(EasyMock.<Bundle>anyObject())).andReturn(0).anyTimes();
+
         // Installs feature f1
         expect(installedBundle1.getBundleId()).andReturn(12345L);
         expect(installedBundle1.getBundleId()).andReturn(12345L);
@@ -831,14 +944,24 @@ public class FeaturesServiceTest extends TestCase {
         //
         // This is the real test to make sure the host is actually refreshed
         //
-        packageAdmin.refreshPackages(aryEq(new Bundle[] { installedBundle1 }));
+        expect(bundleContext.getBundle(0)).andReturn(sysBundle);
+        expect(sysBundle.adapt(FrameworkWiring.class)).andReturn(frameworkWiring);
+        final Capture<FrameworkListener> capture =  new Capture<FrameworkListener>();
+        frameworkWiring.refreshBundles(eq(Collections.singleton(installedBundle1)), capture(capture));
+        expectLastCall().andAnswer(new IAnswer<Object>() {
+            public Object answer() throws Throwable {
+                for (FrameworkListener listener : capture.getValues()) {
+                    listener.frameworkEvent(new FrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, sysBundle, null));
+                }
+                return null;
+            }
+        });
 
         expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
 
-        replay(packageAdmin, bundleContext, installedBundle1, installedBundle2);
+        replay(frameworkWiring, sysBundle, bundleContext, installedBundle1, installedBundle2);
 
         FeaturesServiceImpl svc = new FeaturesServiceImpl();
-        svc.setPackageAdmin(packageAdmin);
         svc.setBundleContext(bundleContext);
         svc.addRepository(uri);
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/d380c4d0/itests/src/test/java/org/apache/karaf/itests/JaasTest.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/JaasTest.java b/itests/src/test/java/org/apache/karaf/itests/JaasTest.java
index d00bfe8..623129e 100644
--- a/itests/src/test/java/org/apache/karaf/itests/JaasTest.java
+++ b/itests/src/test/java/org/apache/karaf/itests/JaasTest.java
@@ -64,7 +64,7 @@ public class JaasTest extends KarafTestSupport {
         for (int i=0; i<10; i++) {
             doLogin();
         }
-        assertEquals(3, bundleContext.getServiceReferences(ArtifactInstaller.class.getName(), null).length);
+        assertEquals(2, bundleContext.getServiceReferences(ArtifactInstaller.class.getName(), null).length);
     }
 
     private void doLogin() throws Exception {

http://git-wip-us.apache.org/repos/asf/karaf/blob/d380c4d0/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java
----------------------------------------------------------------------
diff --git a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java
index 73f9623..568f231 100644
--- a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java
+++ b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.karaf.features.BundleInfo;
+import org.apache.karaf.features.Conditional;
 import org.apache.karaf.features.ConfigFileInfo;
 import org.apache.karaf.features.Feature;
 
@@ -56,6 +57,10 @@ public class ExtendedFeature implements Feature {
 
     public List<ConfigFileInfo> getConfigurationFiles() {
         return this.feature.getConfigurationFiles();
+	}
+
+    public List<Conditional> getConditional() {
+        return this.feature.getConditional();
     }
 
     public List<Feature> getDependencies() {


Mime
View raw message