FELIX-2226 Support detailed resource "access" by URL instead of request parameters
FELIX-2226 No redirect to .../obr?list=a
FELIX-2228 Encode and decode search query

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@926574 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java
index 4206b6d..dec0c08 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java
@@ -78,12 +78,6 @@
      */
     protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
     {
-        String query = request.getQueryString();
-        if ( query == null || query.length() == 0 )
-        {
-            response.sendRedirect( LABEL + "?list=a" );
-            return;
-        }
         // prepare variables
         DefaultVariableResolver vars = ( ( DefaultVariableResolver ) WebConsoleUtil.getVariableResolver( request ) );
         vars.put( "__data__", getData( request ) );
@@ -166,14 +160,13 @@
         AbstractBundleRepositoryRenderHelper helper = getHelper();
         if ( helper == null || !helper.hasRepositoryAdmin() )
         {
-            return "";
+            return "{}";
         }
 
-        boolean details = request.getParameter( "details" ) != null;
+        RequestInfo info = new RequestInfo( request );
 
         final String filter;
-        String list = WebConsoleUtil.urlDecode( request.getParameter( "list" ) );
-        String query = WebConsoleUtil.urlDecode( request.getParameter( "query" ) );
+        String list = info.getList();
         if ( list != null )
         {
             if ( "-".equals( list ) )
@@ -193,39 +186,46 @@
                     + "*))";
             }
         }
-        else if ( query != null )
+        else
         {
-            if ( query.indexOf( '=' ) > 0 )
+            String query = info.getQuery();
+            if ( query != null )
             {
-                if (query.startsWith( "(" )) {
-                    filter = query;
-                } else {
-                    filter = "(" + query + ")";
+                if ( query.indexOf( '=' ) > 0 )
+                {
+                    if ( query.startsWith( "(" ) )
+                    {
+                        filter = query;
+                    }
+                    else
+                    {
+                        filter = "(" + query + ")";
+                    }
+                }
+                else
+                {
+                    filter = "(|(presentationame=*" + query + "*)(symbolicname=*" + query + "*))";
                 }
             }
             else
             {
-                filter = "(|(presentationame=*" + query + "*)(symbolicname=*" + query + "*))";
-            }
-        }
-        else
-        {
-            StringBuffer sb = new StringBuffer( "(&" );
-            for ( Enumeration e = request.getParameterNames(); e.hasMoreElements(); )
-            {
-                String k = ( String ) e.nextElement();
-                String v = request.getParameter( k );
-                if ( v != null && v.length() > 0 && !"details".equals( k ) && !"deploy".equals( k )
-                    && !"deploystart".equals( k ) && !"bundle".equals( k ) && !"optional".equals( k ) )
+                StringBuffer sb = new StringBuffer( "(&" );
+                for ( Enumeration e = request.getParameterNames(); e.hasMoreElements(); )
                 {
-                    sb.append( "(" ).append( k ).append( "=" ).append( v ).append( ")" );
+                    String k = ( String ) e.nextElement();
+                    String v = request.getParameter( k );
+                    if ( v != null && v.length() > 0 && !"details".equals( k ) && !"deploy".equals( k )
+                        && !"deploystart".equals( k ) && !"bundle".equals( k ) && !"optional".equals( k ) )
+                    {
+                        sb.append( "(" ).append( k ).append( "=" ).append( v ).append( ")" );
+                    }
                 }
+                sb.append( ")" );
+                filter = sb.toString();
             }
-            sb.append( ")" );
-            filter = sb.toString();
         }
 
-        return helper.getData( filter, details, getBundleContext().getBundles() );
+        return helper.getData( filter, info.showDetails(), getBundleContext().getBundles() );
     }
 
 
@@ -248,4 +248,70 @@
         }
     }
 
