FELIX-3827 - Bnd iPOJO Plugin, error when manipulating embed jars/bundles
* Deal with components embed in Bundle-Classpath
* Added a property (include-embed-bundles) to activate that behavior
* If embed dependency is already manipulated, its already generated IPOJO-Component
header is merged within the bnd generated Manifest
* If embed contains annotated iPOJO classes, but has not been manipulated,
components are manipulated and the generated byte code is placed in the
generated bundle (we don't touch the embed dependency)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1429888 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/BndJarResourceStore.java b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/BndJarResourceStore.java
index f90b5d3..26b8697 100644
--- a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/BndJarResourceStore.java
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/BndJarResourceStore.java
@@ -34,7 +34,16 @@
import java.io.IOException;
import java.io.InputStream;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
public class BndJarResourceStore implements ResourceStore {
@@ -44,6 +53,7 @@
private MetadataRenderer m_renderer = new MetadataRenderer();
private List<Element> m_metadata;
+ private boolean m_includeEmbedComponents;
public BndJarResourceStore(Analyzer analyzer, Reporter reporter) {
m_metadata = new ArrayList<Element>();
@@ -52,7 +62,13 @@
}
public byte[] read(String path) throws IOException {
+
+ // Find the resource either in the global jar or in one of the embed dependencies
Resource resource = m_analyzer.getJar().getResource(path);
+ if (resource == null) {
+ Jar embed = findJar(path);
+ resource = embed.getResource(path);
+ }
InputStream is = null;
try {
is = resource.openInputStream();
@@ -65,6 +81,7 @@
public void accept(ResourceVisitor visitor) {
try {
+ // TODO make this configurable (react to other annotations)
// Only visit classes annotated with @Component or @Handler
String annotations = Component.class.getPackage().getName() + ".*";
@@ -72,6 +89,8 @@
Clazz.QUERY.ANNOTATION.name(), annotations,
Clazz.QUERY.NAMED.name(), "*");
+ classes = filter(classes);
+
// Iterates over discovered resources
for (Clazz clazz : classes) {
visitor.visit(clazz.getPath());
@@ -81,6 +100,61 @@
}
}
+ private Collection<Clazz> filter(Collection<Clazz> classes) throws Exception {
+ Set<Clazz> manipulable = new HashSet<Clazz>();
+ for (Clazz clazz : classes) {
+
+ // If it is i the main jar, simply use it
+ if (m_analyzer.getJar().getResource(clazz.getPath()) != null) {
+ manipulable.add(clazz);
+ continue;
+ }
+
+ if (m_includeEmbedComponents) {
+ // Otherwise ...
+ // Try to see if it is in an embed dependencies
+ Jar jar = findJar(clazz.getPath());
+ if (jar == null) {
+ m_reporter.error("Resource for class %s not found in classpath", clazz.getFQN());
+ continue;
+ }
+
+ // Is it a Bundle ?
+ if (jar.getBsn() != null) {
+ // OSGi Bundle case
+
+ // Check if the bundle was manipulated before
+ Attributes attr = jar.getManifest().getMainAttributes();
+ if (Manifests.getComponents(attr) != null) {
+ // Bundle has been previously manipulated
+ // TODO We should ignore the resource since it was already manipulated
+ // TODO But we should also merge its IPOJO-Components header
+ } else {
+ // Bundle was not manipulated
+ manipulable.add(clazz);
+ }
+
+ } else {
+ // Simple Jar file with iPOJO annotations
+ m_reporter.warning("Class %s found in a non-Bundle archive %s", clazz.getFQN(), jar.getName());
+ continue;
+ }
+ } else {
+ m_reporter.warning("Embed components are excluded, Component %s will not be manipulated", clazz.getFQN());
+ }
+ }
+ return manipulable;
+ }
+
+ private Jar findJar(String path) {
+ for (Jar jar : m_analyzer.getClasspath()) {
+ if (jar.getResource(path) != null) {
+ return jar;
+ }
+ }
+ return null;
+ }
+
public void open() throws IOException {
// nothing to do
}
@@ -105,8 +179,27 @@
}
public void close() throws IOException {
+
// Write the iPOJO header (including manipulation metadata)
StringBuilder builder = new StringBuilder();
+
+ if (m_includeEmbedComponents) {
+ // Incorporate metadata of embed dependencies (if any)
+ for (Jar jar : m_analyzer.getClasspath()) {
+ try {
+ Manifest manifest = jar.getManifest();
+ Attributes main = manifest.getMainAttributes();
+ String components = Manifests.getComponents(main);
+ if (components != null) {
+ m_reporter.progress("Merging components from %s", jar.getName());
+ builder.append(components);
+ }
+ } catch (Exception e) {
+ m_reporter.warning("Cannot open MANIFEST of %s", jar.getName());
+ }
+ }
+ }
+
for (Element metadata : m_metadata) {
builder.append(m_renderer.render(metadata));
}
@@ -138,4 +231,8 @@
}
+
+ public void setIncludeEmbedComponents(boolean excludeEmbedComponents) {
+ m_includeEmbedComponents = excludeEmbedComponents;
+ }
}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/Manifests.java b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/Manifests.java
new file mode 100644
index 0000000..f3af117
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/Manifests.java
@@ -0,0 +1,56 @@
+/*
+ * 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.ipojo.bnd;
+
+import aQute.lib.osgi.Analyzer;
+import aQute.lib.osgi.Jar;
+
+import java.util.jar.Attributes;
+
+/**
+ * A {@code Manifests} is a utility class for extracting data from Bundle's Manifest.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Manifests {
+ private Manifests() {}
+
+ public static String getComponents(Attributes main) {
+ String value = main.getValue("iPOJO-Components");
+ if (value == null) {
+ value = main.getValue("IPOJO-Components");
+ }
+
+ return value;
+ }
+
+ public static boolean hasEmbedComponents(Analyzer analyzer) throws Exception {
+ for (Jar jar : analyzer.getClasspath()) {
+ // Check if the bundle was manipulated before
+ Attributes attr = jar.getManifest().getMainAttributes();
+ if (getComponents(attr) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/PojoizationPlugin.java b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/PojoizationPlugin.java
index 00ca452..9340589 100644
--- a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/PojoizationPlugin.java
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/PojoizationPlugin.java
@@ -18,11 +18,6 @@
*/
package org.apache.felix.ipojo.bnd;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
import aQute.bnd.service.AnalyzerPlugin;
import aQute.bnd.service.Plugin;
import aQute.lib.osgi.Analyzer;
@@ -39,6 +34,13 @@
import org.apache.felix.ipojo.manipulator.visitor.writer.ManipulatedResourcesWriter;
import org.apache.felix.ipojo.metadata.Element;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.felix.ipojo.bnd.Manifests.hasEmbedComponents;
+
/**
* A {@code BndIpojoPlugin} is ...
*
@@ -48,12 +50,15 @@
private static final String PROPERTY_METADATA = "metadata";
private static final String PROPERTY_USE_LOCAL_SCHEMAS = "use-local-schemas";
+ private static final String PROPERTY_INCLUDE_EMBED_BUNDLES = "include-embed-bundles";
private static final String DEFAULT_METADATA = "META-INF/metadata.xml";
private static final boolean DEFAULT_USE_LOCAL_SCHEMAS = false;
+ private static final boolean DEFAULT_INCLUDE_EMBED_BUNDLES = false;
private String m_metadata = DEFAULT_METADATA;
private boolean m_useLocalSchemas = DEFAULT_USE_LOCAL_SCHEMAS;
+ private boolean m_includeEmbedBundles = DEFAULT_INCLUDE_EMBED_BUNDLES;
private Reporter m_reporter;
@@ -68,6 +73,11 @@
if (configuration.containsKey(PROPERTY_USE_LOCAL_SCHEMAS)) {
m_useLocalSchemas = true;
}
+
+ // Process embed bundles ?
+ if (configuration.containsKey(PROPERTY_INCLUDE_EMBED_BUNDLES)) {
+ m_includeEmbedBundles = true;
+ }
}
public void setReporter(Reporter reporter) {
@@ -83,6 +93,7 @@
// Build ResourceStore
BndJarResourceStore store = new BndJarResourceStore(analyzer, this.m_reporter);
+ store.setIncludeEmbedComponents(m_includeEmbedBundles);
// Build MetadataProvider
CompositeMetadataProvider provider = new CompositeMetadataProvider(reporter);
@@ -105,7 +116,7 @@
provider.addMetadataProvider(new AnnotationMetadataProvider(store, reporter));
CacheableMetadataProvider cache = new CacheableMetadataProvider(provider);
- if (cache.getMetadatas().isEmpty()) {
+ if (cache.getMetadatas().isEmpty() && !hasEmbedComponents(analyzer)) {
return false;
}
@@ -128,6 +139,12 @@
return false;
}
+ private boolean hasEmbedComponents(Analyzer analyzer) throws Exception {
+ // We want to process components from embed jars ?
+ return m_includeEmbedBundles && Manifests.hasEmbedComponents(analyzer);
+
+ }
+
private List<Element> findElements(List<Element> metadatas, String name) {
List<Element> found = new ArrayList<Element>();
for (Element element : metadatas) {
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/ResourceMetadataProvider.java b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/ResourceMetadataProvider.java
index f40e681..a5f2cb0 100644
--- a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/ResourceMetadataProvider.java
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/ResourceMetadataProvider.java
@@ -18,17 +18,17 @@
*/
package org.apache.felix.ipojo.bnd;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
import aQute.lib.osgi.Resource;
import org.apache.felix.ipojo.manipulator.MetadataProvider;
import org.apache.felix.ipojo.manipulator.Reporter;
import org.apache.felix.ipojo.manipulator.metadata.StreamMetadataProvider;
import org.apache.felix.ipojo.metadata.Element;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A {@code ResourceMetadataProvider} is ...
*
@@ -60,8 +60,8 @@
try {
stream = m_resource.openInputStream();
} catch (Exception e) {
- m_reporter.error(e.getMessage());
- throw new IOException("Cannot read metadata");
+ m_reporter.error("%s", e.getMessage());
+ throw new IOException("Cannot read metadata", e);
}
StreamMetadataProvider provider = new StreamMetadataProvider(stream, m_reporter);
provider.setValidateUsingLocalSchemas(m_validateUsingLocalSchemas);
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/BndJarResourceStoreTestCase.java b/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/BndJarResourceStoreTestCase.java
new file mode 100644
index 0000000..2bdb996
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/BndJarResourceStoreTestCase.java
@@ -0,0 +1,172 @@
+/*
+ * 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.ipojo.bnd;
+
+import aQute.lib.osgi.Analyzer;
+import aQute.lib.osgi.Clazz;
+import aQute.lib.osgi.Jar;
+import aQute.lib.osgi.Resource;
+import aQute.lib.osgi.URLResource;
+import aQute.libg.reporter.Reporter;
+import junit.framework.TestCase;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: guillaume
+ * Date: 04/01/13
+ * Time: 15:33
+ * To change this template use File | Settings | File Templates.
+ */
+public class BndJarResourceStoreTestCase extends TestCase {
+ @Mock
+ private Reporter reporter;
+
+ @Spy
+ private Analyzer analyzer = new Analyzer();
+
+ @Spy
+ private Jar dot = new Jar("root.jar");
+
+ @Spy
+ private Jar embed = new Jar("embed.jar");
+
+ @Override
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testAnalysisWithOnlyEmbedComponents() throws Exception {
+ PojoizationPlugin plugin = new PojoizationPlugin();
+
+ Map<String, String> props = new HashMap<String, String>();
+ props.put("include-embed-bundles", "true");
+
+ Resource resource = new URLResource(getClass().getResource("/EMBED-MANIFEST.MF"));
+ doReturn(dot).when(analyzer).getJar();
+ doReturn(resource).when(embed).getResource(eq("META-INF/MANIFEST.MF"));
+ analyzer.setClasspath(new Jar[] {embed});
+
+ plugin.setReporter(reporter);
+ plugin.setProperties(props);
+
+ plugin.analyzeJar(analyzer);
+
+ assertContains("instance { $component=\"org.apache.felix.ipojo.IPOJOURLHandler\" }",
+ analyzer.getProperty("IPOJO-Components"));
+ }
+
+ public void testAnalysisWithExcludedEmbedComponents() throws Exception {
+ PojoizationPlugin plugin = new PojoizationPlugin();
+
+ Map<String, String> props = new HashMap<String, String>();
+
+ Resource resource = new URLResource(getClass().getResource("/EMBED-MANIFEST.MF"));
+ doReturn(dot).when(analyzer).getJar();
+ doReturn(resource).when(embed).getResource(eq("META-INF/MANIFEST.MF"));
+ analyzer.setClasspath(new Jar[] {embed});
+
+ plugin.setReporter(reporter);
+ plugin.setProperties(props);
+
+ plugin.analyzeJar(analyzer);
+
+ assertNull(analyzer.getProperty("IPOJO-Components"));
+ }
+
+
+ public void testAnalysisWithBothLocalAndEmbedComponents() throws Exception {
+ PojoizationPlugin plugin = new PojoizationPlugin();
+
+ Map<String, String> props = new HashMap<String, String>();
+ props.put("include-embed-bundles", "true");
+
+ Resource resource = new URLResource(getClass().getResource("/EMBED-MANIFEST.MF"));
+ Resource resource2 = new URLResource(getClass().getResource("/metadata-components-only.xml"));
+ doReturn(dot).when(analyzer).getJar();
+ doReturn(resource).when(embed).getResource(eq("META-INF/MANIFEST.MF"));
+ doReturn(resource2).when(dot).getResource(eq("META-INF/metadata.xml"));
+ analyzer.setClasspath(new Jar[]{embed});
+
+ plugin.setReporter(reporter);
+ plugin.setProperties(props);
+
+ plugin.analyzeJar(analyzer);
+
+ assertContains("instance { $component=\"org.apache.felix.ipojo.IPOJOURLHandler\" }",
+ analyzer.getProperty("IPOJO-Components"));
+ assertContains("component { $class=\"com.acme.Thermometer\" }",
+ analyzer.getProperty("IPOJO-Components"));
+ }
+
+
+ public void testAnalysisWithLocallyDefinedComponentAndEmbedResource() throws Exception {
+ PojoizationPlugin plugin = new PojoizationPlugin();
+
+ Map<String, String> props = new HashMap<String, String>();
+ props.put("include-embed-bundles", "true");
+ String path = EmptyComponent.class.getName().replace('.', '/').concat(".class");
+
+ Resource resource2 = new URLResource(getClass().getResource("/metadata-test-component.xml"));
+ doReturn(dot).when(analyzer).getJar();
+ doReturn(resource2).when(dot).getResource(eq("META-INF/metadata.xml"));
+
+ Collection<Clazz> classes = new ArrayList<Clazz>();
+ Resource typeResource = new URLResource(getClass().getResource("EmptyComponent.class"));
+ Clazz clazz = new Clazz(path, typeResource);
+ clazz.parseClassFile();
+ classes.add(clazz);
+ doReturn(classes).when(analyzer).getClasses(Matchers.<String[]>anyVararg());
+
+ Resource resource = new URLResource(getClass().getResource("/EMBED-MANIFEST-EMPTY.MF"));
+ doReturn(resource).when(embed).getResource(eq("META-INF/MANIFEST.MF"));
+ doReturn(typeResource).when(embed).getResource(path);
+ doReturn("aaa").when(embed).getBsn();
+
+ analyzer.setClasspath(new Jar[] {embed});
+
+ plugin.setReporter(reporter);
+ plugin.setProperties(props);
+
+ plugin.analyzeJar(analyzer);
+
+ assertContains("component { $classname=\"org.apache.felix.ipojo.bnd.EmptyComponent\" manipulation { method { $name=\"$init\" }}}",
+ analyzer.getProperty("IPOJO-Components"));
+ verify(dot).putResource(eq(path), any(Resource.class));
+ }
+
+ private void assertContains(String expected, String actual) {
+ assertTrue(actual.contains(expected));
+ }
+
+}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/EmptyComponent.java b/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/EmptyComponent.java
new file mode 100644
index 0000000..6ea32e5
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/EmptyComponent.java
@@ -0,0 +1,31 @@
+/*
+ * 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.ipojo.bnd;
+
+/**
+* Created with IntelliJ IDEA.
+* User: guillaume
+* Date: 07/01/13
+* Time: 11:31
+* To change this template use File | Settings | File Templates.
+*/
+public class EmptyComponent {
+
+}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/EMBED-MANIFEST-EMPTY.MF b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/EMBED-MANIFEST-EMPTY.MF
new file mode 100644
index 0000000..bc04d39
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/EMBED-MANIFEST-EMPTY.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Bundle-Name: Embed
+Bundle-Vendor: The Apache Software Foundation
+Bundle-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.apache.felix.ipojo.test
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/EMBED-MANIFEST.MF b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/EMBED-MANIFEST.MF
new file mode 100644
index 0000000..b19fc3e
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/EMBED-MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-Name: Embed
+Bundle-Vendor: The Apache Software Foundation
+Bundle-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.apache.felix.ipojo.test
+IPOJO-Components: instance { $component="org.apache.felix.ipojo.IPOJOU
+ RLHandler" }component { $immediate="true" $classname="org.apache.feli
+ x.ipojo.IPOJOURLHandler" $public="false" callback { $transition="inva
+ lidate" $method="stop" }provides { property { $mandatory="false" $nam
+ e="url.handler.protocol" $value="ipojo" $type="java.lang.String" }}ma
+ nipulation { $super="org.osgi.service.url.AbstractURLStreamHandlerSer
+ vice" field { $name="m_context" $type="org.osgi.framework.BundleConte
+ xt" }field { $name="m_temp" $type="java.io.File" }method { $arguments
+ ="{org.osgi.framework.BundleContext}" $name="$init" }method { $name="
+ stop" }method { $arguments="{java.net.URL}" $name="openConnection" $r
+ eturn="java.net.URLConnection" }method { $arguments="{java.net.URL,ja
+ va.io.File}" $name="save" }method { $arguments="{java.io.InputStream,
+ java.io.File}" $name="save" }method { $arguments="{java.util.jar.JarF
+ ile}" $name="findMetadata" $return="java.io.File" }interface { $name=
+ "org.osgi.service.url.URLStreamHandlerService" }}}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-test-component.xml b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-test-component.xml
new file mode 100644
index 0000000..8df4c4c
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-test-component.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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.
+ -->
+
+<ipojo>
+ <component classname="org.apache.felix.ipojo.bnd.EmptyComponent"/>
+</ipojo>