FELIX-370:
 - Moved maven-obr-plugin from clement sandbox to trunk 
 - Fixed problem with repository containg space in path (on Win32 is default)
 - Update maven-bundle-plugin in order to use maven-obr-plugin

General:
 - Some iPOJO module compiling as bundle where defined in the plugins profile
 - Wrong parent POM in some upnp module



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@578326 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/pom.xml b/bundleplugin/pom.xml
index f99debc..9869d4e 100644
--- a/bundleplugin/pom.xml
+++ b/bundleplugin/pom.xml
@@ -28,19 +28,27 @@
  </parent>
 
  <modelVersion>4.0.0</modelVersion>
- <groupId>org.apache.felix</groupId>
+
  <artifactId>maven-bundle-plugin</artifactId>
  <version>1.1.0-SNAPSHOT</version>
-
  <packaging>maven-plugin</packaging>
+ 
  <name>Maven Bundle Plugin</name>
- <description> provides a maven plugin that allows that builds the jar by
-  embedding packages from the classpath (wildcarded). Plus a zillion
-  other features. See http://www.aqute.biz/php/tools/bnd.php
-  </description>
+ <description>
+  Provides a maven plugin which allows the creation of an OSGi bundle by
+  embedding used packages as class or jars by inspecting the classpath used to
+  compile the bundle. Plus a zillion other features. 
+  The plugin relies on the bnd tools, so to gather more information you can look
+  at is documentation http://www.aqute.biz/php/tools/bnd.php
+ </description>
  
  <dependencies>
   <dependency>
+   <groupId>org.apache.felix</groupId>
+   <artifactId>maven-obr-plugin</artifactId>
+   <version>0.1.0-SNAPSHOT</version>
+  </dependency>
+  <dependency>
    <groupId>biz.aQute</groupId>
    <artifactId>bndlib</artifactId>
    <version>0.0.189</version>
diff --git a/bundleplugin/src/main/resources/META-INF/plexus/components.xml b/bundleplugin/src/main/resources/META-INF/plexus/components.xml
index 1eb9b8e..cfe5bbd 100644
--- a/bundleplugin/src/main/resources/META-INF/plexus/components.xml
+++ b/bundleplugin/src/main/resources/META-INF/plexus/components.xml
@@ -34,8 +34,14 @@
               <test-compile>org.apache.maven.plugins:maven-compiler-plugin:testCompile</test-compile>
               <test>org.apache.maven.plugins:maven-surefire-plugin:test</test>
               <package>org.apache.felix:maven-bundle-plugin:bundle</package>
-              <install>org.apache.maven.plugins:maven-install-plugin:install</install>
-              <deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy>
+              <install>
+                org.apache.maven.plugins:maven-install-plugin:install,
+                org.apache.felix:maven-obr-plugin:repository
+		</install>
+              <deploy>
+                org.apache.maven.plugins:maven-deploy-plugin:deploy,
+                org.apache.felix:maven-obr-plugin:deploy
+              </deploy>
             </phases>
             <!-- END SNIPPET: bundle-lifecycle -->
           </lifecycle>
diff --git a/maven-obr-plugin/pom.xml b/maven-obr-plugin/pom.xml
new file mode 100644
index 0000000..b6244a3
--- /dev/null
+++ b/maven-obr-plugin/pom.xml
@@ -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.
+-->
+<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/maven-v4_0_0.xsd">
+
+  <parent>
+    <artifactId>felix</artifactId>
+    <groupId>org.apache.felix</groupId>
+    <version>1.1.0-SNAPSHOT</version>
+    <relativePath>../pom/pom.xml</relativePath>
+ </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>maven-obr-plugin</artifactId>
+  <version>0.1.0-SNAPSHOT</version>
+  <packaging>maven-plugin</packaging>
+  
+  <name>OBR Maven Plugin</name>
+  <description>
+    Provides a maven plugin which allows the automatic cration and update of an
+    OSGi Bundle Repository complaint to OSGi RFC 0112.
+  </description>
+
+  <dependencies>
+    <dependency>
+      <groupId>net.sf.kxml</groupId>
+      <artifactId>kxml2</artifactId>
+      <version>2.2.2</version>
+    </dependency>
+    <dependency>
+      <groupId>${groupId}</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <version>1.0.0</version>
+    </dependency>
+    <dependency>
+      <groupId>${groupId}</groupId>
+      <artifactId>org.apache.felix.bundlerepository</artifactId>
+      <version>1.0.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>2.0.7</version>
+    </dependency>
+    <dependency>
+      <groupId>xerces</groupId>
+      <artifactId>xercesImpl</artifactId>
+      <version>2.4.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-project</artifactId>
+      <version>2.0.7</version>
+    </dependency>
+  </dependencies>
+
+</project>
+
diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Capability.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Capability.java
new file mode 100644
index 0000000..a8f55cc
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Capability.java
@@ -0,0 +1,107 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import org.w3c.dom.Document;

+import org.w3c.dom.Element;

+import org.w3c.dom.Node;

+

+/**

+ * This class describe and store capability node.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class Capability {

+

+    /**

+     * m_name: name of the capability.

+     */

+    private String m_name;

+

+    /**

+     * m_p: List of PElement.

+     */

+    private List m_p = new ArrayList();

+

+    /**

+     * get the name attribute.

+     * 

+     * @return name attribute

+     */

+    public String getName() {

+        return m_name;

+    }

+

+    /**

+     * set the name attribute.

+     * 

+     * @param name new name value

+     *            

+     */

+    public void setName(String name) {

+        this.m_name = name;

+    }

+

+    /**

+     * return the capabilities.

+     * 

+     * @return List of PElement

+     */

+    public List getP() {

+        return m_p;

+    }

+

+    /**

+     * set the capabilities.

+     * 

+     * @param mp List of PElement

+     *            

+     */

+    public void setP(List mp) {

+        this.m_p = mp;

+    }

+

+    /**

+     * add one element in List.

+     * 

+     * @param pelement PElement

+     *            

+     */

+    public void addP(PElement pelement) {

+        m_p.add(pelement);

+    }

+

+    /**

+     * transform this object to Node.

+     * 

+     * @param father father document for create Node

+     * @return node

+     */

+    public Node getNode(Document father) {

+        Element capability = father.createElement("capability");

+        capability.setAttribute("name", this.getName());

+        for (int i = 0; i < this.getP().size(); i++) {

+            capability.appendChild(((PElement) (this.getP().get(i))).getNode(father));

+        }

+        return capability;

+    }

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Category.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Category.java
new file mode 100644
index 0000000..3032271
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Category.java
@@ -0,0 +1,64 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import org.w3c.dom.Document;

+import org.w3c.dom.Element;

+import org.w3c.dom.Node;

+

+/**

+ * describe and store category node.

+ * 

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+

+public class Category {

+    /**

+     * id of the category.

+     */

+    private String m_id;

+

+    /**

+     * get the id attribute.

+     * 

+     * @return id

+     */

+    public String getId() {

+        return m_id;

+    }

+

+    /**

+     * set the id attribute.

+     * @param id new id value

+     */

+    public void setId(String id) {

+        this.m_id = id;

+    }

+

+    /**

+     * transform this object to node.

+     * @param father father document for create Node

+     * @return node

+     */

+    public Node getNode(Document father) {

+        Element category = father.createElement("category");

+        category.setAttribute("id", this.getId());

+        return category;

+    }

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Config.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Config.java
new file mode 100644
index 0000000..08bc867
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Config.java
@@ -0,0 +1,79 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+/**

+ * this class is used to store some user information about configuration of the plugin.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ *

+ */

+public class Config {

+

+    /**

+     * use relative path or not.

+     */

+    private boolean m_pathRelative; // use relative or absolute path in repository.xml

+

+    /**

+     * deploy file or not.

+     */

+    private boolean m_fileRemote; // deploy file on remote server

+

+    /**

+     * constructor: set default configuration: use relative path and don't upload file.

+     *

+     */

+    public Config() {

+        // default configuration

+        m_pathRelative = true;

+        m_fileRemote = false;

+    }

+

+    /**

+     * set relativePath attribute.

+     * @param value new value of attribute

+     */

+    public void setPathRelative(boolean value) {

+        m_pathRelative = value;

+    }

+

+    /**

+     * set fileRemote attribute.

+     * @param value new value of attribute

+     */

+    public void setRemotely(boolean value) {

+        m_fileRemote = value;

+    }

+

+    /**

+     * get use path relative.

+     * @return true if plugin use relative path, else false

+     */

+    public boolean isPathRelative() {

+        return m_pathRelative;

+    }

+

+    /**

+     * get if use upload file.

+     * @return true if the file will be uploaded, else false

+     */

+    public boolean isRemotely() {

+        return m_fileRemote;

+    }

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ExtractBindexInfo.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ExtractBindexInfo.java
new file mode 100644
index 0000000..6d0be6e
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ExtractBindexInfo.java
@@ -0,0 +1,242 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.io.File;

+import java.net.MalformedURLException;

+import java.net.URI;

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+

+import org.apache.maven.plugin.MojoExecutionException;

+import org.osgi.impl.bundle.obr.resource.BundleInfo;

+import org.osgi.impl.bundle.obr.resource.CapabilityImpl;

+import org.osgi.impl.bundle.obr.resource.RepositoryImpl;

+import org.osgi.impl.bundle.obr.resource.RequirementImpl;

+import org.osgi.impl.bundle.obr.resource.ResourceImpl;

+import org.osgi.impl.bundle.obr.resource.VersionImpl;

+

+/**

+ * this class is used to configure bindex and get information built by bindex about targeted bundle.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class ExtractBindexInfo {

+

+    /**

+     * attribute get from bindex which describe targeted resource.

+     */

+    private ResourceImpl m_resource;

+

+    /**

+     * configure bindex and build information.

+     * @param repoFilename URI on OBR descriptor file

+     * @param outFile path on targeted jar-file

+     * @throws MojoExecutionException occurs if bindex configuration failed

+     */

+    public ExtractBindexInfo(URI repoFilename, String outFile) throws MojoExecutionException {

+

+        this.m_resource = null;

+        RepositoryImpl repository = null;

+        try {

+            repository = new RepositoryImpl(new File(repoFilename).getAbsoluteFile().toURL());

+        } catch (MalformedURLException e) {

+            e.printStackTrace();

+            throw new MojoExecutionException("MalformedURLException");

+        }

+        BundleInfo info = null;

+        try {

+            info = new BundleInfo(repository, new File(outFile));

+        } catch (Exception e) {

+            e.printStackTrace();

+            throw new MojoExecutionException("Exception");

+        }

+

+        try {

+            m_resource = info.build();

+        } catch (Exception e) {

+            e.printStackTrace();

+            throw new MojoExecutionException("Exception");

+        }

+    }

+

+    /**

+     * transform logical operator in xml syntax.

+     * @param filter string which contains logical operator

+     * @return string in correct xml syntax

+     */

+    private String parseFilter(String filter) {

+        filter.replaceAll("&", "&amp");

+        filter.replaceAll(">=", "&gt");

+

+        return filter;

+    }

+

+    /**

+     * extract capabilities from bindex information.

+     * @return bundle capabilities List

+     */

+    public List getCapabilities() {

+        List list = new ArrayList();

+        Collection res = m_resource.getCapabilityList();

+        Iterator it = res.iterator();

+        while (it.hasNext()) {

+            Capability capability = new Capability();

+            CapabilityImpl ci = (CapabilityImpl) it.next();

+            capability.setName(ci.getName());

+            // System.out.println(ci.getName()) ;

+            if (!(ci.getName().compareTo("bundle") == 0)) {

+                Map properties = ci.getProperties();

+                for (Iterator k = properties.keySet().iterator(); k.hasNext();) {

+                    PElement p = new PElement();

+                    String key = (String) k.next();

+                    List values = (List) properties.get(key);

+                    for (Iterator v = values.iterator(); v.hasNext();) {

+                        Object value = v.next();

+                        p.setN(key);

+                        if (value != null) {

+                            p.setV(value.toString());

+                        } else {

+                            System.out.println("Missing value " + key);

+                        }

+                        String type = null;

+                        if (value instanceof Number) {

+                            type = "number";

+                        } else { 

+                            if (value.getClass() == VersionImpl.class) { type = "version"; }

+                        }

+                        if (type != null) {

+                            p.setT(type);

+                        }

+                    }

+                    capability.addP(p);

+                }

+

+                list.add(capability);

+            }

+        }

+        return list;

+    }

+

+    /**

+     * extract requirement from bindex information.

+     * @return bundle requirement List

+     */

+    public List getRequirement() {

+        List list = new ArrayList();

+        Collection res = m_resource.getRequirementList();

+        Iterator it = res.iterator();

+        while (it.hasNext()) {

+            RequirementImpl ci = (RequirementImpl) it.next();

+            Require require = new Require();

+

+            require.setExtend(String.valueOf(ci.isExtend()));

+            require.setMultiple(String.valueOf(ci.isMultiple()));

+            require.setOptional(String.valueOf(ci.isOptional()));

+            require.setName(ci.getName());

+            require.setFilter(this.parseFilter(ci.getFilter()));

+            require.setValue(ci.getComment());

+            list.add(require);

+        }

+        return list;

+    }

+

+    /**

+     * extract symbolic name from bindex information.

+     * @return bundle symbolic name

+     */

+    public String getSymbolicName() {

+        return m_resource.getSymbolicName();

+    }

+

+    /**

+     * extract version from bindex information.

+     * @return bundle version

+     */

+    public String getVersion() {

+        if (m_resource.getVersion() != null) {

+            return m_resource.getVersion().toString();

+        } else {

+            return null;

+        }

+    }

+

+    /**

+     * extract presentation name from bindex information.

+     * @return bundle presentation name

+     */

+    public String getPresentationName() {

+        return m_resource.getPresentationName();

+    }

+

+    /**

+     * extract copyright from bindex information.

+     * @return bundle copyright

+     */

+    public String getCopyright() {

+        return m_resource.getCopyright();

+    }

+

+    /**

+     * extract description from bindex information.

+     * @return bundle description

+     */

+    public String getDescription() {

+        return m_resource.getDescription();

+    }

+

+    /**

+     * extract documentation from bindex information.

+     * @return bundle documentation

+     */

+    public String getDocumentation() {

+        if (m_resource.getDocumentation() != null) {

+            return m_resource.getDocumentation().toString();

+        } else {

+            return null;

+        }

+    }

+

+    /**

+     * extract license from bindex information.

+     * @return bundle license

+     */

+    public String getLicense() {

+        if (m_resource.getLicense() != null) {

+            return m_resource.getLicense().toString();

+        } else {

+            return null;

+        }

+    }

+

+    /**

+     * extract source from bindex information.

+     * @return bundle source

+     */

+    public String getSource() {

+        if (m_resource.getSource() != null) {

+            return m_resource.getSource().toString();

+        } else {

+            return null;

+        }

+    }

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrDeploy.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrDeploy.java
new file mode 100644
index 0000000..ace1e30
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrDeploy.java
@@ -0,0 +1,304 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.io.BufferedWriter;

+import java.io.File;

+import java.io.FileWriter;

+import java.io.IOException;

+import java.io.Writer;

+import java.util.List;

+

+import org.apache.maven.artifact.manager.WagonManager;

+import org.apache.maven.artifact.repository.ArtifactRepository;

+import org.apache.maven.model.Resource;

+import org.apache.maven.plugin.AbstractMojo;

+import org.apache.maven.plugin.MojoExecutionException;

+import org.apache.maven.plugin.MojoFailureException;

+import org.apache.maven.project.MavenProject;

+import org.apache.maven.settings.Settings;

+import org.apache.maven.wagon.ResourceDoesNotExistException;

+import org.apache.maven.wagon.TransferFailedException;

+import org.apache.maven.wagon.authorization.AuthorizationException;

+

+/**

+ * deploy the bundle to a remote site.

+ * this goal is used when you compile a project with a pom file

+ * @goal deployment

+ * @phase deploy

+ * @requiresDependencyResolution compile

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+

+public class ObrDeploy extends AbstractMojo {

+

+    /**

+     * setting of maven.

+     * 

+     * @parameter expression="${settings}"

+     * @require

+     */

+    private Settings m_settings;

+

+    /**

+     * name of the repository xml descriptor file.

+     * 

+     * @parameter expression="${repository-name}" default-value="repository.xml"

+     */

+    private String m_repositoryName;

+

+    /**

+     * The local Maven repository.

+     * 

+     * @parameter expression="${localRepository}"

+     * @required

+     */

+    private ArtifactRepository m_localRepo;

+

+    /**

+     * Project in use.

+     * 

+     * @parameter expression="${project}"

+     * @require

+     */

+    private MavenProject m_project;

+

+    /**

+     * Wagon Manager.

+     * @component

+     */

+    private WagonManager m_wagonManager;

+

+    /**

+     * obr file define by the user.

+     * 

+     * @parameter expression="${ignore-lock}"

+     * 

+     */

+    private boolean m_ignoreLock;

+

+    /**

+     * used to store pathfile in local repo.

+     */

+    private String m_fileInLocalRepo;

+

+    /**

+     * Enable/Disable this goal

+     * @description If true evrything the goal do nothing, the goal just skip over 

+     * @parameter expression="${maven.obr.installToRemoteOBR}" default-value="false"

+     */

+    private boolean installToRemoteOBR;    

+

+    

+    /**

+     * main method for this goal.

+     * @implements org.apache.maven.plugin.Mojo.execute 

+     * @throws MojoExecutionException if the plugin failed

+     * @throws MojoFailureException if the plugin failed

+     */

+    public void execute() throws MojoExecutionException, MojoFailureException {

+        getLog().info("Obr-deploy start:");

+        if(!installToRemoteOBR)

+        {

+        	getLog().info("maven-obr-plugin:deploy goal is disable due to one of the following reason:");

+        	getLog().info(" - 'installToRemoteOBR' configuration set to false");

+        	getLog().info(" - JVM property maven.obr.installToRemoteOBR set to false");

+        	return;

+        }

+        ArtifactRepository ar = m_project.getDistributionManagementArtifactRepository();

+

+        // locate the obr.xml file

+        String obrXmlFile = null;

+        List l = m_project.getResources();

+        for (int i = 0; i < l.size(); i++) {

+            File f = new File(((Resource) l.get(i)).getDirectory() + File.separator + "obr.xml");

+            if (f.exists()) {

+                obrXmlFile = ((Resource) l.get(i)).getDirectory() + File.separator + "obr.xml";

+                break;

+            }

+        }

+

+        // the obr.xml file is not present

+        if (obrXmlFile == null) {

+            getLog().warn("obr.xml is not present, use default");

+        }

+

+        File repoDescriptorFile = null;

+

+        // init the wagon connection

+        RemoteFileManager remoteFile = new RemoteFileManager(ar, m_wagonManager, m_settings, getLog());

+        remoteFile.connect();

+

+        // create a non-empty file used to lock the repository descriptor file

+        File lockFile = null;

+        Writer output = null;

+        try {

+            lockFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), null);

+            output = new BufferedWriter(new FileWriter(lockFile));

+            output.write("locked");

+            output.close();

+        } catch (IOException e) {

+            getLog().error("Unable to create temporary file");

+            throw new MojoFailureException("IOException");

+        }

+

+        if (m_ignoreLock) {

+            try {

+                remoteFile.put(lockFile, m_repositoryName + ".lock");

+            } catch (TransferFailedException e) {

+                getLog().error("Transfer failed");

+                e.printStackTrace();

+                throw new MojoFailureException("TransferFailedException");

+

+            } catch (ResourceDoesNotExistException e) {

+                throw new MojoFailureException("ResourceDoesNotExistException");

+            } catch (AuthorizationException e) {

+                getLog().error("Authorization failed");

+                e.printStackTrace();

+                throw new MojoFailureException("AuthorizationException");

+            }

+

+        } else {

+            int countError = 0;

+            while (remoteFile.isLockedFile(remoteFile, m_repositoryName) && countError < 2) {

+                countError++;

+                getLog().warn("File is locked, retry in 10s");

+                try {

+                    Thread.sleep(10000);

+                } catch (InterruptedException e) {

+                    getLog().warn("Sleep interupted");

+                }

+            }

+

+            if (countError == 2) {

+                getLog().error("File: " + m_repositoryName + " is locked. Try -Dignore-lock=true if you want force uploading");

+                throw new MojoFailureException("fileLocked");

+            }

+        }

+

+        // file is not locked, so we lock it now

+        try {

+            remoteFile.put(lockFile, m_repositoryName + ".lock");

+        } catch (TransferFailedException e) {

+            getLog().error("Transfer failed");

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+

+        } catch (ResourceDoesNotExistException e) {

+            throw new MojoFailureException("ResourceDoesNotExistException");

+        } catch (AuthorizationException e) {

+            getLog().error("Authorization failed");

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        }

+

+        try {

+            repoDescriptorFile = remoteFile.get(m_repositoryName);

+        } catch (TransferFailedException e) {

+            getLog().error("Transfer failed");

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+

+        } catch (ResourceDoesNotExistException e) {

+            // file doesn't exist! create a new one

+            getLog().warn("file specified does not exist: " + m_repositoryName);

+            getLog().warn("Create a new repository descriptor file " + m_repositoryName);

+            try {

+                File f = File.createTempFile(String.valueOf(System.currentTimeMillis()), null);

+                repoDescriptorFile = new File(f.getParent() + File.separator + String.valueOf(System.currentTimeMillis()) + ".xml");

+            } catch (IOException e1) {

+                getLog().error("canno't create temporary file");

+                e1.printStackTrace();

+                return;

+            }

+        } catch (AuthorizationException e) {

+            getLog().error("Authorization failed");

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        } catch (IOException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("IOException");

+        }

+

+        Config userConfig = new Config();

+        userConfig.setPathRelative(true);

+        userConfig.setRemotely(true);

+

+        PathFile file = null;

+

+        // get the path to local maven repository

+        file = new PathFile(PathFile.uniformSeparator(m_settings.getLocalRepository()) + File.separator + PathFile.uniformSeparator(m_localRepo.pathOf(m_project.getArtifact())));

+        if (file.isExists()) {

+            m_fileInLocalRepo = file.getOnlyAbsoluteFilename();

+        } else {

+            getLog().error("file not found in local repository: " + m_settings.getLocalRepository() + File.separator + m_localRepo.pathOf(m_project.getArtifact()));

+            return;

+        }

+

+        file = new PathFile("file:/" + repoDescriptorFile.getAbsolutePath());

+

+        ObrUpdate obrUpdate = new ObrUpdate(file, obrXmlFile, m_project, m_fileInLocalRepo, PathFile.uniformSeparator(m_settings.getLocalRepository()), userConfig, getLog());

+

+        obrUpdate.updateRepository();

+

+        // the reposiroty descriptor file is modified, we upload it on the remote repository

+        try {

+            remoteFile.put(repoDescriptorFile, m_repositoryName);

+        } catch (TransferFailedException e) {

+            getLog().error("Transfer failed");

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+        } catch (ResourceDoesNotExistException e) {

+            getLog().error("Resource does not exist:" + repoDescriptorFile.getName());

+            e.printStackTrace();

+            throw new MojoFailureException("ResourceDoesNotExistException");

+        } catch (AuthorizationException e) {

+            getLog().error("Authorization failed");

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        }

+        repoDescriptorFile.delete();

+

+        // we remove lockFile activation

+        lockFile = null;

+        try {

+            lockFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), null);

+        } catch (IOException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("IOException");

+        }

+        try {

+            remoteFile.put(lockFile, m_repositoryName + ".lock");

+        } catch (TransferFailedException e) {

+            getLog().error("Transfer failed");

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+        } catch (ResourceDoesNotExistException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("ResourceDoesNotExistException");

+        } catch (AuthorizationException e) {

+            getLog().error("Authorization failed");

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        }

+

+        remoteFile.disconnect();

+    }

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrDeployFile.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrDeployFile.java
new file mode 100644
index 0000000..1296a56
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrDeployFile.java
@@ -0,0 +1,283 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.io.BufferedWriter;

+import java.io.File;

+import java.io.FileWriter;

+import java.io.IOException;

+import java.io.Writer;

+

+import org.apache.maven.artifact.manager.WagonManager;

+import org.apache.maven.artifact.repository.ArtifactRepository;

+import org.apache.maven.plugin.AbstractMojo;

+import org.apache.maven.plugin.MojoExecutionException;

+import org.apache.maven.plugin.MojoFailureException;

+import org.apache.maven.project.MavenProject;

+import org.apache.maven.settings.Settings;

+import org.apache.maven.wagon.ResourceDoesNotExistException;

+import org.apache.maven.wagon.TransferFailedException;

+import org.apache.maven.wagon.authorization.AuthorizationException;

