You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by gn...@apache.org on 2010/03/04 21:54:00 UTC

svn commit: r919175 - in /felix/trunk/webconsole: ./ src/main/java/org/apache/felix/webconsole/internal/core/ src/main/java/org/apache/felix/webconsole/internal/obr/ src/main/resources/res/ui/ src/main/resources/templates/

Author: gnodet
Date: Thu Mar  4 20:53:59 2010
New Revision: 919175

URL: http://svn.apache.org/viewvc?rev=919175&view=rev
Log:
FELIX-2162, FELIX-2171: rewrite the OBR webconsole page to be more scalable and display detailed informations about a given bundle

Added:
    felix/trunk/webconsole/src/main/resources/res/ui/jquery.uuid.js
    felix/trunk/webconsole/src/main/resources/res/ui/obr.css
Modified:
    felix/trunk/webconsole/pom.xml
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BaseUpdateInstallHelper.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateHelper.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java
    felix/trunk/webconsole/src/main/resources/res/ui/obr.js
    felix/trunk/webconsole/src/main/resources/templates/obr.html

Modified: felix/trunk/webconsole/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/pom.xml?rev=919175&r1=919174&r2=919175&view=diff
==============================================================================
--- felix/trunk/webconsole/pom.xml (original)
+++ felix/trunk/webconsole/pom.xml Thu Mar  4 20:53:59 2010
@@ -92,6 +92,7 @@
                             org.osgi.service.http,
                             org.apache.felix.scr;
                             org.apache.felix.shell;
+                            org.apache.felix.bundlerepository;
                             org.osgi.service.*;resolution:=optional,
                             javax.portlet;resolution:=optional,
                             javax.servlet.*;version=2.4,
@@ -100,9 +101,9 @@
                         <Embed-Dependency>
                             <!-- Import/Export-Package parsing -->
                             org.apache.felix.bundlerepository;
-                                inline=org/apache/felix/bundlerepository/R4*.class|
-                                    org/apache/felix/bundlerepository/Util.class|
-                                    org/apache/felix/bundlerepository/VersionRange.class,
+                                inline=org/apache/felix/bundlerepository/impl/R4*.class|
+                                    org/apache/felix/bundlerepository/impl/Util.class|
+                                    org/apache/felix/bundlerepository/impl/VersionRange.class,
                             
                             <!-- ServiceTracker -->
                             org.osgi.compendium;
@@ -166,9 +167,9 @@
                                         <!-- <_donotcopy>(LICENSE.json|NOTICE.bare)</_donotcopy> -->
                                         <Embed-Dependency>
                                             org.apache.felix.bundlerepository;
-                                                inline=org/apache/felix/bundlerepository/R4*.class|
-                                                    org/apache/felix/bundlerepository/Util.class|
-                                                    org/apache/felix/bundlerepository/VersionRange.class
+                                                inline=org/apache/felix/bundlerepository/impl/R4*.class|
+                                                    org/apache/felix/bundlerepository/impl/Util.class|
+                                                    org/apache/felix/bundlerepository/impl/VersionRange.class
                                         </Embed-Dependency>
                                     </instructions>
                                 </configuration>
@@ -232,19 +233,11 @@
             <scope>provided</scope>
         </dependency>
 
-        <!-- OBR Service API -->
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.osgi.service.obr</artifactId>
-            <version>1.0.2</version>
-            <scope>provided</scope>
-        </dependency>
-
         <!--  Parsing Import/Export-Package headers -->
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.bundlerepository</artifactId>
-            <version>1.0.3</version>
+            <version>1.5.0-SNAPSHOT</version>
             <scope>compile</scope>
             <optional>true</optional>
         </dependency>

Modified: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BaseUpdateInstallHelper.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BaseUpdateInstallHelper.java?rev=919175&r1=919174&r2=919175&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BaseUpdateInstallHelper.java (original)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BaseUpdateInstallHelper.java Thu Mar  4 20:53:59 2010
@@ -71,7 +71,7 @@
      * @throws BundleException
      * @throws IOException
      */
-    protected Bundle doRun() throws BundleException, IOException
+    protected Bundle doRun() throws Exception
     {
         // now deploy the resolved bundles
         InputStream bundleStream = null;
@@ -121,14 +121,10 @@
                     { bundle } );
             }
         }
-        catch ( IOException ioe )
+        catch ( Exception ioe )
         {
             getLog().log( LogService.LOG_ERROR, "Cannot install or update bundle from " + bundleFile, ioe );
         }
