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() {
