[FELIX-4517] Generate generic capabilities and requirements for services from blueprint and scr descriptors
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1595765 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/pom.xml b/bundleplugin/pom.xml
index 22afd41..7698072 100644
--- a/bundleplugin/pom.xml
+++ b/bundleplugin/pom.xml
@@ -70,6 +70,11 @@
<version>1.6.6</version>
</dependency>
<dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.utils</artifactId>
+ <version>1.6.0</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>2.0.7</version>
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BlueprintPlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BlueprintPlugin.java
index e67c251..5a56dd9 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BlueprintPlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BlueprintPlugin.java
@@ -26,8 +26,11 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -39,6 +42,7 @@
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
+import aQute.bnd.header.Attrs;
import aQute.bnd.service.AnalyzerPlugin;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Descriptors.PackageRef;
@@ -48,6 +52,12 @@
import aQute.libg.generics.Create;
import aQute.libg.qtokens.QuotedTokenizer;
import aQute.service.reporter.Reporter;
+import org.apache.felix.utils.manifest.Attribute;
+import org.apache.felix.utils.manifest.Clause;
+import org.apache.felix.utils.manifest.Directive;
+import org.osgi.framework.Constants;
+
+import static org.apache.felix.utils.manifest.Parser.parseHeader;
public class BlueprintPlugin implements AnalyzerPlugin
@@ -67,6 +77,11 @@
public boolean analyzeJar( Analyzer analyzer ) throws Exception
{
+ String mode = analyzer.getProperty("service_mode");
+ if (mode == null) {
+ mode = "generic";
+ }
+
transformer.setParameter( "nsh_interface",
analyzer.getProperty( "nsh_interface" ) != null ? analyzer.getProperty( "nsh_interface" ) : "" );
transformer.setParameter( "nsh_namespace",
@@ -114,7 +129,9 @@
}
// Group and analyze
- Map<String, Set<Attribute>> hdrs = Create.map();
+ Set<String> caps = Create.set();
+ Set<String> reqs = Create.set();
+ Map<String, Set<Clause>> hdrs = Create.map();
for ( String str : headers )
{
int idx = str.indexOf( ':' );
@@ -126,21 +143,137 @@
}
String h = str.substring( 0, idx ).trim();
String v = str.substring( idx + 1 ).trim();
- Set<Attribute> att = hdrs.get( h );
- if ( att == null )
+ Clause[] hc = parseHeader(v);
+ // Convert generic caps/reqs
+ if ("Import-Service".equals(h))
{
- att = new TreeSet<Attribute>();
- hdrs.put( h, att );
+ if (!"service".equals(mode))
+ {
+ Clause clause = hc[0];
+ String multiple = clause.getDirective("multiple");
+ String avail = clause.getDirective("availability");
+ String filter = clause.getAttribute("filter");
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("osgi.service;effective:=active;");
+ if ("optional".equals(avail)) {
+ sb.append("resolution:=optional;");
+ }
+ if ("true".equals(multiple)) {
+ sb.append("cardinality:=multiple;");
+ }
+ if (filter == null) {
+ filter = "(" + Constants.OBJECTCLASS + "=" + clause.getName() + ")";
+ } else if (!filter.startsWith("(") && !filter.endsWith(")")) {
+ filter = "(&(" + Constants.OBJECTCLASS + "=" + clause.getName() + ")(" + filter + "))";
+ } else {
+ filter = "(&(" + Constants.OBJECTCLASS + "=" + clause.getName() + ")" + filter + ")";
+ }
+ sb.append("filter:=\"").append(filter).append("\"");
+ reqs.add(sb.toString());
+ }
+ else if (!"generic".equals(mode))
+ {
+ Set<Clause> clauses = hdrs.get(h);
+ if (clauses == null) {
+ clauses = new HashSet<Clause>();
+ hdrs.put(h, clauses);
+ }
+ clauses.addAll(Arrays.asList(hc));
+ }
}
- att.addAll( parseHeader( v, null ) );
+ else if ("Export-Service".equals(h))
+ {
+ if (!"service".equals(mode))
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("osgi.service;effective:=active;objectClass");
+ if (hc.length > 1) {
+ sb.append(":List<String>=\"");
+ } else {
+ sb.append("=\"");
+ }
+ for (int i = 0; i < hc.length; i++)
+ {
+ if (i > 0)
+ {
+ sb.append(",");
+ }
+ sb.append(hc[i].getName());
+ }
+ sb.append("\"");
+ for (int i = 0; i < hc[0].getAttributes().length; i++)
+ {
+ sb.append(";");
+ sb.append(hc[0].getAttributes()[i].getName());
+ sb.append("=\"");
+ sb.append(hc[0].getAttributes()[i].getValue());
+ sb.append("\"");
+ }
+ caps.add(sb.toString());
+ }
+ else if (!"generic".equals(mode))
+ {
+ Set<Clause> clauses = hdrs.get(h);
+ if (clauses == null) {
+ clauses = new HashSet<Clause>();
+ hdrs.put(h, clauses);
+ }
+ clauses.addAll(Arrays.asList(hc));
+ }
+ }
+ else
+ {
+ Set<Clause> clauses = hdrs.get(h);
+ if (clauses == null)
+ {
+ clauses = new HashSet<Clause>();
+ hdrs.put(h, clauses);
+ }
+ clauses.addAll(Arrays.asList( hc ) );
+ }
+ }
+ if (!caps.isEmpty())
+ {
+ StringBuilder sb = new StringBuilder();
+ String header = analyzer.getProperty("Provide-Capability");
+ if (header != null)
+ {
+ sb.append(header);
+ }
+ for (String cap : caps) {
+ if (sb.length() > 0) {
+ sb.append(",");
+ }
+ sb.append(cap);
+ }
+ System.err.println("Provide-Capability: " + sb.toString());
+ analyzer.setProperty("Provide-Capability", sb.toString());
+ }
+ if (!reqs.isEmpty())
+ {
+ StringBuilder sb = new StringBuilder();
+ String header = analyzer.getProperty("Require-Capability");
+ if (header != null)
+ {
+ sb.append(header);
+ }
+ for (String req : reqs) {
+ if (sb.length() > 0) {
+ sb.append(",");
+ }
+ sb.append(req);
+ }
+ System.err.println("Require-Capability: " + sb.toString());
+ analyzer.setProperty("Require-Capability", sb.toString());
}
// Merge
for ( String header : hdrs.keySet() )
{
if ( "Import-Class".equals( header ) || "Import-Package".equals( header ) )
{
- Set<Attribute> newAttr = hdrs.get( header );
- for ( Attribute a : newAttr )
+ Set<Clause> newAttr = hdrs.get(header);
+ for ( Clause a : newAttr )
{
String pkg = a.getName();
if ( "Import-Class".equals( header ) )
@@ -158,50 +291,34 @@
PackageRef pkgRef = analyzer.getPackageRef( pkg );
if ( !analyzer.getReferred().containsKey( pkgRef ) )
{
- analyzer.getReferred().put( pkgRef ).putAll( a.getProperties() );
+ Attrs attrs = analyzer.getReferred().put(pkgRef);
+ for (Attribute attribute : a.getAttributes())
+ {
+ attrs.put(attribute.getName(), attribute.getValue());
+ }
}
}
}
else
{
- Set<Attribute> orgAttr = parseHeader( analyzer.getProperty( header ), null );
- Set<Attribute> newAttr = hdrs.get( header );
- for ( Iterator<Attribute> it = newAttr.iterator(); it.hasNext(); )
+ Set<String> merge = Create.set();
+ for (Clause clause : parseHeader(analyzer.getProperty(header)))
{
- Attribute a = it.next();
- for ( Attribute b : orgAttr )
- {
- if ( b.getName().equals( a.getName() ) )
- {
- it.remove();
- break;
- }
- }
+ merge.add(clause.toString());
}
- orgAttr.addAll( newAttr );
- // Rebuild from orgAttr
+ for (Clause clause : hdrs.get(header))
+ {
+ merge.add(clause.toString());
+ }
StringBuilder sb = new StringBuilder();
- for ( Attribute a : orgAttr )
+ for (String clause : merge)
{
if ( sb.length() > 0 )
{
sb.append( "," );
}
- sb.append( a.getName() );
- for ( Map.Entry<String, String> prop : a.getProperties().entrySet() )
- {
- sb.append( ';' ).append( prop.getKey() ).append( "=" );
- if ( prop.getValue().matches( "[0-9a-zA-Z_-]+" ) )
- {
- sb.append( prop.getValue() );
- }
- else
- {
- sb.append( "\"" );
- sb.append( prop.getValue().replace( "\"", "\\\"" ) );
- sb.append( "\"" );
- }
- }
+ sb.append(clause);
+
}
analyzer.setProperty( header, sb.toString() );
}
@@ -274,150 +391,4 @@
return tf.newTransformer( source );
}
- public static class Attribute implements Comparable<Attribute>
- {
- private final String name;
- private final Map<String, String> properties;
-
-
- public Attribute( String name, Map<String, String> properties )
- {
- this.name = name;
- this.properties = properties;
- }
-
-
- public String getName()
- {
- return name;
- }
-
-
- public Map<String, String> getProperties()
- {
- return properties;
- }
-
-
- public int compareTo( Attribute a )
- {
- int c = name.compareTo( a.name );
- if ( c == 0 )
- {
- c = properties.equals( a.properties ) ? 0 : properties.size() < a.properties.size() ? -1 : properties
- .hashCode() < a.properties.hashCode() ? -1 : +1;
- }
- return c;
- }
-
-
- @Override
- public boolean equals( Object o )
- {
- if ( this == o )
- return true;
- if ( o == null || getClass() != o.getClass() )
- return false;
-
- Attribute attribute = ( Attribute ) o;
-
- if ( name != null ? !name.equals( attribute.name ) : attribute.name != null )
- return false;
- if ( properties != null ? !properties.equals( attribute.properties ) : attribute.properties != null )
- return false;
-
- return true;
- }
-
-
- @Override
- public int hashCode()
- {
- int result = name != null ? name.hashCode() : 0;
- result = 31 * result + ( properties != null ? properties.hashCode() : 0 );
- return result;
- }
- }
-
-
- public static Set<Attribute> parseHeader( String value, Reporter logger )
- {
- if ( ( value == null ) || ( value.trim().length() == 0 ) )
- {
- return new TreeSet<Attribute>();
- }
- Set<Attribute> result = new TreeSet<Attribute>();
- QuotedTokenizer qt = new QuotedTokenizer( value, ";=," );
- char del = '\0';
- do
- {
- boolean hadAttribute = false;
- Map<String, String> clause = Create.map();
- List<String> aliases = Create.list();
- String name = qt.nextToken( ",;" );
-
- del = qt.getSeparator();
- if ( ( name == null ) || ( name.length() == 0 ) )
- {
- if ( ( logger != null ) && ( logger.isPedantic() ) )
- {
- logger
- .warning( "Empty clause, usually caused by repeating a comma without any name field or by having "
- + "spaces after the backslash of a property file: " + value );
- }
-
- if ( name != null )
- continue;
- break;
- }
- name = name.trim();
-
- aliases.add( name );
- String advalue;
- while ( del == ';' )
- {
- String adname = qt.nextToken();
- if ( ( del = qt.getSeparator() ) != '=' )
- {
- if ( ( hadAttribute ) && ( logger != null ) )
- {
- logger.error( "Header contains name field after attribute or directive: " + adname + " from "
- + value + ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4" );
- }
-
- if ( ( adname != null ) && ( adname.length() > 0 ) )
- aliases.add( adname.trim() );
- }
- else
- {
- advalue = qt.nextToken();
- if ( ( clause.containsKey( adname ) ) && ( logger != null ) && ( logger.isPedantic() ) )
- {
- logger.warning( "Duplicate attribute/directive name " + adname + " in " + value
- + ". This attribute/directive will be ignored" );
- }
-
- if ( advalue == null )
- {
- if ( logger != null )
- {
- logger.error( "No value after '=' sign for attribute " + adname );
- }
- advalue = "";
- }
- clause.put( adname.trim(), advalue.trim() );
- del = qt.getSeparator();
- hadAttribute = true;
- }
- }
-
- for ( String clauseName : aliases )
- {
- result.add( new Attribute( clauseName, clause ) );
- }
- }
- while ( del == ',' );
- return result;
- }
-
}
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
index b3a669d..4411bc0 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BundlePlugin.java
@@ -448,6 +448,11 @@
{
properties.putAll( getDefaultProperties( currentProject ) );
properties.putAll( transformDirectives( originalInstructions ) );
+ if (properties.getProperty("Bundle-Activator") != null
+ && properties.getProperty("Bundle-Activator").isEmpty())
+ {
+ properties.remove("Bundle-Activator");
+ }
Builder builder = new Builder();
synchronized ( BundlePlugin.class ) // protect setBase...getBndLastModified which uses static DateFormat
@@ -1261,7 +1266,9 @@
properties.put( "classifier", classifier == null ? "" : classifier );
// Add default plugins
- header( properties, Analyzer.PLUGIN, BlueprintPlugin.class.getName() + "," + SpringXMLType.class.getName() );
+ header( properties, Analyzer.PLUGIN, ScrPlugin.class.getName() + ","
+ + BlueprintPlugin.class.getName() + ","
+ + SpringXMLType.class.getName() );
return properties;
}
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ScrPlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ScrPlugin.java
new file mode 100644
index 0000000..f3fc73b
--- /dev/null
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/ScrPlugin.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.felix.bundleplugin;
+
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import aQute.bnd.osgi.Analyzer;
+import aQute.bnd.osgi.Descriptors.PackageRef;
+import aQute.bnd.osgi.Jar;
+import aQute.bnd.osgi.Processor;
+import aQute.bnd.osgi.Resource;
+import aQute.bnd.service.AnalyzerPlugin;
+import aQute.libg.generics.Create;
+import org.apache.felix.utils.manifest.Attribute;
+import org.apache.felix.utils.manifest.Clause;
+import org.osgi.framework.Constants;
+
+import static org.apache.felix.utils.manifest.Parser.parseHeader;
+
+
+public class ScrPlugin implements AnalyzerPlugin
+{
+
+ Transformer transformer;
+
+ public ScrPlugin() throws Exception
+ {
+ transformer = getTransformer( getClass().getResource( "scr.xsl" ) );
+ }
+
+
+ public boolean analyzeJar( Analyzer analyzer ) throws Exception
+ {
+ Set<String> headers = Create.set();
+
+ String bpHeader = analyzer.getProperty( "Service-Component" );
+
+ Map<String, ? extends Map<String, String>> map = Processor.parseHeader( bpHeader, null );
+ for ( String root : map.keySet() )
+ {
+ Resource resource = analyzer.getJar().getResource(root);
+ if ( resource != null ) {
+ process(analyzer, root, resource, headers);
+ }
+ }
+
+ // Group and analyze
+ for ( String str : headers )
+ {
+ int idx = str.indexOf( ':' );
+ if ( idx < 0 )
+ {
+ analyzer.warning( ( new StringBuilder( "Error analyzing services in scr resource: " ) ).append( str ).toString() );
+ continue;
+ }
+ String h = str.substring( 0, idx ).trim();
+ String v = str.substring( idx + 1 ).trim();
+
+ StringBuilder sb = new StringBuilder();
+ String header = analyzer.getProperty( h );
+ if (header != null && !header.isEmpty())
+ {
+ sb.append(header);
+ sb.append(",");
+ }
+ sb.append( v );
+ analyzer.setProperty(h, sb.toString());
+ }
+ return false;
+ }
+
+
+ private void process( Analyzer analyzer, String path, Resource resource, Set<String> headers )
+ {
+ InputStream in = null;
+ try
+ {
+ in = resource.openInputStream();
+
+ // Retrieve headers
+ Set<String> set = analyze( in );
+ headers.addAll( set );
+ }
+ catch ( Exception e )
+ {
+ analyzer.error( ( new StringBuilder( "Unexpected exception in processing scr resources(" ) )
+ .append( path ).append( "): " ).append( e ).toString() );
+ }
+ finally
+ {
+ try
+ {
+ if ( in != null )
+ {
+ in.close();
+ }
+ }
+ catch ( IOException e )
+ {
+ }
+ }
+ }
+
+
+ public Set<String> analyze( InputStream in ) throws Exception
+ {
+ Set<String> refers = new HashSet<String>();
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ javax.xml.transform.Result r = new StreamResult( bout );
+ javax.xml.transform.Source s = new StreamSource( in );
+ transformer.transform( s, r );
+ ByteArrayInputStream bin = new ByteArrayInputStream( bout.toByteArray() );
+ bout.close();
+ BufferedReader br = new BufferedReader( new InputStreamReader( bin ) );
+ for ( String line = br.readLine(); line != null; line = br.readLine() )
+ {
+ line = line.trim();
+ if ( line.length() > 0 )
+ {
+ refers.add( line );
+ }
+ }
+
+ br.close();
+ return refers;
+ }
+
+
+ protected Transformer getTransformer( URL url ) throws Exception
+ {
+ TransformerFactory tf = TransformerFactory.newInstance();
+ javax.xml.transform.Source source = new StreamSource( url.openStream() );
+ return tf.newTransformer( source );
+ }
+
+}
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/blueprint.xsl b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/blueprint.xsl
index 58ca714..aa2578b 100644
--- a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/blueprint.xsl
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/blueprint.xsl
@@ -68,7 +68,7 @@
</xsl:text>
</xsl:for-each>
- <xsl:for-each select="//bp:service">
+ <xsl:for-each select="//bp:service[@interface or bp:interfaces/bp:value]">
<xsl:choose>
<xsl:when test="@interface">
<xsl:value-of select="concat('Export-Service:', @interface)" />
@@ -76,7 +76,13 @@
<xsl:otherwise>
<xsl:choose>
<xsl:when test="bp:interfaces/bp:value/text()">
- <xsl:value-of select="concat('Export-Service:', bp:interfaces/bp:value/text())" />
+ <xsl:value-of select="'Export-Service:'" />
+ <xsl:for-each select="bp:interfaces/bp:value/text()">
+ <xsl:value-of select="."/>
+ <xsl:if test="position() != last()">
+ <xsl:value-of select="';'" />
+ </xsl:if>
+ </xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:otherwise>
diff --git a/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/scr.xsl b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/scr.xsl
new file mode 100644
index 0000000..f1de2c6
--- /dev/null
+++ b/bundleplugin/src/main/resources/org/apache/felix/bundleplugin/scr.xsl
@@ -0,0 +1,77 @@
+<?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.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <xsl:output method="text" />
+
+ <xsl:template match="/">
+
+ <xsl:for-each select="//scr:component[service/provide/@interface]">
+ <xsl:value-of select="'Provide-Capability: osgi.service;effective:=active;'" />
+ <xsl:choose>
+ <xsl:when test="count(service/provide/@interface) = 1">
+ <xsl:value-of select="'objectClass="'" />
+ <xsl:value-of select="service/provide/@interface"/>
+ <xsl:value-of select="'"'" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'objectClass:List<String>="'" />
+ <xsl:for-each select="service/provide/@interface">
+ <xsl:value-of select="."/>
+ <xsl:if test="position() != last()">
+ <xsl:value-of select="','" />
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:value-of select="'"'" />
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:for-each select="property[@name != 'service.pid' and @value and not(contains(@value, '$'))]">
+ <xsl:value-of select="';'" />
+ <xsl:value-of select="@name"/>
+ <xsl:value-of select="'="'" />
+ <xsl:value-of select="@value"/>
+ <xsl:value-of select="'"'" />
+ </xsl:for-each>
+ <xsl:text>
+ </xsl:text>
+ </xsl:for-each>
+
+ <xsl:for-each select="//scr:component/reference">
+ <xsl:value-of select="'Require-Capability: osgi.service;effective:=active; '" />
+ <xsl:choose>
+ <xsl:when test="@cardinality = '0..1' or @cardinality = '0..n'">
+ <xsl:value-of select="'resolution:=optional;'" />
+ </xsl:when>
+ </xsl:choose>
+ <xsl:choose>
+ <xsl:when test="@target">
+ <xsl:value-of select="concat('filter:="(&(objectClass=', @interface, ')', @target, ')"')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('filter:="(objectClass=', @interface, ')"')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>
+ </xsl:text>
+ </xsl:for-each>
+
+ </xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/bundleplugin/src/test/java/org/apache/felix/bundleplugin/BlueprintComponentTest.java b/bundleplugin/src/test/java/org/apache/felix/bundleplugin/BlueprintComponentTest.java
index 21bc367..14c6b61 100644
--- a/bundleplugin/src/test/java/org/apache/felix/bundleplugin/BlueprintComponentTest.java
+++ b/bundleplugin/src/test/java/org/apache/felix/bundleplugin/BlueprintComponentTest.java
@@ -28,8 +28,11 @@
import java.util.Properties;
import java.util.jar.Manifest;
+import aQute.libg.generics.Create;
import junit.framework.TestCase;
+import org.apache.felix.utils.manifest.Clause;
+import org.apache.felix.utils.manifest.Parser;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
import org.osgi.framework.Constants;
@@ -81,6 +84,7 @@
plugin.setOutputDirectory( new File( "target/tmp/basedir/target/classes" ) );
Map instructions = new HashMap();
+ instructions.put( "service_mode", "service" );
instructions.put( "Test", "Foo" );
instructions.put( "nsh_interface", "foo.bar.Namespace" );
@@ -99,7 +103,11 @@
assertNotNull( impSvc );
String impPkg = manifest.getMainAttributes().getValue( Constants.IMPORT_PACKAGE );
- List<String> pkgs = Arrays.asList( impPkg.split( "," ) );
+ List<String> pkgs = Create.list();
+ for (Clause clause : Parser.parseHeader(impPkg))
+ {
+ pkgs.add(clause.getName());
+ }
for ( int i = 1; i <= 13; i++ )
{
assertTrue( pkgs.contains( "p" + i ) );