+

+/**

+ * deploy the bundle to a ftp site.

+ * this goal is used when you upload a jar file (in command line)

+ * @goal deploy-file

+ * @phase deploy

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class ObrDeployFile extends AbstractMojo {

+

+    /**

+     * setting of maven.

+     * 

+     * @parameter expression="${settings}"

+     * @require

+     */

+

+    private Settings m_settings;

+

+    /**

+     * name of the repository xml descriptor file.

+     * 

+     * @parameter expression="${repository-name}" default-value="repository.xml"

+     */

+    private String m_repositoryName;

+

+    /**

+     * The local Maven repository.

+     * 

+     * @parameter expression="${localRepository}"

+     * @required

+     */

+    private ArtifactRepository m_localRepo;

+

+    /**

+     * Project in use.

+     * 

+     * @parameter expression="${project}"

+     * @require

+     */

+    private MavenProject m_project;

+

+    /**

+     * Wagon Manager.

+     * @component

+     */

+    private WagonManager m_wagonManager;

+

+    /**

+     * obr file define by the user.

+     * 

+     * @parameter expression="${obr-file}"

+     * 

+     */

+    private String m_obrFile;

+

+    /**

+     * obr file define by the user.

+     * 

+     * @parameter expression="${ignore-lock}"

+     * 

+     */

+    private boolean m_ignoreLock;

+

+    /**

+     * used to store pathfile in local repo.

+     */

+    private String m_fileInLocalRepo;

+

+    /**

+     * main method for this goal.

+     * @implements org.apache.maven.plugin.Mojo.execute 

+     * @throws MojoExecutionException if the plugin failed

+     * @throws MojoFailureException if the plugin failed

+     */

+    public void execute() throws MojoExecutionException, MojoFailureException {

+        getLog().info("Obr-deploy-file start:");

+

+        ArtifactRepository ar = m_project.getDistributionManagementArtifactRepository();

+

+        // locate the obr.xml file

+        PathFile fileObrXml = new PathFile(m_obrFile);

+        if (!fileObrXml.isExists()) {

+            getLog().warn("obr.xml file not found, use default");

+        }

+

+        File repoDescriptorFile = null;

+

+        RemoteFileManager remoteFile = new RemoteFileManager(ar, m_wagonManager, m_settings, getLog());

+

+        remoteFile.connect();

+

+        // create a non-empty file used to lock the repository descriptor file

+        File lockFile = null;

+        Writer output = null;

+        try {

+            lockFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), null);

+            output = new BufferedWriter(new FileWriter(lockFile));

+            output.write("locked");

+            output.close();

+        } catch (IOException e) {

+            getLog().error("Unable to create temporary file");

+            throw new MojoFailureException("IOException");

+        }

+

+        if (m_ignoreLock) {

+            try {

+                remoteFile.put(lockFile, m_repositoryName + ".lock");

+            } catch (TransferFailedException e) {

+                getLog().error("Transfer failed");

+                e.printStackTrace();

+                throw new MojoFailureException("TransferFailedException");

+

+            } catch (ResourceDoesNotExistException e) {

+                throw new MojoFailureException("ResourceDoesNotExistException");

+            } catch (AuthorizationException e) {

+                getLog().error("Authorization failed");

+                e.printStackTrace();

+                throw new MojoFailureException("AuthorizationException");

+            }

+

+        } else {

+            int countError = 0;

+            while (remoteFile.isLockedFile(remoteFile, m_repositoryName) && countError < 2) {

+                countError++;

+                getLog().warn("File is locked, retry in 10s");

+                try {

+                    Thread.sleep(10000);

+                } catch (InterruptedException e) {

+                    getLog().warn("Sleep Interupted");

+                }

+            }

+

+            if (countError == 2) {

+                getLog().error("File: " + m_repositoryName + " is locked. Try -Dignore-lock=true if you want to force uploading");

+                throw new MojoFailureException("fileLocked");

+            }

+        }

+

+        // file is not locked, so we lock it now

+        try {

+            remoteFile.put(lockFile, m_repositoryName + ".lock");

+        } catch (TransferFailedException e) {

+            getLog().error("Transfer failed");

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+

+        } catch (ResourceDoesNotExistException e) {

+            throw new MojoFailureException("ResourceDoesNotExistException");

+        } catch (AuthorizationException e) {

+            getLog().error("Authorization failed");

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        }

+

+        try {

+            repoDescriptorFile = remoteFile.get(m_repositoryName);

+        } catch (TransferFailedException e) {

+            getLog().error("Transfer failed");

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+

+        } catch (ResourceDoesNotExistException e) {

+            // file doesn't exist! create a new one

+            getLog().warn("file specified does not exist: " + m_repositoryName);

+            getLog().warn("Create a new repository descriptor file " + m_repositoryName);

+            try {

+                File f = File.createTempFile(String.valueOf(System.currentTimeMillis()), null);

+                repoDescriptorFile = new File(f.getParent() + File.separator + String.valueOf(System.currentTimeMillis()) + ".xml");

+            } catch (IOException e1) {

+                getLog().error("canno't create temporary file");

+                e1.printStackTrace();

+                return;

+            }

+        } catch (AuthorizationException e) {

+            getLog().error("Authorization failed");

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        } catch (IOException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("IOException");

+        }

+

+        Config userConfig = new Config();

+        userConfig.setPathRelative(true);

+        userConfig.setRemotely(true);

+

+        PathFile file = null;

+

+        // get the path to local maven repository

+        file = new PathFile(PathFile.uniformSeparator(m_settings.getLocalRepository()) + File.separator + PathFile.uniformSeparator(m_localRepo.pathOf(m_project.getArtifact())));

+        if (file.isExists()) {

+            m_fileInLocalRepo = file.getOnlyAbsoluteFilename();

+        } else {

+            getLog().error("file not found in local repository: " + m_settings.getLocalRepository() + File.separator + m_localRepo.pathOf(m_project.getArtifact()));

+            return;

+        }

+

+        file = new PathFile("file:/" + repoDescriptorFile.getAbsolutePath());

+

+        ObrUpdate obrUpdate = new ObrUpdate(file, fileObrXml.getOnlyAbsoluteFilename(), m_project, m_fileInLocalRepo, PathFile.uniformSeparator(m_settings.getLocalRepository()), userConfig, getLog());

+

+        obrUpdate.updateRepository();

+

+        // the reposiroty descriptor file is modified, we upload it on the remote repository

+        try {

+            remoteFile.put(repoDescriptorFile, m_repositoryName);

+        } catch (TransferFailedException e) {

+            getLog().error("Transfer failed");

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+        } catch (ResourceDoesNotExistException e) {

+            getLog().error("Resource does not exist:" + repoDescriptorFile.getName());

+            e.printStackTrace();

+            throw new MojoFailureException("ResourceDoesNotExistException");

+        } catch (AuthorizationException e) {

+            getLog().error("Authorization failed");

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        }

+        repoDescriptorFile.delete();

+

+        // we remove lockFile activation

+        lockFile = null;

+        try {

+            lockFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), null);

+        } catch (IOException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("IOException");

+        }

+        try {

+            remoteFile.put(lockFile, m_repositoryName + ".lock");

+        } catch (TransferFailedException e) {

+            getLog().error("Transfer failed");

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+        } catch (ResourceDoesNotExistException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("ResourceDoesNotExistException");

+        } catch (AuthorizationException e) {

+            getLog().error("Authorization failed");

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        }

+        remoteFile.disconnect();

+    }

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrInstall.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrInstall.java
new file mode 100644
index 0000000..496aafa
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrInstall.java
@@ -0,0 +1,163 @@
+/* 
+ * 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.sandbox.obr.plugin;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.settings.Settings;
+
+/**
+ * construct the repository.xml with a project Maven compiled
+ *
+ * @goal repository
+ * @phase install
+ * @requiresDependencyResolution compile
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ObrInstall extends AbstractMojo {
+    /**
+     * The local Maven repository.
+     * 
+     * @parameter expression="${localRepository}"
+     * @required
+     */
+    private ArtifactRepository m_localRepo;
+
+    /**
+     * path to the repository.xml.
+     * 
+     * @parameter expression="${repository-path}"
+     * @require
+     */
+    private String m_repositoryPath;
+
+    /**
+     * Project in use.
+     * 
+     * @parameter expression="${project}"
+     * @require
+     */
+
+    private MavenProject m_project;
+
+    /**
+     * setting of maven.
+     * 
+     * @parameter expression="${settings}"
+     * @require
+     */
+
+    private Settings m_settings;
+    
+    /**
+     * Enable/Disable this goal
+     * @description If true evrything the goal do nothing, the goal just skip over 
+     * @parameter expression="${maven.obr.installToLocalOBR}" default-value="true"
+     */
+    private boolean installToLocalOBR;    
+    
+
+    /**
+     * path to file in the maven local repository.
+     */
+    private String m_fileInLocalRepo;
+
+    /**
+     * main method for this goal.
+     * @implements org.apache.maven.plugin.Mojo.execute 
+     * @throws MojoExecutionException if the plugin failed
+     */
+    public void execute() throws MojoExecutionException {
+        getLog().info("Obr Plugin starts:");
+        if(!installToLocalOBR)
+        {
+        	getLog().info("maven-obr-plugin:repository goal is disable due to one of the following reason:");
+        	getLog().info(" - 'installToLocalOBR' configuration set to false");
+        	getLog().info(" - JVM property maven.obr.installToLocalOBR set to false");
+        	return;
+        }
+        
+        if (m_repositoryPath == null) {
+            m_repositoryPath = "file:/" + m_localRepo.getBasedir() + File.separator + "repository.xml";
+            getLog().warn("-DpathRepo is not define, use default repository: " + m_repositoryPath);
+        }
+
+        PathFile file = new PathFile(m_repositoryPath);
+        if (file.isExists()) {
+            if (!m_repositoryPath.startsWith("file:/")) { m_repositoryPath = "file:/" + m_repositoryPath; }
+        }
+
+        // locate the obr.xml file
+        String obrXmlFile = null;
+        List l = m_project.getResources();
+        for (int i = 0; i < l.size(); i++) {
+            File f = new File(((Resource) l.get(i)).getDirectory() + File.separator + "obr.xml");
+            if (f.exists()) {
+                obrXmlFile = ((Resource) l.get(i)).getDirectory() + File.separator + "obr.xml";
+                break;
+            }
+        }
+        // the obr.xml file is not present
+        if (obrXmlFile == null) {
+            getLog().warn("obr.xml is not present, use default");
+        }
+
+        // get the path to local maven repository
+        file = new PathFile(PathFile.uniformSeparator(m_settings.getLocalRepository()) + 
+        		File.separator + PathFile.uniformSeparator(m_localRepo.pathOf(m_project.getArtifact())));
+	
+        if (file.isExists()) {
+            m_fileInLocalRepo = file.	getOnlyAbsoluteFilename();
+        } else {
+            getLog().error("file not found in local repository: " + m_settings.getLocalRepository() + File.separator + m_localRepo.pathOf(m_project.getArtifact()));
+		getLog().error("file not found in local repository: " 
+				+ m_localRepo.getBasedir() + File.separator + m_localRepo.pathOf(m_project.getArtifact()));
+            return;
+        }
+
+        // verify the repository.xml
+        PathFile fileRepo = new PathFile(m_repositoryPath);
+        if (fileRepo.isRelative()) { fileRepo.setBaseDir(m_settings.getLocalRepository()); }
+
+        // create the folder to the repository
+        PathFile repoExist = new PathFile(fileRepo.getAbsolutePath());
+        if (!repoExist.isExists()) { fileRepo.createPath(); }
+
+        // build the user configuration (use default)
+        Config user = new Config();
+
+    	getLog().debug("Maven2 Local File repository = "+fileRepo.getAbsoluteFilename());
+    	getLog().debug("OBR repository = "+obrXmlFile);
+	
+        ObrUpdate obrUpdate = new ObrUpdate(fileRepo, obrXmlFile, m_project, m_fileInLocalRepo, PathFile.uniformSeparator(m_settings.getLocalRepository()), user, getLog());
+        try {
+            obrUpdate.updateRepository();
+        } catch (MojoExecutionException e) {
+            e.printStackTrace();
+            throw new MojoExecutionException("MojoFailureException");
+        }
+    }
+
+}
diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrInstallFile.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrInstallFile.java
new file mode 100644
index 0000000..812ae87
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrInstallFile.java
@@ -0,0 +1,180 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.io.File;

+

+import org.apache.maven.artifact.repository.ArtifactRepository;

+import org.apache.maven.plugin.AbstractMojo;

+import org.apache.maven.plugin.MojoExecutionException;

+import org.apache.maven.plugin.MojoFailureException;

+import org.apache.maven.project.MavenProject;

+import org.apache.maven.settings.Settings;

+

+/**

+ * construct the repository.xml from a compiled bundle.

+ * @description construct the repository.xml from a compiled bundle.

+ * @goal install-file

+ * @requiresProject false

+ * @phase install

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class ObrInstallFile extends AbstractMojo {

+    /**

+     * The local Maven repository.

+     * 

+     * @parameter expression="${localRepository}"

+     * @required

+     */

+    private ArtifactRepository m_localRepo;

+

+    /**

+     * path to the repository.xml.

+     * 

+     * @parameter expression="${repository-path}"

+     * @require

+     */

+    private String m_repositoryPath;

+

+    /**

+     * setting of maven.

+     * 

+     * @parameter expression="${settings}"

+     * @require

+     */

+    private Settings m_settings;

+

+    /**

+     * Artifact Id.

+     * @description symbolic name define by the user

+     * @parameter expression="${artifactId}"

+     */

+    private String m_artifactId;

+

+    /**

+     * Group Id.

+     * @description groupId define by the user

+     * @parameter expression="${groupId}"

+     */

+    private String m_groupId;

+

+    /**

+     * Version.

+     * @description version define by the user

+     * @parameter expression="${version}"

+     */

+    private String m_version;

+

+    /**

+     * Packaging.

+     * @description packaging define by the user

+     * @parameter expression="${packaging}"

+     */

+    private String m_packaging;

+

+    /**

+     * OBR File.

+     * @description obr file define by the user

+     * @parameter expression="${obr-file}"

+     */

+    private String m_obrFile;

+

+    /**

+     * store user information in a project.

+     */

+    private MavenProject m_project;

+

+    /**

+     * main method for this goal.

+     * @implements org.apache.maven.plugin.Mojo.execute 

+     * @throws MojoExecutionException if the plugin failed

+     * @throws MojoFailureException if the plugin failed

+     */

+    public void execute() throws MojoExecutionException, MojoFailureException {

+        getLog().info("Install-File Obr starts:");

+

+        m_project = new MavenProject();

+        m_project.setArtifactId(m_artifactId);

+        m_project.setGroupId(m_groupId);

+        m_project.setVersion(m_version);

+        m_project.setPackaging(m_packaging);

+

+        PathFile fileOut;

+

+        if (m_groupId == null) {

+            getLog().error("-DgroupId=VALUE is required");

+            return;

+        }

+        if (m_artifactId == null) {

+            getLog().error("-Dartifactid=VALUE is required");

+            return;

+        }

+        if (m_version == null) {

+            getLog().error("-Dversion=VALUE is required");

+            return;

+        }

+        if (m_packaging == null) {

+            getLog().error("-Dpackaging=VALUE is required");

+            return;

+        }

+

+        // copy the file to the local repository

+        PathFile repoLocal = new PathFile(m_localRepo.getBasedir());

+

+        // get the target file in mvn repo

+        fileOut = new PathFile(PathFile.uniformSeparator(m_settings.getLocalRepository()) + File.separator + m_groupId.replace('.', File.separatorChar) + File.separator + m_artifactId + File.separator + m_version + File.separator + m_artifactId

+                + "-" + m_version + "." + m_packaging);

+

+        if (!fileOut.isExists()) {

+            getLog().error("file doesn't exist: " + fileOut.getAbsoluteFilename());

+            return;

+        } else {

+            getLog().info("Target file: " + fileOut.getAbsoluteFilename());

+        }

+

+        if (m_repositoryPath == null) {

+            m_repositoryPath = "file:" + repoLocal.getOnlyAbsoluteFilename() + "repository.xml";

+            getLog().warn("-DpathRepo is not define, use default repository: " + m_repositoryPath);

+        }

+

+        PathFile fileRepo = new PathFile(m_repositoryPath);

+        if (fileRepo.isRelative()) {

+            fileRepo.setBaseDir(m_settings.getLocalRepository());

+        }

+

+        // create the folder to the repository

+        PathFile repoExist = new PathFile(fileRepo.getAbsolutePath());

+        if (!repoExist.isExists()) {

+            fileRepo.createPath();

+        }

+

+        PathFile fileObrXml = new PathFile(m_obrFile);

+        if (!fileObrXml.isExists()) {

+            getLog().warn("obr.xml file not found, use default");

+        }

+

+        // build the user config

+        Config userConfig = new Config();

+

+        ObrUpdate obrUpdate = new ObrUpdate(fileRepo, fileObrXml.getOnlyAbsoluteFilename(), m_project, fileOut.getOnlyAbsoluteFilename(), m_localRepo.getBasedir(), userConfig, getLog());

+        obrUpdate.updateRepository();

+

+    }

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrUpdate.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrUpdate.java
new file mode 100644
index 0000000..a3de7b2
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ObrUpdate.java
@@ -0,0 +1,517 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.io.File;

+import java.io.FileNotFoundException;

+import java.io.FileOutputStream;

+import java.io.IOException;

+import java.net.URI;

+import java.text.SimpleDateFormat;

+import java.util.Date;

+import java.util.Properties;

+

+import javax.xml.parsers.DocumentBuilder;

+import javax.xml.parsers.DocumentBuilderFactory;

+import javax.xml.parsers.ParserConfigurationException;

+import javax.xml.transform.Result;

+import javax.xml.transform.Transformer;

+import javax.xml.transform.TransformerConfigurationException;

+import javax.xml.transform.TransformerException;

+import javax.xml.transform.TransformerFactory;

+import javax.xml.transform.dom.DOMSource;

+import javax.xml.transform.stream.StreamResult;

+

+import org.apache.maven.plugin.MojoExecutionException;

+import org.apache.maven.plugin.logging.Log;

+import org.apache.maven.project.MavenProject;

+import org.w3c.dom.Document;

+import org.w3c.dom.Element;

+import org.w3c.dom.NamedNodeMap;

+import org.w3c.dom.Node;

+import org.w3c.dom.NodeList;

+import org.xml.sax.SAXException;

+

+/**

+ * this class parse the old repository.xml file build the bundle resource description and update the repository.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+

+public class ObrUpdate {

+    /**

+     * generate the date format to insert it in repository descriptor file.

+     */

+    static SimpleDateFormat m_format = new SimpleDateFormat("yyyyMMddHHmmss.SSS");

+

+    /**

+     * logger for this plugin.

+     */

+    private Log m_logger;

+

+    /**

+     * name and path to the repository descriptor file.

+     */

+    private URI m_repoFilename;

+

+    /**

+     * name and path to the obr.xml file.

+     */

+    private String m_obrXml;

+

+    /**

+     * name and path to the bundle jar file.

+     */

+    private String m_outputFile;

+

+    /**

+     * maven project description.

+     */

+    private MavenProject m_project;

+

+    /**

+     * user configuration information.

+     */

+    private Config m_userConfig;

+

+    /**

+     * used to build another xml document.

+     */

+    private DocumentBuilder m_constructor;

+

+    /**

+     * root on parent document.

+     */

+    private Document m_repoDoc;

+

+    /**

+     * used to determine the first free id.

+     */

+    private boolean[] m_idTab;

+

+    /**

+     * first Node on repository descriptor tree.

+     */

+    private Node m_root;

+

+    /**

+     * used to extract bindex bundle information.

+     */

+    private ExtractBindexInfo m_ebi;

+

+    /**

+     * used to store bundle information.

+     */

+    private ResourcesBundle m_resourceBundle;

+

+    /**

+     * pathfile to the repository descriptor file.

+     */

+    private PathFile m_repo;

+

+    /**

+     * initialize information.

+     * @param repoFilename path to the repository descriptor file

+     * @param obrXml path and filename to the obr.xml file

+     * @param project maven project description

+     * @param outputFile path to the bundle jar file

+     * @param localRepo only path to the local repository

+     * @param userConfig user information

+     * @param log plugin logger

+     */

+    public ObrUpdate(PathFile repoFilename, String obrXml, MavenProject project, String outputFile, String localRepo, Config userConfig, Log log) {

+        // this.m_localRepo = localRepo;

+        this.m_outputFile = outputFile;

+        this.m_repoFilename = repoFilename.getUri();

+        this.m_obrXml = obrXml;

+        this.m_project = project;

+        this.m_logger = log;

+

+        this.m_userConfig = userConfig;

+        // init the tab

+        m_idTab = new boolean[0];

+

+        m_resourceBundle = new ResourcesBundle(log);

+

+        if (userConfig.isRemotely()) {

+            this.m_repo = new PathFile(localRepo);

+        } else {

+            this.m_repo = repoFilename;

+        }

+        // System.err.println("Construct: "+repoFilename.getAbsoluteFilename());

+    }

+

+    /**

+     * update the repository descriptor file. parse the old repository descriptor file, get the old reference of the bundle or determine the id for a new bundle, extract information from bindex set the new information in descriptor file and save it.

+     * @throws MojoExecutionException if the plugin failed

+     */

+    public void updateRepository() throws MojoExecutionException {

+    	

+    	m_logger.debug(" (f) m_obrXml = " + m_obrXml);

+    	m_logger.debug(" (f) m_outputFile = " + m_outputFile);

+    	m_logger.debug(" (f) m_repoFilename = " + m_repoFilename);

+

+        m_constructor = initConstructor();

+

+        if (m_constructor == null)  {

+            return;

+        }

+

+        // get the file size

+        PathFile pf = new PathFile(m_outputFile);

+        File fout = pf.getFile();

+        pf.setBaseDir(m_repo.getOnlyAbsolutePath());

+        if (fout.exists()) {

+            m_resourceBundle.setSize(String.valueOf(fout.length()));

+            if (m_userConfig.isPathRelative()) {

+                m_resourceBundle.setUri(pf.getOnlyRelativeFilename().replace('\\', '/'));

+            } else {

+                m_resourceBundle.setUri("file:" + m_outputFile);

+            }

+        } else {

+            m_logger.error("file doesn't exist: " + m_outputFile);

+            return;

+        }

+

+        // parse repository

+        if (parseRepositoryXml() == -1) { return; }

+

+        // parse the obr.xml file

+        if (m_obrXml != null) {

+            // URL url = getClass().getResource("/SchemaObr.xsd");

+            // TODO validate obr.xml file

+

+            Document obrXmlDoc = parseFile(m_obrXml, m_constructor);

+            if (obrXmlDoc == null) { return; }

+            Node obrXmlRoot = (Element) obrXmlDoc.getDocumentElement();

+            // sort the obr file

+            sortObrXml(obrXmlRoot);

+        }

+

+        // use bindex to extract bundle information

+        try {

+            m_ebi = new ExtractBindexInfo(m_repoFilename, m_outputFile);

+        } catch (MojoExecutionException e) {

+            m_logger.error("unable to build Bindex informations");

+            e.printStackTrace();

+            throw new MojoExecutionException("MojoFailureException");

+        }

+

+        m_resourceBundle.construct(m_project, m_ebi);

+

+        if (!walkOnTree(m_root)) {

+            // the correct resource node was not found, we must create it

+            // we calcul the new id

+            int id = -1;

+            for (int i = 1; i < m_idTab.length; i++) {

+                if (!m_idTab[i]) {

+                    id = i;

+                    break;

+                }

+            }

+            if (id == -1) { id = m_idTab.length; }

+

+            searchRepository(m_root, id);

+        }

+

+        // the repository.xml file have been modified, so we save it

+        writeToFile(m_repoFilename, m_root);

+    }

+

+    /**

+     * init the document builder from xerces.

+     * @return DocumentBuilder ready to create new document

+     */

+    private DocumentBuilder initConstructor() {

+        DocumentBuilder constructor = null;

+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

+        try {

+            constructor = factory.newDocumentBuilder();

+        } catch (ParserConfigurationException e) {

+            m_logger.error("unable to create a new xml document");

+            e.printStackTrace();

+        }

+        return constructor;

+

+    }

+

+    /**

+     * Parse the reporitory descriptor file.

+     * 

+     * @return 0 if the bundle is already in the descriptor, else -1

+     * @throws MojoExecutionException if the plugin failed

+     */