-        catch ( BundleException be )
-        {
-            getLog().log( LogService.LOG_ERROR, "Cannot install or update bundle from " + bundleFile, be );
-        }
         finally
         {
             if ( bundleFile != null )

Modified: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java?rev=919175&r1=919174&r2=919175&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java (original)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java Thu Mar  4 20:53:59 2010
@@ -40,10 +40,10 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.felix.bundlerepository.R4Attribute;
-import org.apache.felix.bundlerepository.R4Export;
-import org.apache.felix.bundlerepository.R4Import;
-import org.apache.felix.bundlerepository.R4Package;
+import org.apache.felix.bundlerepository.impl.R4Attribute;
+import org.apache.felix.bundlerepository.impl.R4Export;
+import org.apache.felix.bundlerepository.impl.R4Import;
+import org.apache.felix.bundlerepository.impl.R4Package;
 import org.apache.felix.webconsole.ConfigurationPrinter;
 import org.apache.felix.webconsole.DefaultVariableResolver;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;

Modified: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateHelper.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateHelper.java?rev=919175&r1=919174&r2=919175&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateHelper.java (original)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UpdateHelper.java Thu Mar  4 20:53:59 2010
@@ -22,23 +22,23 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Resolver;
+import org.apache.felix.bundlerepository.Resource;
 import org.apache.felix.webconsole.internal.obr.DeployerThread;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.log.LogService;
-import org.osgi.service.obr.RepositoryAdmin;
-import org.osgi.service.obr.Resolver;
-import org.osgi.service.obr.Resource;
 
 
 abstract class UpdateHelper extends BaseUpdateInstallHelper
 {
 
-    // Define a constant of that name to prevent NoClassDefFoundError in
-    // updateFromOBR trying to load the class with RepositoryAdmin.class
-    private static final String REPOSITORY_ADMIN_NAME = "org.osgi.service.obr.RepositoryAdmin";
-
     private final Bundle bundle;
 
 
@@ -63,7 +63,7 @@
     }
 
 
-    protected Bundle doRun() throws BundleException, IOException
+    protected Bundle doRun() throws Exception
     {
         // update the bundle from the file if defined
         if ( getBundleFile() != null )
@@ -115,9 +115,9 @@
     }
 
 
-    private boolean updateFromOBR()
+    private boolean updateFromOBR() throws InvalidSyntaxException
     {
-        RepositoryAdmin ra = ( RepositoryAdmin ) getService( REPOSITORY_ADMIN_NAME );
+        RepositoryAdmin ra = ( RepositoryAdmin ) getService( RepositoryAdmin.class.getName() );
         if ( ra != null )
         {
             getLog().log( LogService.LOG_DEBUG, "Trying to update from OSGi Bundle Repository" );
@@ -154,7 +154,7 @@
                         .getOptionalResources() );
 
                     // deploy the resolved bundles and ensure they are started
-                    resolver.deploy( true );
+                    resolver.deploy( Resolver.START );
                     getLog().log( LogService.LOG_INFO, "Bundle updated from OSGi Bundle Repository" );
 
                     return true;
@@ -168,7 +168,7 @@
         }
         else
         {
-            getLog().log( LogService.LOG_INFO, "Cannot update from OSGi Bundle Repository: Service not available" );
+            getLog().log( LogService.LOG_DEBUG, "Cannot updated from OSGi Bundle Repository: Service not available" );
         }
 
         // fallback to false, nothing done

Modified: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java?rev=919175&r1=919174&r2=919175&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java (original)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/BundleRepositoryRender.java Thu Mar  4 20:53:59 2010
@@ -19,12 +19,18 @@
 package org.apache.felix.webconsole.internal.obr;
 
 import java.io.IOException;
-import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.felix.bundlerepository.Capability;
+import org.apache.felix.bundlerepository.Reason;
+import org.apache.felix.bundlerepository.Requirement;
 import org.apache.felix.webconsole.DefaultVariableResolver;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
 import org.apache.felix.webconsole.WebConsoleUtil;
@@ -34,10 +40,11 @@
 import org.json.JSONObject;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
-import org.osgi.service.obr.Repository;
-import org.osgi.service.obr.RepositoryAdmin;
-import org.osgi.service.obr.Resolver;
-import org.osgi.service.obr.Resource;
+import org.apache.felix.bundlerepository.Repository;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Resolver;
+import org.apache.felix.bundlerepository.Resource;
+import org.osgi.framework.InvalidSyntaxException;
 
 /**
  * This class provides a plugin for rendering the available OSGi Bundle Repositories
@@ -47,11 +54,11 @@
 {
     private static final String LABEL = "obr";
     private static final String TITLE = "OSGi Repository";
-    private static final String[] CSS = null;
+    private static final String[] CSS = { "/res/ui/obr.css" };
 
     // Define a constant of that name to prevent NoClassDefFoundError in
     // updateFromOBR trying to load the class with RepositoryAdmin.class
-    private static final String REPOSITORY_ADMIN_NAME = "org.osgi.service.obr.RepositoryAdmin";
+    private static final String REPOSITORY_ADMIN_NAME = RepositoryAdmin.class.getName();
 
     // templates
     private final String TEMPLATE;
@@ -72,9 +79,15 @@
      */
     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());
+        vars.put("__data__", getData(request));
 
         response.getWriter().print(TEMPLATE);
     }
@@ -97,17 +110,18 @@
         final String action = request.getParameter("action");
         final String deploy = request.getParameter("deploy");
         final String deploystart = request.getParameter("deploystart");
+        final String optional = request.getParameter("optional");
 
         if (action != null)
         {
             doAction(action, request.getParameter("url"), admin);
-            response.getWriter().print(getData());
+            response.getWriter().print(getData(request));
             return;
         }
 
         if (deploy != null || deploystart != null)
         {
-            doDeploy(request.getParameterValues("bundle"), deploystart != null, admin);
+            doDeploy(request.getParameterValues("bundle"), deploystart != null, optional != null, admin);
             doGet(request, response);
             return;
         }
@@ -128,19 +142,122 @@
         }
     }
 
-    private final String getData()
+    private final String getData(HttpServletRequest request)
     {
         final Bundle[] bundles = getBundleContext().getBundles();
         try
         {
-            return toJSON(getRepositoryAdmin(), bundles).toString();
+            RepositoryAdmin admin = getRepositoryAdmin();
+            Resource[] resources = null;
+            boolean details = request.getParameter("details") != null;
+            if (admin != null)
+            {
+                String list = request.getParameter("list");
+                String query = request.getParameter("query");
+                if (list != null)
+                {
+                    String filter;
+                    if ("-".equals(list))
+                    {
+                        StringBuffer sb = new StringBuffer("(!(|");
+                        for (int c = 0; c < 26; c++)
+                        {
+                            sb.append("(presentationname=").append((char) ('a' + c))
+                                    .append("*)(presentationname=")
+                                    .append((char)('A' + c)).append("*)");
+                        }
+                        sb.append("))");
+                        filter = sb.toString();
+                    }
+                    else
+                    {
+                        filter = "(|(presentationname=" + list.toLowerCase() + "*)(presentationname=" + list.toUpperCase() + "*))";
+                    }
+                    resources = admin.discoverResources(filter);
+                }
+                else if (query != null)
+                {
+                    if (query.indexOf('=') > 0)
+                    {
+                        resources = admin.discoverResources(new Requirement[] { parseRequirement(admin, query) });
+                    }
+                    else
+                    {
+                        resources = admin.discoverResources("(|(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))
+                        {
+                            sb.append("(").append(k).append("=").append(v).append(")");
+                        }
+                    }
+                    sb.append(")");
+                    resources = admin.discoverResources(sb.toString());
+
+                }
+            }
+            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++)
+                {
+                    json.append("repositories", new JSONObject()
+                            .put("lastModified", repositories[i].getLastModified())
+                            .put("name", repositories[i].getName())
+                            .put("url", repositories[i].getURI()));
+                }
+            }
+            for (int i = 0; resources != null && i < resources.length; i++)
+            {
+                json.append("resources", toJSON(resources[i], bundles, details));
+            }
+            return json.toString();
+        }
+        catch (InvalidSyntaxException e)
+        {
+            log("Failed to parse filter.", e);
+            return "";
         }
         catch (JSONException e)
         {
             log("Failed to serialize repository to JSON object.", e);
             return "";
         }
+    }
 
+    private Requirement parseRequirement(RepositoryAdmin admin, String req) throws InvalidSyntaxException {
+        int p = req.indexOf(':');
+        String name;
+        String filter;
+        if (p > 0) {
+            name = req.substring(0, p);
+            filter = req.substring(p + 1);
+        } else {
+            if (req.contains("package")) {
+                name = "package";
+            } else if (req.contains("service")) {
+                name = "service";
+            } else {
+                name = "bundle";
+            }
+            filter = req;
+        }
+        if (!filter.startsWith("(")) {
+            filter = "(" + filter + ")";
+        }
+        return admin.requirement(name, filter);
     }
 
     private final void doAction(String action, String urlParam, RepositoryAdmin admin)
