You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by pi...@apache.org on 2012/10/09 10:16:46 UTC
svn commit: r1395904 - in /karaf/branches/karaf-2.3.x:
deployer/features/src/main/java/org/apache/karaf/deployer/features/
features/core/src/main/java/org/apache/karaf/features/
features/core/src/main/java/org/apache/karaf/features/internal/ features/c...
Author: pieber
Date: Tue Oct 9 08:16:45 2012
New Revision: 1395904
URL: http://svn.apache.org/viewvc?rev=1395904&view=rev
Log:
[KARAF-1752] Correct hard coded features uri in features deployer feature discovery (thanks to Andrei Pozolotin for this patch)
Signed-off-by: Andreas Pieber <an...@gmail.com>
Added:
karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java
karaf/branches/karaf-2.3.x/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.1.0.xsd
karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/resources/features-01.xml
Modified:
karaf/branches/karaf-2.3.x/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java
karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java
karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojo.java
karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojoTest.java
Modified: karaf/branches/karaf-2.3.x/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java?rev=1395904&r1=1395903&r2=1395904&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java (original)
+++ karaf/branches/karaf-2.3.x/deployer/features/src/main/java/org/apache/karaf/deployer/features/FeatureDeploymentListener.java Tue Oct 9 08:16:45 2012
@@ -36,6 +36,7 @@ import org.w3c.dom.Document;
import org.apache.felix.fileinstall.ArtifactUrlTransformer;
import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.FeaturesNamespaces;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.features.Repository;
import org.osgi.framework.Bundle;
@@ -88,14 +89,37 @@ public class FeatureDeploymentListener i
bundleContext.removeBundleListener(this);
}
+ private boolean isKnownFeaturesURI(String uri){
+ if(uri == null){
+ return true;
+ }
+ if(FeaturesNamespaces.URI_0_0_0.equalsIgnoreCase(uri)){
+ return true;
+ }
+ if(FeaturesNamespaces.URI_1_0_0.equalsIgnoreCase(uri)){
+ return true;
+ }
+ if(FeaturesNamespaces.URI_1_1_0.equalsIgnoreCase(uri)){
+ return true;
+ }
+ if(FeaturesNamespaces.URI_CURRENT.equalsIgnoreCase(uri)){
+ return true;
+ }
+ return false;
+ }
+
public boolean canHandle(File artifact) {
try {
if (artifact.isFile() && artifact.getName().endsWith(".xml")) {
Document doc = parse(artifact);
String name = doc.getDocumentElement().getLocalName();
String uri = doc.getDocumentElement().getNamespaceURI();
- if ("features".equals(name) && (uri == null || "".equals(uri) || "http://karaf.apache.org/xmlns/features/v1.0.0".equalsIgnoreCase(uri))) {
- return true;
+ if ("features".equals(name) ) {
+ if(isKnownFeaturesURI(uri)){
+ return true;
+ } else {
+ logger.error("unknown features uri", new Exception("" + uri));
+ }
}
}
} catch (Exception e) {
Added: karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java?rev=1395904&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java (added)
+++ karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java Tue Oct 9 08:16:45 2012
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import javax.xml.namespace.QName;
+
+/**
+ * Provides features XML/XSD constants.
+ */
+public interface FeaturesNamespaces {
+
+ String URI_0_0_0 = "";
+ String URI_1_0_0 = "http://karaf.apache.org/xmlns/features/v1.0.0";
+ String URI_1_1_0 = "http://karaf.apache.org/xmlns/features/v1.1.0";
+
+ String URI_CURRENT = URI_1_1_0;
+
+ QName FEATURES_0_0_0 = new QName("features");
+ QName FEATURES_1_0_0 = new QName(URI_1_0_0, "features");
+ QName FEATURES_1_1_0 = new QName(URI_1_1_0, "features");
+
+ QName FEATURES_CURRENT = FEATURES_1_1_0;
+
+}
Modified: karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java?rev=1395904&r1=1395903&r2=1395904&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java (original)
+++ karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java Tue Oct 9 08:16:45 2012
@@ -56,6 +56,8 @@ public class FeatureValidationUtil {
}
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+
+ /** FIXME should move to 1.1.0 ? */
// root element has namespace - we can use schema validation
Schema schema = factory.newSchema(new StreamSource(FeatureValidationUtil.class
.getResourceAsStream("/org/apache/karaf/features/karaf-features-1.0.0.xsd")));
Added: karaf/branches/karaf-2.3.x/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.1.0.xsd
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.1.0.xsd?rev=1395904&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.1.0.xsd (added)
+++ karaf/branches/karaf-2.3.x/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.1.0.xsd Tue Oct 9 08:16:45 2012
@@ -0,0 +1,237 @@
+<?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.1.0"
+ xmlns:tns="http://karaf.apache.org/xmlns/features/v1.1.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: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="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>
Modified: karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojo.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojo.java?rev=1395904&r1=1395903&r2=1395904&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojo.java (original)
+++ karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/main/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojo.java Tue Oct 9 08:16:45 2012
@@ -87,6 +87,19 @@ public class GenerateFeaturesXmlMojo ext
private ArtifactCollector collector;
/**
+ * Installation mode. If present, generate "feature.install" attribute:
+ *
+ * <a href="http://karaf.apache.org/xmlns/features/v1.1.0">Installation mode</a>
+ *
+ * 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 feature:install command or as part of the etc/org.apache.karaf.features.cfg file.
+ *
+ * @parameter
+ */
+ protected String installMode;
+
+ /**
* The file to generate
*
* @parameter default-value="${project.build.directory}/classes/feature.xml"
@@ -524,12 +537,12 @@ public class GenerateFeaturesXmlMojo ext
return String.format("%s/%s/%s/%s", artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType());
}
- private class Feature {
+ /*package*/ class Feature {
private Stack<Artifact> artifacts = new Stack<Artifact>();
private final Artifact artifact;
- private Feature(Artifact artifact) {
+ /*package*/ Feature(Artifact artifact) {
super();
this.artifact = artifact;
artifacts.push(artifact);
@@ -547,10 +560,25 @@ public class GenerateFeaturesXmlMojo ext
}
return false;
}
+
+ private String writeAttr(String name, String value){
+ return " " + name + "=" + "'" + value + "'" + " ";
+ }
public void write(PrintStream out) {
- out.println(" <feature name='" + artifact.getArtifactId() + "' version='"
- + artifact.getBaseVersion() + "'>");
+
+ out.print(
+ " <feature " +
+ writeAttr("name", artifact.getArtifactId()) +
+ writeAttr("version", artifact.getBaseVersion()) +
+ " "
+ );
+
+ if(installMode != null){
+ out.print(writeAttr("install", installMode));
+ }
+
+ out.println(" > ");
Stack<Artifact> resulting = new Stack<Artifact>();
resulting.addAll(artifacts);
@@ -575,7 +603,9 @@ public class GenerateFeaturesXmlMojo ext
}
}
}
- out.println(" </feature>");
+
+ out.println(" </feature>");
+
}
public List<Artifact> getDependencies() {
Modified: karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojoTest.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojoTest.java?rev=1395904&r1=1395903&r2=1395904&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojoTest.java (original)
+++ karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/java/org/apache/karaf/tooling/features/GenerateFeaturesXmlMojoTest.java Tue Oct 9 08:16:45 2012
@@ -16,7 +16,14 @@
*/
package org.apache.karaf.tooling.features;
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.io.StringWriter;
+
+import org.apache.karaf.tooling.features.GenerateFeaturesXmlMojo.Feature;
import org.apache.maven.artifact.Artifact;
+import org.codehaus.plexus.util.FileUtils;
import org.easymock.EasyMock;
import static org.easymock.EasyMock.*;
@@ -28,6 +35,14 @@ import junit.framework.TestCase;
*/
public class GenerateFeaturesXmlMojoTest extends TestCase {
+ private GenerateFeaturesXmlMojo mojo;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mojo = new GenerateFeaturesXmlMojo();
+ }
+
public void testToString() throws Exception {
Artifact artifact = EasyMock.createMock(Artifact.class);
@@ -41,4 +56,36 @@ public class GenerateFeaturesXmlMojoTest
assertEquals("org.apache.karaf.test/test-artifact/1.2.3", GenerateFeaturesXmlMojo.toString(artifact));
}
+ public void testInstallMode() throws Exception {
+
+ Artifact artifact = EasyMock.createMock(Artifact.class);
+
+ expect(artifact.getGroupId()).andReturn("org.apache.karaf.test").anyTimes();
+ expect(artifact.getArtifactId()).andReturn("test-artifact").anyTimes();
+ expect(artifact.getBaseVersion()).andReturn("1.2.3").anyTimes();
+ expect(artifact.getVersion()).andReturn("1.2.3").anyTimes();
+ expect(artifact.getType()).andReturn("jar").anyTimes();
+
+ replay(artifact);
+
+ mojo.installMode="auto";
+
+ Feature feature = mojo.new Feature(artifact);
+
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+
+ PrintStream out = new PrintStream(byteStream);
+
+ feature.write(out);
+
+ String source = byteStream.toString("UTF-8");
+
+ System.out.println(source);
+
+ String target = FileUtils.fileRead("./src/test/resources/features-01.xml", "UTF-8");
+
+ assertTrue(target.contains(source));
+
+ }
+
}
Added: karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/resources/features-01.xml
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/resources/features-01.xml?rev=1395904&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/resources/features-01.xml (added)
+++ karaf/branches/karaf-2.3.x/tooling/features-maven-plugin/src/test/resources/features-01.xml Tue Oct 9 08:16:45 2012
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+
+ 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.
+-->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0" name="test-repo">
+
+ <feature name='test-artifact' version='1.2.3' install='auto' >
+ <bundle>mvn:org.apache.karaf.test/test-artifact/1.2.3</bundle>
+ </feature>
+
+</features>