+    private int parseRepositoryXml() throws MojoExecutionException {

+

+        File fout = new File(m_repoFilename);

+        if (!fout.exists()) {

+            // create the repository.xml

+            try {

+

+            	fout.createNewFile();

+                m_logger.info("Created new repository.xml file in "+fout.getAbsolutePath());

+            } catch (IOException e) {

+                m_logger.error("Cannot create file " + fout.getAbsolutePath());

+                e.printStackTrace();

+                return -1;

+            }

+

+            Document doc = m_constructor.newDocument();

+            // create xml tree

+            Date d = new Date();

+            d.setTime(System.currentTimeMillis());

+            Element root = doc.createElement("repository");

+            root.setAttribute("lastmodified", m_format.format(d));

+            root.setAttribute("name", "MyRepository");

+            try {

+                writeToFile(m_repoFilename, root);

+            } catch (MojoExecutionException e) {

+                e.printStackTrace();

+                throw new MojoExecutionException("MojoExecutionException");

+            }

+        }

+

+        // now we parse the repository.xml file

+        m_repoDoc = parseFile(fout.getAbsolutePath(), m_constructor);

+        if (m_repoDoc == null) { return -1; }

+

+        m_root = (Element) m_repoDoc.getDocumentElement();

+        return 0;

+

+    }

+

+    /**

+     * transform a xml file to a xerces Document.

+     * @param filename path to the xml file

+     * @param constructor DocumentBuilder get from xerces

+     * @return Document which describe this file

+     */

+    private Document parseFile(String filename, DocumentBuilder constructor) {

+        if (constructor == null) { return null; }

+        // The document is the root of the DOM tree.

+        m_logger.info("Try to open: " + filename);

+        Document doc = null;

+        try {

+            doc = constructor.parse(new File(filename));

+        } catch (SAXException e) {

+            e.printStackTrace();

+            return null;

+        } catch (IOException e) {

+            m_logger.error("cannot open file: " + filename);

+            e.printStackTrace();

+            return null;

+        }

+        return doc;

+    }

+

+    /**

+     * put the information from obr.xml into ressourceBundle object.

+     * @param node Node to the OBR.xml file

+     */

+    private void sortObrXml(Node node) {

+        if (node.getNodeName().compareTo("require") == 0) {

+            Require newRequireNode = new Require();

+            NamedNodeMap list = node.getAttributes();

+            try {

+                newRequireNode.setExtend(list.getNamedItem("extend").getNodeValue());

+                newRequireNode.setMultiple(list.getNamedItem("multiple").getNodeValue());

+                newRequireNode.setOptional(list.getNamedItem("optional").getNodeValue());

+                newRequireNode.setFilter(list.getNamedItem("filter").getNodeValue());

+                newRequireNode.setName(list.getNamedItem("name").getNodeValue());

+            } catch (NullPointerException e) {

+                m_logger.error("the obr.xml file seems to be invalid in a \"require\" tag (one or more attributes are missing)");

+                // e.printStackTrace();

+            }

+            newRequireNode.setValue(node.getTextContent());

+            m_resourceBundle.addRequire(newRequireNode);

+        } else if (node.getNodeName().compareTo("capability") == 0) {

+            Capability newCapability = new Capability();

+            try {

+                newCapability.setName(node.getAttributes().getNamedItem("name").getNodeValue());

+            } catch (NullPointerException e) {

+                m_logger.error("attribute \"name\" is missing in obr.xml in a \"capability\" tag");

+                e.printStackTrace();

+            }

+            NodeList list = node.getChildNodes();

+            for (int i = 0; i < list.getLength(); i++) {

+                PElement p = new PElement();

+                Node n = list.item(i);

+                Node item = null;

+                // System.err.println(n.getNodeName());

+                if (n.getNodeName().compareTo("p") == 0) {

+

+                    p.setN(n.getAttributes().getNamedItem("n").getNodeValue());

+                    item = n.getAttributes().getNamedItem("t");

+                    if (item != null) { p.setT(item.getNodeValue()); }

+                    item = n.getAttributes().getNamedItem("v");

+                    if (item != null) { p.setV(item.getNodeValue()); }

+

+                    newCapability.addP(p);

+                }

+            }

+            m_resourceBundle.addCapability(newCapability);

+        } else if (node.getNodeName().compareTo("category") == 0) {

+            Category newCategory = new Category();

+            newCategory.setId(node.getAttributes().getNamedItem("id").getNodeValue());

+            m_resourceBundle.addCategory(newCategory);

+        } else {

+            NodeList list = node.getChildNodes();

+            for (int i = 0; i < list.getLength(); i++) {

+                sortObrXml(list.item(i));

+            }

+        }

+    }

+

+    /**

+     * write a Node in a xml file.

+     * @param outputFilename URI to the output file

+     * @param treeToBeWrite Node root of the tree to be write in file

+     * @throws MojoExecutionException if the plugin failed

+     */

+    private void writeToFile(URI outputFilename, Node treeToBeWrite) throws MojoExecutionException {

+        // init the transformer

+        Transformer transformer = null;

+        TransformerFactory tfabrique = TransformerFactory.newInstance();

+        try {

+            transformer = tfabrique.newTransformer();

+        } catch (TransformerConfigurationException e) {

+            m_logger.error("Unable to write to file: " + outputFilename.toString());

+            e.printStackTrace();

+            throw new MojoExecutionException("TransformerConfigurationException");

+        }

+        Properties proprietes = new Properties();

+        proprietes.put("method", "xml");

+        proprietes.put("version", "1.0");

+        proprietes.put("encoding", "ISO-8859-1");

+        proprietes.put("standalone", "yes");

+        proprietes.put("indent", "yes");

+        proprietes.put("omit-xml-declaration", "no");

+        transformer.setOutputProperties(proprietes);

+

+        DOMSource input = new DOMSource(treeToBeWrite);

+

+        File fichier = new File(outputFilename);

+        FileOutputStream flux = null;

+        try {

+            flux = new FileOutputStream(fichier);

+        } catch (FileNotFoundException e) {

+            m_logger.error("Unable to write to file: " + fichier.getName());

+            e.printStackTrace();

+            throw new MojoExecutionException("FileNotFoundException");

+        }

+        Result output = new StreamResult(flux);

+        try {

+            transformer.transform(input, output);

+        } catch (TransformerException e) {

+            e.printStackTrace();

+            throw new MojoExecutionException("TransformerException");

+        }

+

+        try {

+            flux.flush();

+            flux.close();

+        } catch (IOException e) {

+            e.printStackTrace();

+            throw new MojoExecutionException("IOException");

+        }

+

+    }

+

+    /**

+     * walk on the tree until the targeted node was found.

+     * @param node targeted node

+     * @return true if the requiered node was found else false.

+     */

+    private boolean walkOnTree(Node node) {

+

+        if (node.getNodeName().compareTo("resource") == 0) {

+            return resource(node);

+        } else { // look at the repository node (first in the file)

+            if (node.getNodeName().compareTo("repository") == 0) {

+                Date d = new Date();

+                d.setTime(System.currentTimeMillis());

+                NamedNodeMap nList = node.getAttributes();

+                Node n = nList.getNamedItem("lastmodified");

+                n.setNodeValue(m_format.format(d));

+            }

+            NodeList list = node.getChildNodes();

+            if (list.getLength() > 0) {

+                for (int i = 0; i < list.getLength(); i++) {

+                    if (walkOnTree(list.item(i))) { return true; }

+                }

+            }

+            return false;

+        }

+

+    }

+

+    /**

+     * put the resource bundle in the tree.

+     * @param node Node on the xml file

+     * @param id id of the bundle ressource

+     */

+    private void searchRepository(Node node, int id) {

+        if (node.getNodeName().compareTo("repository") == 0) {

+            m_resourceBundle.setId(String.valueOf(id));

+            node.appendChild(m_resourceBundle.getNode(m_repoDoc));

+            return;

+        } else {

+            NodeList list = node.getChildNodes();

+            if (list.getLength() > 0) {

+                for (int i = 0; i < list.getLength(); i++) {

+                    searchRepository(list.item(i), id);

+                }

+            }

+        }

+

+    }

+

+    /**

+     * compare two node and update the array which compute the smallest free id.

+     * @param node : node

+     * @return true if the node is the same bundle than the ressourceBundle, else false.

+     */

+    private boolean resource(Node node) {

+

+        // this part save all the id free if we need to add resource

+        int id = Integer.parseInt(node.getAttributes().getNamedItem("id").getNodeValue());

+        if (id >= m_idTab.length) {

+            // resize tab

+            boolean[] bt = new boolean[id + 1];

+            for (int i = 0; i < id + 1; i++) {

+                if (m_idTab.length > i && m_idTab[i]) {

+                    bt[i] = true;

+                } else {

+                    bt[i] = false;

+                }

+            }

+

+            m_idTab = bt;

+        }

+        m_idTab[id] = true;

+

+        NamedNodeMap map = node.getAttributes();

+

+        if (m_resourceBundle.isSameBundleResource(map.getNamedItem("symbolicname").getNodeValue(), map.getNamedItem("presentationname").getNodeValue(), map.getNamedItem("version").getNodeValue())) {

+            m_resourceBundle.setId(String.valueOf(id));

+            node.getParentNode().replaceChild(m_resourceBundle.getNode(m_repoDoc), node);

+            return true;

+        }

+        return false;

+    }

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/PElement.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/PElement.java
new file mode 100644
index 0000000..de881da
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/PElement.java
@@ -0,0 +1,108 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import org.w3c.dom.Document;

+import org.w3c.dom.Element;

+import org.w3c.dom.Node;

+

+/**

+ * this class describe the p element in a capability tag.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ * 

+ */