@@ -149,20 +266,20 @@
         Repository[] repos = admin.listRepositories();
         Repository repo = getRepository(repos, urlParam);
 
-        URL url = repo != null ? repo.getURL() : new URL(urlParam);
+        String uri = repo != null ? repo.getURI() : urlParam;
 
         if ("delete".equals(action))
         {
-            if (!admin.removeRepository(url))
+            if (!admin.removeRepository(uri))
             {
-                throw new ServletException("Failed to remove repository with URL " + url);
+                throw new ServletException("Failed to remove repository with URL " + uri);
             }
         }
         else if ("add".equals(action) || "refresh".equals(action))
         {
             try
             {
-                admin.addRepository(url);
+                admin.addRepository(uri);
             }
             catch (IOException e)
             {
@@ -170,47 +287,53 @@
             }
             catch (Exception e)
             {
-                throw new ServletException("Failed to " + action + " repository " + url
+                throw new ServletException("Failed to " + action + " repository " + uri
                     + ": " + e.toString());
             }
 
         }
     }
 
-    private final void doDeploy(String[] bundles, boolean start, RepositoryAdmin repoAdmin)
+    private final void doDeploy(String[] bundles, boolean start, boolean optional, RepositoryAdmin repoAdmin)
     {
-        // check whether we have to do something
-        if (bundles == null || bundles.length == 0)
-        {
-            log("No resources to deploy");
-            return;
-        }
-
-        Resolver resolver = repoAdmin.resolver();
-
-        // prepare the deployment
-        for (int i = 0; i < bundles.length; i++)
+        try
         {
-            String bundle = bundles[i];
-            if (bundle == null || bundle.equals("-"))
+            // check whether we have to do something
+            if (bundles == null || bundles.length == 0)
             {
-                continue;
+                log("No resources to deploy");
+                return;
             }
 
-            String filter = "(id=" + bundle + ")";
-            Resource[] resources = repoAdmin.discoverResources(filter);
-            if (resources != null && resources.length > 0)
+            Resolver resolver = repoAdmin.resolver();
+
+            // prepare the deployment
+            for (int i = 0; i < bundles.length; i++)
             {
-                resolver.add(resources[0]);
+                String bundle = bundles[i];
+                if (bundle == null || bundle.equals("-"))
+                {
+                    continue;
+                }
+
+                String filter = "(id=" + bundle + ")";
+                Resource[] resources = repoAdmin.discoverResources(filter);
+                if (resources != null && resources.length > 0)
+                {
+                    resolver.add(resources[0]);
+                }
             }
-        }
 
-        DeployerThread dt = new DeployerThread(resolver, new Logger(getBundleContext()),
-            start);
-        dt.start();
+            DeployerThread dt = new DeployerThread(resolver, new Logger(getBundleContext()), start, optional);
+            dt.start();
+        }
+        catch (InvalidSyntaxException e)
+        {
+            throw new IllegalStateException(e); 
+        }
     }
 
-    private static final Repository getRepository(Repository[] repos, String repositoryUrl)
+    private final Repository getRepository(Repository[] repos, String repositoryUrl)
     {
         if (repositoryUrl == null || repositoryUrl.length() == 0)
         {
@@ -219,7 +342,7 @@
 
         for (int i = 0; i < repos.length; i++)
         {
-            if (repositoryUrl.equals(repos[i].getURL().toString()))
+            if (repositoryUrl.equals(repos[i].getURI()))
             {
                 return repos[i];
             }
@@ -228,43 +351,7 @@
         return null;
     }
 
-    private static final JSONObject toJSON(RepositoryAdmin admin, Bundle[] bundles)
-        throws JSONException
-    {
-        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++)
-            {
-                json.append("repositories", toJSON(repositories[i], bundles));
-            }
-        }
-
-        return json;
-
-    }
-
-    private static final JSONObject toJSON(Repository repo, Bundle[] bundles)
-        throws JSONException
-    {
-        JSONObject json = new JSONObject() //
-        .put("lastModified", repo.getLastModified()) //
-        .put("name", repo.getName()) //
-        .put("url", repo.getURL()); //
-
-        Resource[] resources = repo.getResources();
-        for (int i = 0; resources != null && i < resources.length; i++)
-        {
-            json.append("resources", toJSON(resources[i], bundles));
-        }
-
-        return json;
-    }
-
-    private static final JSONObject toJSON(Resource resource, Bundle[] bundles)
+    private final JSONObject toJSON(Resource resource, Bundle[] bundles, boolean details)
         throws JSONException
     {
         final String symbolicName = resource.getSymbolicName();
@@ -282,15 +369,55 @@
         .put("id", resource.getId()) //
         .put("presentationname", resource.getPresentationName()) //
         .put("symbolicname", symbolicName) //
-        .put("url", resource.getURL()) //
+        .put("url", resource.getURI()) //
         .put("version", version) //
-        .put("url", resource.getURL()) //
         .put("categories", resource.getCategories()) //
         .put("installed", installed);
 
-        // TODO: do we need these ?
-        // resource.getCapabilities()
-        // resource.getRequirements()
+        if (details)
+        {
+            Capability[] caps = resource.getCapabilities();
+            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())));
+            }
+            Requirement[] reqs = resource.getRequirements();
+            for (int i = 0; reqs != null && i < reqs.length; i++)
+            {
+                json.append("requirements", new JSONObject().
+                    put("name", reqs[i].getName()).
+                    put("filter", reqs[i].getFilter()).
+                    put("optional", reqs[i].isOptional()));
+            }
+
+            final RepositoryAdmin admin = getRepositoryAdmin();
+            final List repos = new ArrayList();
+            repos.add(admin.getSystemRepository());
+            repos.addAll(Arrays.asList(admin.listRepositories()));
+            Resolver resolver = admin.resolver((Repository[]) repos.toArray(new Repository[repos.size()]));
+            resolver.add(resource);
+            resolver.resolve();
+            Resource[] required = resolver.getRequiredResources();
+            for (int i = 0; required != null && i < required.length; i++)
+            {
+                json.append("required", toJSON(required[i], bundles, false));
+            }
+            Resource[] optional = resolver.getOptionalResources();
+            for (int i = 0; optional != null && i < optional.length; i++)
+            {
+                json.append("optional", toJSON(optional[i], bundles, false));
+            }
+            Reason[] unsatisfied = resolver.getUnsatisfiedRequirements();
+            for (int i = 0; unsatisfied != null && i < unsatisfied.length; i++)
+            {
+                json.append("unsatisfied", new JSONObject().
+                    put("name", unsatisfied[i].getRequirement().getName()).
+                    put("filter", unsatisfied[i].getRequirement().getFilter()).
+                    put("optional", unsatisfied[i].getRequirement().isOptional()));
+            }
+        }
         return json;
     }
 

