FELIX-2102, FELIX-2103: Improve the obr url handler to support installing bundles by their symbolicname and version

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@911615 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ObrURLStreamHandlerService.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ObrURLStreamHandlerService.java
index ea64f33..d0e6141 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ObrURLStreamHandlerService.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ObrURLStreamHandlerService.java
@@ -19,6 +19,7 @@
 package org.apache.felix.bundlerepository;
 
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLStreamHandler;
@@ -30,6 +31,8 @@
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.Version;
 import org.osgi.service.obr.RepositoryAdmin;
 import org.osgi.service.obr.Resource;
@@ -52,6 +55,14 @@
 public class ObrURLStreamHandlerService extends AbstractURLStreamHandlerService
 {
     /**
+     * Syntax for the url; to be shown on exception messages.
+     */
+    private static final String SYNTAX = "obr:<bundle-symbolic-name>['/'<bundle-version>]";
+    /**
+     * Property defining the obr update strategy
+     */
+    public static final String OBR_UPDATE_STRATEGY = "obr.update.strategy";
+    /**
      * The BundleContext to search for the bundles.
      */
     private final BundleContext m_bundleContext;
@@ -69,10 +80,6 @@
      * Default: newest
      */
     private String m_updateStrategy = "newest";
-    /**
-     * Property defining the obr update strategy
-     */
-    public static final String OBR_UPDATE_STRATEGY = "obr.update.strategy";
 
     /**
      * Constructor
@@ -119,7 +126,8 @@
 
         if (remoteURL == null)
         {
-            throw new IOException("could not resolve obr url to remote url! " + u);
+            String path = u.getPath();
+            remoteURL = getRemoteObrInstallUrl(path);
         }
 
         return remoteURL.openConnection();
@@ -127,6 +135,66 @@
     }
 
     /**
+     * Assume the URL is a query URL and try to find a matching resource.
+     *
+     * Note: the code from the below method comes from OPS4j Pax URL handler
+     *
+     * @param path the OBR url path
+     * @return the remote URL of the resolved bundle
+     * @throws IOException if an error occurs
+     */
+    private URL getRemoteObrInstallUrl(String path) throws IOException
+    {
+        if( path == null || path.trim().length() == 0 )
+        {
+            throw new MalformedURLException( "Path cannot be null or empty. Syntax " + SYNTAX );
+        }
+        final String[] segments = path.split( "/" );
+        if( segments.length > 2 )
+        {
+            throw new MalformedURLException( "Path cannot contain more then one '/'. Syntax  " + SYNTAX );
+        }
+        final StringBuffer buffer = new StringBuffer();
+        // add bundle symbolic name filter
+        buffer.append( "(symbolicname=" ).append( segments[ 0 ] ).append( ")" );
+        if( !validateFilter( buffer.toString() ) )
+        {
+            throw new MalformedURLException( "Invalid symbolic name value." );
+        }
+        // add bundle version filter
+        if( segments.length > 1 )
+        {
+            buffer.insert( 0, "(&" ).append( "(version=" ).append( segments[ 1 ] ).append( "))" );
+            if( !validateFilter( buffer.toString() ) )
+            {
+                throw new MalformedURLException( "Invalid version value." );
+            }
+        }
+        Resource[] discoverResources =
+           m_reRepositoryAdmin.discoverResources(buffer.toString());
+        if (discoverResources == null || discoverResources.length == 0)
+        {
+            throw new IOException( "No resource found for filter [" + buffer.toString() + "]" );
+        }
+        ResourceSelectionStrategy strategy = new NewestSelectionStrategy(m_logger);
+        Resource selected = strategy.selectOne(Version.emptyVersion, discoverResources);
+
+        return selected.getURL();
+    }
+
+    private boolean validateFilter(String filter) {
+        try
+        {
+            FrameworkUtil.createFilter(filter);
+            return true;
+        }
+        catch (InvalidSyntaxException e)
+        {
+            return false;
+        }
+    }
+
+    /**
      * Determines the remote url for the given bundle according to
      * the configured {@link ResourceSelectionStrategy}.
      *
@@ -136,19 +204,24 @@
      */
     private URL getRemoteUrlForBundle(Bundle bundle) throws IOException
     {
-        String bundleVersion =
-            (String) bundle.getHeaders().get(Constants.BUNDLE_VERSION);
+        String symbolicName = bundle.getSymbolicName();
+        String version = (String) bundle.getHeaders().get(Constants.BUNDLE_VERSION);
+
         StringBuffer buffer = new StringBuffer();
         buffer.append("(symbolicname=");
-        buffer.append(bundle.getSymbolicName());
+        buffer.append(symbolicName);
         buffer.append(")");
 
         Resource[] discoverResources =
             m_reRepositoryAdmin.discoverResources(buffer.toString());
+        if (discoverResources == null || discoverResources.length == 0)
+        {
+            throw new IOException( "No resource found for filter [" + buffer.toString() + "]" );
+        }
 
         ResourceSelectionStrategy strategy = getStrategy(m_updateStrategy);
         Resource selected = strategy.selectOne(
-            Version.parseVersion(bundleVersion), discoverResources);
+            Version.parseVersion(version), discoverResources);
 
         return selected.getURL();
     }
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ResolverImpl.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ResolverImpl.java
index d6519e3..f0865d0 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ResolverImpl.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/ResolverImpl.java
@@ -560,7 +560,7 @@
                         Bundle bundle = m_context.installBundle(
                             "obr://"
                             + deployResources[i].getSymbolicName()
-                            + "/" + System.currentTimeMillis(),
+                            + "/-" + System.currentTimeMillis(),
                             url.openStream());
 
                         // If necessary, save the installed bundle to be