New manipulator structure - Move the bnd ipojo plugin

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1209867 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/.classpath b/ipojo/manipulator/bnd-ipojo-plugin/.classpath
new file mode 100644
index 0000000..f9479ba
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/.project b/ipojo/manipulator/bnd-ipojo-plugin/.project
new file mode 100644
index 0000000..f064da4
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>bnd-ipojo-plugin</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/pom.xml b/ipojo/manipulator/bnd-ipojo-plugin/pom.xml
new file mode 100644
index 0000000..8915404
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/pom.xml
@@ -0,0 +1,72 @@
+<?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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>felix-parent</artifactId>
+        <version>1.2.1</version>
+        <relativePath>../../../pom/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>bnd-ipojo-plugin</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <name>Apache Felix iPOJO Bnd Plugin</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>biz.aQute</groupId>
+            <artifactId>bndlib</artifactId>
+            <version>1.43.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.ipojo.manipulator</artifactId>
+            <version>1.9.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.ipojo.annotations</artifactId>
+            <version>1.8.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.8.5</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
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
new file mode 100644
index 0000000..24e1305
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/BndJarResourceStore.java
@@ -0,0 +1,142 @@
+/*
+ * 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.libg.reporter.Reporter;
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Handler;
+import org.apache.felix.ipojo.manipulator.Pojoization;
+import org.apache.felix.ipojo.manipulator.ResourceStore;
+import org.apache.felix.ipojo.manipulator.ResourceVisitor;
+import org.apache.felix.ipojo.manipulator.render.MetadataRenderer;
+import org.apache.felix.ipojo.manipulator.util.Metadatas;
+import org.apache.felix.ipojo.manipulator.util.Streams;
+import org.apache.felix.ipojo.metadata.Element;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+public class BndJarResourceStore implements ResourceStore {
+
+    private Analyzer m_analyzer;
+    private Reporter m_reporter;
+
+    private MetadataRenderer m_renderer = new MetadataRenderer();
+
+    private List<Element> m_metadata;
+
+    public BndJarResourceStore(Analyzer analyzer, Reporter reporter) {
+        m_metadata = new ArrayList<Element>();
+        m_analyzer = analyzer;
+        m_reporter = reporter;
+    }
+
+    public byte[] read(String path) throws IOException {
+        Resource resource = m_analyzer.getJar().getResource(path);
+        InputStream is = null;
+        try {
+            is = resource.openInputStream();
+        } catch (Exception e) {
+            throw new IOException("Cannot read " + path);
+        }
+        return Streams.readBytes(is);
+    }
+
+    public void accept(ResourceVisitor visitor) {
+
+        try {
+            // Only visit classes annotated with @Component or @Handler
+            String annotations = Component.class.getName() + "|" + Handler.class.getName();
+
+            Collection<Clazz> classes = m_analyzer.getClasses("",
+                    Clazz.QUERY.ANNOTATION.name(), annotations,
+                    Clazz.QUERY.NAMED.name(), "*");
+
+            // Iterates over discovered resources
+            for (Clazz clazz : classes) {
+                visitor.visit(clazz.getPath());
+            }
+        } catch (Exception e) {
+            m_reporter.error("Cannot find iPOJO annotated types: " + e.getMessage());
+        }
+    }
+
+    public void open() throws IOException {
+        // nothing to do
+    }
+
+    public void writeMetadata(Element metadata) {
+        m_metadata.add(metadata);
+
+        // Find referred packages and add them into Bnd
+        for (String referred : Metadatas.findReferredPackages(metadata)) {
+            if (m_analyzer.getReferred().get(referred) == null) {
+                // The given package is not referred ATM
+                m_analyzer.getReferred().put(referred, new HashMap<String, String>());
+            }
+        }
+
+        // IPOJO-Components will be written during the close method.
+    }
+
+    public void write(String resourcePath, byte[] resource) throws IOException {
+        Jar jar = m_analyzer.getJar();
+        jar.putResource(resourcePath, new ByteArrayResource(resource));
+    }
+
+    public void close() throws IOException {
+        // Write the iPOJO header (including manipulation metadata)
+        StringBuilder builder = new StringBuilder();
+        for (Element metadata : m_metadata) {
+            builder.append(m_renderer.render(metadata));
+        }
+
+        if (builder.length() != 0) {
+            m_analyzer.setProperty("IPOJO-Components", builder.toString());
+        }
+
+        // Add some mandatory imported packages
+        Map<String, String> version = new TreeMap<String, String>();
+        version.put("version", Pojoization.IPOJO_PACKAGE_VERSION);
+
+        if (m_analyzer.getReferred().get("org.apache.felix.ipojo") == null) {
+            m_analyzer.getReferred().put("org.apache.felix.ipojo", version);
+        }
+        if (m_analyzer.getReferred().get("org.apache.felix.ipojo.architecture") == null) {
+            m_analyzer.getReferred().put("org.apache.felix.ipojo.architecture", version);
+        }
+        if (m_analyzer.getReferred().get("org.osgi.service.cm") == null) {
+            Map<String, String> cm = new TreeMap<String, String>();
+            cm.put("version", "1.2");
+            m_analyzer.getReferred().put("org.osgi.service.cm", cm);
+        }
+        if (m_analyzer.getReferred().get("org.osgi.service.log") == null) {
+            Map<String, String> log = new TreeMap<String, String>();
+            log.put("version", "1.3");
+            m_analyzer.getReferred().put("org.osgi.service.log", log);
+        }
+
+
+    }
+}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/BndReporter.java b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/BndReporter.java
new file mode 100644
index 0000000..8625853
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/BndReporter.java
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import aQute.libg.reporter.Reporter;
+import org.apache.felix.ipojo.manipulator.reporter.EmptyReporter;
+
+/**
+ * A {@code BndReporter} knows how to wrap a Bnd Reporter into an iPOJO Reporter.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class BndReporter extends EmptyReporter {
+
+    /**
+     * Bnd reporter.
+     */
+    private Reporter m_reporter;
+
+    /**
+     * Errors which occur during the manipulation.
+     */
+    private List<String> m_errors = new ArrayList<String>();
+
+    /**
+     * Warnings which occur during the manipulation.
+     */
+    private List<String> m_warnings = new ArrayList<String>();
+
+    public BndReporter(aQute.libg.reporter.Reporter reporter) {
+        m_reporter = reporter;
+    }
+
+    public List<String> getErrors() {
+        return m_errors;
+    }
+
+    public List<String> getWarnings() {
+        return m_warnings;
+    }
+
+    @Override
+    public void trace(String message, Object... args) {
+        m_reporter.trace(message, args);
+    }
+
+    @Override
+    public void info(String message, Object... args) {
+        m_reporter.progress(message, args);
+    }
+
+    @Override
+    public void warn(String message, Object... args) {
+        m_reporter.warning(message, args);
+        m_warnings.add(String.format(message, args));
+    }
+
+    @Override
+    public void error(String message, Object... args) {
+        m_reporter.error(message, args);
+        m_errors.add(String.format(message, args));
+    }
+}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/ByteArrayResource.java b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/ByteArrayResource.java
new file mode 100644
index 0000000..d97bc39
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/ByteArrayResource.java
@@ -0,0 +1,41 @@
+/*
+ * 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.AbstractResource;
+
+/**
+ * A {@code ByteArrayResource} is ...
+ *
+ *  @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ByteArrayResource extends AbstractResource {
+
+    private byte[] m_content;
+
+    public ByteArrayResource(byte[] content) {
+        super(System.currentTimeMillis());
+        m_content = content;
+    }
+
+    @Override
+    protected byte[] getBytes() throws Exception {
+        return m_content;
+    }
+}
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
new file mode 100644
index 0000000..ed359c6
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/PojoizationPlugin.java
@@ -0,0 +1,151 @@
+/*
+ * 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 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;
+import aQute.lib.osgi.Resource;
+import aQute.libg.reporter.Reporter;
+import org.apache.felix.ipojo.manipulator.ManipulationVisitor;
+import org.apache.felix.ipojo.manipulator.Pojoization;
+import org.apache.felix.ipojo.manipulator.ResourceStore;
+import org.apache.felix.ipojo.manipulator.metadata.AnnotationMetadataProvider;
+import org.apache.felix.ipojo.manipulator.metadata.CacheableMetadataProvider;
+import org.apache.felix.ipojo.manipulator.metadata.CompositeMetadataProvider;
+import org.apache.felix.ipojo.manipulator.metadata.FileMetadataProvider;
+import org.apache.felix.ipojo.manipulator.visitor.check.CheckFieldConsistencyVisitor;
+import org.apache.felix.ipojo.manipulator.visitor.writer.ManipulatedResourcesWriter;
+import org.apache.felix.ipojo.metadata.Element;
+
+/**
+ * A {@code BndIpojoPlugin} is ...
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PojoizationPlugin implements Plugin, AnalyzerPlugin {
+
+    private static final String PROPERTY_METADATA = "metadata";
+    private static final String PROPERTY_USE_LOCAL_SCHEMAS = "use-local-schemas";
+
+    private static final String DEFAULT_METADATA = "META-INF/metadata.xml";
+    private static final boolean DEFAULT_USE_LOCAL_SCHEMAS = false;
+
+    private String m_metadata = DEFAULT_METADATA;
+    private boolean m_useLocalSchemas = DEFAULT_USE_LOCAL_SCHEMAS;
+
+    private Reporter m_reporter;
+
+    public void setProperties(Map<String, String> configuration) {
+
+        // Use metadata file if any
+        if (configuration.containsKey(PROPERTY_METADATA)) {
+            m_metadata = configuration.get(PROPERTY_METADATA);
+        }
+
+        // Use local schemas ?
+        if (configuration.containsKey(PROPERTY_USE_LOCAL_SCHEMAS)) {
+            m_useLocalSchemas = true;
+        }
+    }
+
+    public void setReporter(Reporter reporter) {
+        m_reporter = reporter;
+    }
+
+    public boolean analyzeJar(Analyzer analyzer) throws Exception {
+
+        long start = System.currentTimeMillis();
+
+        // Wraps the Bnd Reporter
+        BndReporter reporter = new BndReporter(this.m_reporter);
+
+        // Build ResourceStore
+        BndJarResourceStore store = new BndJarResourceStore(analyzer, this.m_reporter);
+
+        // Build MetadataProvider
+        CompositeMetadataProvider provider = new CompositeMetadataProvider(reporter);
+
+        File file = new File(m_metadata);
+        if (file.exists()) {
+            // Absolute file system resource
+            FileMetadataProvider fmp = new FileMetadataProvider(file, reporter);
+            fmp.setValidateUsingLocalSchemas(m_useLocalSchemas);
+            provider.addMetadataProvider(fmp);
+        } else {
+            // In archive resource
+            Resource resource = analyzer.getJar().getResource(m_metadata);
+            if (resource != null) {
+                ResourceMetadataProvider rmp = new ResourceMetadataProvider(resource, reporter);
+                rmp.setValidateUsingLocalSchemas(m_useLocalSchemas);
+                provider.addMetadataProvider(rmp);
+            }
+        }
+        provider.addMetadataProvider(new AnnotationMetadataProvider(store, reporter));
+
+        CacheableMetadataProvider cache = new CacheableMetadataProvider(provider);
+        if (cache.getMetadatas().isEmpty()) {
+            return false;
+        }
+
+        Pojoization pojoization = new Pojoization(reporter);
+        if (m_useLocalSchemas) {
+            pojoization.setUseLocalXSD();
+        }
+
+        pojoization.pojoization(store, cache, createVisitor(store, reporter));
+
+        int nbComponents = findElements(cache.getMetadatas(), "component").size();
+        int nbHandlers = findElements(cache.getMetadatas(), "handler").size();
+        this.m_reporter.progress("iPOJO manipulation performed performed in %s ms (%d components, %d handlers).",
+                               (System.currentTimeMillis() - start),
+                               nbComponents,
+                               nbHandlers);
+
+        // Return true if a new run should be performed after the analyze
+        return false;
+    }
+
+    private List<Element> findElements(List<Element> metadatas, String name) {
+        List<Element> found = new ArrayList<Element>();
+        for (Element element : metadatas) {
+            if (name.equalsIgnoreCase(element.getName())) {
+                found.add(element);
+            }
+        }
+        return found;
+    }
+
+    private ManipulationVisitor createVisitor(ResourceStore store, BndReporter reporter) {
+        ManipulatedResourcesWriter writer = new ManipulatedResourcesWriter();
+        writer.setReporter(reporter);
+        writer.setResourceStore(store);
+
+        CheckFieldConsistencyVisitor checkFieldConsistencyVisitor = new CheckFieldConsistencyVisitor(writer);
+        checkFieldConsistencyVisitor.setReporter(reporter);
+        return checkFieldConsistencyVisitor;
+
+    }
+
+}
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
new file mode 100644
index 0000000..f40e681
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/main/java/org/apache/felix/ipojo/bnd/ResourceMetadataProvider.java
@@ -0,0 +1,73 @@
+/*
+ * 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 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;
+
+/**
+ * A {@code ResourceMetadataProvider} is ...
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceMetadataProvider implements MetadataProvider {
+
+    private Resource m_resource;
+
+    private List<Element> m_cached;
+
+    private boolean m_validateUsingLocalSchemas = false;
+
+    private Reporter m_reporter;
+
+    public ResourceMetadataProvider(Resource resource, Reporter reporter) {
+        m_resource = resource;
+        m_reporter = reporter;
+    }
+
+    public void setValidateUsingLocalSchemas(boolean validateUsingLocalSchemas) {
+        this.m_validateUsingLocalSchemas = validateUsingLocalSchemas;
+    }
+
+    public List<Element> getMetadatas() throws IOException {
+        if (m_cached == null) {
+            m_cached = new ArrayList<Element>();
+            InputStream stream = null;
+            try {
+                stream = m_resource.openInputStream();
+            } catch (Exception e) {
+                m_reporter.error(e.getMessage());
+                throw new IOException("Cannot read metadata");
+            }
+            StreamMetadataProvider provider = new StreamMetadataProvider(stream, m_reporter);
+            provider.setValidateUsingLocalSchemas(m_validateUsingLocalSchemas);
+            m_cached.addAll(provider.getMetadatas());
+        }
+
+        return m_cached;
+    }
+}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/PojoizationPluginTestCase.java b/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/PojoizationPluginTestCase.java
new file mode 100644
index 0000000..0ad3002
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/java/org/apache/felix/ipojo/bnd/PojoizationPluginTestCase.java
@@ -0,0 +1,113 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.Map;
+
+import aQute.lib.osgi.Analyzer;
+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.easymock.EasyMock;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class PojoizationPluginTestCase extends TestCase {
+
+    @Mock
+    private Reporter reporter;
+
+    @Spy
+    private Analyzer analyzer = new Analyzer();
+
+    @Spy
+    private Jar jar = new Jar("mock.jar");
+
+    @Override
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    public void testAnalysisWithComponentOnlyMetadataXml() throws Exception {
+        PojoizationPlugin plugin = new PojoizationPlugin();
+
+        Map<String, String> props = new HashMap<String, String>();
+
+        Resource resource = new URLResource(getClass().getResource("/metadata-components-only.xml"));
+        doReturn(jar).when(analyzer).getJar();
+        doReturn(resource).when(jar).getResource(eq("META-INF/metadata.xml"));
+
+        plugin.setReporter(reporter);
+        plugin.setProperties(props);
+
+        plugin.analyzeJar(analyzer);
+
+        assertEquals("component { $class=\"com.acme.Thermometer\" }",
+                analyzer.getProperty("IPOJO-Components"));
+    }
+
+    public void testAnalysisWithInstanceOnlyMetadataXml() throws Exception {
+        PojoizationPlugin plugin = new PojoizationPlugin();
+
+        Map<String, String> props = new HashMap<String, String>();
+
+        Resource resource = new URLResource(getClass().getResource("/metadata-instances-only.xml"));
+        doReturn(jar).when(analyzer).getJar();
+        doReturn(resource).when(jar).getResource(eq("META-INF/metadata.xml"));
+
+        plugin.setReporter(reporter);
+        plugin.setProperties(props);
+
+        plugin.analyzeJar(analyzer);
+
+        assertEquals("instance { $component=\"com.acme.Thermometer\" }",
+                analyzer.getProperty("IPOJO-Components"));
+    }
+
+    public void testAnalysisWithComponentsAndInstancesMetadataXml() throws Exception {
+        PojoizationPlugin plugin = new PojoizationPlugin();
+
+        Map<String, String> props = new HashMap<String, String>();
+
+        Resource resource = new URLResource(getClass().getResource("/metadata-components-and-instances.xml"));
+        doReturn(jar).when(analyzer).getJar();
+        doReturn(resource).when(jar).getResource(eq("META-INF/metadata.xml"));
+
+        plugin.setReporter(reporter);
+        plugin.setProperties(props);
+
+        plugin.analyzeJar(analyzer);
+
+        assertEquals("component { $class=\"com.acme.Thermometer\" }" +
+                     "instance { $component=\"com.acme.Thermometer\" }" +
+                     "instance { $component=\"com.acme.Thermometer\" }",
+                     analyzer.getProperty("IPOJO-Components"));
+    }
+}
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-components-and-instances.xml b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-components-and-instances.xml
new file mode 100644
index 0000000..19bc3d4
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-components-and-instances.xml
@@ -0,0 +1,5 @@
+<ipojo>
+  <component class="com.acme.Thermometer" />
+  <instance component="com.acme.Thermometer" />
+  <instance component="com.acme.Thermometer" />
+</ipojo>
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-components-only.xml b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-components-only.xml
new file mode 100644
index 0000000..cc913fe
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-components-only.xml
@@ -0,0 +1,3 @@
+<ipojo>
+  <component class="com.acme.Thermometer"/>
+</ipojo>
diff --git a/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-instances-only.xml b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-instances-only.xml
new file mode 100644
index 0000000..56aa63a
--- /dev/null
+++ b/ipojo/manipulator/bnd-ipojo-plugin/src/test/resources/metadata-instances-only.xml
@@ -0,0 +1,3 @@
+<ipojo>
+    <instance component="com.acme.Thermometer"/>
+</ipojo>
\ No newline at end of file