+    private static class RequestInfo {
+
+        private final HttpServletRequest request;
+
+        private boolean details;
+        private String query;
+        private String list;
+
+
+        RequestInfo( final HttpServletRequest request )
+        {
+            this.request = request;
+        }
+
+
+        boolean showDetails()
+        {
+            getQuery();
+            return details;
+        }
+
+
+        String getQuery()
+        {
+            if ( query == null )
+            {
+                String query = WebConsoleUtil.urlDecode( request.getParameter( "query" ) );
+                boolean details = false;
+                if ( query == null && request.getPathInfo().length() > 5 )
+                {
+                    // cut off "/obr/" prefix (might want to use getTitle ??)
+                    String path = request.getPathInfo().substring( 5 );
+                    int slash = path.indexOf( '/' );
+                    if ( slash < 0 )
+                    {
+                        // symbolic name only, version ??
+                        query = "(symbolicname=" + path + ")";
+                    }
+                    else
+                    {
+                        query = "(&(symbolicname=" + path.substring( 0, slash ) + ")(version="
+                            + path.substring( slash + 1 ) + "))";
+                        details = true;
+                    }
+                }
+
+                this.query = query;
+                this.details = details;
+            }
+            return query;
+        }
+
+
+        String getList()
+        {
+            if ( list == null )
+            {
+                list = WebConsoleUtil.urlDecode( request.getParameter( "list" ) );
+                if ( list == null && !request.getParameterNames().hasMoreElements() && getQuery() == null )
+                {
+                    list = "a";
+                }
+            }
+            return list;
+        }
+    }
 }
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/FelixBundleRepositoryRenderHelper.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/FelixBundleRepositoryRenderHelper.java
index 0ba2eeb..a9df5ba 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/FelixBundleRepositoryRenderHelper.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/FelixBundleRepositoryRenderHelper.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import javax.servlet.ServletException;
 import org.apache.felix.bundlerepository.Capability;
+import org.apache.felix.bundlerepository.Property;
 import org.apache.felix.bundlerepository.Reason;
 import org.apache.felix.bundlerepository.Repository;
 import org.apache.felix.bundlerepository.RepositoryAdmin;
@@ -59,15 +60,14 @@
             {
                 JSONObject json = new JSONObject();
                 json.put( "status", admin != null );
-                if ( admin != null )
+                json.put( "details", details );
+
+                final Repository repositories[] = admin.listRepositories();
+                for ( int i = 0; repositories != null && i < repositories.length; i++ )
                 {
-                    final Repository repositories[] = admin.listRepositories();
-                    for ( int i = 0; repositories != null && i < repositories.length; i++ )
-                    {
-                        json.append( "repositories", new JSONObject().put( "lastModified",
-                            repositories[i].getLastModified() ).put( "name", repositories[i].getName() ).put( "url",
-                            repositories[i].getURI() ) );
-                    }
+                    json.append( "repositories", new JSONObject().put( "lastModified",
+                        repositories[i].getLastModified() ).put( "name", repositories[i].getName() ).put( "url",
+                        repositories[i].getURI() ) );
                 }
 
                 Resource[] resources = admin.discoverResources( filter );
@@ -89,7 +89,7 @@
         }
 
         // fall back to no data
-        return "";
+        return "{}";
     }
 
 
@@ -211,7 +211,7 @@
             for ( int i = 0; caps != null && i < caps.length; i++ )
             {
                 json.append( "capabilities", new JSONObject().put( "name", caps[i].getName() ).put( "properties",
-                    new JSONObject( caps[i].getProperties() ) ) );
+                    toJSON( caps[i].getProperties() ) ) );
             }
             Requirement[] reqs = resource.getRequirements();
             for ( int i = 0; reqs != null && i < reqs.length; i++ )
@@ -244,4 +244,15 @@
         }
         return json;
     }
+
+
+    private JSONObject toJSON( final Property[] props ) throws JSONException
+    {
+        JSONObject json = new JSONObject();
+        for ( int i = 0; props != null && i < props.length; i++ )
+        {
+            json.put( props[i].getName(), props[i].getValue() );
+        }
+        return json;
+    }
 }
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/OsgiBundleRepositoryRenderHelper.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/OsgiBundleRepositoryRenderHelper.java
index 6dbe992..61477b1 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/OsgiBundleRepositoryRenderHelper.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/OsgiBundleRepositoryRenderHelper.java
@@ -50,18 +50,15 @@
 
     String getData( final String filter, final boolean details, Bundle[] bundles )
     {
-        try
+        RepositoryAdmin admin = ( RepositoryAdmin ) getRepositoryAdmin();
+        if ( admin != null )
         {
-            RepositoryAdmin admin = ( RepositoryAdmin ) getRepositoryAdmin();
-            if ( admin == null )
+            try
             {
-                return "";
-            }
+                JSONObject json = new JSONObject();
+                json.put( "status", admin != null );
+                json.put( "details", details );
 
-            JSONObject json = new JSONObject();
-            json.put( "status", admin != null );
-            if ( admin != null )
-            {
                 final Repository repositories[] = admin.listRepositories();
                 for ( int i = 0; repositories != null && i < repositories.length; i++ )
                 {
@@ -69,21 +66,23 @@
                         repositories[i].getLastModified() ).put( "name", repositories[i].getName() ).put( "url",
                         repositories[i].getURL() ) );
                 }
-            }
 