Modified: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java?rev=919175&r1=919174&r2=919175&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java (original)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/obr/DeployerThread.java Thu Mar  4 20:53:59 2010
@@ -19,11 +19,11 @@
 package org.apache.felix.webconsole.internal.obr;
 
 
+import org.apache.felix.bundlerepository.Reason;
+import org.apache.felix.bundlerepository.Resolver;
+import org.apache.felix.bundlerepository.Resource;
 import org.apache.felix.webconsole.internal.Logger;
 import org.osgi.service.log.LogService;
-import org.osgi.service.obr.Requirement;
-import org.osgi.service.obr.Resolver;
-import org.osgi.service.obr.Resource;
 
 
 public class DeployerThread extends Thread
@@ -35,34 +35,40 @@
 
     private final boolean startBundles;
 
+    private final boolean optionalDependencies;
 
-    public DeployerThread( Resolver obrResolver, Logger logger, boolean startBundles )
+
+    public DeployerThread( Resolver obrResolver, Logger logger, boolean startBundles, boolean optionalDependencies )
     {
-        this( obrResolver, logger, startBundles, "OBR Bundle Deployer" );
+        this( obrResolver, logger, startBundles, optionalDependencies, "OBR Bundle Deployer" );
     }
 
 
-    public DeployerThread( Resolver obrResolver, Logger logger, boolean startBundles, String name )
+    public DeployerThread( Resolver obrResolver, Logger logger, boolean startBundles, boolean optionalDependencies, String name )
     {
         super( name );
         this.obrResolver = obrResolver;
         this.logger = logger;
         this.startBundles = startBundles;
+        this.optionalDependencies = optionalDependencies;
     }
 
 
     public void run()
     {
+        int flags = 0;
+        flags += (startBundles ? Resolver.START : 0);
+        flags += (optionalDependencies ? 0 : Resolver.NO_OPTIONAL_RESOURCES);
         try
         {
-            if ( obrResolver.resolve() )
+            if ( obrResolver.resolve( flags ) )
             {
 
                 logResource( logger, "Installing Requested Resources", obrResolver.getAddedResources() );
                 logResource( logger, "Installing Required Resources", obrResolver.getRequiredResources() );
                 logResource( logger, "Installing Optional Resources", obrResolver.getOptionalResources() );
 
-                obrResolver.deploy( startBundles );
+                obrResolver.deploy( flags );
             }
             else
             {
@@ -91,17 +97,17 @@
     }
 
 
-    public static void logRequirements( Logger logger, String message, Requirement[] req )
+    public static void logRequirements( Logger logger, String message, Reason[] reasons )
     {
         logger.log( LogService.LOG_ERROR, message );
-        for ( int i = 0; req != null && i < req.length; i++ )
+        for ( int i = 0; reasons != null && i < reasons.length; i++ )
         {
-            String moreInfo = req[i].getComment();
+            String moreInfo = reasons[i].getRequirement().getComment();
             if ( moreInfo == null )
             {
-                moreInfo = req[i].getFilter().toString();
+                moreInfo = reasons[i].getRequirement().getFilter().toString();
             }
-            logger.log( LogService.LOG_ERROR, "  " + i + ": " + req[i].getName() + " (" + moreInfo + ")" );
+            logger.log( LogService.LOG_ERROR, "  " + i + ": " + reasons[i].getRequirement().getName() + " (" + moreInfo + ")" );
         }
     }
 

Added: felix/trunk/webconsole/src/main/resources/res/ui/jquery.uuid.js
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/resources/res/ui/jquery.uuid.js?rev=919175&view=auto
==============================================================================
--- felix/trunk/webconsole/src/main/resources/res/ui/jquery.uuid.js (added)
+++ felix/trunk/webconsole/src/main/resources/res/ui/jquery.uuid.js Thu Mar  4 20:53:59 2010
@@ -0,0 +1,24 @@
+/*
+Usage 1: define the default prefix by using an object with the property prefix as a parameter which contains a string value; {prefix: 'id'}
+Usage 2: call the function jQuery.uuid() with a string parameter p to be used as a prefix to generate a random uuid;
+Usage 3: call the function jQuery.uuid() with no parameters to generate a uuid with the default prefix; defaul prefix: '' (empty string)
+*/
+
+/*
+Generate fragment of random numbers
+*/
+jQuery._uuid_default_prefix = '';
+jQuery._uuidlet = function () {
+	return(((1+Math.random())*0x10000)|0).toString(16).substring(1);
+};
+/*
+Generates random uuid
+*/
+jQuery.uuid = function (p) {
+	if (typeof(p) == 'object' && typeof(p.prefix) == 'string') {
+		jQuery._uuid_default_prefix = p.prefix;
+	} else {
+		p = p || jQuery._uuid_default_prefix || '';
+		return(p+jQuery._uuidlet()+jQuery._uuidlet()+"-"+jQuery._uuidlet()+"-"+jQuery._uuidlet()+"-"+jQuery._uuidlet()+"-"+jQuery._uuidlet()+jQuery._uuidlet()+jQuery._uuidlet());
+	};
+};
\ No newline at end of file

Added: felix/trunk/webconsole/src/main/resources/res/ui/obr.css
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/resources/res/ui/obr.css?rev=919175&view=auto
==============================================================================
--- felix/trunk/webconsole/src/main/resources/res/ui/obr.css (added)
+++ felix/trunk/webconsole/src/main/resources/res/ui/obr.css Thu Mar  4 20:53:59 2010
@@ -0,0 +1,6 @@
+label {
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 1em;
+    font-style: normal;
+    font-weight: normal;
+}
\ No newline at end of file

Modified: felix/trunk/webconsole/src/main/resources/res/ui/obr.js
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/resources/res/ui/obr.js?rev=919175&r1=919174&r2=919175&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/resources/res/ui/obr.js (original)
+++ felix/trunk/webconsole/src/main/resources/res/ui/obr.js Thu Mar  4 20:53:59 2010
@@ -22,6 +22,29 @@
 var searchField = false;
 var ifStatusOK = false;
 
+//This prototype is provided by the Mozilla foundation and
+//is distributed under the MIT license.
+//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
+if (!Array.prototype.map)
+{
+  Array.prototype.map = function(fun /*, thisp*/)
+  {
+    var len = this.length;
+    if (typeof fun != "function")
+      throw new TypeError();
+
+    var res = new Array(len);
+    var thisp = arguments[1];
+    for (var i = 0; i < len; i++)
+    {
+      if (i in this)
+        res[i] = fun.call(thisp, this[i], i, this);
+    }
+
+    return res;
+  };
+}
+
 /* displays a date in the user's local timezone */
 function localTm(time) {
 	return (time ? new Date(time) : new Date()).toLocaleString();
@@ -38,43 +61,337 @@
 	}
 }
 
+function showDetails( symbolicname, version ) {
+    window.location.href = window.location.pathname + '?details&symbolicname=' + symbolicname + '&version=' + version;
+}
+
+function showVersions( symbolicname ) {
+	var _id = symbolicname.replace(/\./g, '_');
+    $("#block" + _id).append("<div id='pluginInlineVersions" + _id + "' style='margin-left: 4em'><ul/></div>");
+    $("#img" + _id).each(function() {
+        $(this).
+            removeClass('ui-icon-triangle-1-e').//right
+            addClass('ui-icon-triangle-1-s');//down
+    });
+    $("#entry" + _id).each(function() {
+        $(this).
+            unbind('click').
+            click(function() {hideVersions(symbolicname)}).
+            attr("title", "Hide Versions");
+    });
+    var versions = [];
+    for (var i in obrData.resources ) {
+        if (obrData.resources[i].symbolicname == symbolicname) {
+            versions.push(obrData.resources[i].version);
+        }
+    }
+    versions.sort();
+    for (var i in versions) {
+        var txt = "<li><a href='javascript: showDetails(\"" + symbolicname + "\",\"" + versions[i] + "\")'>" + versions[i] + "</a></li>";
+        $("#pluginInlineVersions" + _id + " > ul").append(txt);
+    }
+}
+
+function hideVersions( symbolicname ) {
+	var _id = symbolicname.replace(/\./g, '_');
+    $("#img" + _id).each(function() {
+        $(this).
+            removeClass('ui-icon-triangle-1-s').//down
+            addClass('ui-icon-triangle-1-e');//right
+    });
+    $("#pluginInlineVersions" + _id).each(function() {
+        $(this).
+            remove();
+    });
+    $("#entry" + _id).each(function() {
+        $(this).
+            unbind('click').
+            click(function() {showVersions(symbolicname)}).
+            attr("title", "Show Versions");
+    });
+}
+
 function renderResource(res) {
-	// aply filtering
-	var match = searchField.val();
-	if (match) {
-		match = new RegExp( match );
-		if ( !match.test(res.presentationname) ) return;
-	}
-	
 	// proceed with resource
 	var _id = res.symbolicname.replace(/\./g, '_');
-	var _tr = resTable.find('#' + _id);
+	var _tr = resTable.find('#row' + _id);
 
 	if (_tr.length == 0) { // not created yet, create it
-		var _select = createElement('select', null, { name : 'bundle' }, [
-			createElement( 'option', null, { value : '-' }, [
-				text( i18n.selectVersion)
-			]),
-			createElement( 'option', null, { value : res.id }, [
-				text( res.version + (res.installed ? ' *' : '') )
-			])
-		]);
-		_tr = tr( null, { 'id' : _id } , [
-			td( null, null, [ _select ] ),
-			td( null, null, [ text(res.presentationname) ] ),
+	    var blockElement = createElement('span', '', {
+            id: 'block' + _id
+	    });
+        var titleElement = createElement('span', '', {
+            id: 'entry' + _id,
+            title: "Show Versions"
+        });
+        var inputElement = createElement('span', 'ui-icon ui-icon-triangle-1-e', {
+            id: 'img' + _id,
+            style: {display: "inline-block"}
+        });
+        blockElement.appendChild(titleElement);
+        titleElement.appendChild(inputElement);
+        titleElement.appendChild(text(" "));
+        titleElement.appendChild(text(res.presentationname ? res.presentationname : res.symbolicname));
+        $(titleElement).click(function() {showVersions(res.symbolicname)});
+
+		_tr = tr( null, { 'id' : 'row' + _id } , [
+			td( null, null, [ blockElement ] ),
 			td( null, null, [ text(res.installed ? res.version : '') ] )
 		]);
 		resTable.append( _tr );
-	} else { // append the additional version
-		_tr.find( 'select' ).append (
-			createElement( 'option', null, { value : res.id }, [
-				text( res.version  + (res.installed ? ' *' : '') )
-			])
-		);
-		if (res.installed) _tr.find( 'td:eq(2)' ).text( res.version );
 	}
 }
 
+function getCapabilitiesByName(res, name) {
+    var caps = [];
+    for (var v in res.capabilities) {
+        if (res.capabilities[v].name == name) {
+            caps.push(res.capabilities[v]);
+        }
+    }
+    return caps;
+}
+
+function getRequirementsByName(res, name) {
+    var caps = [];
+    for (var v in res.requirements) {
+        if (res.requirements[v].name == name) {
+            caps.push(res.requirements[v]);
+        }
+    }
+    return caps;
+}
+
+function createDetailedTable(enclosing, name, headers, rows, callback) {
+    if (rows && rows.length > 0) {
+        var uuid = jQuery.uuid();
+        var title = createElement('span', null, null, [
+                                createElement('span', 'ui-icon ui-icon-triangle-1-e', { id: "img"+uuid, style: {display: "inline-block"} }),
+                                text(" "),
+                                text(name)
+                             ]);
+        enclosing.append(tr(null, null, [
+            td(null, null, [ title ]),
+            td(null, null, [ createElement('table', 'nicetable ui-widget ui-helper-hidden', { id: "alt1"+uuid }, [
+                                createElement('thead', null, null, [
+                                    tr(null, null, headers.map(function(x) {
+                                        return th('ui-widget-header', null, [text(x)]);
+                                    }))
+                                ]),
+                                createElement('tbody', null, null,
+                                    rows.map(function(x) {
+                                        var values = callback(x);
+                                        var tds = values.map(function(x) {
+                                            return td(null, null, [x]);
+                                        });
+                                        return tr(null, null, tds);
+                                    })
+                                )
+                             ]),
+                             createElement('span', null, { id: "alt2"+uuid }, [
+                                text(rows.length)
+                             ])
+            ])
+        ]));
+        $(title).
+                unbind('click').
+                click(function(event) {
+                    event.preventDefault();
+                    $("#img"+uuid).toggleClass('ui-icon-triangle-1-s').//down
+                                   toggleClass('ui-icon-triangle-1-e');//right
+                    $("#alt1"+uuid).toggle();
+                    $("#alt2"+uuid).toggle();
+                });
+    }
+}
+
+function trim(stringToTrim) {
+	return stringToTrim.replace(/^\s+|\s+$/g,"");
+}
+
+function parseSimpleFilter(filter) {
+    filter = filter.substring(1, filter.length-1);
+    var start = 0;
+    var pos = 0;
+    var c = filter.charAt(pos);
+    while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
+        if (c == '<' && filterChars[pos+1] == '*') {
+            break;
+        }
+        if (c == '*' && filterChars[pos+1] == '>') {
+            break;
+        }
+        pos++;
+        c = filter.charAt(pos);
+    }
+    if (pos == start) {
+        throw ("Missing attr: " + filter.substring(pos));
+    }
+
+    var attr = trim(filter.substring(start, pos));
+    var oper = filter.substring(pos, pos+2);
+    var value;
+    if (oper == '*>' || oper == '~=' || oper == '>=' || oper == '<=' || oper == '<*') {
+        value = trim(filter.substring(pos+2));
+        if (value == '') {
+            throw ("Missing value: " + filter.substring(pos));
+        }
+
+        return { operator: oper, operands: [ attr, value ]};
+    } else {
+        if (c != '=') {
+            throw ("Invalid operator: " + filter.substring(pos));
+        }
+        oper = '=';
+        value = filter.substring(pos+1);
+        if (value == '*' ) {
+            return { operator: '=*', operands: [ attr ]};
+        }
+        return { operator: '=', operands: [ attr, value ]};
+    }
+}
+
+function parseFilter(filter) {
+    if (filter.charAt(0) != "(" || filter.charAt(filter.length-1) != ")") {
+        throw "Wrong parenthesis: " + filter;
+    }
+    if (filter.charAt(1) == "!") {
+        return { operator: filter.charAt(1), operands: [ parseFilter(filter.substring(2, filter.length-1)) ] };
+    }
+    if (filter.charAt(1) == "|" || filter.charAt(1) == "&") {
+        var inner = filter.substring(2, filter.length-1);
+        var flts = inner.match(/\([^\(\)]*(\([^\(\)]*(\([^\(\)]*(\([^\(\)]*\))*[^\(\)]*\))*[^\(\)]*\))*[^\(\)]*\)/g);
+        return { operator: filter.charAt(1), operands: flts.map(function(x) { return parseFilter(x); }) };
+    }
+    return parseSimpleFilter(filter);
+}
+
+function simplify(filter) {
+    if (filter.operator == '&' || filter.operator == '|') {
+        filter.operands = filter.operands.map(function(x) { return simplify(x); });
+    } else if (filter.operator == '!') {
+        if (filter.operands[0].operator == '<=') {
+            filter.operator = '>';
+            filter.operands = filter.operands[0].operands;
+        } else if (filter.operands[0].operator == '>=') {
+            filter.operator = '<';
+            filter.operands = filter.operands[0].operands;
+        }
+    }
+    return filter;
+}
+
+function addRow(tbody, key, value) {
+    if (value) {
+        tbody.append( tr(null, null, [
+            td(null, null, [ text(key) ]),
+            td(null, null, [ text(value) ])
+        ]));
+    }
+}
+
+function renderDetailedResource(res) {
+    var tbody = $('#detailsTableBody');
+
+    tbody.append( tr(null, null, [
+        th('ui-widget-header', null, [
+            text("Resource")
+        ]),
+        th('ui-widget-header', null, [
+            createElement('form', 'button-group', { method: "post"}, [
+                createElement('input', null, { type: "hidden", name: "bundle", value: res.id}),
+                createElement('input', 'ui-state-default ui-corner-all', { type: "submit", name: "deploy", value: "Deploy" }, [ text("dummy")]),
+                createElement('input', 'ui-state-default ui-corner-all', { type: "submit", name: "deploystart", value: "Start" }, [ text("dummy")]),
+                text(" "),
+                createElement('input', 'ui-state-default ui-corner-all', { id: "optional", type: "checkbox", name: "optional" }),
+                text(" "),
+                createElement('label', 'ui-widget', { 'for': "optional" }, [ text("optional") ])
+            ])
+        ])
+    ]));
+
+    addRow(tbody, "Name", res.presentationname);
+    addRow(tbody, "Description", res.description);
+    addRow(tbody, "Symbolic name", res.symbolicname);
+    addRow(tbody, "Version", res.version);
+    addRow(tbody, "URI", res.uri);
+    addRow(tbody, "Documentation", res.documentation);
+    addRow(tbody, "Javadoc", res.javadoc);
+    addRow(tbody, "Source", res.source);
+    addRow(tbody, "License", res.license);
+    addRow(tbody, "Copyright", res.copyright);
+    addRow(tbody, "Size", res.size);
+
+    // Exported packages
+    createDetailedTable(tbody, "Exported packages", ["Package", "Version"],
+                        getCapabilitiesByName(res, "package").sort(function(a,b) {
+                            var pa = a.properties['package'], pb = b.properties['package']; return pa == pb ? 0 : pa < pb ? -1 : +1;
+                        }),
+                        function(p) {
+                            return [ text(p.properties['package']), text(p.properties['version']) ];
+                        });
+    // Exported services
+    createDetailedTable(tbody, "Exported services", ["Service"], getCapabilitiesByName(res, "service"), function(p) {
+                            return [ text(p.properties['service']) ];
+                        });
+    // Imported packages
+    createDetailedTable(tbody, "Imported packages", ["Package", "Version", "Optional"], getRequirementsByName(res, "package"), function(p) {
+                            var f = parseFilter(p.filter);
+                            simplify(f);
+                            var n, vmin = "[0.0.0", vmax = "infinity)";
+                            if (f.operator == '&') {
+                                for (var i in f.operands) {
+                                    var fi = f.operands[i];
+                                    if (fi.operands[0] == 'package' && fi.operator == '=') {
+                                        n = fi.operands[1];
+                                    }
+                                    if (fi.operands[0] == 'version') {
+                                        if (fi.operator == '>=') {
+                                            vmin = '[' + fi.operands[1];
+                                        }
+                                        if (fi.operator == '>') {
+                                            vmin = '(' + fi.operands[1];
+                                        }
+                                        if (fi.operator == '<=') {
+                                            vmax = fi.operands[1] + "]";
+                                        }
+                                        if (fi.operator == '<') {
+                                            vmax = fi.operands[1] + ")";
+                                        }
+                                    }
+                                }
+                            }
+                            return [ text(n ? n : p.filter), text(vmin + ", " + vmax), text(p.optional) ];
+                        });
+    // Imported bundles
+    createDetailedTable(tbody, "Imported bundles", ["Bundle", "Version", "Optional"], getRequirementsByName(res, "bundle"), function(p) {
+                            return [ text(p.filter), text(""), text(p.optional) ];
+                        });
+    // Imported services
+    createDetailedTable(tbody, "Imported bundles", ["Service", "Optional"], getRequirementsByName(res, "service"), function(p) {
+                            return [ text(p.filter), text(p.optional) ];
+                        });
+    // 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) });
+                            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) });
+                            a.appendChild(text(p.presentationname ? p.presentationname : p.symbolicname));
+                            return [ a, text(p.version) ];
+                        });
+    // Unsatisfied requirements
+    createDetailedTable(tbody, "Unsatisfied Requirements", ["Requirement", "Optional"], res.unsatisfied, function(p) {
+                            return [ text(p.filter), text(p.optional) ];
+                        });
+
+//    $('#detailsTableBody').append( tr(null, null, [ th('ui-widget-header', { colspan: 2 }, [ text("Resource") ]) ]) );
+//    $('#detailsTableBody').append( tbody );
+}
+
 function renderRepository(repo) {
 	var _tr = repoTableTemplate.clone();
 	_tr.find('td:eq(0)').text( repo.name );
@@ -87,21 +404,27 @@
 		doRepoAction('delete', repo.url);
 	});
 	repoTable.append(_tr);