+public class PElement {

+    /**

+     * store the v tag (value).

+     */

+    private String m_v;

+

+    /**

+     * store the t tag (type).

+     */

+    private String m_t;

+

+    /**

+     * store the n tag (name).

+     */

+    private String m_n;

+

+    /**

+     * get the n tag.

+     * @return attribute n

+     */

+    public String getN() {

+        return m_n;

+    }

+

+    /**

+     * set the n tage.

+     * @param n new value

+     */

+    public void setN(String n) {

+        this.m_n = n;

+    }

+

+    /**

+     * get the t tag.

+     * @return attribute t

+     */

+    public String getT() {

+        return m_t;

+    }

+

+    /**

+     * set the t tag.

+     * @param t new value

+     */

+    public void setT(String t) {

+        this.m_t = t;

+    }

+

+    /**

+     * get the v tag.

+     * @return attribute v

+     */

+    public String getV() {

+        return m_v;

+    }

+

+    /**

+     * set the v tag.

+     * @param v new value

+     */

+    public void setV(String v) {

+        this.m_v = v;

+    }

+

+    /**

+     * transform this object to node.

+     * @param father father document for create Node

+     * @return node

+     */

+    public Node getNode(Document father) {

+        Element p = father.createElement("p");

+        p.setAttribute("n", this.getN());

+        if (this.getT() != null) { p.setAttribute("t", this.getT()); }

+

+        if (this.getV() != null) { p.setAttribute("v", this.getV()); }

+

+        return p;

+    }

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/PathFile.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/PathFile.java
new file mode 100644
index 0000000..1f516f2
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/PathFile.java
@@ -0,0 +1,544 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.io.File;

+import java.io.FileInputStream;

+import java.io.FileOutputStream;

+import java.io.IOException;

+import java.net.URI;

+import java.net.URISyntaxException;

+import java.nio.channels.FileChannel;

+

+/**

+ * this class provide some functions to simplify file manipulation.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class PathFile {

+

+    /**

+     * full filename.

+     */

+    private String m_fullFilename;

+

+    /**

+     * store only the filename of the file.

+     */

+    private String m_fileName;

+

+    /**

+     * store only the path of this file.

+     */

+    private String m_pathFile;

+

+    /**

+     * store the base Directory in case of relative path.

+     */

+    private String m_baseDir;

+

+    /**

+     * store the protocol used (ie:file, http...).

+     */

+    private String m_protocol;

+

+    /**

+     * if the path is relative or absolute.

+     */

+    private boolean m_relative;

+

+    /**

+     * if this file is a folder.

+     */

+    private boolean m_folder;

+

+    /**

+     * if the file exist or not.

+     */

+    private boolean m_exist;

+

+    /**

+     * if this file is a file (not a folder).

+     */

+    private boolean m_file;

+

+    /**

+     * if this filename is valid or incomplete.

+     */

+    private boolean m_valid;

+

+    /**

+     * build all the attribute information.

+     * @param filename path to the file

+     */

+    public PathFile(String filename) {

+

+        this.m_fullFilename = filename;

+        if (filename == null) {

+            this.m_valid = false;

+            return;

+        }

+        this.m_valid = true;

+        m_protocol = extractProtocol(filename);

+        m_pathFile = extractPathFile(filename);

+        if (m_pathFile.startsWith("//")) {

+            // avoid problems on Unix like system

+            m_pathFile = m_pathFile.substring(1);

+        }

+        m_fileName = extractFileName(filename);

+        m_relative = extractRelative();

+        if (!m_relative && (getProtocol().compareTo("file") == 0 || getProtocol().compareTo("") == 0)) {

+            File f = new File(getOnlyAbsoluteFilename());

+            m_file = f.isFile();

+            m_folder = f.isDirectory();

+            m_exist = f.exists();

+            if (m_folder) {

+                m_pathFile = m_pathFile + m_fileName + File.separator;

+                m_fileName = "";

+            }

+        }

+        if (m_exist) {

+            m_protocol = "file";

+        } else {

+            if (m_fileName.compareTo("") == 0) {

+                m_folder = true;

+                m_file = false;

+            } else {

+                m_folder = false;

+                m_file = true;

+            }

+

+        }

+

+        // add the '/' before the complete path if it is absolute path

+        if (!this.isRelative() && !m_pathFile.startsWith("/")) { m_pathFile = "/".concat(m_pathFile); }

+

+    }

+

+    /**

+     * get if the filename is relative or absolute.

+     * @return true if the path is relative, else false

+     */

+    private boolean extractRelative() {

+        if (m_pathFile.startsWith("." + File.separator, 1) || m_pathFile.startsWith(".." + File.separator, 1)) {

+            m_pathFile = m_pathFile.substring(1);

+            m_valid = false;

+            return true;

+        }

+

+        return false;

+    }

+

+    /**

+     * get only the name from the filname.

+     * @param fullFilename full filename

+     * @return the name of the file or folder

+     */

+    private String extractFileName(String fullFilename) {

+        int index = fullFilename.lastIndexOf(File.separator);

+        return fullFilename.substring(index + 1, fullFilename.length());

+    }

+

+    /**

+     * get the path form the filename.

+     * @param fullFilename full filename

+     * @return the path of the file

+     */

+    private String extractPathFile(String fullFilename) {

+        String substring;

+        if (extractFileName(fullFilename).compareTo("") == 0) {

+            // it is a folder

+            substring = fullFilename;

+        } else {

+            substring = fullFilename.substring(0, fullFilename.indexOf(extractFileName(fullFilename)));

+        }

+

+        if (getProtocol().compareTo("") != 0) {

+            substring = substring.substring(5);

+        }

+

+        return substring;

+    }

+

+    /**

+     * determine which protocol is used.

+     * @param filename the full fileneme

+     * @return "file" or "http" or ""

+     */

+    private String extractProtocol(String filename) {

+        if (filename.startsWith("file:")) { return "file"; }

+        if (filename.startsWith("http:")) { return "http"; }

+        return "";

+    }

+

+    /**

+     * set the base directory.

+     * @param baseDir new value for the base directory

+     */

+    public void setBaseDir(String baseDir) {

+        this.m_baseDir = baseDir;

+        if (isRelative() && this.m_fullFilename != null) {

+            this.m_valid = true;

+            if (getProtocol().compareTo("file") == 0 || getProtocol().compareTo("") == 0) {

+                File f = new File(getOnlyAbsoluteFilename());

+                m_file = f.isFile();

+                m_folder = f.isDirectory();

+                m_exist = f.exists();

+            }

+            if (m_exist) {

+                m_protocol = "file";

+            }

+        }

+

+    }

+

+    /**

+     * get the base directory.

+     * @return base directory

+     */

+    public String getBaseDir() {

+        return this.m_baseDir;

+    }

+

+    public boolean isValid() {

+        return m_valid;

+    }

+

+    public boolean isRelative() {

+        return m_relative;

+    }

+

+    public boolean isExists() {

+        return m_exist;

+    }

+

+    public boolean isFile() {

+        return m_file;

+    }

+

+    public boolean isFolder() {

+        return m_folder;

+    }

+

+    /**

+     * get a File which points on the same file.

+     * @return a File object

+     */

+    public File getFile() {

+        if (!this.isValid()) { return null; }

+        String path = PathFile.uniformSeparator(this.getOnlyAbsoluteFilename());

+        if (File.separatorChar == '\\') { path = path.replace('\\', '/'); }

+        File f = new File(path);

+        return f;

+    }

+

+    /**

+     * get an URI which points on the same file.

+     * @return an URI object

+     */

+    public URI getUri() {

+        if (!this.isValid()) { return null; }

+        String path = PathFile.uniformSeparator(getAbsoluteFilename());

+        if (File.separatorChar == '\\') { 

+        	path = path.replace("\\", "/");

+        	path = path.replace(" ", "%20");

+        }

+

+        URI uri = null;

+        try {

+            uri = new URI(path);

+        } catch (URISyntaxException e) {        	

+            System.err.println("Malformed URI: " + path);

+            System.err.println(e.getMessage());

+            return null;

+        }

+        return uri;

+    }

+

+    /**

+     * get protocol + relative path of this file.

+     * @return the relative path or null if it is not valid

+     */

+    public String getRelativePath() {

+        if (!this.isValid()) { return null; }

+

+        return getProtocol() + ":/" + getOnlyRelativePath();

+    }

+

+    /**

+     * get only (without protocol) relative path of this file.

+     * @return the relative path or null if it is not valid

+     */

+    public String getOnlyRelativePath() {

+        if (!this.isValid()) { return null; }

+        if (this.isRelative()) {

+            return m_pathFile;

+

+        } else {

+            if (m_baseDir != null) {

+                // System.err.println(m_pathFile);

+                // System.err.println(m_baseDir);

+                if (m_pathFile.startsWith(m_baseDir)) {

+                    /*

+                     * String ch1 = m_pathFile; String ch2 = m_baseDir; System.err.println(ch1); System.err.println(ch2); System.err.println("."+File.separator+ch1.substring(ch2.length()));

+                     */

+                    return "." + File.separator + m_pathFile.substring(m_baseDir.length());

+                }

+            }

+            return null;

+        }

+    }

+

+    /**

+     * calcul absolute path from relative path.

+     * @param baseDir base directory

+     * @param path path to convert

+     * @return the absolute path or null

+     */

+    private String calculAbsolutePath(String baseDir, String path) {

+        if (path.startsWith(".." + File.separatorChar)) {

+            String base = baseDir;

+            int lastIndex;

+            lastIndex = base.lastIndexOf(File.separator);

+            if (lastIndex == base.length()) {

+                base = base.substring(0, base.length() - 1);

+                lastIndex = base.lastIndexOf(File.separator);

+            }

+            if (lastIndex < base.length()) {

+                return calculAbsolutePath(base.substring(0, lastIndex + 1), path.substring(3));

+            } else {

+                return null;

+            }

+        } else if (path.startsWith("." + File.separatorChar)) {

+            String res;

+            if (File.separatorChar == '\\') {

+                res = path.replaceFirst(".", baseDir.replace('\\', '/'));

+            } else {

+                res = path.replaceFirst(".", baseDir);

+            }

+

+            return PathFile.uniformSeparator(res);

+        } else {

+            return PathFile.uniformSeparator(baseDir + path);

+        }

+    }

+

+    /**

+     * get only (without protocol) absolute path (without filename).

+     * @return absolute path

+     */

+    public String getOnlyAbsolutePath() {

+        if (!this.isValid()) { return null; }

+        if (isRelative()) {

+            return calculAbsolutePath(m_baseDir, m_pathFile);

+        } else {

+            return m_pathFile;

+        }

+    }

+

+    /**

+     * get protocol + absolute path (without filename).

+     * @return absolute path

+     */

+    public String getAbsolutePath() {

+

+        if (isRelative()) {

+            return getProtocol() + ":/" + calculAbsolutePath(m_baseDir, m_pathFile);

+        } else {

+            if (getProtocol().compareTo("") == 0 || m_pathFile == null) {

+                return m_pathFile;

+            } else {

+                return getProtocol() + ":" + m_pathFile;

+            }

+        }

+    }

+

+    /**

+     * get only (without protocol) absolute path + filename.

+     * @return absolute filename

+     */

+    public String getOnlyAbsoluteFilename() {

+        if (getOnlyAbsolutePath() != null && getFilename() != null) {

+            return getOnlyAbsolutePath() + getFilename();

+        } else {

+            return null;

+        }

+    }

+

+    /**

+     * get protocol + absolute path + filename.

+     * @return absolute filenama

+     */

+    public String getAbsoluteFilename() {

+        if (getAbsolutePath() != null && getFilename() != null) {

+            return getAbsolutePath() + getFilename();

+        } else {

+            return null;

+        }

+    }

+

+    /**

+     * get only (without protocol) relative path + filename.

+     * @return relative filename

+     */

+    public String getOnlyRelativeFilename() {

+        if (!this.isValid()) { return ""; }

+

+        return getOnlyRelativePath() + getFilename();

+

+    }

+

+    /**

+     * get protocol + relative path + filename.

+     * @return relative filename

+     */

+    public String getRelativeFilename() {

+        if (!this.isValid()) { return ""; }

+

+        if (this.isRelative()) {

+            return getRelativePath() + getFilename();

+        } else {

+            return getAbsoluteFilename();

+        }

+    }

+

+    public String getFilename() {

+        return m_fileName;

+    }

+

+    public String getProtocol() {

+        return m_protocol;

+    }

+

+    /**

+     * create all the directories not also present in the current path.

+     * @return true if all directories was created, else false

+     */

+    public boolean createPath() {

+        File path = new File(this.getOnlyAbsolutePath());

+        if (path.exists()) { return true; }

+        return path.mkdirs();

+    }

+

+    /**

+     * create all the directories not also present in the current path and the file.

+     * @return true it was created, else false

+     */

+    public boolean createFile() {

+        File path = new File(this.getOnlyAbsolutePath());

+        if (!path.exists()) {

+            if (!this.createPath()) { return false; }

+        }

+        path = new File(this.getOnlyAbsoluteFilename());

+        try {

+            return path.createNewFile();

+        } catch (IOException e) {

+            return false;

+        }

+

+    }

+

+    /**

+     * delete the current file.

+     * @return true if it was deleted, else false

+     */

+    public boolean delete() {

+        File path = new File(this.getAbsoluteFilename());

+        if (path.exists()) {

+            return path.delete();

+        } else {

+            return true;

+        }

+

+    }

+

+    /**

+     * replace all '\' by '\\' in the given string.

+     * @param path string where replace the search pattern

+     * @return string replaced

+     */

+    public static String doubleSeparator(String path) {

+        // double the '\' in the path

+        if (path != null && File.separatorChar == '\\') {

+            return path.replace("\\", "\\\\");

+        } else {

+            return null;

+        }

+    }

+

+    /**

+     * file separator('\' or '/') by the one of the current system.

+     * @param path string where replace the search pattern

+     * @return string replaced

+     */

+    public static String uniformSeparator(String path) {

+        if (File.separatorChar == '\\') {

+            if (path.startsWith("/")) {

+                return path.substring(1).replace('/', File.separatorChar);

+            } else {

+                return path.replace('/', File.separatorChar);

+            }

+        } else {

+            return path.replace('\\', File.separatorChar);

+        }

+    }

+

+    /**

+     * copy file from src to dest.

+     * @param src source file

+     * @param dest destination file

+     * @return true if the file was correctly copied, else false

+     */

+    public static boolean copyFile(PathFile src, PathFile dest) {

+        FileChannel in = null;

+        FileChannel out = null;

+

+        if (!src.isExists()) {

+            System.err.println("src file must exist: " + src.getAbsoluteFilename());

+            return false;

+        }

+        if (!dest.isExists()) {

+            dest.createFile();

+        }

+        try {

+            in = new FileInputStream(src.getOnlyAbsoluteFilename()).getChannel();

+            out = new FileOutputStream(dest.getOnlyAbsoluteFilename()).getChannel();

+

+            in.transferTo(0, in.size(), out);

+        } catch (Exception e) {

+            e.printStackTrace();

+        } finally {

+            if (in != null) {

+                try {

+                    in.close();

+                } catch (IOException e) {

+                    return false;

+                }

+            }

+            if (out != null) {

+                try {

+                    out.close();

+                } catch (IOException e) {

+                    return false;

+                }

+            }

+        }

+        return true;

+    }

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/RemoteFileManager.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/RemoteFileManager.java
new file mode 100644
index 0000000..df2ddf0
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/RemoteFileManager.java
@@ -0,0 +1,229 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.io.File;

+import java.io.IOException;

+

+import org.apache.maven.artifact.manager.WagonConfigurationException;

+import org.apache.maven.artifact.manager.WagonManager;

+import org.apache.maven.artifact.repository.ArtifactRepository;

+import org.apache.maven.plugin.MojoExecutionException;

+import org.apache.maven.plugin.MojoFailureException;

+import org.apache.maven.plugin.logging.Log;

+import org.apache.maven.settings.Proxy;

+import org.apache.maven.settings.Settings;

+import org.apache.maven.wagon.ConnectionException;

+import org.apache.maven.wagon.ResourceDoesNotExistException;

+import org.apache.maven.wagon.TransferFailedException;

+import org.apache.maven.wagon.UnsupportedProtocolException;

+import org.apache.maven.wagon.Wagon;

+import org.apache.maven.wagon.authentication.AuthenticationException;

+import org.apache.maven.wagon.authorization.AuthorizationException;

+import org.apache.maven.wagon.observers.Debug;

+import org.apache.maven.wagon.proxy.ProxyInfo;

+import org.apache.maven.wagon.repository.Repository;

+

+/**

+ * this class is used to manage all connections by wagon.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class RemoteFileManager {

+

+    /**

+     * save the connection.

+     */

+    private Wagon m_wagon;

+

+    /**

+     * the wagon manager.

+     */

+    private WagonManager m_wagonManager;

+

+    /**

+     * artifact repository.

+     */

+    private ArtifactRepository m_artifactRepository;

+

+    /**

+     * the project settings.

+     */

+    private Settings m_settings;

+

+    /**

+     * logger instance.

+     */

+    private Log m_log;

+

+    /**

+     * initialize main information.

+     * @param ar ArtifactRepository provides by maven

+     * @param wm WagonManager provides by maven

+     * @param settings settings of the current project provides by maven

+     * @param log logger

+     */

+    public RemoteFileManager(ArtifactRepository ar, WagonManager wm, Settings settings, Log log) {

+        m_artifactRepository = ar;

+        m_wagonManager = wm;

+        m_settings = settings;

+        m_log = log;

+        m_wagon = null;

+    }

+

+    /**

+     * disconnect the current object.

+     *

+     */

+    public void disconnect() {

+        if (m_wagon == null) {

+            m_log.error("must be connected first!");

+            return;

+        }

+        try {

+            m_wagon.disconnect();

+        } catch (ConnectionException e) {

+            m_log.error("Error disconnecting wagon - ignored", e);

+        }

+    }

+

+    /**

+     * connect the current object to artifact repository given in constructor.

+     * @throws MojoExecutionException if connection failed

+     */

+    public void connect() throws MojoExecutionException {

+        String url = m_artifactRepository.getUrl();

+        String id = m_artifactRepository.getId();

+

+        Repository repository = new Repository(id, url);

+

+        try {

+            m_wagon = m_wagonManager.getWagon(repository);

+            //configureWagon(m_wagon, repository.getId());

+        } catch (UnsupportedProtocolException e) {

+            throw new MojoExecutionException("Unsupported protocol: '" + repository.getProtocol() + "'", e);

+        } catch (WagonConfigurationException e) {

+            throw new MojoExecutionException("Unable to configure Wagon: '" + repository.getProtocol() + "'", e);

+        }

+

+        try {

+            Debug debug = new Debug();

+            m_wagon.addTransferListener(debug);

+

+            ProxyInfo proxyInfo = getProxyInfo(m_settings);

+            if (proxyInfo != null) {

+                m_wagon.connect(repository, m_wagonManager.getAuthenticationInfo(id), proxyInfo);

+            } else {

+                m_wagon.connect(repository, m_wagonManager.getAuthenticationInfo(id));

+            }

+

+        } catch (ConnectionException e) {

+            throw new MojoExecutionException("Error uploading file", e);

+        } catch (AuthenticationException e) {

+            throw new MojoExecutionException("Error uploading file", e);

+        }

+    }

+

+    /**

+     * get a file from the current repository connected.

+     * @param url url to the targeted file

+     * @return  get a file descriptor on the requiered resource

+     * @throws IOException if an IO error occurs

+     * @throws TransferFailedException  if the transfer failed 

+     * @throws ResourceDoesNotExistException if the targeted resource doesn't exist

+     * @throws AuthorizationException if the connection authorization failed

+     */

+    public File get(String url) throws IOException, TransferFailedException, ResourceDoesNotExistException, AuthorizationException {

+

+        if (m_wagon == null) {

+            m_log.error("must be connected first!");

+            return null;

+        }

+

+        File file = File.createTempFile(String.valueOf(System.currentTimeMillis()), "tmp");

+        m_wagon.get(url, file);

+        return file;

+    }

+

+    /**

+     * put a file on the current repository connected.

+     * @param file file to upload

+     * @param url url to copy file

+     * @throws TransferFailedException if the transfer failed 

+     * @throws ResourceDoesNotExistException if the targeted resource doesn't exist

+     * @throws AuthorizationException if the connection authorization failed

+     */

+    public void put(File file, String url) throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException {

+        if (m_wagon == null) {

+            m_log.error("must be connected first!");

+            return;

+        }

+        m_wagon.put(file, url);

+    }

+

+    /**

+     * Convenience method to map a Proxy object from the user system settings to a ProxyInfo object.

+     * @param settings project settings given by maven

+     * @return a proxyInfo object instancied or null if no active proxy is define in the settings.xml

+     */

+    public static ProxyInfo getProxyInfo(Settings settings) {

+        ProxyInfo proxyInfo = null;

+        if (settings != null && settings.getActiveProxy() != null) {

+            Proxy settingsProxy = settings.getActiveProxy();

+

+            proxyInfo = new ProxyInfo();

+            proxyInfo.setHost(settingsProxy.getHost());

+            proxyInfo.setType(settingsProxy.getProtocol());

+            proxyInfo.setPort(settingsProxy.getPort());

+            proxyInfo.setNonProxyHosts(settingsProxy.getNonProxyHosts());

+            proxyInfo.setUserName(settingsProxy.getUsername());

+            proxyInfo.setPassword(settingsProxy.getPassword());

+        }

+

+        return proxyInfo;

+    }

+

+    /**

+     * this method indicates if the targeted file is locked or not.

+     * @param remote connection manager

+     * @param fileName name targeted

+     * @return  true if thr reuiered file is locked, else false

+     * @throws MojoFailureException if the plugin failed

+     */

+    public boolean isLockedFile(RemoteFileManager remote, String fileName) throws MojoFailureException {

+        File file = null;

+        try {

+            file = remote.get(fileName + ".lock");

+        } catch (TransferFailedException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("TransferFailedException");

+

+        } catch (ResourceDoesNotExistException e) {

+            return false;

+        } catch (AuthorizationException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("AuthorizationException");

+        } catch (IOException e) {

+            e.printStackTrace();

+            throw new MojoFailureException("IOException");

+        }

+        if (file != null && file.length() == 0) { return false; }

+        return true;

+    }

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Require.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Require.java
new file mode 100644
index 0000000..d46e463
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/Require.java
@@ -0,0 +1,175 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import org.w3c.dom.Document;

+import org.w3c.dom.Element;

+import org.w3c.dom.Node;

+

+/**

+ * this class store a Require tag.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class Require {

+

+    /**

+     * store the extend attribute.

+     */

+    private String m_extend;

+

+    /**

+     * store the multiple attribute.

+     */

+    private String m_multiple;

+

+    /**

+     * store the optional attribute.

+     */

+    private String m_optional;

+

+    /**

+     * store the name attribute.

+     */

+    private String m_name;

+

+    /**

+     * store the filter attribute.

+     */

+    private String m_filter;

+

+    /**

+     * store the value of the tag.

+     */

+    private String m_value;

+

+    /**

+     * get the extend attribute.

+     * @return a string which contains the value of the boolean

+     */

+    public String getExtend() {

+        return m_extend;

+    }

+

+    /**

+     * set the extend attribute.

+     * @param extend new value for the extend attribute

+     */

+    public void setExtend(String extend) {

+        this.m_extend = extend;

+    }

+

+    /**

+     * get the filter attribute.

+     * @return m_filter value

+     */

+    public String getFilter() {

+        return m_filter;

+    }

+

+    /**

+     * set the filter attribute.

+     * @param filter new value for filter

+     */

+    public void setFilter(String filter) {

+        this.m_filter = filter;

+    }

+

+    /**

+     * get multiple attribute.

+     * @return m_multiple value

+     */

+    public String getMultiple() {

+        return m_multiple;

+    }

+

+    /**

+     * set multiple attribute.

+     * @param multiple new value for m_multiple

+     */

+    public void setMultiple(String multiple) {

+        this.m_multiple = multiple;

+    }

+

+    /**

+     * get name attribute.

+     * @return m_name value

+     */

+    public String getName() {

+        return m_name;

+    }

+

+    /**

+     * set name attribute.

+     * @param name new value for m_name

+     */

+    public void setName(String name) {

+        this.m_name = name;

+    }

+

+    /**

+     * get the optional attribute.

+     * @return m_optional value

+     */

+    public String getOptional() {

+        return m_optional;

+    }

+

+    /**

+     * set the optional attribute.

+     * @param optionnal new value for m_optional

+     */

+    public void setOptional(String optionnal) {

+        this.m_optional = optionnal;

+    }

+

+    /**

+     * get value of the tag.

+     * @return value of this tag

+     */

+    public String getValue() {

+        return m_value;

+    }

+

+    /**

+     * set the value of the tag.

+     * @param value new value for this tag

+     */

+    public void setValue(String value) {

+        this.m_value = value;

+    }

+

+    /**

+     * transform this object to Node.

+     * 

+     * @param father father document for create Node

+     * @return node

+     */

+    public Node getNode(Document father) {

+        Element require = father.createElement("require");

+        require.setAttribute("name", this.getName());

+        require.setAttribute("filter", this.getFilter());

+        require.setAttribute("extend", this.getExtend());

+        require.setAttribute("multiple", this.getMultiple());

+        require.setAttribute("optional", this.getOptional());

+        require.setTextContent(this.getValue());

+

+        return require;

+    }

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ResourcesBundle.java b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ResourcesBundle.java
new file mode 100644
index 0000000..7a3744c
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/apache/felix/sandbox/obr/plugin/ResourcesBundle.java
@@ -0,0 +1,520 @@
+/* 

+ * 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.sandbox.obr.plugin;

+

+import java.util.ArrayList;

+import java.util.Iterator;

+import java.util.List;

+

+import org.apache.maven.plugin.logging.Log;

+import org.apache.maven.project.MavenProject;

+import org.w3c.dom.Document;

+import org.w3c.dom.Element;

+import org.w3c.dom.Node;

+

+/**

+ * this class describe all information by bundle.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class ResourcesBundle {

+    /**

+     * store the bundle symbolic name.

+     */

+    private String m_symbolicName;

+

+    /**

+     * store the bundle presentation name.

+     */

+    private String m_presentationName;

+

+    /**

+     * store the bundle version.

+     */

+    private String m_version;

+

+    /**

+     * store the bundle URI.

+     */

+    private String m_uri;

+

+    /**

+     * store the bundle description.

+     */

+    private String m_description;

+

+    /**

+     * store the bundle size.

+     */

+    private String m_size;

+

+    /**

+     * store the bundle documentation.

+     */

+    private String m_documentation;

+

+    /**

+     * store the bundle source.

+     */

+    private String m_source;

+

+    /**

+     * store the bundle license.

+     */

+    private String m_license;

+

+    /**

+     * store the bundle id.

+     */

+    private String m_id;

+

+    /**

+     * store the bundle categories.

+     */

+    private List m_category = new ArrayList();

+

+    /**

+     * store the bundle capabilities.

+     */

+    private List m_capability = new ArrayList();

+

+    /**

+     * store the bundle requirement.

+     */

+    private List m_require = new ArrayList();

+

+    /**

+     * get the plugin logger.

+     */

+    private Log m_logger;

+

+    /**

+     * initialize logger.

+     * @param log log use by plugin

+     */

+    public ResourcesBundle(Log log) {

+        m_logger = log;

+    }

+

+    public List getCapability() {

+        return m_capability;

+    }

+

+    public void setCapability(List capability) {

+        this.m_capability = capability;

+    }

+

+    public List getCategory() {

+        return m_category;

+    }

+

+    public void setCategory(List category) {

+        this.m_category = category;

+    }

+

+    public String getLicense() {

+        return m_license;

+    }

+

+    public void setLicense(String license) {

+        this.m_license = license;

+    }

+

+    public String getDescription() {

+        return m_description;

+    }

+

+    public void setDescription(String description) {

+        this.m_description = description;

+    }

+

+    public String getDocumentation() {

+        return m_documentation;

+    }

+

+    public void setDocumentation(String documentation) {

+        this.m_documentation = documentation;

+    }

+

+    public String getPresentationName() {

+        return m_presentationName;

+    }

+

+    public void setPresentationName(String name) {

+        m_presentationName = name;

+    }

+

+    public String getSize() {

+        return m_size;

+    }

+

+    public void setSize(String size) {

+        this.m_size = size;

+    }

+

+    public String getSymbolicName() {

+        return m_symbolicName;

+    }

+

+    public void setSymbolicName(String name) {

+        m_symbolicName = name;

+    }

+

+    public String getUri() {

+        return m_uri;

+    }

+

+    public void setUri(String url) {

+        this.m_uri = url;

+    }

+

+    public String getVersion() {

+        return m_version;

+    }

+

+    public void setVersion(String version) {

+        this.m_version = version;

+    }

+

+    public List getRequire() {

+        return m_require;

+    }

+

+    public void setRequire(List require) {

+        this.m_require = require;

+    }

+

+    public String getSource() {

+        return m_source;

+    }

+

+    public void setSource(String source) {

+        this.m_source = source;

+    }

+

+    public String getId() {

+        return m_id;

+    }

+

+    public void setId(String id) {

+        this.m_id = id;

+    }

+

+    /**

+     * add a new capability for this bundle description.

+     * @param capability the Capability to add

+     */

+    public void addCapability(Capability capability) {

+        m_capability.add(capability);

+    }

+

+    /**

+     * add a new requirement for this bundle description.

+     * @param require th Require to add

+     */

+    public void addRequire(Require require) {

+        m_require.add(require);

+    }

+

+    /**

+     * add a new category for this bundle decription.

+     * @param category the Category to add

+     */

+    public void addCategory(Category category) {

+        m_category.add(category);

+    }

+

+    /**

+     * transform this object to Node.

+     * tranform all sub-object to node also

+     * @param father father document for create Node

+     * @return node

+     */

+    public Node getNode(Document father) {

+        // return the complete resource tree

+        if (!this.isValid() || this.getId() == null) {

+            m_logger.error("those properties was not defined:" + this.getInvalidProperties());

+            return null;

+        }

+

+        Element resource = father.createElement("resource");

+        Element description = father.createElement("description");

+        Element size = father.createElement("size");

+        Element documentation = father.createElement("documentation");

+        Element source = father.createElement("source");

+        Element license = father.createElement("license");

+

+        resource.setAttribute("id", this.getId());

+        resource.setAttribute("symbolicname", this.getSymbolicName());

+        resource.setAttribute("presentationname", this.getPresentationName());

+        resource.setAttribute("uri", this.getUri());

+        resource.setAttribute("version", this.getVersion());

+

+        description.setTextContent(this.getDescription());

+        resource.appendChild(description);

+

+        size.setTextContent(this.getSize());

+        resource.appendChild(size);

+

+        if (this.getDocumentation() != null) {

+            documentation.setTextContent(this.getDocumentation());

+            resource.appendChild(documentation);

+        }

+

+        if (this.getSource() != null) {

+            source.setTextContent(this.getSource());

+            resource.appendChild(source);

+        }

+

+        if (this.getLicense() != null) {

+            license.setTextContent(this.getLicense());

+            resource.appendChild(license);

+        }

+

+        List list = (ArrayList) this.getNodeCategories(father);

+        for (int i = 0; i < list.size(); i++) {

+            resource.appendChild((Node) list.get(i));

+        }

+

+        list = (ArrayList) this.getNodeCapabilities(father);

+        for (int i = 0; i < list.size(); i++) {

+            resource.appendChild((Node) list.get(i));

+        }

+

+        list = (ArrayList) this.getNodeRequirement(father);

+        for (int i = 0; i < list.size(); i++) {

+            resource.appendChild((Node) list.get(i));

+        }

+

+        return resource;

+    }

+

+    /**

+     * this method gets information form pom.xml to complete missing data from those given by user.

+     * @param project project information given by maven

+     * @param ebi bundle information extracted from bindex

+     * @return true

+     */

+    public boolean construct(MavenProject project, ExtractBindexInfo ebi) {

+

+        if (ebi.getPresentationName() != null) {

+            this.setPresentationName(ebi.getPresentationName());

+            if (project.getName() != null) { m_logger.warn("pom property override:<presentationname> " + project.getName()); }

+        } else {

+            this.setPresentationName(project.getName());

+        }

+

+        if (ebi.getSymbolicName() != null) {

+            this.setSymbolicName(ebi.getSymbolicName());

+            if (project.getArtifactId() != null) { m_logger.warn("pom property override:<symbolicname> " + project.getArtifactId()); }

+        } else {

+            this.setSymbolicName(project.getArtifactId());

+        }

+

+        if (ebi.getVersion() != null) {

+            this.setVersion(ebi.getVersion());

+            if (project.getVersion() != null) { m_logger.warn("pom property override:<version> " + project.getVersion()); }

+        } else {

+            this.setVersion(project.getVersion());

+        }

+

+        if (ebi.getDescription() != null) {

+            this.setDescription(ebi.getDescription());

+            if (project.getDescription() != null) { m_logger.warn("pom property override:<description> " + project.getDescription()); }

+        } else {

+            this.setDescription(project.getDescription());

+        }

+

+        if (ebi.getDocumentation() != null) {

+            this.setDocumentation(ebi.getDocumentation());

+            if (project.getUrl() != null) { m_logger.warn("pom property override:<documentation> " + project.getUrl()); }

+        } else {

+            this.setDocumentation(project.getUrl());

+        }

+

+        if (ebi.getSource() != null) {

+            this.setSource(ebi.getSource());

+            if (project.getScm() != null) { m_logger.warn("pom property override:<source> " + project.getScm()); }

+        } else {

+            String src = null;

+            if (project.getScm() != null) { src = project.getScm().getUrl(); }

+            this.setSource(src);

+        }

+

+        if (ebi.getLicense() != null) {

+            this.setLicense(ebi.getLicense());

+            String lic = null;

+            List l = project.getLicenses();

+            Iterator it = l.iterator();

+            while (it.hasNext()) {

+                if (it.next() != null) {

+                    m_logger.warn("pom property override:<source> " + lic);

+                    break;

+                }

+            }

+        } else {

+            String lic = null;

+            List l = project.getLicenses();

+            Iterator it = l.iterator();

+            while (it.hasNext()) {

+                lic = it.next() + ";";

+            }

+

+            this.setLicense(lic);

+        }

+

+        // create the first capability (ie : bundle)

+        Capability capability = new Capability();

+        capability.setName("bundle");

+        PElement p = new PElement();

+        p.setN("manifestversion");

+        p.setV("2");

+        capability.addP(p);

+

+        p = new PElement();

+        p.setN("presentationname");

+        p.setV(this.getPresentationName());

+        capability.addP(p);

+

+        p = new PElement();

+        p.setN("symbolicname");

+        p.setV(this.getSymbolicName());

+        capability.addP(p);

+

+        p = new PElement();

+        p.setN("version");

+        p.setT("version");

+        p.setV(this.getVersion());

+        capability.addP(p);

+

+        this.addCapability(capability);

+

+        List capabilities = (ArrayList) ebi.getCapabilities();

+        for (int i = 0; i < capabilities.size(); i++) {

+            this.addCapability((Capability) capabilities.get(i));

+        }

+

+        List requirement = (ArrayList) ebi.getRequirement();

+        for (int i = 0; i < requirement.size(); i++) {

+            this.addRequire((Require) requirement.get(i));

+        }

+

+        // we also add the goupId

+        Category category = new Category();

+        category.setId(project.getGroupId());

+        this.addCategory(category);

+

+        return true;

+    }

+

+    /**

+     * return if the bundle resource is complete.

+     * @return false if an information is missing, else true

+     */

+    public boolean isValid() {

+        // we must verify require properties are present

+        return this.getPresentationName() != null 

+            && this.getSymbolicName() != null

+            && this.getVersion() != null 

+            && this.getUri() != null 

+            && this.getSize() != null;

+    }

+

+    /**

+     * test if this bundle has the same symbolicname, presentationname and version number.

+     * @param symbolicName symbolicName to compare with current bundle

+     * @param presentationName presentationName to compare with current bundlde

+     * @param version version to compare with current bundle

+     * @return true if the information are the same, else false

+     */

+    public boolean isSameBundleResource(String symbolicName, String presentationName, String version) {

+        if (this.isValid()) {

+            boolean result;

+            result = (symbolicName.compareTo(this.getSymbolicName()) == 0) && (version.compareTo(this.getVersion()) == 0) && (presentationName.compareTo(this.getPresentationName()) == 0);

+            return result;

+

+        } else {

+            return false;

+        }

+

+    }

+

+    /**

+     * return a list of categories transformed to node.

+     * @param father father document to create node from same document

+     * @return List of Node

+     */

+    private List getNodeCategories(Document father) {

+        List listNode = new ArrayList();

+        List listCategory = (ArrayList) this.getCategory();

+        for (int i = 0; i < listCategory.size(); i++) {

+            listNode.add(((Category) listCategory.get(i)).getNode(father));

+        }

+        return listNode;

+    }

+

+    /**

+     * return a list of capabilities transformed to node.

+     * @param father father document to create node from same document

+     * @return List of Node

+     */

+    private List getNodeCapabilities(Document father) {

+        List listNode = new ArrayList();

+        List listCapability = (ArrayList) this.getCapability();

+        for (int i = 0; i < listCapability.size(); i++) {

+            listNode.add(((Capability) listCapability.get(i)).getNode(father));

+        }

+        return listNode;

+    }

+

+    /**

+     * return a list of requirement transformed to node.

+     * @param father father document to create node from same document

+     * @return List of Node.

+     */

+    private List getNodeRequirement(Document father) {

+        List listNode = new ArrayList();

+        List listRequirement = (ArrayList) this.getRequire();

+        for (int i = 0; i < listRequirement.size(); i++) {

+            listNode.add(((Require) listRequirement.get(i)).getNode(father));

+        }

+        return listNode;

+    }

+

+    /**

+     * return the list of properties not define in this bundle resource.

+     * @return list of properties not define

+     */

+    private String getInvalidProperties() {

+        if (this.isValid()) {

+            if (this.getId() == null) {

+                return "id";

+            } else {

+                return "";

+            }

+        }

+        String result = "";

+        if (this.getPresentationName() == null) { result = result + "presentationName;"; }

+        if (this.getSymbolicName() == null) { result = result + "symbolicName;"; }

+        if (this.getVersion() == null) { result = result + "version;"; }

+        if (this.getUri() == null) { result = result + "Uri;"; }

+        if (this.getSize() == null) { result = result + "Size"; }

+        return result;

+    }

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/BundleInfo.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/BundleInfo.java
new file mode 100644
index 0000000..5da803b
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/BundleInfo.java
@@ -0,0 +1,490 @@
+package org.osgi.impl.bundle.obr.resource;

+

+import java.io.*;

+import java.net.URL;

+import java.util.*;

+import java.util.zip.*;

+

+import org.osgi.service.obr.Resource;

+

+/**

+ * Convert a bundle to a generic resource description and store its local

+ * dependencies (like for example a license file in the JAR) in a zip file.

+ * 

+ * @version $Revision: 1.18 $

+ */

+public class BundleInfo {

+	Manifest	manifest;

+	File		bundleJar;

+	ZipFile		jar;

+	String		license;

+	Properties	localization;

+	RepositoryImpl	repository;

+

+	/**

+	 * Parse a zipFile from the file system. We only need the manifest and the

+	 * localization. So a zip file is used to minimze memory consumption.

+	 * 

+	 * @param bundleJar Path name

+	 * @throws Exception Any errors that occur

+	 */

+	public BundleInfo(RepositoryImpl repository, File bundleJar) throws Exception {

+		this.bundleJar = bundleJar;

+		this.repository = repository;

+		

+		if (!this.bundleJar.exists())

+			throw new FileNotFoundException(bundleJar.toString());

+

+		jar = new ZipFile(bundleJar);

+		ZipEntry entry = jar.getEntry("META-INF/MANIFEST.MF");

+		if (entry == null)

+			throw new FileNotFoundException("No Manifest in "

+					+ bundleJar.toString());

+		manifest = new Manifest(jar.getInputStream(entry));

+	}

+

+	public BundleInfo(Manifest manifest) throws Exception {

+		this.manifest = manifest;

+	}

+

+	/**

+	 * Convert the bundle to a Resource. All URIs are going to be abslute, but

+	 * could be local.

+	 * 

+	 * @return the resource

+	 * @throws Exception

+	 */

+	public ResourceImpl build() throws Exception {

+		ResourceImpl resource;

+		// Setup the manifest

+		// and create a resource

+		resource = new ResourceImpl(repository, manifest.getSymbolicName(), manifest

+				.getVersion());

+

+		try {

+

+			// Calculate the location URL of the JAR

+			URL location = new URL("jar:" + bundleJar.toURL().toString() + "!/");

+			resource.setURL(bundleJar.toURL());

+			resource.setFile(bundleJar);

+

+			doReferences(resource, location);

+			doSize(resource);

+			doCategories(resource);

+			doImportExportServices(resource);

+			doDeclarativeServices(resource);

+			doFragment(resource);

+			doRequires(resource);

+			doBundle(resource);

+			doExports(resource);

+			doImports(resource);

+			doExecutionEnvironment(resource);

+

+			return resource;

+		}

+		finally {

+			try {

+				jar.close();

+			}

+			catch (Exception e) {

+				// ignore

+			}

+		}

+	}

+

+	/**

+	 * Check the size and add it.

+	 * 

+	 * @param resource

+	 */

+	void doSize(ResourceImpl resource) {

+		long size = bundleJar.length();

+		if (size > 0)

+			resource.setSize(size);

+	}

+

+	/**

+	 * Find the categories, break them up and add them.

+	 * 

+	 * @param resource

+	 */

+	void doCategories(ResourceImpl resource) {

+		for (int i = 0; i < manifest.getCategories().length; i++) {

+			String category = manifest.getCategories()[i];

+			resource.addCategory(category);

+		}

+	}

+

+	void doReferences(ResourceImpl resource, URL location) {

+		// Presentation name

+		String name = translated("Bundle-Name");

+		if (name != null)

+			resource.setPresentationName(name);

+

+		// Handle license. -l allows a global license

+		// set when no license is included.

+

+		String license = translated("Bundle-License");

+		if (license != null)

+			resource.setLicense(toURL(location, license));

+		else if (this.license != null)

+			resource.setLicense(toURL(location, this.license));

+

+		String description = translated("Bundle-Description");

+		if (description != null)

+			resource.setDescription(description);

+

+		String copyright = translated("Bundle-Copyright");

+		if (copyright != null)

+			resource.setCopyright(copyright);

+

+		String documentation = translated("Bundle-DocURL");

+		if (documentation != null)

+			resource.setDocumentation(toURL(location, documentation));

+

+		String source = manifest.getValue("Bundle-Source");

+		if (source != null)

+			resource.setSource(toURL(location, source));

+	}

+

+	URL toURL(URL location, String source) {

+		try {

+			return new URL(location, source);

+		}

+		catch (Exception e) {

+			System.err.println("Error in converting url: " + location + " : "

+					+ source);

+			return null;

+		}

+	}

+

+	void doDeclarativeServices(ResourceImpl resource) throws Exception {

+		String serviceComponent = manifest.getValue("service-component");

+		if (serviceComponent == null)

+			return;

+

+		StringTokenizer st = new StringTokenizer(serviceComponent, " ,\t");

+		String parts[] = new String[st.countTokens()];

+		for (int i = 0; i < parts.length; i++)

+			parts[i] = st.nextToken();

+

+		for (int i = 0; i < parts.length; i++) {

+			ZipEntry entry = jar.getEntry(parts[i]);

+			if (entry == null) {

+				System.err.println("Bad Service-Component header: "

+						+ serviceComponent + ", no such file " + parts[i]);

+			}

+			InputStream in = jar.getInputStream(entry);

+			// TODO parse declarative services files.

+			in.close();

+		}

+	}

+

+	void doImportExportServices(ResourceImpl resource) throws IOException {

+		String importServices = manifest.getValue("import-service");

+		if (importServices != null) {

+			List entries = manifest.getEntries(importServices);

+			for (Iterator i = entries.iterator(); i.hasNext();) {

+				ManifestEntry entry = (ManifestEntry) i.next();

+				RequirementImpl ri = new RequirementImpl("service");

+				ri.setFilter(createServiceFilter(entry));

+				ri.setComment("Import Service " + entry.getName());

+

+				// TODO the following is arbitrary

+				ri.setOptional(true);

+				ri.setMultiple(false);

+				resource.addRequirement(ri);

+			}

+		}

+

+		String exportServices = manifest.getValue("export-service");

+		if (exportServices != null) {

+			List entries = manifest.getEntries(exportServices);

+			for (Iterator i = entries.iterator(); i.hasNext();) {

+				ManifestEntry entry = (ManifestEntry) i.next();

+				CapabilityImpl cap = createServiceCapability(entry);

+				resource.addCapability(cap);

+			}

+		}

+	}

+

+	String translated(String key) {

+		return translate(manifest.getValue(key));

+	}

+

+	void doFragment(ResourceImpl resource) {

+		// Check if we are a fragment

+		ManifestEntry entry = manifest.getHost();

+		if (entry == null) {

+			return;

+		}

+		else {

+			// We are a fragment, create a requirement

+			// to our host.

+			RequirementImpl r = new RequirementImpl("bundle");

+			StringBuffer sb = new StringBuffer();

+			sb.append("(&(symbolicname=");

+			sb.append(entry.getName());

+			sb.append(")(version>=");

+			sb.append(entry.getVersion());

+			sb.append("))");

+			r.setFilter(sb.toString());

+			r.setComment("Required Host " + entry.getName() );

+			r.setExtend(true);

+			r.setOptional(false);

+			r.setMultiple(false);

+			resource.addRequirement(r);

+

+			// And insert a capability that we are available

+			// as a fragment. ### Do we need that with extend?

+			CapabilityImpl capability = new CapabilityImpl("fragment");

+			capability.addProperty("host", entry.getName());

+			capability.addProperty("version", entry.getVersion());

+			resource.addCapability(capability);

+		}

+	}

+

+	void doRequires(ResourceImpl resource) {

+		List entries = manifest.getRequire();

+		if (entries == null)

+			return;

+

+		for (Iterator i = entries.iterator(); i.hasNext();) {

+			ManifestEntry entry = (ManifestEntry) i.next();

+			RequirementImpl r = new RequirementImpl("bundle");

+

+			StringBuffer sb = new StringBuffer();

+			sb.append("(&(symbolicname=");

+			sb.append(entry.getName());

+			sb.append(")(version>=");

+			sb.append(entry.getVersion());

+			sb.append("))");

+			r.setFilter(sb.toString());

+			r.setComment("Require Bundle " + entry.getName() + "; "

+					+ entry.getVersion());

+			if (entry.directives == null

+					|| "true".equalsIgnoreCase((String) entry.directives

+							.get("resolution")))

+				r.setOptional(false);

+			else

+				r.setOptional(true);

+			resource.addRequirement(r);

+		}

+	}

+

+	void doExecutionEnvironment(ResourceImpl resource) {

+		String[] parts = manifest.getRequiredExecutionEnvironments();

+		if (parts == null)

+			return;

+

+		StringBuffer sb = new StringBuffer();

+		sb.append("(|");

+		for (int i = 0; i < parts.length; i++) {

+			String part = parts[i];

+			sb.append("(ee=");

+			sb.append(part);

+			sb.append(")");

+		}

+		sb.append(")");

+

+		RequirementImpl req = new RequirementImpl("ee");

+		req.setFilter(sb.toString());

+		req.setComment("Execution Environment " + sb.toString());

+		resource.addRequirement(req);

+	}

+

+	void doImports(ResourceImpl resource) {

+		List requirements = new ArrayList();

+		List packages = manifest.getImports();

+		if (packages == null)

+			return;

+

+		for (Iterator i = packages.iterator(); i.hasNext();) {

+			ManifestEntry pack = (ManifestEntry) i.next();

+			RequirementImpl requirement = new RequirementImpl("package");

+

+			createImportFilter(requirement, "package", pack);

+			requirement.setComment("Import package " + pack);

+			requirements.add(requirement);

+		}

+		for (Iterator i = requirements.iterator(); i.hasNext();)

+			resource.addRequirement((RequirementImpl) i.next());

+	}

+

+	String createServiceFilter(ManifestEntry pack) {

+		StringBuffer filter = new StringBuffer();

+		filter.append("(service=");

+		filter.append(pack.getName());

+		filter.append(")");

+		return filter.toString();

+	}

+

+	void createImportFilter(RequirementImpl req, String name, ManifestEntry pack) {

+		StringBuffer filter = new StringBuffer();

+		filter.append("(&(");

+		filter.append(name);

+		filter.append("=");

+		filter.append(pack.getName());

+		filter.append(")");

+		VersionImpl version = pack.getVersion();

+		if (version != null) {

+			VersionRange range = version.getRange();

+			if (range != null) {

+				filter.append("(version");

+				filter.append(">");

+				if (range.getIncludeMinimum())

+					filter.append("=");

+				filter.append(range.getMinimum());

+				filter.append(")");

+

+				filter.append("(version");

+				filter.append("<");

+				if (range.getIncludeMaximum())

+					filter.append("=");

+				filter.append(range.getMaximum());

+				filter.append(")");

+			}

+			else {

+				filter.append("(version>=");

+				filter.append(pack.getVersion());

+				filter.append(")");

+			}

+		}

+		Map attributes = pack.getAttributes();

+		Set attrs = doImportPackageAttributes(req, filter, attributes);

+		if (attrs.size() > 0) {

+			String del = "";

+			filter.append("(mandatory:<*");

+			for (Iterator i = attrs.iterator(); i.hasNext();) {

+				filter.append(del);

+				filter.append(i.next());

+				del = ", ";

+			}

+			filter.append(")");

+		}

+		filter.append(")");

+		req.setFilter(filter.toString());

+	}

+

+	Set doImportPackageAttributes(RequirementImpl req, StringBuffer filter,

+			Map attributes) {

+		HashSet set = new HashSet();

+

+		if (attributes != null)

+			for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {

+				String attribute = (String) i.next();

+				String value = (String) attributes.get(attribute);

+				if (attribute.equalsIgnoreCase("specification-version")

+						|| attribute.equalsIgnoreCase("version"))

+					continue;

+				else if (attribute.equalsIgnoreCase("resolution:")) {

+					req.setOptional(value.equalsIgnoreCase("optional"));

+				}

+				if (attribute.endsWith(":")) {

+					// Ignore

+				}

+				else {

+					filter.append("(");

+					filter.append(attribute);

+					filter.append("=");

+					filter.append(attributes.get(attribute));

+					filter.append(")");

+					set.add(attribute);

+				}

+			}

+		return set;

+	}

+

+	void doBundle(ResourceImpl resource) {

+		CapabilityImpl capability = new CapabilityImpl("bundle");

+		capability.addProperty("symbolicname", manifest.getSymbolicName());

+		if (manifest.getValue("Bundle-Name") != null)

+			capability.addProperty(

+					Resource.PRESENTATION_NAME,

+					translated("Bundle-Name"));

+		capability.addProperty("version", manifest.getVersion());

+		capability

+				.addProperty("manifestversion", manifest.getManifestVersion());

+

+		/**

+		 * Is this needed TODO

+		 */

+		ManifestEntry host = manifest.getHost();

+		if (host != null) {

+			capability.addProperty("host", host.getName());

+			if (host.getVersion() != null)

+				capability.addProperty("version", host.getVersion());

+		}

+		resource.addCapability(capability);

+	}

+

+	void doExports(ResourceImpl resource) {

+		List capabilities = new ArrayList();

+		List packages = manifest.getExports();

+		if (packages != null) {

+			for (Iterator i = packages.iterator(); i.hasNext();) {

+				ManifestEntry pack = (ManifestEntry) i.next();

+				CapabilityImpl capability = createCapability("package", pack);

+				capabilities.add(capability);

+			}

+        }

+		for (Iterator i = capabilities.iterator(); i.hasNext();)

+			resource.addCapability((CapabilityImpl) i.next());

+	}

+

+	CapabilityImpl createServiceCapability(ManifestEntry pack) {

+		CapabilityImpl capability = new CapabilityImpl("service");

+		capability.addProperty("service", pack.getName());

+		return capability;

+	}

+

+	CapabilityImpl createCapability(String name, ManifestEntry pack) {

+		CapabilityImpl capability = new CapabilityImpl(name);

+		capability.addProperty(name, pack.getName());

+		capability.addProperty("version", pack.getVersion());

+		Map attributes = pack.getAttributes();

+		if (attributes != null)

+			for (Iterator at = attributes.keySet().iterator(); at.hasNext();) {

+				String key = (String) at.next();

+				if (key.equalsIgnoreCase("specification-version")

+						|| key.equalsIgnoreCase("version"))

+					continue;

+				else {

+					Object value = attributes.get(key);

+					capability.addProperty(key, value);

+				}

+			}

+		return capability;

+	}

+

+	String translate(String s) {

+		if (s == null)

+			return null;

+

+		if (!s.startsWith("%")) {

+			return s;

+		}

+

+		if (localization == null)

+			try {

+				localization = new Properties();

+				String path = manifest

+						.getValue("Bundle-Localization", "bundle");

+				path += ".properties";

+				InputStream in = jar.getInputStream(new ZipEntry(path));

+				if (in != null) {

+					localization.load(in);

+					in.close();

+				}

+			}

+			catch (IOException e) {

+				e.printStackTrace();

+			}

+		s = s.substring(1);

+		return localization.getProperty(s, s);

+	}

+

+	File getZipFile() {

+		return bundleJar;

+	}

+}
\ No newline at end of file
diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/CapabilityImpl.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/CapabilityImpl.java
new file mode 100644
index 0000000..34b0460
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/CapabilityImpl.java
@@ -0,0 +1,92 @@
+package org.osgi.impl.bundle.obr.resource;

+

+import java.util.*;

+

+import org.osgi.service.obr.Capability;

+import org.xmlpull.v1.XmlPullParser;

+

+

+

+public class CapabilityImpl implements Capability {

+	String				name;

+	Map	properties	= new TreeMap();

+

+	public CapabilityImpl(String name) {

+		this.name = name;

+	}

+

+	public CapabilityImpl(XmlPullParser parser) throws Exception {

+		parser.require(XmlPullParser.START_TAG, null, "capability");

+		name = parser.getAttributeValue(null,"name");

+		while ( parser.nextTag() == XmlPullParser.START_TAG ) {

+			if ( parser.getName().equals("p")) {

+				String name = parser.getAttributeValue(null,"n");

+				String value = parser.getAttributeValue(null,"v");

+				String type = parser.getAttributeValue(null,"t");

+				Object v = value;

+

+				if ( "nummeric".equals(type))

+					v = new Long(value);

+				else if ( "version".equals(type))

+					v = new VersionImpl(value);

+				addProperty(name,v);

+			}

+			parser.next();

+			parser.require(XmlPullParser.END_TAG, null, "p" );

+		}

+		parser.require(XmlPullParser.END_TAG, null, "capability" );

+	}

+

+

+	public void addProperty(String key, Object value) {

+		List values = (List) properties.get(key);

+		if (values == null) {

+			values = new ArrayList();

+			properties.put(key, values);

+		}

+		values.add(value);

+	}

+

+	public Tag toXML() {

+		return toXML(this);

+	}

+	

+	public static Tag toXML(Capability capability) {

+		Tag tag = new Tag("capability");

+		tag.addAttribute("name", capability.getName());

+		Map properties = capability.getProperties();

+		for ( Iterator k= properties.keySet().iterator(); k.hasNext(); ) {

+			String key = (String) k.next();

+			List values = (List) properties.get(key);

+			for ( Iterator v = values.iterator(); v.hasNext(); ) {

+				Object value = v.next();

+				Tag p = new Tag("p");

+				tag.addContent(p);

+				p.addAttribute("n", key);

+				if ( value != null )

+					p.addAttribute("v", value.toString());

+				else

+					System.out.println("Missing value " + key);

+				String type = null;

+				if (value instanceof Number )

+					type = "number";

+				else if (value.getClass() == VersionImpl.class)

+					type = "version";

+				if (type != null)

+					p.addAttribute("t", type);

+			}

+		}

+		return tag;

+	}

+

+

+	public String getName() {

+		return name;

+	}

+

+

+	public Map getProperties() {

+		return properties;

+	}

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/FilterImpl.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/FilterImpl.java
new file mode 100644
index 0000000..d6fc637
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/FilterImpl.java
@@ -0,0 +1,431 @@
+/**

+ * Copyright (c) 2000 Gatespace AB. All Rights Reserved.

+ *

+ * Gatespace grants Open Services Gateway Initiative (OSGi) an irrevocable,

+ * perpetual, non-exclusive, worldwide, paid-up right and license to

+ * reproduce, display, perform, prepare and have prepared derivative works

+ * based upon and distribute and sublicense this material and derivative

+ * works thereof as set out in the OSGi MEMBER AGREEMENT as of January 24

+ * 2000, for use in accordance with Section 2.2 of the BY-LAWS of the

+ * OSGi MEMBER AGREEMENT.

+ */

+

+package org.osgi.impl.bundle.obr.resource;

+

+import java.lang.reflect.*;

+import java.math.BigInteger;

+import java.util.*;

+

+public class FilterImpl {

+	final char		WILDCARD	= 65535;

+

+	final int		EQ			= 0;

+	final int		LE			= 1;

+	final int		GE			= 2;

+	final int		APPROX		= 3;

+	final int		LESS		= 4;

+	final int		GREATER		= 5;

+	final int		SUBSET		= 6;

+	final int		SUPERSET	= 7;

+

+	private String	filter;

+

+	abstract class Query {

+		static final String	GARBAGE		= "Trailing garbage";

+		static final String	MALFORMED	= "Malformed query";

+		static final String	EMPTY		= "Empty list";

+		static final String	SUBEXPR		= "No subexpression";

+		static final String	OPERATOR	= "Undefined operator";

+		static final String	TRUNCATED	= "Truncated expression";

+		static final String	EQUALITY	= "Only equality supported";

+

+		private String		tail;

+

+		boolean match() throws IllegalArgumentException {

+			tail = filter;

+			boolean val = doQuery();

+			if (tail.length() > 0)

+				error(GARBAGE);

+			return val;

+		}

+

+		private boolean doQuery() throws IllegalArgumentException {

+			if (tail.length() < 3 || !prefix("("))

+				error(MALFORMED);

+			boolean val;

+

+			switch (tail.charAt(0)) {

+				case '&' :

+					val = doAnd();

+					break;

+				case '|' :

+					val = doOr();

+					break;

+				case '!' :

+					val = doNot();

+					break;

+				default :

+					val = doSimple();

+					break;

+			}

+

+			if (!prefix(")"))

+				error(MALFORMED);

+			return val;

+		}

+

+		private boolean doAnd() throws IllegalArgumentException {

+			tail = tail.substring(1);

+			boolean val = true;

+			if (!tail.startsWith("("))

+				error(EMPTY);

+			do {

+				if (!doQuery())

+					val = false;

+			} while (tail.startsWith("("));

+			return val;

+		}

+

+		private boolean doOr() throws IllegalArgumentException {

+			tail = tail.substring(1);

+			boolean val = false;

+			if (!tail.startsWith("("))

+				error(EMPTY);

+			do {

+				if (doQuery())

+					val = true;

+			} while (tail.startsWith("("));

+			return val;

+		}

+

+		private boolean doNot() throws IllegalArgumentException {

+			tail = tail.substring(1);

+			if (!tail.startsWith("("))

+				error(SUBEXPR);

+			return !doQuery();

+		}

+

+		private boolean doSimple() throws IllegalArgumentException {

+			int op = 0;

+			Object attr = getAttr();

+

+			if (prefix("="))

+				op = EQ;

+			else if (prefix("<="))

+				op = LE;

+			else if (prefix(">="))

+				op = GE;

+			else if (prefix("~="))

+				op = APPROX;

+			else if (prefix("*>"))

+				op = SUPERSET;

+			else if (prefix("<*"))

+				op = SUBSET;

+			else if (prefix("<"))

+				op = LESS;

+			else if (prefix(">"))

+				op = GREATER;

+			else

+				error(OPERATOR);

+

+			return compare(attr, op, getValue());

+		}

+

+		private boolean prefix(String pre) {

+			if (!tail.startsWith(pre))

+				return false;

+			tail = tail.substring(pre.length());

+			return true;

+		}

+

+		private Object getAttr() {

+			int len = tail.length();

+			int ix = 0;

+			label: for (; ix < len; ix++) {

+				switch (tail.charAt(ix)) {

+					case '(' :

+					case ')' :

+					case '<' :

+					case '>' :

+					case '=' :

+					case '~' :

+					case '*' :

+					case '}' :

+					case '{' :

+					case '\\' :

+						break label;

+				}

+			}

+			String attr = tail.substring(0, ix).toLowerCase();

+			tail = tail.substring(ix);

+			return getProp(attr);

+		}

+

+		abstract Object getProp(String key);

+

+		private String getValue() {

+			StringBuffer sb = new StringBuffer();

+			int len = tail.length();

+			int ix = 0;

+			label: for (; ix < len; ix++) {

+				char c = tail.charAt(ix);

+				switch (c) {

+					case '(' :

+					case ')' :

+						break label;

+					case '*' :

+						sb.append(WILDCARD);

+						break;

+					case '\\' :

+						if (ix == len - 1)

+							break label;

+						sb.append(tail.charAt(++ix));

+						break;

+					default :

+						sb.append(c);

+						break;

+				}

+			}

+			tail = tail.substring(ix);

+			return sb.toString();

+		}

+

+		private void error(String m) throws IllegalArgumentException {

+			throw new IllegalArgumentException(m + " " + tail);

+		}

+

+		private boolean compare(Object obj, int op, String s) {

+			if (obj == null) {

+				// No value is ok for a subset

+				if (op == SUBSET)

+					return true;

+

+				// No value is ok for a superset when the value is

+				// empty

+				if (op == SUPERSET) {

+					return s.trim().length() == 0;

+				}

+

+				return false;

+			}

+			try {

+				Class numClass = obj.getClass();

+				if (numClass == String.class) {

+					return compareString((String) obj, op, s);

+				}

+				else if (numClass == Character.class) {

+					return compareString(obj.toString(), op, s);

+				}

+				else if (numClass == Long.class) {

+					return compareSign(op, Long.valueOf(s)

+							.compareTo((Long) obj));

+				}

+				else if (numClass == Integer.class) {

+					return compareSign(op, Integer.valueOf(s).compareTo(

+							(Integer) obj));

+				}

+				else if (numClass == Short.class) {

+					return compareSign(op, Short.valueOf(s).compareTo(

+							(Short) obj));

+				}

+				else if (numClass == Byte.class) {

+					return compareSign(op, Byte.valueOf(s)

+							.compareTo((Byte) obj));

+				}

+				else if (numClass == Double.class) {

+					return compareSign(op, Double.valueOf(s).compareTo(

+							(Double) obj));

+				}

+				else if (numClass == Float.class) {

+					return compareSign(op, Float.valueOf(s).compareTo(

+							(Float) obj));

+				}

+				else if (numClass == Boolean.class) {

+					if (op != EQ)

+						return false;

+					int a = Boolean.valueOf(s).booleanValue() ? 1 : 0;

+					int b = ((Boolean) obj).booleanValue() ? 1 : 0;

+					return compareSign(op, a - b);

+				}

+				else if (numClass == BigInteger.class) {

+					return compareSign(op, new BigInteger(s)

+							.compareTo((BigInteger) obj));

+				}

+				else if (obj instanceof Collection) {

+					if (op == SUBSET || op == SUPERSET) {

+						StringSet set = new StringSet(s);

+						if (op == SUBSET)

+							return set.containsAll((Collection) obj);

+						else

+							return ((Collection) obj).containsAll(set);

+					}

+

+					for (Iterator i = ((Collection) obj).iterator(); i

+							.hasNext();) {

+						Object element = i.next();

+						if (compare(element, op, s))

+							return true;

+					}

+				}

+				else if (numClass.isArray()) {

+					int len = Array.getLength(obj);

+					for (int i = 0; i < len; i++)

+						if (compare(Array.get(obj, i), op, s))

+							return true;

+				}

+				else {

+					try {

+						if (op == SUPERSET || op == SUBSET) {

+							StringSet set = new StringSet(s);

+							if (op == SUPERSET)

+								return set.contains(obj);

+							else

+								return set.size() == 0

+										|| (set.size() == 1 && set.iterator()

+												.next().equals(obj));

+						}

+						else {

+							Constructor constructor = numClass

+									.getConstructor(new Class[] {String.class});

+							Object instance = constructor

+									.newInstance(new Object[] {s});

+							switch (op) {

+								case EQ :

+									return obj.equals(instance);

+								case LESS :

+									return ((Comparable) obj)

+											.compareTo(instance) < 0;

+								case GREATER :

+									return ((Comparable) obj)

+											.compareTo(instance) > 0;

+								case LE :

+									return ((Comparable) obj)

+											.compareTo(instance) <= 0;

+								case GE :

+									return ((Comparable) obj)

+											.compareTo(instance) >= 0;

+							}

+						}

+					}

+					catch (Exception e) {

+						e.printStackTrace();

+						// Ignore

+					}

+				}

+			}

+			catch (Exception e) {

+			}

+			return false;

+		}

+	}

+

+	class DictQuery extends Query {

+		private Map	dict;

+

+		DictQuery(Map dict) {

+			this.dict = dict;

+		}

+

+		Object getProp(String key) {

+			return dict.get(key);

+		}

+	}

+

+	public FilterImpl(String filter) throws IllegalArgumentException {

+		// NYI: Normalize the filter string?

+		this.filter = filter;

+		if (filter == null || filter.length() == 0)

+			throw new IllegalArgumentException("Null query");

+	}

+

+	public boolean match(Map dict) {

+		try {

+			return new DictQuery(dict).match();

+		}

+		catch (IllegalArgumentException e) {

+			return false;

+		}

+	}

+

+	public String toString() {

+		return filter;

+	}

+

+	public boolean equals(Object obj) {

+		return obj != null && obj instanceof FilterImpl

+				&& filter.equals(((FilterImpl) obj).filter);

+	}

+

+	public int hashCode() {

+		return filter.hashCode();

+	}

+

+	boolean compareString(String s1, int op, String s2) {

+		switch (op) {

+			case EQ :

+				return patSubstr(s1, s2);

+			case APPROX :

+				return patSubstr(fixupString(s1), fixupString(s2));

+			default :

+				return compareSign(op, s2.compareTo(s1));

+		}

+	}

+

+	boolean compareSign(int op, int cmp) {

+		switch (op) {

+			case LE :

+				return cmp >= 0;

+			case GE :

+				return cmp <= 0;

+			case EQ :

+				return cmp == 0;

+			default : /* APPROX */

+				return cmp == 0;

+		}

+	}

+

+	String fixupString(String s) {

+		StringBuffer sb = new StringBuffer();

+		int len = s.length();

+		boolean isStart = true;

+		boolean isWhite = false;

+		for (int i = 0; i < len; i++) {

+			char c = s.charAt(i);

+			if (Character.isWhitespace(c)) {

+				isWhite = true;

+			}

+			else {

+				if (!isStart && isWhite)

+					sb.append(' ');

+				if (Character.isUpperCase(c))

+					c = Character.toLowerCase(c);

+				sb.append(c);

+				isStart = false;

+				isWhite = false;

+			}

+		}

+		return sb.toString();

+	}

+

+	boolean patSubstr(String s, String pat) {

+		if (s == null)

+			return false;

+		if (pat.length() == 0)

+			return s.length() == 0;

+		if (pat.charAt(0) == WILDCARD) {

+			pat = pat.substring(1);

+			for (;;) {

+				if (patSubstr(s, pat))

+					return true;

+				if (s.length() == 0)

+					return false;

+				s = s.substring(1);

+			}

+		}

+		else {

+			if (s.length() == 0 || s.charAt(0) != pat.charAt(0))

+				return false;

+			return patSubstr(s.substring(1), pat.substring(1));

+		}

+	}

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Manifest.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Manifest.java
new file mode 100644
index 0000000..25b0e12
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Manifest.java
@@ -0,0 +1,386 @@
+package org.osgi.impl.bundle.obr.resource;

+

+import java.io.*;

+import java.util.*;

+

+import org.osgi.impl.bundle.obr.resource.VersionImpl;

+

+

+public class Manifest extends Hashtable {

+	List				imports;

+	List				exports;

+	ManifestEntry		name;

+	String				activator;

+	String				classpath[]	= new String[] {"."};

+	int					section;

+	String				location;

+	Native				_native[];

+	Vector				duplicates	= new Vector();

+	final static String	wordparts	= "~!@#$%^&*_:/?><.-+";

+	ManifestEntry		bsn;

+	VersionImpl			version;

+	ManifestEntry		host;

+	List				require;

+

+	public Manifest(InputStream in) throws IOException {

+		parse(new InputStreamReader(in, "UTF8"));

+	}

+

+	public Manifest(Reader in) throws IOException {

+		parse(in);

+	}

+

+	public Object put(Object header, Object value) {

+		if (containsKey(header)) {

+			if (!((String) header).equalsIgnoreCase("comment"))

+				duplicates.add(header + ":" + value);

+		}

+		return super.put(header, value);

+	}

+

+	void parse(Reader in) throws IOException {

+		BufferedReader rdr = new BufferedReader(in);

+		String current = " ";

+		String buffer = rdr.readLine();

+		int section = 0;

+		if (buffer != null && !buffer.startsWith("Manifest-Version")) {

+			System.err

+					.println("The first line of a manifest file must be the Manifest-Version attribute");

+			throw new IOException(

+					"The first line of a manifest file must be the Manifest-Version attribute");

+		}

+		while (buffer != null && current != null && section == 0) {

+			if (current.startsWith(" ")) {

+				buffer += current.substring(1);

+			}

+			else {

+				section += entry(buffer);

+				buffer = current;

+			}

+			current = rdr.readLine();

+		}

+		entry(buffer);

+	}

+

+	int entry(String line) throws IOException {

+		if (line.length() < 2)

+			return 1;

+		int colon = line.indexOf(':');

+		if (colon < 1) {

+			error("Invalid header '" + line + "'");

+		}

+		else {

+			String header = line.substring(0, colon).toLowerCase();

+			String alphanum = "abcdefghijklmnopqrstuvwxyz0123456789";

+			String set = alphanum;

+			if (alphanum.indexOf(header.charAt(0)) < 0)

+				error("Header does not start with alphanum: " + header);

+			for (int i = 0; i < header.length(); i++) {

+				if (set.indexOf(header.charAt(i)) < 0)

+					error("Header contains non alphanum, - _: " + header);

+				set = "_-" + alphanum;

+			}

+			String value = "";

+			if (colon + 2 < line.length())

+				value = line.substring(colon + 2);

+			else

+				error("No value for manifest header " + header);

+			if (section == 0) {

+				if (header.equals("bundle-symbolicname")) {

+					bsn = (ManifestEntry) getEntries(value).get(0);

+				}

+				if (header.equals("bundle-version")) {

+					try {

+						version = new VersionImpl(value.trim());

+					}

+					catch (Exception e) {

+						version = new VersionImpl("0");

+						System.err.println("Invalid version attr for: " + bsn

+								+ " value is " + value);

+					}

+				}

+				if (header.equals("fragment-host"))

+					host = (ManifestEntry) getEntries(value).get(0);

+				if (header.equals("require-bundle"))

+					require = getEntries(value);

+				if (header.equals("import-package"))

+					imports = getEntries(value);

+				else if (header.equals("export-package"))

+					exports = getEntries(value);

+				else if (header.equals("bundle-activator"))

+					activator = value.trim();

+				else if (header.equals("bundle-updatelocation"))

+					location = value.trim();

+				else if (header.equals("bundle-classpath"))

+					classpath = getClasspath(value);

+				else if (header.equals("bundle-nativecode"))

+					_native = getNative(value);

+				put(header, value);

+			}

+		}

+		return 0;

+	}

+

+	void error(String msg) throws IOException {

+		System.err.println("Reading manifest: " + msg);

+	}

+

+	void warning(String msg) throws IOException {

+		System.err.println("Reading manifest: " + msg);

+	}

+

+	StreamTokenizer getStreamTokenizer(String line) {

+		StreamTokenizer st = new StreamTokenizer(new StringReader(line));

+		st.resetSyntax();

+		st.wordChars('a', 'z');

+		st.wordChars('A', 'Z');

+		st.wordChars('0', '9');

+		st.whitespaceChars(0, ' ');

+		st.quoteChar('"');

+		for (int i = 0; i < wordparts.length(); i++)

+			st.wordChars(wordparts.charAt(i), wordparts.charAt(i));

+		return st;

+	}

+

+	String word(StreamTokenizer st) throws IOException {

+		switch (st.nextToken()) {

+			case '"' :

+			case StreamTokenizer.TT_WORD :

+				String result = st.sval;

+				st.nextToken();

+				return result;

+		}

+		return null;

+	}

+

+	Parameter getParameter(StreamTokenizer st) throws IOException {

+

+		Parameter parameter = new Parameter();

+		parameter.key = word(st);

+		if (st.ttype == ':') {

+			st.nextToken();

+			parameter.type = Parameter.DIRECTIVE;

+		}

+		else {

+			parameter.type = Parameter.ATTRIBUTE;

+		}

+

+		if (st.ttype == '=') {

+			parameter.value = word(st);

+			while (st.ttype == StreamTokenizer.TT_WORD || st.ttype == '"') {

+				parameter.value += " " + st.sval;

+				st.nextToken();

+			}

+		}

+

+		return parameter;

+	}

+

+	public List getEntries(String line) throws IOException {

+		List v = new Vector();

+		Set aliases = new HashSet();

+

+		StreamTokenizer st = getStreamTokenizer(line);

+		do {

+			Parameter parameter = getParameter(st);

+			ManifestEntry p = new ManifestEntry(parameter.key);

+			while (st.ttype == ';') {

+				parameter = getParameter(st);

+				if (parameter.value == null) {

+					aliases.add(parameter.key);

+				}

+				else {

+					if (parameter.type == Parameter.ATTRIBUTE)

+						p.addParameter(parameter);

+					else

+						p.addParameter(parameter);

+				}

+			}

+			v.add(p);

+			for (Iterator a = aliases.iterator(); a.hasNext();) {

+				v.add(p.getAlias((String) a.next()));

+			}

+		} while (st.ttype == ',');

+		return v;

+	}

+

+	Native[] getNative(String line) throws IOException {

+		Vector v = new Vector();

+		StreamTokenizer st = getStreamTokenizer(line);

+		do {

+			Native spec = new Native();

+			Vector names = new Vector();

+			do {

+				Parameter parameter = getParameter(st);

+				if (parameter.value == null)

+					names.add(parameter.key);

+				else if (parameter.is("processor", Parameter.ATTRIBUTE))

+					spec.processor = parameter.value;

+				else if (parameter.is("osname", Parameter.ATTRIBUTE))

+					spec.osname = parameter.value;

+				else if (parameter.is("osversion", Parameter.ATTRIBUTE))

+					spec.osversion = parameter.value;

+				else if (parameter.is("language", Parameter.ATTRIBUTE))

+					spec.language = parameter.value;

+				else if (parameter.is("selection-filter", Parameter.DIRECTIVE))

+					spec.filter = parameter.value;

+				else

+					warning("Unknown parameter for native code : " + parameter);

+			} while (st.ttype == ';');

+			spec.paths = new String[names.size()];

+			names.copyInto(spec.paths);

+			v.add(spec);

+		} while (st.ttype == ',');

+		Native[] result = new Native[v.size()];

+		v.copyInto(result);

+		return result;

+	}

+

+	String[] getClasspath(String line) throws IOException {

+		StringTokenizer st = new StringTokenizer(line, " \t,");

+		String result[] = new String[st.countTokens()];

+		for (int i = 0; i < result.length; i++)

+			result[i] = st.nextToken();

+		return result;

+	}

+

+	public List getImports() {

+		return imports;

+	}

+

+	public List getExports() {

+		return exports;

+	}

+

+	public String getActivator() {

+		return activator;

+	}

+

+	public String getLocation() {

+		return location;

+	}

+

+	public String[] getClasspath() {

+		return classpath;

+	}

+

+	public Native[] getNative() {

+		return _native;

+	}

+

+	public Object get(Object key) {

+		if (key instanceof String)

+			return super.get(((String) key).toLowerCase());

+		else

+			return null;

+	}

+

+	public String getValue(String key) {

+		return (String) super.get(key.toLowerCase());

+	}

+

+	public String getValue(String key, String deflt) {

+		String s = getValue(key);

+		if (s == null)

+			return deflt;

+		else

+			return s;

+	}

+

+	public String[] getRequiredExecutionEnvironments() {

+		String ees = getValue("Bundle-RequiredExecutionEnvironment");

+		if (ees != null)

+			return ees.trim().split("\\s*,\\s*");

+		else

+			return null;

+	}

+

+	public VersionImpl getVersion() {

+		if (version == null)

+			return new VersionImpl("0");

+		return version;

+	}

+

+	public String getSymbolicName() {

+		ManifestEntry bsn = getBsn();

+

+		if (bsn == null) {

+			//nox if the symbolic name is not define in manifest fils, we take the 

+			//artefactId from the pom.xml file

+			/*String name = getValue("Bundle-Name");

+			if (name == null)

+				name = "Untitled-" + hashCode();*/

+			return null;

+		}

+		else

+			return bsn.getName();

+	}

+

+	public String getManifestVersion() {

+		return getValue("Bundle-ManifestVersion", "1");

+	}

+

+	public String getCopyright() {

+		return getValue("Bundle-Copyright");

+	}

+

+	public String getDocumentation() {

+		return getValue("Bundle-DocURL");

+	}

+

+	public String[] getCategories() {

+		String cats = getValue("Bundle-Category");

+		if (cats == null)

+			return new String[0];

+		else

+			return cats.split("\\s*,\\s*");

+	}

+

+	public Native[] get_native() {

+		return _native;

+	}

+

+	public void set_native(Native[] _native) {

+		this._native = _native;

+	}

+

+	public ManifestEntry getBsn() {

+		return bsn;

+	}

+

+	public void setBsn(ManifestEntry bsn) {

+		this.bsn = bsn;

+	}

+

+	public Vector getDuplicates() {

+		return duplicates;

+	}

+

+	public void setDuplicates(Vector duplicates) {

+		this.duplicates = duplicates;

+	}

+

+	public ManifestEntry getHost() {

+		return host;

+	}

+

+	public void setHost(ManifestEntry host) {

+		this.host = host;

+	}

+

+	public List getRequire() {

+		return require;

+	}

+

+}

+

+class Native {

+	String	filter;

+	int		index	= -1;

+	String	paths[];

+	String	osname;

+	String	osversion;

+	String	language;

+	String	processor;

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/ManifestEntry.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/ManifestEntry.java
new file mode 100644
index 0000000..f26f22f
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/ManifestEntry.java
@@ -0,0 +1,96 @@
+package org.osgi.impl.bundle.obr.resource;

+

+import java.util.*;

+

+import org.osgi.impl.bundle.obr.resource.VersionImpl;

+

+

+public class ManifestEntry implements Comparable {

+	String		name;

+	VersionImpl	version;

+	Map			attributes;

+	public Map	directives;

+	public Set	uses;

+

+	public ManifestEntry(String name) {

+		this.name = name;

+	}

+

+	public ManifestEntry(String name, VersionImpl version) {

+		this.name = name;

+		this.version = version;

+	}

+

+	public String toString() {

+		if (version == null)

+			return name;

+		return name + " ;version=" + version;

+	}

+

+	public String getName() {

+		return name;

+	}

+

+	public VersionImpl getVersion() {

+		if (version != null)

+			return version;

+		return new VersionImpl("0");

+	}

+

+	/*

+	 * (non-Javadoc)

+	 * 

+	 * @see java.lang.Comparable#compareTo(java.lang.Object)

+	 */

+	public int compareTo(Object o) {

+		ManifestEntry p = (ManifestEntry) o;

+		return name.compareTo(p.name);

+	}

+

+	/**

+	 * @return

+	 */

+	public Object getPath() {

+		return getName().replace('.', '/');

+	}

+

+	public Map getDirectives() {

+		return directives;

+	}

+

+	public Map getAttributes() {

+		return attributes;

+	}

+

+	/**

+	 * @param parameter

+	 */

+	public void addParameter(Parameter parameter) {

+		switch (parameter.type) {

+			case Parameter.ATTRIBUTE :

+				if (attributes == null)

+					attributes = new HashMap();

+				attributes.put(parameter.key, parameter.value);

+				if (parameter.key.equalsIgnoreCase("version")

+						|| parameter.key

+								.equalsIgnoreCase("specification-version"))

+					this.version = new VersionImpl(parameter.value);

+				break;

+

+			case Parameter.DIRECTIVE :

+				if (directives == null)

+					directives = new HashMap();

+				directives.put(parameter.key, parameter.value);

+				break;

+		}

+	}

+

+	public ManifestEntry getAlias(String key) {

+		ManifestEntry me = new ManifestEntry(key);

+		me.attributes = attributes;

+		me.directives = directives;

+		me.version = version;

+		return me;

+	}

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Parameter.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Parameter.java
new file mode 100644
index 0000000..1edfb7e
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Parameter.java
@@ -0,0 +1,59 @@
+/*

+ * $Header: /cvshome/bundles/bundles.obr/src/bundles/obr/resource/Parameter.java,v 1.1 2006/07/27 10:31:02 pkriens Exp $

+ * 

+ * Copyright (c) The OSGi Alliance (2005). All Rights Reserved.

+ * 

+ * Implementation of certain elements of the OSGi Specification may be subject

+ * to third party intellectual property rights, including without limitation,

+ * patent rights (such a third party may or may not be a member of the OSGi

+ * Alliance). The OSGi Alliance is not responsible and shall not be held

+ * responsible in any manner for identifying or failing to identify any or all

+ * such third party intellectual property rights.

+ * 

+ * This document and the information contained herein are provided on an "AS IS"

+ * basis and THE OSGI ALLIANCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,

+ * INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION

+ * HEREIN WILL NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF

+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL THE

+ * OSGI ALLIANCE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF

+ * USE OF DATA, INTERRUPTION OF BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR

+ * EXEMPLARY, INCIDENTIAL, PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN

+ * CONNECTION WITH THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF

+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.

+ * 

+ * All Company, brand and product names may be trademarks that are the sole

+ * property of their respective owners. All rights reserved.

+ */

+

+package org.osgi.impl.bundle.obr.resource;

+

+class Parameter {

+	final static int	ATTRIBUTE	= 1;

+	final static int	DIRECTIVE	= 2;

+	final static int	SINGLE		= 0;

+

+	int					type;

+	String				key;

+	String				value;

+

+	public String toString() {

+		StringBuffer sb = new StringBuffer();

+		sb.append(key);

+		switch (type) {

+			case ATTRIBUTE :

+				sb.append("=");

+				break;

+			case DIRECTIVE :

+				sb.append(":=");

+				break;

+			case SINGLE :

+				return sb.toString();

+		}

+		sb.append(value);

+		return sb.toString();

+	}

+

+	boolean is(String s, int type) {

+		return this.type == type && key.equalsIgnoreCase(s);

+	}

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/RepositoryImpl.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/RepositoryImpl.java
new file mode 100644
index 0000000..0c27962
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/RepositoryImpl.java
@@ -0,0 +1,386 @@
+/*

+ * $Header: /cvshome/bundles/bundles.obr/src/bundles/obr/resource/RepositoryImpl.java,v 1.11 2006/04/27 09:00:16 pkriens Exp $

+ * 

+ * Copyright (c) The OSGi Alliance (2005). All Rights Reserved.

+ * 

+ * Implementation of certain elements of the OSGi Specification may be subject

+ * to third party intellectual property rights, including without limitation,

+ * patent rights (such a third party may or may not be a member of the OSGi

+ * Alliance). The OSGi Alliance is not responsible and shall not be held

+ * responsible in any manner for identifying or failing to identify any or all

+ * such third party intellectual property rights.

+ * 

+ * This document and the information contained herein are provided on an "AS IS"

+ * basis and THE OSGI ALLIANCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,

+ * INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION

+ * HEREIN WILL NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF

+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL THE

+ * OSGI ALLIANCE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF

+ * USE OF DATA, INTERRUPTION OF BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR

+ * EXEMPLARY, INCIDENTIAL, PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN

+ * CONNECTION WITH THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF

+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.

+ * 

+ * All Company, brand and product names may be trademarks that are the sole

+ * property of their respective owners. All rights reserved.

+ */

+

+package org.osgi.impl.bundle.obr.resource;

+

+import java.io.*;

+import java.net.*;

+import java.util.*;

+import java.util.zip.*;

+

+import org.kxml2.io.KXmlParser;

+import org.osgi.service.obr.Repository;

+import org.osgi.service.obr.Resource;

+

+import org.xmlpull.v1.*;

+

+/**

+ * Implements the basic repository. A repository holds a set of resources.

+ * 

+ * 

+ * @version $Revision: 1.11 $

+ */

+public class RepositoryImpl implements Repository {

+	transient Set			resources		= new HashSet();

+	URL						url;

+	String					date;

+	Set						visited			= new HashSet();

+	final static Resource[]	EMPTY_RESOURCE	= new Resource[0];

+	String					name			= "Untitled";

+	long					lastModified;

+	Exception				exception;

+	int						ranking=0;

+

+	/**

+	 * Each repository is identified by a single URL.

+	 * 

+	 * A repository can hold referrals to other repositories. These referred

+	 * repositories are included at the point of referall.

+	 * 

+	 * @param url

+	 */

+	public RepositoryImpl(URL url) {

+		this.url = url;

+	}

+

+	/**

+	 * Refresh the repository from the URL.

+	 * 

+	 * @throws Exception

+	 */

+	public boolean refresh() {

+		exception = null;

+		try {

+			resources.clear();

+			parseDocument(url);

+			visited = null;

+			return true;

+		}

+		catch (Exception e) {

+			e.printStackTrace();

+			exception = e;

+		}

+		return false;

+	}

+

+	/**

+	 * Parse the repository.

+	 * 

+	 * @param parser

+	 * @throws Exception

+	 */

+	private void parseRepository(XmlPullParser parser) throws Exception {

+		try {

+			parser.require(XmlPullParser.START_DOCUMENT, null, null);

+			parser.nextTag();

+			if (parser.getName().equals("bundles"))

+				parseOscar(parser);

+			else {

+				parser.require(XmlPullParser.START_TAG, null, "repository");

+				date = parser.getAttributeValue(null, "lastmodified");

+				name = parser.getAttributeValue(null, "name");

+				if (name == null)

+					name = "Untitled";

+

+				while (parser.nextTag() == XmlPullParser.START_TAG) {

+					if (parser.getName().equals("resource")) {

+						ResourceImpl resource = new ResourceImpl(this, parser);

+						resources.add(resource);

+					}

+					else if (parser.getName().equals("referral"))

+						referral(parser);

+					else

+						throw new IllegalArgumentException(

+								"Invalid tag in repository: " + url + " "

+										+ parser.getName());

+				}

+				parser.require(XmlPullParser.END_TAG, null, "repository");

+			}

+		}

+		catch (XmlPullParserException e) {

+			e.printStackTrace();

+			throw new IllegalArgumentException("XML unregognized around: "

+					+ e.getLineNumber() + " " + e.getMessage());

+		}

+	}

+

+	/**

+	 * Parse an old style OBR repository.

+	 * 

+	 * <dtd-version>1.0</dtd-version> <repository> <name>Oscar Bundle

+	 * Repository</name> <url>http://oscar-osgi.sourceforge.net/</url>

+	 * <date>Fri May 07 16:45:07 CEST 2004</date> <extern-repositories> <!--

+	 * Stefano Lenzi (kismet@interfree.it) -->

+	 * <url>http://domoware.isti.cnr.it/osgi-obr/niche-osgi-obr.xml</url>

+	 * <!--Manuel Palencia (santillan@dit.upm.es) --> <!--

+	 * <url>http://jmood.forge.os4os.org/repository.xml</url> --> <!-- Enrique

+	 * Rodriguez (erodriguez@apache.org) -->

+	 * <url>http://update.cainenable.org/repository.xml</url>

+	 * </extern-repositories> </repository> <bundle> <bundle-name>Bundle

+	 * Repository</bundle-name> <bundle-description> A bundle repository

+	 * service for Oscar. </bundle-description> <bundle-updatelocation>

+	 * http://oscar-osgi.sf.net/repo/bundlerepository/bundlerepository.jar

+	 * </bundle-updatelocation> <bundle-sourceurl>

+	 * http://oscar-osgi.sf.net/repo/bundlerepository/bundlerepository-src.jar

+	 * </bundle-sourceurl> <bundle-version>1.1.3</bundle-version>

+	 * <bundle-docurl> http://oscar-osgi.sf.net/repo/bundlerepository/

+	 * </bundle-docurl> <bundle-category>General</bundle-category>

+	 * <import-package package="org.osgi.framework"/> <export-package

+	 * package="org.ungoverned.osgi.service.bundlerepository"

+	 * specification-version="1.1.0"/> </bundle> *

+	 */

+	private void parseOscar(XmlPullParser parser) throws Exception {

+		parser.require(XmlPullParser.START_TAG, null, "bundles");

+		while (true) {

+			int event = parser.next();

+

+			// Error ..

+			if (event == XmlPullParser.TEXT)

+				event = parser.next();

+

+			if (event != XmlPullParser.START_TAG)

+				break;

+

+			ResourceImpl resource = new ResourceImpl(this);

+

+			if (parser.getName().equals("bundle")) {

+				while (parser.nextTag() == XmlPullParser.START_TAG) {

+					String key = parser.getName();

+					if (key.equals("import-package")) {

+						RequirementImpl requirement = new RequirementImpl(

+								"package");

+						

+						requirement.setOptional(false);

+						requirement.setMultiple(false);

+						

+						String p = parser.getAttributeValue(null, "package");

+						StringBuffer sb = new StringBuffer();

+						sb.append("(&(package=");

+						sb.append(p);

+						sb.append(")");

+						String version = parser.getAttributeValue(null,

+								"specification-version");

+						VersionImpl v = new VersionImpl("0");

+						if (version != null) {

+							sb.append("(version=");

+							sb.append(v= new VersionImpl(version));

+							sb.append(")");

+						}

+						sb.append(")");

+						requirement.setFilter(sb.toString());

+						requirement.setComment("Import-Package: " + p + ";" + v );

+						resource.addRequirement(requirement);

+						

+						parser.nextTag();

+					}

+					else if (key.equals("export-package")) {

+						CapabilityImpl capability = new CapabilityImpl(

+								"package");

+						capability.addProperty("package", parser

+								.getAttributeValue(null, "package"));

+						String version = parser.getAttributeValue(null,

+								"specification-version");

+						if (version != null) {

+							capability.addProperty("version", new VersionImpl(

+									version));

+						}

+						resource.addCapability(capability);

+						parser.nextTag();

+					}

+					else {

+						String value = parser.nextText().trim();

+						if (key.equals("bundle-sourceurl"))

+							resource.setSource(new URL(value));

+						else if (key.equals("bundle-docurl"))

+							resource.setDocumentation(new URL(value));

+						else if (key.equals("bundle-updatelocation"))

+							resource.setURL(new URL(value));

+						else if (key.equals("bundle-description"))

+							resource.setDescription(value);

+						else if (key.equals("bundle-category"))

+							resource.addCategory(value);

+						else if (key.equals("bundle-name")) {

+							resource.setName(value);

+							resource.setPresentationName(value);

+						}

+						else if (key.equals("bundle-version"))

+							resource.setVersion(new VersionImpl(value));

+						else {

+							resource.put(key, value);

+						}

+					}

+				}

+				resources.add(resource);

+				parser.require(XmlPullParser.END_TAG, null, "bundle");

+			}

+			else if (parser.getName().equals("repository")) {

+				parser.require(XmlPullParser.START_TAG, null, "repository");

+				while (parser.nextTag() == XmlPullParser.START_TAG) {

+					String tag = parser.getName();

+					if (tag.equals("name")) {

+						String name = parser.nextText();

+						if (this.name == null)

+							this.name = name.trim();

+					}

+					else if (tag.equals("url"))

+						parser.nextText().trim();

+					else if (tag.equals("date"))

+						parser.nextText().trim();

+					else if (tag.equals("extern-repositories")) {

+						parser.require(XmlPullParser.START_TAG, null,

+								"extern-repositories");

+						while (parser.nextTag() == XmlPullParser.START_TAG) {

+							if (parser.getName().equals("url"))

+								parseDocument(new URL(parser.nextText().trim()));

+							else

+								throw new IllegalArgumentException(

+										"Invalid tag in repository while parsing extern repositories: "

+												+ url + " " + parser.getName());

+						}

+						parser.require(XmlPullParser.END_TAG, null,

+								"extern-repositories");

+					}

+					else

+						throw new IllegalArgumentException(

+								"Invalid tag in repository: " + url + " "

+										+ parser.getName());

+				}

+				parser.require(XmlPullParser.END_TAG, null, "repository");

+			}

+			else if (parser.getName().equals("dtd-version")) {

+				parser.nextText();

+			}

+			else

+				throw new IllegalArgumentException(

+						"Invalid tag in repository: " + url + " "

+								+ parser.getName());

+		}

+		parser.require(XmlPullParser.END_TAG, null, "bundles");

+	}

+

+	/**

+	 * We have a referral to another repository. Just create another parser and

+	 * read it inline.

+	 * 

+	 * @param parser

+	 */

+	void referral(XmlPullParser parser) {

+		// TODO handle depth!

+		try {

+			parser.require(XmlPullParser.START_TAG, null, "referral");

+			// String depth = parser.getAttributeValue(null, "depth");

+			String path = parser.getAttributeValue(null, "url");

+			URL url = new URL(this.url, path);

+			parseDocument(url);

+			parser.next();

+			parser.require(XmlPullParser.END_TAG, null, "referral");

+		}

+		catch (Exception e) {

+			e.printStackTrace();

+		}

+	}

+

+	/**

+	 * Parse a repository document.

+	 * 

+	 * @param url

+	 * @throws IOException

+	 * @throws XmlPullParserException

+	 * @throws Exception

+	 */

+	void parseDocument(URL url) throws IOException, XmlPullParserException,

+			Exception {

+		if (!visited.contains(url)) {

+			visited.add(url);

+			try {

+				System.out.println("Visiting: " + url);

+				InputStream in = null;

+				

+				if ( url.getPath().endsWith(".zip")) {

+					ZipInputStream zin = new ZipInputStream( url.openStream() );

+					ZipEntry entry = zin.getNextEntry();

+					while ( entry != null ) {

+						if ( entry.getName().equals("repository.xml")) {

+							in = zin;

+							break;

+						}

+						entry = zin.getNextEntry();

+					}

+				} else {

+					in = url.openStream();

+				}

+				Reader reader = new InputStreamReader(in);

+				XmlPullParser parser = new KXmlParser();

+				parser.setInput(reader);

+				parseRepository(parser);

+			} catch( MalformedURLException e ) {

+				System.out.println("Cannot create connection to url");

+			}

+		}

+	}

+

+	public URL getURL() {

+		return url;

+	}

+

+	/**

+	 * @return

+	 */

+	public Collection getResourceList() {

+		return resources;

+	}

+

+	public Resource[] getResources() {

+		return (Resource[]) getResourceList().toArray(EMPTY_RESOURCE);

+	}

+

+	public String getName() {

+		return name;

+	}

+

+	public Resource getResource(String id) {

+		for (Iterator i = getResourceList().iterator(); i.hasNext();) {

+			ResourceImpl resource = (ResourceImpl) i.next();

+			if (resource.getId().equals(id))

+				return resource;

+		}

+		return null;

+	}

+

+	public long getLastModified() {

+		return lastModified;

+	}

+

+	public int getRanking() {

+		return ranking;

+	}

+

+	public void setRanking(int ranking) {

+		this.ranking = ranking;

+	}

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/RequirementImpl.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/RequirementImpl.java
new file mode 100644
index 0000000..97032a7
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/RequirementImpl.java
@@ -0,0 +1,160 @@
+package org.osgi.impl.bundle.obr.resource;

+

+import org.osgi.service.obr.*;

+import org.xmlpull.v1.XmlPullParser;

+

+

+

+/**

+ * Implements the Requirement interface.

+ * 

+ * 

+ * @version $Revision: 1.8 $

+ */

+public class RequirementImpl implements Requirement {

+	int		id;

+	String	name;

+	String	filter="()";

+	FilterImpl	_filter;

+	String	comment;

+	boolean optional;

+	boolean multiple;

+	boolean extend;

+	

+	/**

+	 * Create a requirement with the given name.

+	 * 

+	 * @param name

+	 */

+	public RequirementImpl(String name) {

+		this.name = name;

+	}

+

+

+	/**

+	 * Parse the requirement from the pull parser.

+	 * 

+	 * @param parser

+	 * @throws Exception

+	 */

+	public RequirementImpl(XmlPullParser parser) throws Exception {

+		parser.require(XmlPullParser.START_TAG, null, null );

+		name = parser.getAttributeValue(null, "name");

+		filter = parser.getAttributeValue(null, "filter");

+		

+		String opt = parser.getAttributeValue(null,"optional");

+		String mul = parser.getAttributeValue(null,"multiple");

+		String ext = parser.getAttributeValue(null,"extend");

+		optional = "true".equalsIgnoreCase(opt);

+		multiple = "true".equalsIgnoreCase(mul);

+		extend = "true".equalsIgnoreCase(ext);

+		

+		

+		StringBuffer sb = new StringBuffer();

+		while ( parser.next() == XmlPullParser.TEXT ) {

+			sb.append( parser.getText() );

+		}

+		if ( sb.length() > 0 )

+			setComment(sb.toString().trim());

+			

+		parser.require(XmlPullParser.END_TAG, null, null );

+	}

+

+	public void setFilter(String filter) {

+		this.filter = filter;

+		_filter= null;

+	}

+

+	public String getFilter() {

+		return filter;

+	}

+

+	public Tag toXML(String name) {

+		Tag tag = toXML(this);

+		tag.rename(name);

+		return tag;

+	}

+

+

+	public String getName() {

+		return name;

+	}

+

+	public boolean isSatisfied(Capability capability) {

+		if (_filter == null)

+			_filter = new FilterImpl(filter);

+

+		boolean result = _filter.match(capability.getProperties());

+		return result;

+	}

+

+	public String toString() {

+		return name + " " + filter;

+	}

+

+

+	public String getComment() {

+		return comment;

+	}

+

+

+	public void setComment(String comment) {

+		this.comment=comment;

+	}

+

+

+	public static Tag toXML(Requirement requirement) {

+		Tag req = new Tag("require");

+		req.addAttribute("name", requirement.getName());

+		req.addAttribute("filter", requirement.getFilter());

+		

+		req.addAttribute("optional", requirement.isOptional()+"");

+		req.addAttribute("multiple", requirement.isMultiple()+"");

+		req.addAttribute("extend", requirement.isExtend()+"");

+		

+		if ( requirement.getComment() != null )

+			req.addContent(requirement.getComment());

+		

+		return req;

+	}

+

+

+	public boolean isMultiple() {

+		return multiple;

+	}

+

+

+	public boolean isOptional() {

+		return optional;

+	}

+

+

+	public void setOptional(boolean b) {

+		optional = b;

+	}

+

+	public void setMultiple(boolean b) {

+		multiple = b;

+	}

+

+

+	public boolean equals(Object o) {

+		if ( ! (o instanceof Requirement) )

+			return false;

+		

+		Requirement r2 = (Requirement)o;

+		return filter.equals(r2.getFilter()) && name.equals(r2.getName()); 

+	}

+	

+	public int hashCode() {

+		return filter.hashCode() ^ name.hashCode();

+	}

+	

+	public boolean isExtend() {

+		return extend;

+	}

+	

+	public void setExtend(boolean extend) {

+		this.extend = extend;

+	}

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/ResourceImpl.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/ResourceImpl.java
new file mode 100644
index 0000000..beedc0d
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/ResourceImpl.java
@@ -0,0 +1,351 @@
+package org.osgi.impl.bundle.obr.resource;

+

+import java.io.File;

+import java.net.URL;

+import java.util.*;

+

+import org.osgi.framework.Version;

+import org.osgi.service.obr.*;

+import org.xmlpull.v1.XmlPullParser;

+

+public class ResourceImpl implements Resource {

+	List			capabilities	= new ArrayList();

+	List			requirements	= new ArrayList();

+	URL				url;

+	String			symbolicName;

+	VersionImpl		version;

+	List			categories		= new ArrayList();

+	long			size			= -1;

+	String			id;

+	static int		ID				= 1;

+	Map				map				= new HashMap();

+	RepositoryImpl	repository;

+	String			presentationName;

+	File			file;

+

+	{

+		id = Integer.toString(ID++);

+	}

+

+	public ResourceImpl(RepositoryImpl repository, String name,

+			VersionImpl version) {

+		this.version = version;

+		this.symbolicName = name;

+		this.repository = repository;

+	}

+

+	public ResourceImpl(RepositoryImpl repository, XmlPullParser parser)

+			throws Exception {

+		this.repository = repository;

+		parser.require(XmlPullParser.START_TAG, null, "resource");

+		symbolicName = parser.getAttributeValue(null, "symbolicname");

+		if (symbolicName == null)

+			System.err.println("Hey, no symb name! "

+					+ parser.getAttributeValue(null, "uri"));

+

+		map.put(SYMBOLIC_NAME, symbolicName);

+		presentationName = parser.getAttributeValue(null, PRESENTATION_NAME);

+		if (presentationName != null)

+			map.put(PRESENTATION_NAME, presentationName);

+		String v = parser.getAttributeValue(null, "version");

+		if (v == null)

+			setVersion(new VersionImpl("0"));

+		else

+			setVersion(new VersionImpl(v));

+

+		setURL(toURL(parser.getAttributeValue(null, "uri")));

+

+		while (parser.nextTag() == XmlPullParser.START_TAG) {

+			if (parser.getName().equals("category")) {

+				categories.add(parser.getAttributeValue(null, "id").trim());

+			}

+			else if (parser.getName().equals("require"))

+				addRequirement(new RequirementImpl(parser));

+			else if (parser.getName().equals("capability"))

+				addCapability(new CapabilityImpl(parser));

+			else {

+				String text = parser.nextText();

+				if (text != null)

+					map.put(parser.getName(), text.trim());

+			}

+			parser.next();

+		}

+		parser.require(XmlPullParser.END_TAG, null, "resource");

+	}

+

+	public ResourceImpl(RepositoryImpl impl) {

+		this.repository = impl;

+	}

+

+	private URL toURL(String attributeValue) throws Exception {

+		if (attributeValue == null)

+			return null;

+

+		return new URL(repository.getURL(), attributeValue);

+	}

+

+	public void addCategory(String category) {

+		categories.add(category);

+	}

+

+	public void addCapability(CapabilityImpl capability) {

+		if (capability != null)

+			capabilities.add(capability);

+	}

+

+	public void addRequirement(RequirementImpl requirement) {

+		if (requirement != null)

+			requirements.add(requirement);

+	}

+

+	public void setLicense(URL license) {

+		if (license != null)

+			map.put(LICENSE_URL, license);

+	}

+

+	public String getDescription() {

+		return (String) map.get(DESCRIPTION);

+	}

+

+	public void setDescription(String description) {

+		if (description != null)

+			map.put(DESCRIPTION, description);

+	}

+

+	public Capability[] getCapabilities() {

+		return (Capability[]) capabilities.toArray(new Capability[capabilities

+				.size()]);

+	}

+

+	public URL getLicense() {

+		return (URL) map.get(LICENSE_URL);

+	}

+

+	public String getSymbolicName() {

+		return symbolicName;

+	}

+

+	public Requirement[] getRequirements() {

+		return (Requirement[]) requirements

+				.toArray(new Requirement[requirements.size()]);

+	}

+

+	public Tag toXML() {

+		return toXML(this );

+	}

+

+	public static Tag toXML(Resource resource) {

+		return toXML(resource,true);

+	}

+

+	public static Tag toXML(Resource resource, boolean relative ) {

+		Tag meta = new Tag("resource");

+		URL url = resource.getURL();

+		String urlString = url.toExternalForm();

+		

+		if ( relative )

+			urlString = makeRelative(resource.getRepository().getURL(), url);

+		

+		meta.addAttribute("uri", urlString );

+		meta.addAttribute(SYMBOLIC_NAME, resource.getSymbolicName());

+		if (resource.getPresentationName() != null)

+			meta

+					.addAttribute(PRESENTATION_NAME, resource

+							.getPresentationName());

+		meta.addAttribute(VERSION, resource.getVersion().toString());

+		meta.addAttribute("id", resource.getId());

+		Map map = new TreeMap(resource.getProperties());

+		for (int i = 0; i < Resource.KEYS.length; i++) {

+			String key = KEYS[i];

+			if (!(key.equals(URL) || key.equals(SYMBOLIC_NAME) || key

+					.equals(VERSION) || key.equals(PRESENTATION_NAME))) {

+				Object value = map.get(KEYS[i]);

+				if (value != null) {

+					if (value instanceof URL)

+						value = makeRelative(resource.getRepository().getURL(),(URL) value);

+					meta.addContent(new Tag(key, value.toString()));

+				}

+			}

+		}

+

+		String[] categories = resource.getCategories();

+		for (int i = 0; i < categories.length; i++) {

+			String category = categories[i];

+			meta.addContent(new Tag("category", new String[] {"id",

+					category.toLowerCase()}));

+		}

+

+		Capability[] capabilities = resource.getCapabilities();

+		for (int i = 0; i < capabilities.length; i++) {

+			meta.addContent(CapabilityImpl.toXML(capabilities[i]));

+		}

+

+		Requirement[] requirements = resource.getRequirements();

+		for (int i = 0; i < requirements.length; i++) {

+			meta.addContent(RequirementImpl.toXML(requirements[i]));

+		}

+		return meta;

+	}

+

+	public URL getURL() {

+		return url;

+	}

+

+	static String makeRelative(URL repository, URL url) {

+		try {

+			if (repository != null) {

+				String a = url.toExternalForm();

+				String b = repository.toExternalForm();

+				int index = b.lastIndexOf('/');

+				if ( index > 0 )

+					b = b.substring(0,index+1);

+				if (a.startsWith(b))

+					return a.substring(b.length());

+			}

+		}

+		catch (Exception e) {

+			// Ignore

+		}

+		return url.toExternalForm();

+	}

+

+	public void setURL(URL url) {

+		this.url = url;

+		if (url != null)

+			map.put(URL, url);

+	}

+

+	public String getCopyright() {

+		return (String) map.get(COPYRIGHT);

+	}

+

+	public Version getVersion() {

+		if (version == null)

+			version = new VersionImpl("0");

+		return version;

+	}

+

+	void setVersion(VersionImpl version) {

+		if (version == null)

+			this.version = new VersionImpl("0");

+		else

+			this.version = version;

+	}

+

+	public void setCopyright(String copyright) {

+		if (copyright != null)

+			map.put(COPYRIGHT, copyright);

+	}

+

+	public URL getDocumentation() {

+		return (URL) map.get(DOCUMENTATION_URL);

+	}

+

+	public void setDocumentation(URL documentation) {

+		if (documentation != null)

+			map.put(DOCUMENTATION_URL, documentation);

+	}

+

+	public URL getSource() {

+		return (URL) map.get(SOURCE_URL);

+	}

+

+	public void setSource(URL source) {

+		if (source != null)

+			map.put(SOURCE_URL, source);

+	}

+

+	public boolean satisfies(RequirementImpl requirement) {

+		for (Iterator i = capabilities.iterator(); i.hasNext();) {

+			CapabilityImpl capability = (CapabilityImpl) i.next();

+			if (requirement.isSatisfied(capability))

+				return true;

+		}

+		return false;

+	}

+

+	public String toString() {

+		return symbolicName + "-" + version;

+	}

+

+	public long getSize() {

+		return size;

+	}

+

+	public void setSize(long size) {

+		this.size = size;

+		map.put(SIZE, new Long(size));

+	}

+

+	public Collection getRequirementList() {

+		return requirements;

+	}

+

+	public Collection getCapabilityList() {

+		return capabilities;

+	}

+

+	public int hashCode() {

+		return symbolicName.hashCode() ^ version.hashCode();

+	}

+

+	public boolean equals(Object o) {

+		try {

+			ResourceImpl other = (ResourceImpl) o;

+			return symbolicName.equals(other.symbolicName)

+					&& version.equals(other.version);

+		}

+		catch (ClassCastException e) {

+			return false;

+		}

+	}

+

+	public String[] getCategories() {

+		return (String[]) categories.toArray(new String[categories.size()]);

+	}

+

+	public Map getProperties() {

+		return Collections.unmodifiableMap(map);

+	}

+

+	public String getId() {

+		return id;

+	}

+

+	public Repository getRepository() {

+		return repository;

+	}

+

+	void setName(String value) {

+		this.symbolicName = value;

+	}

+

+	void put(String name, Object value) {

+		map.put(name, value);

+	}

+

+	public void setPresentationName(String name) {

+		presentationName = name;

+		if (name != null)

+			map.put(PRESENTATION_NAME, name);

+	}

+

+	public String getPresentationName() {

+		return presentationName;

+	}

+

+	public void setFile(File zipFile) {

+		file = zipFile;

+	}

+

+	public Set getExtendList() {

+		Set set = new HashSet();

+		for (Iterator i = requirements.iterator(); i.hasNext();) {

+			RequirementImpl	impl = (RequirementImpl) i.next();

+			if ( impl.isExtend())

+				set.add(impl);

+		}

+		return set;

+	}

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/StringSet.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/StringSet.java
new file mode 100644
index 0000000..d69958d
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/StringSet.java
@@ -0,0 +1,39 @@
+/*

+ * $Header: /cvshome/bundles/bundles.obr/src/bundles/obr/resource/StringSet.java,v 1.1 2006/01/03 16:22:46 pkriens Exp $

+ * 

+ * Copyright (c) The OSGi Alliance (2006). All Rights Reserved.

+ * 

+ * Implementation of certain elements of the OSGi Specification may be subject

+ * to third party intellectual property rights, including without limitation,

+ * patent rights (such a third party may or may not be a member of the OSGi

+ * Alliance). The OSGi Alliance is not responsible and shall not be held

+ * responsible in any manner for identifying or failing to identify any or all

+ * such third party intellectual property rights.

+ * 

+ * This document and the information contained herein are provided on an "AS IS"

+ * basis and THE OSGI ALLIANCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,

+ * INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION

+ * HEREIN WILL NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF

+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL THE

+ * OSGI ALLIANCE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF

+ * USE OF DATA, INTERRUPTION OF BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR

+ * EXEMPLARY, INCIDENTIAL, PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN

+ * CONNECTION WITH THIS DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF

+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE.

+ * 

+ * All Company, brand and product names may be trademarks that are the sole

+ * property of their respective owners. All rights reserved.

+ */

+

+package org.osgi.impl.bundle.obr.resource;

+

+import java.util.*;

+

+public class StringSet extends HashSet {

+

+	public StringSet(String set) {

+		StringTokenizer st = new StringTokenizer(set, ",");

+		while (st.hasMoreTokens())

+			add(st.nextToken().trim());

+	}

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Tag.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Tag.java
new file mode 100644
index 0000000..e077969
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/Tag.java
@@ -0,0 +1,471 @@
+package org.osgi.impl.bundle.obr.resource;

+

+import java.io.*;

+import java.text.SimpleDateFormat;

+import java.util.*;

+

+/**

+ * The Tag class represents a minimal XML tree. It consist of a named element

+ * with a hashtable of named attributes. Methods are provided to walk the tree

+ * and get its constituents. The content of a Tag is a list that contains String

+ * objects or other Tag objects.

+ */

+public class Tag {

+	Tag						parent;

+	String					name;

+	Map						attributes	= new TreeMap();

+	Vector					content		= new Vector();

+

+	static SimpleDateFormat	format		= new SimpleDateFormat(

+												"yyyyMMddhhmmss.SSS");

+

+	/**

+	 * Construct a new Tag with a name.

+	 */

+	public Tag(String name) {

+		this.name = name;

+	}

+

+	/**

+	 * Construct a new Tag with a name.

+	 */

+	public Tag(String name, Map attributes) {

+		this.name = name;

+		this.attributes = attributes;

+	}

+

+	/**

+	 * Construct a new Tag with a name and a set of attributes. The attributes

+	 * are given as ( name, value ) ...

+	 */

+	public Tag(String name, String[] attributes) {

+		this.name = name;

+		for (int i = 0; i < attributes.length; i += 2)

+			addAttribute(attributes[i], attributes[i + 1]);

+	}

+

+	/**

+	 * Construct a new Tag with a single string as content.

+	 */

+	public Tag(String name, String content) {

+		this.name = name;

+		addContent(content);

+	}

+

+	/**

+	 * Add a new attribute.

+	 */

+	public void addAttribute(String key, String value) {

+		attributes.put(key, value);

+	}

+

+	/**

+	 * Add a new attribute.

+	 */

+	public void addAttribute(String key, Object value) {

+		if (value == null)

+			return;

+		attributes.put(key, value.toString());

+	}

+

+	/**

+	 * Add a new attribute.

+	 */

+	public void addAttribute(String key, int value) {

+		attributes.put(key, Integer.toString(value));

+	}

+

+	/**

+	 * Add a new date attribute. The date is formatted as the SimpleDateFormat

+	 * describes at the top of this class.

+	 */

+	public void addAttribute(String key, Date value) {

+		attributes.put(key, format.format(value));

+	}

+

+	/**

+	 * Add a new content string.

+	 */

+	public void addContent(String string) {

+		content.addElement(string);

+	}

+

+	/**

+	 * Add a new content tag.

+	 */

+	public void addContent(Tag tag) {

+		content.addElement(tag);

+		tag.parent = this;

+	}

+

+	/**

+	 * Return the name of the tag.

+	 */

+	public String getName() {

+		return name;

+	}

+

+	/**

+	 * Return the attribute value.

+	 */

+	public String getAttribute(String key) {

+		return (String) attributes.get(key);

+	}

+

+	/**

+	 * Return the attribute value or a default if not defined.

+	 */

+	public String getAttribute(String key, String deflt) {

+		String answer = getAttribute(key);

+		return answer == null ? deflt : answer;

+	}

+

+	/**

+	 * Answer the attributes as a Dictionary object.

+	 */

+	public Map getAttributes() {

+		return attributes;

+	}

+

+	/**

+	 * Return the contents.

+	 */

+	public Vector getContents() {

+		return content;

+	}

+

+	/**

+	 * Return a string representation of this Tag and all its children

+	 * recursively.

+	 */

+	public String toString() {

+		StringWriter sw = new StringWriter();

+		print(0, new PrintWriter(sw));

+		return sw.toString();

+	}

+

+	/**

+	 * Return only the tags of the first level of descendants that match the

+	 * name.

+	 */

+	public Vector getContents(String tag) {

+		Vector out = new Vector();

+		for (Enumeration e = content.elements(); e.hasMoreElements();) {

+			Object o = e.nextElement();

+			if (o instanceof Tag && ((Tag) o).getName().equals(tag))

+				out.addElement(o);

+		}

+		return out;

+	}

+

+	/**

+	 * Return the whole contents as a String (no tag info and attributes).

+	 */

+	public String getContentsAsString() {

+		StringBuffer sb = new StringBuffer();

+		getContentsAsString(sb);

+		return sb.toString();

+	}

+

+	/**

+	 * convenient method to get the contents in a StringBuffer.

+	 */

+	public void getContentsAsString(StringBuffer sb) {

+		for (Enumeration e = content.elements(); e.hasMoreElements();) {

+			Object o = e.nextElement();

+			if (o instanceof Tag)

+				((Tag) o).getContentsAsString(sb);

+			else

+				sb.append(o.toString());

+		}

+	}

+

+	/**

+	 * Print the tag formatted to a PrintWriter.

+	 */

+	public void print(int indent, PrintWriter pw) {

+		pw.print("\n");

+		spaces(pw, indent);

+		pw.print('<');

+		pw.print(name);

+

+		for (Iterator e = attributes.keySet().iterator(); e.hasNext();) {

+			String key = (String) e.next();

+			String value = escape((String) attributes.get(key));

+			pw.print(' ');

+			pw.print(key);

+			pw.print("=");

+			String quote = "'";

+			if (value.indexOf(quote) >= 0)

+				quote = "\"";

+			pw.print(quote);

+			pw.print(value);

+			pw.print(quote);

+		}

+

+		if (content.size() == 0)

+			pw.print('/');

+		else {

+			pw.print('>');

+			for (Enumeration e = content.elements(); e.hasMoreElements();) {

+				Object content = e.nextElement();

+				if (content instanceof String) {

+					formatted(pw, indent + 2, 60, escape((String) content));

+				}

+				else if (content instanceof Tag) {

+					Tag tag = (Tag) content;

+					tag.print(indent + 2, pw);

+				}

+			}

+			pw.print("\n");

+			spaces(pw, indent);

+			pw.print("</");

+			pw.print(name);

+		}

+		pw.print('>');

+	}

+

+	/**

+	 * Convenience method to print a string nicely and does character conversion

+	 * to entities.

+	 */

+	void formatted(PrintWriter pw, int left, int width, String s) {

+		int pos = width + 1;

+		s = s.trim();

+

+		for (int i = 0; i < s.length(); i++) {

+			char c = s.charAt(i);

+			if (i == 0 || (Character.isWhitespace(c) && pos > width - 3)) {

+				pw.print("\n");

+				spaces(pw, left);

+				pos = 0;

+			}

+			switch (c) {

+				case '<' :

+					pw.print("&lt;");

+					pos += 4;

+					break;

+				case '>' :

+					pw.print("&gt;");

+					pos += 4;

+					break;

+				case '&' :

+					pw.print("&amp;");

+					pos += 5;

+					break;

+				default :

+					pw.print(c);

+					pos++;

+					break;

+			}

+

+		}

+	}

+

+	/**

+	 * Escape a string, do entity conversion.

+	 */

+	String escape(String s) {

+		if  ( s == null )

+			System.out.println("??");

+		

+		StringBuffer sb = new StringBuffer();

+		for (int i = 0; i < s.length(); i++) {

+			char c = s.charAt(i);

+			switch (c) {

+				case '<' :

+					sb.append("&lt;");

+					break;

+				case '>' :

+					sb.append("&gt;");

+					break;

+				case '&' :

+					sb.append("&amp;");

+					break;

+				default :

+					sb.append(c);

+					break;

+			}

+		}

+		return sb.toString();

+	}

+

+	/**

+	 * Make spaces.

+	 */

+	void spaces(PrintWriter pw, int n) {

+		while (n-- > 0)

+			pw.print(' ');

+	}

+

+	/**

+	 * root/preferences/native/os

+	 */

+	public Tag[] select(String path) {

+		return select(path, (Tag) null);

+	}

+

+	public Tag[] select(String path, Tag mapping) {

+		Vector v = new Vector();

+		select(path, v, mapping);

+		Tag[] result = new Tag[v.size()];

+		v.copyInto(result);

+		return result;

+	}

+

+	void select(String path, Vector results, Tag mapping) {

+		if (path.startsWith("//")) {

+			int i = path.indexOf('/', 2);

+			String name = path.substring(2, i < 0 ? path.length() : i);

+

+			for (Enumeration e = content.elements(); e.hasMoreElements();) {

+				Object o = e.nextElement();

+				if (o instanceof Tag) {

+					Tag child = (Tag) o;

+					if (match(name, child, mapping))

+						results.add(child);

+					child.select(path, results, mapping);

+				}

+

+			}

+			return;

+		}

+

+		if (path.length() == 0) {

+			results.addElement(this);

+			return;

+		}

+

+		int i = path.indexOf("/");

+		String elementName = path;

+		String remainder = "";

+		if (i > 0) {

+			elementName = path.substring(0, i);

+			remainder = path.substring(i + 1);

+		}

+

+		for (Enumeration e = content.elements(); e.hasMoreElements();) {

+			Object o = e.nextElement();

+			if (o instanceof Tag) {

+				Tag child = (Tag) o;

+				if (child.getName().equals(elementName)

+						|| elementName.equals("*"))

+					child.select(remainder, results, mapping);

+			}

+		}

+	}

+

+	public boolean match(String search, Tag child, Tag mapping) {

+		String target = child.getName();

+		String sn = null;

+		String tn = null;

+

+		if (search.equals("*"))

+			return true;

+

+		int s = search.indexOf(':');

+		if (s > 0) {

+			sn = search.substring(0, s);

+			search = search.substring(s + 1);

+		}

+		int t = target.indexOf(':');

+		if (t > 0) {

+			tn = target.substring(0, t);

+			target = target.substring(t + 1);

+		}

+

+		if (!search.equals(target)) // different tag names

+			return false;

+

+		if (mapping == null) {

+			return tn == sn || (sn != null && sn.equals(tn));

+		}

+		else {

+			String suri = sn == null ? mapping.getAttribute("xmlns") : mapping

+					.getAttribute("xmlns:" + sn);

+			String turi = tn == null ? child.findRecursiveAttribute("xmlns")

+					: child.findRecursiveAttribute("xmlns:" + tn);

+			return turi == suri

+					|| (turi != null && suri != null && turi.equals(suri));

+		}

+	}

+

+	public String getString(String path) {

+		String attribute = null;

+		int index = path.indexOf("@");

+		if (index >= 0) {

+			// attribute

+			attribute = path.substring(index + 1);

+

+			if (index > 0) {

+				// prefix path

+				path = path.substring(index - 1); // skip -1

+			}

+			else

+				path = "";

+		}

+		Tag tags[] = select(path);

+		StringBuffer sb = new StringBuffer();

+		for (int i = 0; i < tags.length; i++) {

+			if (attribute == null)

+				tags[i].getContentsAsString(sb);

+			else

+				sb.append(tags[i].getAttribute(attribute));

+		}

+		return sb.toString();

+	}

+

+	public String getStringContent() {

+		StringBuffer sb = new StringBuffer();

+		for (Enumeration e = content.elements(); e.hasMoreElements();) {

+			Object c = e.nextElement();

+			if (!(c instanceof Tag))

+				sb.append(c);

+		}

+		return sb.toString();

+	}

+

+	public String getNameSpace() {

+		return getNameSpace(name);

+	}

+

+	public String getNameSpace(String name) {

+		int index = name.indexOf(':');

+		if (index > 0) {

+			String ns = name.substring(0, index);

+			return findRecursiveAttribute("xmlns:" + ns);

+		}

+		else

+			return findRecursiveAttribute("xmlns");

+	}

+

+	public String findRecursiveAttribute(String name) {

+		String value = getAttribute(name);

+		if (value != null)

+			return value;

+		if (parent != null)

+			return parent.findRecursiveAttribute(name);

+		return null;

+	}

+

+	public String getLocalName() {

+		int index = name.indexOf(':');

+		if (index <= 0)

+			return name;

+

+		return name.substring(index + 1);

+	}

+

+	public void rename(String string) {

+		name = string;

+	}

+

+

+	public static void convert( Collection c, String type, Tag parent ) {

+		for ( Iterator i=c.iterator(); i.hasNext(); ) {

+			Map	map = (Map) i.next();

+			parent.addContent( new Tag(type, map) );

+		}

+	}

+

+}

diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/VersionImpl.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/VersionImpl.java
new file mode 100644
index 0000000..8071166
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/VersionImpl.java
@@ -0,0 +1,121 @@
+/*

+ * $Header: /cvshome/bundles/bundles.obr/src/bundles/obr/resource/VersionImpl.java,v 1.3 2006/02/15 16:36:57 pkriens Exp $

+ * 

+ * Copyright (c) OSGi Alliance (2004, 2005). All Rights Reserved.

+ * 

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v1.0 which accompanies this 

+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html.

+ */

+

+package org.osgi.impl.bundle.obr.resource;

+

+import org.osgi.framework.Version;

+

+/**

+ * Version identifier for bundles and packages.

+ * 

+ * <p>

+ * Version identifiers have four components.

+ * <ol>

+ * <li>Major version. A non-negative integer.</li>

+ * <li>Minor version. A non-negative integer.</li>

+ * <li>Micro version. A non-negative integer.</li>

+ * <li>Qualifier. A text string. See <code>Version(String)</code> for the

+ * format of the qualifier string.</li>

+ * </ol>

+ * 

+ * <p>

+ * <code>Version</code> objects are immutable.

+ * 

+ * @version $Revision: 1.3 $

+ * @since 1.3

+ */

+

+public class VersionImpl extends Version {

+	VersionRange	range;

+

+	/**

+	 * Creates a version identifier from the specified numerical components.

+	 * 

+	 * <p>

+	 * The qualifier is set to the empty string.

+	 * 

+	 * @param major Major component of the version identifier.

+	 * @param minor Minor component of the version identifier.

+	 * @param micro Micro component of the version identifier.

+	 * @throws IllegalArgumentException If the numerical components are

+	 *         negative.

+	 */

+	public VersionImpl(int major, int minor, int micro) {

+		this(major, minor, micro, null);

+	}

+

+	/**

+	 * Creates a version identifier from the specifed components.

+	 * 

+	 * @param major Major component of the version identifier.

+	 * @param minor Minor component of the version identifier.

+	 * @param micro Micro component of the version identifier.

+	 * @param qualifier Qualifier component of the version identifier. If

+	 *        <code>null</code> is specified, then the qualifier will be set

+	 *        to the empty string.

+	 * @throws IllegalArgumentException If the numerical components are negative

+	 *         or the qualifier string is invalid.

+	 */

+	public VersionImpl(int major, int minor, int micro, String qualifier) {

+		super(major, minor, micro, qualifier);

+	}

+

+	// TODO Ugly!

+	public VersionImpl(String string) {

+		super(

+				string.indexOf("[") >= 0 || string.indexOf("(") >= 0 ? new VersionRange(

+						string).getMinimum().toString()

+						: string);

+		if ( string.indexOf("[") >= 0 || string.indexOf("(") >= 0 )

+			range = new VersionRange(string);

+	}

+

+	VersionRange getRange() {

+		return range;

+	}

+	/**

+	 * 	this	other		0		1		-1

+	 * 	

+	 * @param o

+	 * @return

+	 * @see org.osgi.framework.Version#compareTo(java.lang.Object)

+	 */

+	public int compareTo(Object o) {

+		if ( o instanceof VersionImpl ) {

+			VersionImpl	other = (VersionImpl) o;

+			int cs = 0;

+			if ( range != null )

+				cs++;

+			if ( other.range!=null)

+				cs+=2;

+			switch (cs ) {

+				case 0:	// V1 V2

+					return super.compareTo(other);

+					

+				case 1:	// R1 V2

+					return range.isIncluded(other) ? 0 : 1;

+					

+				case 2:	// V1 R2

+					return other.range.isIncluded(this) ? 0 : 1;

+	

+					// TODO experimental

+				case 3:	// R1 R2

+					return range.isIncluded(other.range.getMinimum()) && range.isIncluded(other.range.getMaximum()) ? 0 : 1;			

+			}

+			return -1;

+		} else {

+			return super.compareTo(o);

+		}

+	}

+

+	public boolean equals(Object other) {

+		return compareTo(other) == 0;

+	}

+}
\ No newline at end of file
diff --git a/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/VersionRange.java b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/VersionRange.java
new file mode 100644
index 0000000..820e5ce
--- /dev/null
+++ b/maven-obr-plugin/src/main/java/org/osgi/impl/bundle/obr/resource/VersionRange.java
@@ -0,0 +1,166 @@
+/*******************************************************************************

+ * Copyright (c) 2003, 2005 IBM Corporation and others.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * which accompanies this distribution, and is available at

+ * http://www.eclipse.org/legal/epl-v10.html

+ * 

+ * Contributors:

+ *     IBM Corporation - initial API and implementation

+ *******************************************************************************/

+package org.osgi.impl.bundle.obr.resource;

+

+import org.osgi.framework.Version;

+

+/**

+ * This class represents a version range.

+ * @since 3.1

+ */

+public class VersionRange {

+	private static final Version versionMax = new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);

+	/**

+	 * An empty version

+	 */

+	public static final VersionRange emptyRange = new VersionRange(null);

+

+	private Version minVersion;

+	private boolean includeMin; 

+	private Version maxVersion;

+	private boolean includeMax;

+

+	/**

+	 * Constructs a VersionRange with the specified minVersion and maxVersion.

+	 * @param minVersion the minimum version of the range

+	 * @param maxVersion the maximum version of the range

+	 */

+	public VersionRange(Version minVersion, boolean includeMin, Version maxVersion, boolean includeMax) {

+		this.minVersion = minVersion;

+		this.includeMin = includeMin;

+		this.maxVersion = maxVersion;

+		this.includeMax = includeMax;

+	}

+

+	/**

+	 * Constructs a VersionRange from the given versionRange String.

+	 * @param versionRange a version range String that specifies a range of

+	 * versions.

+	 */

+	public VersionRange(String versionRange) {

+		if (versionRange == null || versionRange.length() == 0) {

+			minVersion = Version.emptyVersion;

+			includeMin = true;

+			maxVersion = VersionRange.versionMax;

+			includeMax = true;

+			return;

+		}

+		versionRange = versionRange.trim();

+		if (versionRange.charAt(0) == '[' || versionRange.charAt(0) == '(') {

+			int comma = versionRange.indexOf(',');

+			if (comma < 0)

+				throw new IllegalArgumentException();

+			char last = versionRange.charAt(versionRange.length() - 1);

+			if (last != ']' && last != ')')

+				throw new IllegalArgumentException();

+

+			minVersion = Version.parseVersion(versionRange.substring(1, comma).trim());

+			includeMin = versionRange.charAt(0) == '[';

+			maxVersion = Version.parseVersion(versionRange.substring(comma + 1, versionRange.length() - 1).trim());

+			includeMax = last == ']';

+		} else {

+			minVersion = Version.parseVersion(versionRange.trim());

+			includeMin = true;

+			maxVersion = VersionRange.versionMax;

+			includeMax = true;

+		}

+	}

+

+	/**

+	 * Returns the minimum Version of this VersionRange

+	 * @return the minimum Version of this VersionRange

+	 */

+	public Version getMinimum() {

+		return minVersion;

+	}

+

+	/**

+	 * Indicates if the minimum version is included in the version range.

+	 * @return true if the minimum version is included in the version range;

+	 * otherwise false is returned

+	 */

+	public boolean getIncludeMinimum() {

+		return includeMin;

+	}

+

+	/**

+	 * Returns the maximum Version of this VersionRange

+	 * @return the maximum Version of this VersionRange

+	 */

+	public Version getMaximum() {

+		return maxVersion;

+	}

+

+	/**

+	 * Indicates if the maximum version is included in the version range.

+	 * @return true if the maximum version is included in the version range;

+	 * otherwise false is returned

+	 */

+	public boolean getIncludeMaximum() {

+		return includeMax;

+	}

+

+	/**

+	 * Returns whether the given version is included in this VersionRange.

+	 * This will depend on the minimum and maximum versions of this VersionRange

+	 * and the given version.

+	 * 

+	 * @param version a version to be tested for inclusion in this VersionRange. 

+	 * (may be <code>null</code>)

+	 * @return <code>true</code> if the version is include, 

+	 * <code>false</code> otherwise 

+	 */

+	public boolean isIncluded(Version version) {

+		Version minRequired = getMinimum();

+		if (minRequired == null)

+			return true;

+		if (version == null)

+			return false;

+		Version maxRequired = getMaximum() == null ? VersionRange.versionMax : getMaximum();

+		int minCheck = includeMin ? 0 : 1;

+		int maxCheck = includeMax ? 0 : -1;

+		return version.compareTo(minRequired) >= minCheck && version.compareTo(maxRequired) <= maxCheck;

+

+	}

+

+	public boolean equals(Object object) {

+		if (!(object instanceof VersionRange))

+			return false;

+		VersionRange vr = (VersionRange) object;

+		if (minVersion != null && vr.getMinimum() != null) {

+			if (minVersion.equals(vr.getMinimum()) && includeMin == vr.includeMin)

+				if (maxVersion != null && vr.getMaximum() != null) {

+					if (maxVersion.equals(vr.getMaximum()) && includeMax == vr.includeMax)

+						return true;

+				}

+				else

+					return maxVersion == vr.getMaximum();

+		}

+		else {

+			return minVersion == vr.getMinimum();

+		}

+		return false;

+	}

+

+	public String toString() {

+		if (minVersion == null)

+			return Version.emptyVersion.toString();

+		if (VersionRange.versionMax.equals(maxVersion))

+			return minVersion.toString();

+		StringBuffer result = new StringBuffer();

+		result.append(includeMin ? '[' : '(');

+		result.append(minVersion);

+		result.append(',');

+		result.append(maxVersion);

+		result.append(includeMax ? ']' : ')');

+		return result.toString();

+	}

+}

diff --git a/maven-obr-plugin/src/main/resources/SchemaObr.xsd b/maven-obr-plugin/src/main/resources/SchemaObr.xsd
new file mode 100644
index 0000000..2a99c1b
--- /dev/null
+++ b/maven-obr-plugin/src/main/resources/SchemaObr.xsd
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>

+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

+<!-- definition of simple elements -->

+

+<!-- definition of attribute  -->

+<xs:attribute name="name"     type="xs:string"/>

+<xs:attribute name="filter"   type="xs:string"/>

+<xs:attribute name="extend"   type="xs:boolean"/>

+<xs:attribute name="multiple" type="xs:boolean"/>

+<xs:attribute name="optional" type="xs:boolean"/>

+<xs:attribute name="id" type="xs:string"/>

+<xs:attribute name="n" type="xs:string"/>

+<xs:attribute name="v" type="xs:string"/>

+<xs:attribute name="t" type="xs:string"/>

+

+<xs:element name="require">

+  <xs:complexType mixed="true">

+    <xs:attribute ref="name"     use="required"/>

+    <xs:attribute ref="filter"   use="required"/>

+    <xs:attribute ref="extend"   use="required"/>

+    <xs:attribute ref="multiple" use="required"/>

+    <xs:attribute ref="optional" use="required"/>

+  </xs:complexType>

+</xs:element>

+

+<xs:element name="category">

+  <xs:complexType>

+	<xs:attribute ref="id" use="required"/>

+  </xs:complexType>

+</xs:element>

+

+<xs:element name="p">

+  <xs:complexType>

+	<xs:attribute ref="n" use="required"/>

+    <xs:attribute ref="v" use="required"/>

+    <xs:attribute ref="t" />

+  </xs:complexType>

+</xs:element>

+

+

+<xs:element name="capability">

+  <xs:complexType>

+    <xs:sequence>

+  		<xs:element ref="p" minOccurs="0" maxOccurs="unbounded"/>

+	</xs:sequence>

+  </xs:complexType>

+</xs:element>

+

+<xs:element name="resource">

+  <xs:complexType>

+    <xs:sequence>

+		  <xs:element ref="capability" minOccurs="0" maxOccurs="unbounded"/> 

+		  <xs:element ref="require" minOccurs="0" maxOccurs="unbounded"/> 

+		  <xs:element ref="category" minOccurs="0" maxOccurs="unbounded"/> 

+	</xs:sequence>

+  </xs:complexType>

+</xs:element>

+</xs:schema>
\ No newline at end of file
diff --git a/pom/pom.xml b/pom/pom.xml
index 41d5bd1..2f9c116 100644
--- a/pom/pom.xml
+++ b/pom/pom.xml
@@ -97,10 +97,9 @@
       </activation>
       <modules>
         <module>../bundleplugin</module>
+        <module>../maven-obr-plugin</module>
         <module>../scrplugin</module>
         <module>../tools/maven2/maven-osgi-plugin</module>
-        <module>../ipojo/metadata</module>
-        <module>../ipojo/manipulator</module>
         <module>../ipojo/plugin</module>
       </modules>
     </profile>
@@ -133,6 +132,7 @@
         <module>../upnp/samples/tv</module>
         <module>../upnp/samples/clock</module>
         <module>../upnp/samples/binarylight</module>
+        
         <module>../org.osgi.foundation</module>
         <module>../javax.servlet</module>
         <module>../org.osgi.core</module>
@@ -157,6 +157,8 @@
         
         <module>../ipojo/core</module>
         <module>../ipojo/arch</module>
+        <module>../ipojo/metadata</module>
+        <module>../ipojo/manipulator</module>
 
         <module>../mosgi</module> 
         <module>../jmood</module> 
diff --git a/upnp/basedriver/pom.xml b/upnp/basedriver/pom.xml
index 96d226f..2358800 100644
--- a/upnp/basedriver/pom.xml
+++ b/upnp/basedriver/pom.xml
@@ -4,7 +4,7 @@
     <groupId>org.apache.felix</groupId>

     <artifactId>felix</artifactId>

     <version>1.1.0-SNAPSHOT</version>

-    <relativePath>../pom/pom.xml</relativePath>

+    <relativePath>../../pom/pom.xml</relativePath>

   </parent>

   <modelVersion>4.0.0</modelVersion>

   <packaging>bundle</packaging>

diff --git a/upnp/extra/pom.xml b/upnp/extra/pom.xml
index 97af362..da6de3a 100644
--- a/upnp/extra/pom.xml
+++ b/upnp/extra/pom.xml
@@ -4,7 +4,7 @@
     <groupId>org.apache.felix</groupId>
     <artifactId>felix</artifactId>
     <version>1.1.0-SNAPSHOT</version>
-    <relativePath>../pom/pom.xml</relativePath>
+    <relativePath>../../pom/pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <packaging>bundle</packaging>