-            Resource[] resources = admin.discoverResources( filter );
-            for ( int i = 0; resources != null && i < resources.length; i++ )
+                Resource[] resources = admin.discoverResources( filter );
+                for ( int i = 0; resources != null && i < resources.length; i++ )
+                {
+                    json.append( "resources", toJSON( resources[i], bundles, details ) );
+                }
+
+                return json.toString();
+            }
+            catch ( JSONException e )
             {
-                json.append( "resources", toJSON( resources[i], bundles, details ) );
+                logger.log( "Failed to serialize repository to JSON object.", e );
             }
+        }
 
-            return json.toString();
-        }
-        catch ( JSONException e )
-        {
-            logger.log( "Failed to serialize repository to JSON object.", e );
-            return "";
-        }
+        // fall back to no data
+        return "{}";
     }
 
 
diff --git a/webconsole/src/main/resources/res/ui/obr.js b/webconsole/src/main/resources/res/ui/obr.js
index 2d5bc0a..baf96a9 100644
--- a/webconsole/src/main/resources/res/ui/obr.js
+++ b/webconsole/src/main/resources/res/ui/obr.js
@@ -68,7 +68,7 @@
 }
 
 function showDetails( symbolicname, version ) {
-    window.location.href = window.location.pathname + '?details&symbolicname=' + symbolicname + '&version=' + version;
+    window.location.href = pluginRoot + '/' + symbolicname + '/' + version;
 }
 
 function showVersions( symbolicname ) {
@@ -379,13 +379,13 @@
                         });
     // Required dependencies
     createDetailedTable(tbody, "Dependencies", ["Name", "Version"], res.required, function(p) {
-                            var a = createElement('a', null, { href: (window.location.pathname + '?details&symbolicname=' + p.symbolicname + '&version=' + p.version) });
+                            var a = createElement('a', null, { href: (pluginRoot + '/' + p.symbolicname + '/' + p.version) });
                             a.appendChild(text(p.presentationname ? p.presentationname : p.symbolicname));
                             return [ a, text(p.version) ];
                         });
     // Optional dependencies
     createDetailedTable(tbody, "Optional Dependencies", ["Name", "Version"], res.optional, function(p) {
-                            var a = createElement('a', null, { href: (window.location.pathname + '?details&symbolicname=' + p.symbolicname + '&version=' + p.version) });
+                            var a = createElement('a', null, { href: (pluginRoot + '/' + p.symbolicname + '/' + p.version) });
                             a.appendChild(text(p.presentationname ? p.presentationname : p.symbolicname));
                             return [ a, text(p.version) ];
                         });
@@ -421,7 +421,7 @@
 		for (var i in obrData.repositories ) {
 			renderRepository( obrData.repositories[i] );
 		}
-		if ($.getUrlVar('details')) {
+		if (obrData.details) {
 		    $('#resTable').addClass('ui-helper-hidden');
 		    $('#detailsTable').removeClass('ui-helper-hidden');
 		    for (var i in obrData.resources ) {
@@ -470,7 +470,11 @@
 	resTable = $('#resTable tbody').empty();
 	searchField = $('#searchField');
 	ifStatusOK = $('#ifStatusOK');
-    searchField.val($.getUrlVar('query'));
+	
+	var query = $.getUrlVar('query');
+	if (query) {
+        searchField.val(decodeURIComponent(query));
+    }
 
 	$('#addRepoBtn').click(function(event) {
         event.preventDefault();
@@ -478,7 +482,7 @@
 	});
 	$('#searchBtn').click(function(event) {
         event.preventDefault();
-        window.location.href = window.location.pathname + '?query=' + searchField.val();
+        window.location.href = pluginRoot + '?query=' + encodeURIComponent(searchField.val());
 	});
 	searchField.keypress(function(event) {
         if (event.keyCode == 13) {
diff --git a/webconsole/src/main/resources/templates/obr.html b/webconsole/src/main/resources/templates/obr.html
index 6f1ae5a..c40ffce 100644
--- a/webconsole/src/main/resources/templates/obr.html
+++ b/webconsole/src/main/resources/templates/obr.html
@@ -1,4 +1,4 @@
-<script type="text/javascript" src="res/ui/obr.js"></script>
+<script type="text/javascript" src="${pluginRoot}/res/ui/obr.js"></script>
 <script type="text/javascript">
 var i18n = {
 	status_ok : '${obr.status.ok}',