-	
-	for(var i in repo.resources) {
-		renderResource( repo.resources[i] );
-	}
 }
 
-function renderData(data) {
-	obrData = data;
+function renderData() {
 	repoTable.empty();
 	resTable.empty();
-	if ( data.status ) {
+	if ( obrData.status ) {
 		$('.statline').html(i18n.status_ok);
 		ifStatusOK.removeClass('ui-helper-hidden');
-		for (var i in data.repositories ) {
-			renderRepository( data.repositories[i] );
+		for (var i in obrData.repositories ) {
+			renderRepository( obrData.repositories[i] );
+		}
+		if ($.getUrlVar('details')) {
+		    $('#resTable').addClass('ui-helper-hidden');
+		    $('#detailsTable').removeClass('ui-helper-hidden');
+		    for (var i in obrData.resources ) {
+			    renderDetailedResource( obrData.resources[i] );
+            }
+		} else {
+		    for (var i in obrData.resources ) {
+			    renderResource( obrData.resources[i] );
+            }
 		}
 	} else {
 		$('.statline').html(i18n.status_no);
@@ -109,6 +432,31 @@
 	}
 }
 
+
+$.extend({
+  getUrlVars: function(){
+    var vars = [], hash;
+    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
+    for(var i = 0; i < hashes.length; i++)
+    {
+      var j = hashes[i].indexOf('=');
+      if (j > 0) {
+        var k = hashes[i].slice(0, j);
+        var v = hashes[i].slice(j + 1);
+        vars.push(k);
+        vars[k] = v;
+      } else {
+        vars.push(hashes[i]);
+        vars[hashes[i]] = true;
+      }
+    }
+    return vars;
+  },
+  getUrlVar: function(name){
+    return $.getUrlVars()[name];
+  }
+});
+
 $(document).ready( function() {
 	repoTable = $('#repoTable tbody');
 	repoTableTemplate = repoTable.find('tr').clone();
@@ -116,14 +464,24 @@
 	resTable = $('#resTable tbody').empty();
 	searchField = $('#searchField');
 	ifStatusOK = $('#ifStatusOK');
+    searchField.val($.getUrlVar('query'));
 
-	$('#addRepoBtn').click(function() {
+	$('#addRepoBtn').click(function(event) {
+        event.preventDefault();
 		doRepoAction('add', addRepoUri.val());
 	});
-	$('#searchBtn').click(function() {
-		renderData(obrData);
-		return false;
+	$('#searchBtn').click(function(event) {
+        event.preventDefault();
+        window.location.href = window.location.pathname + '?query=' + searchField.val();
 	});
+	searchField.keypress(function(event) {
+        if (event.keyCode == 13) {
+            event.preventDefault();
+            $('#searchBtn').click();
+        }
+	});
+
+	renderData();
+    initStaticWidgets();
+});
 
-	renderData(obrData);
-});
\ No newline at end of file

Modified: felix/trunk/webconsole/src/main/resources/templates/obr.html
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/resources/templates/obr.html?rev=919175&r1=919174&r2=919175&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/resources/templates/obr.html [UTF-8] (original)
+++ felix/trunk/webconsole/src/main/resources/templates/obr.html [UTF-8] Thu Mar  4 20:53:59 2010
@@ -1,4 +1,5 @@
-<script type="text/javascript" src="res/ui/obr.js"></script>
+<script type="text/javascript" src="res/ui/jquery.uuid.js"></script>
+<script type="text/javascript" src="res/ui/obr.js"></script>
 <script type="text/javascript">
 var i18n = {
 	status_ok : '${obr.status.ok}',
@@ -44,28 +45,57 @@
 
 <br/>
 
-<form id="installForm" method="post" action="">
-	<div class="ui-widget-header ui-corner-top buttonGroup">
-		<span style="float: left; margin-left: 1em">${obr.res.title}</span>
-		<input type="text" id="searchField" />
-		<button id="searchBtn">${obr.action.search}</button>
-		<input type="submit" name="deploy" value="${obr.action.deploy}" />
-		<input type="submit" name="deploystart" value="${obr.action.deploystart}" />
-	</div>
-
-	<table id="resTable" class="nicetable">
-		<thead>
-			<tr>
-				<th class="col_Version">${version}</th>
-				<th class="col_ResName">${obr.res.name}</th>
-				<th class="col_VersionInst">${obr.res.installedVer}</th>
-			</tr>
-		</thead>
-		<tbody>
-			<tr><td colspan="2">dummy</td></tr>
-		</tbody>
-	</table>
-</form>
+<div class="ui-widget-header ui-corner-top buttonGroup">
+    <span style="float: left; margin-left: 1em">${obr.res.title}</span>
+    <span>
+        <a href="obr?list=a">A</a>
+        <a href="obr?list=b">B</a>
+        <a href="obr?list=c">C</a>
+        <a href="obr?list=d">D</a>
+        <a href="obr?list=e">E</a>
+        <a href="obr?list=f">F</a>
+        <a href="obr?list=g">G</a>
+        <a href="obr?list=h">H</a>
+        <a href="obr?list=i">I</a>
+        <a href="obr?list=j">J</a>
+        <a href="obr?list=k">K</a>
+        <a href="obr?list=l">L</a>
+        <a href="obr?list=m">M</a>
+        <a href="obr?list=n">N</a>
+        <a href="obr?list=o">O</a>
+        <a href="obr?list=p">P</a>
+        <a href="obr?list=q">Q</a>
+        <a href="obr?list=r">R</a>
+        <a href="obr?list=s">S</a>
+        <a href="obr?list=t">T</a>
+        <a href="obr?list=u">U</a>
+        <a href="obr?list=v">V</a>
+        <a href="obr?list=w">W</a>
+        <a href="obr?list=x">X</a>
+        <a href="obr?list=y">Y</a>
+        <a href="obr?list=z">Z</a>
+        <a href="obr?list=-">?</a>
+    </span>
+    <input type="text" id="searchField"/>
+    <button id="searchBtn">${obr.action.search}</button>
+</div>
+
+<table id="resTable" class="nicetable">
+    <thead>
+        <tr>
+            <th class="col_ResName">${obr.res.name}</th>
+            <th class="col_VersionInst">${obr.res.installedVer}</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr><td colspan="2">dummy</td></tr>
+    </tbody>
+</table>
+
+<table id="detailsTable" class="nicetable ui-helper-hidden">
+    <tbody id="detailsTableBody">
+    </tbody>
+</table>
 
 <br/>
 



Re: svn commit: r919175 - in /felix/trunk/webconsole: ./ src/main/java/org/apache/felix/webconsole/internal/core/ src/main/java/org/apache/felix/webconsole/internal/obr/ src/main/resources/res/ui/ src/main/resources/templates/

Posted by Felix Meschberger <fm...@gmail.com>.
Hi,

On 05.03.2010 16:05, Guillaume Nodet wrote:
> On Fri, Mar 5, 2010 at 15:57, Felix Meschberger <fm...@gmail.com> wrote:
>> Hi,
>>
>> This patch has a few issues IMHO:
>>
>> On 04.03.2010 21:54, gnodet@apache.org wrote:
>>> Added:
>>>     felix/trunk/webconsole/src/main/resources/res/ui/jquery.uuid.js
>>
>> For the jquery.uuid.js plugin we need the NOTICE entry and LICENSE file
>>
>>> -    // Define a constant of that name to prevent NoClassDefFoundError in
>>> -    // updateFromOBR trying to load the class with RepositoryAdmin.class
>>> -    private static final String REPOSITORY_ADMIN_NAME = "org.osgi.service.obr.RepositoryAdmin";
>>
>> The reason for me to define this as a string is to prevent an exception
>> if the RepositoryAdmin service API cannot be imported because there is
>> no API provider bundle installed.
>>
> 
> Well, I'd have no problem if the class would not use the RepositoryAdmin.
> Given it *is* used, using a string here does not make sense.
> In addition, the webconsole starts correctly if the package is not available.
> I've checked that (though I may have missed something).

You are right. It looks like the plugin cannot be activated if the
RepositoryAdmin API is not available. Previously the plugin could be
activated and displayed.

And  intererstingly, just defining a variable with the RepositoryAdmin
type does not constitute a requirement to load the class, thus

   RepositoryAdmin admin = getService("RepositoryAdmin");

just silently sets the admin field to null if neither the type nor the
actual service is avaible. Thus the string-thing worked perfectly.

But it is ok for me, too that the plugin is not activated at all if the
API is not available. So lets keep it like it is right now.

Regards
Felix

> 
>>> +        <a href="obr?list=a">A</a>
>>
>> While chances are, the label will never change, I nevertheless think, it
>> is not optimal to hard code this here.
>>
>> This could probably be written as
>>
>>           <a href="${pluginRoot}?list=a">A</a>
>>
>> to ensure the correct URL is used regardless of how the actual bundle is
>> configured/set up/coded.
> 
> Yeah, makes sense.
> 
>> Also there is a function added stemming from the Mozilla project. This
>> should probably also be noted in the NOTICE/LICENSE files, right ?
> 
> Will update the NOTICE/LICENSE files for this function and the jquery.uuid.js
> Thx for the reminder.
> 
>> Sorry, to bother.
>>
>> Regards
>> Felix
>>
> 
> 
> 

Re: svn commit: r919175 - in /felix/trunk/webconsole: ./ src/main/java/org/apache/felix/webconsole/internal/core/ src/main/java/org/apache/felix/webconsole/internal/obr/ src/main/resources/res/ui/ src/main/resources/templates/

Posted by Guillaume Nodet <gn...@gmail.com>.
On Fri, Mar 5, 2010 at 15:57, Felix Meschberger <fm...@gmail.com> wrote:
> Hi,
>
> This patch has a few issues IMHO:
>
> On 04.03.2010 21:54, gnodet@apache.org wrote:
>> Added:
>>     felix/trunk/webconsole/src/main/resources/res/ui/jquery.uuid.js
>
> For the jquery.uuid.js plugin we need the NOTICE entry and LICENSE file
>
>> -    // Define a constant of that name to prevent NoClassDefFoundError in
>> -    // updateFromOBR trying to load the class with RepositoryAdmin.class
>> -    private static final String REPOSITORY_ADMIN_NAME = "org.osgi.service.obr.RepositoryAdmin";
>
> The reason for me to define this as a string is to prevent an exception
> if the RepositoryAdmin service API cannot be imported because there is
> no API provider bundle installed.
>

Well, I'd have no problem if the class would not use the RepositoryAdmin.
Given it *is* used, using a string here does not make sense.
In addition, the webconsole starts correctly if the package is not available.
I've checked that (though I may have missed something).

>> +        <a href="obr?list=a">A</a>
>
> While chances are, the label will never change, I nevertheless think, it
> is not optimal to hard code this here.
>
> This could probably be written as
>
>           <a href="${pluginRoot}?list=a">A</a>
>
> to ensure the correct URL is used regardless of how the actual bundle is
> configured/set up/coded.

Yeah, makes sense.

> Also there is a function added stemming from the Mozilla project. This
> should probably also be noted in the NOTICE/LICENSE files, right ?

Will update the NOTICE/LICENSE files for this function and the jquery.uuid.js
Thx for the reminder.

> Sorry, to bother.
>
> Regards
> Felix
>



-- 
Cheers,
Guillaume Nodet
------------------------
Blog: http://gnodet.blogspot.com/
------------------------
Open Source SOA
http://fusesource.com

Re: svn commit: r919175 - in /felix/trunk/webconsole: ./ src/main/java/org/apache/felix/webconsole/internal/core/ src/main/java/org/apache/felix/webconsole/internal/obr/ src/main/resources/res/ui/ src/main/resources/templates/

Posted by Felix Meschberger <fm...@gmail.com>.
Hi,

This patch has a few issues IMHO:

On 04.03.2010 21:54, gnodet@apache.org wrote:
> Added:
>     felix/trunk/webconsole/src/main/resources/res/ui/jquery.uuid.js

For the jquery.uuid.js plugin we need the NOTICE entry and LICENSE file

> -    // Define a constant of that name to prevent NoClassDefFoundError in
> -    // updateFromOBR trying to load the class with RepositoryAdmin.class
> -    private static final String REPOSITORY_ADMIN_NAME = "org.osgi.service.obr.RepositoryAdmin";

The reason for me to define this as a string is to prevent an exception
if the RepositoryAdmin service API cannot be imported because there is
no API provider bundle installed.

> +        <a href="obr?list=a">A</a>

While chances are, the label will never change, I nevertheless think, it
is not optimal to hard code this here.

This could probably be written as

           <a href="${pluginRoot}?list=a">A</a>

to ensure the correct URL is used regardless of how the actual bundle is
configured/set up/coded.

Also there is a function added stemming from the Mozilla project. This
should probably also be noted in the NOTICE/LICENSE files, right ?

Sorry, to bother.

Regards
Felix