FELIX-566 next step in making bundle display more REST-ful:

    requesting ..../bundles lists all bundles
    requesting ..../bundles/<id> just lists the bundle with the given <id> and its details

bundle actions (start, stop, uninstall, refreshPackages) is now
integrated with the single BundlesServlet handling the bundles
requests

The BundlesServlet is the default start page

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@667270 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/AjaxBundleDetailsAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/AjaxBundleDetailsAction.java
deleted file mode 100644
index b3a312d..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/AjaxBundleDetailsAction.java
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
- * 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.webconsole.internal.core;
-
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-
-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.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.Version;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.component.ComponentConstants;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.service.startlevel.StartLevel;
-
-
-public class AjaxBundleDetailsAction extends BundleAction
-{
-
-    public static final String NAME = "ajaxBundleDetails";
-
-    // bootdelegation property entries. wildcards are converted to package
-    // name prefixes. whether an entry is a wildcard or not is set as a flag
-    // in the bootPkgWildcards array.
-    // see #activate and #isBootDelegated
-    private String[] bootPkgs;
-
-    // a flag for each entry in bootPkgs indicating whether the respective
-    // entry was declared as a wildcard or not
-    // see #activate and #isBootDelegated
-    private boolean[] bootPkgWildcards;
-
-
-    public void activate( BundleContext bundleContext )
-    {
-        super.activate( bundleContext );
-
-        // bootdelegation property parsing from Apache Felix R4SearchPolicyCore
-        String bootDelegation = bundleContext.getProperty( Constants.FRAMEWORK_BOOTDELEGATION );
-        bootDelegation = ( bootDelegation == null ) ? "java.*" : bootDelegation + ",java.*";
-        StringTokenizer st = new StringTokenizer( bootDelegation, " ," );
-        bootPkgs = new String[st.countTokens()];
-        bootPkgWildcards = new boolean[bootPkgs.length];
-        for ( int i = 0; i < bootPkgs.length; i++ )
-        {
-            bootDelegation = st.nextToken();
-            if ( bootDelegation.endsWith( "*" ) )
-            {
-                bootPkgWildcards[i] = true;
-                bootDelegation = bootDelegation.substring( 0, bootDelegation.length() - 1 );
-            }
-            bootPkgs[i] = bootDelegation;
-        }
-    }
-
-
-    public String getName()
-    {
-        return NAME;
-    }
-
-
-    public String getLabel()
-    {
-        return NAME;
-    }
-
-
-    public boolean performAction( HttpServletRequest request, HttpServletResponse response ) throws IOException
-    {
-        JSONObject result = null;
-        try
-        {
-            long bundleId = getBundleId( request );
-            Bundle bundle = getBundleContext().getBundle( bundleId );
-            if ( bundle != null )
-            {
-                Dictionary headers = bundle.getHeaders();
-
-                JSONArray props = new JSONArray();
-                keyVal( props, "Symbolic Name", bundle.getSymbolicName() );
-                keyVal( props, "Version", headers.get( Constants.BUNDLE_VERSION ) );
-                keyVal( props, "Location", bundle.getLocation() );
-                keyVal( props, "Last Modification", new Date( bundle.getLastModified() ) );
-
-                keyVal( props, "Vendor", headers.get( Constants.BUNDLE_VENDOR ) );
-                keyVal( props, "Copyright", headers.get( Constants.BUNDLE_COPYRIGHT ) );
-                keyVal( props, "Description", headers.get( Constants.BUNDLE_DESCRIPTION ) );
-
-                keyVal( props, "Start Level", getStartLevel( bundle ) );
-
-                if ( bundle.getState() == Bundle.INSTALLED )
-                {
-                    listImportExportsUnresolved( props, bundle );
-                }
-                else
-                {
-                    listImportExport( props, bundle );
-                }
-
-                listServices( props, bundle );
-
-                result = new JSONObject();
-                result.put( BundleListRender.BUNDLE_ID, bundleId );
-                result.put( "props", props );
-            }
-        }
-        catch ( Exception exception )
-        {
-            // create an empty result on problems
-            result = new JSONObject();
-        }
-
-        // send the result
-        response.setContentType( "text/javascript" );
-        response.getWriter().print( result.toString() );
-
-        return false;
-    }
-
-
-    private Integer getStartLevel( Bundle bundle )
-    {
-        StartLevel sl = getStartLevel();
-        return ( sl != null ) ? new Integer( sl.getBundleStartLevel( bundle ) ) : null;
-    }
-
-
-    private void listImportExport( JSONArray props, Bundle bundle )
-    {
-        PackageAdmin packageAdmin = getPackageAdmin();
-        if ( packageAdmin == null )
-        {
-            return;
-        }
-
-        Map usingBundles = new TreeMap();
-
-        ExportedPackage[] exports = packageAdmin.getExportedPackages( bundle );
-        if ( exports != null && exports.length > 0 )
-        {
-            // do alphabetical sort
-            Arrays.sort( exports, new Comparator()
-            {
-                public int compare( ExportedPackage p1, ExportedPackage p2 )
-                {
-                    return p1.getName().compareTo( p2.getName() );
-                }
-
-
-                public int compare( Object o1, Object o2 )
-                {
-                    return compare( ( ExportedPackage ) o1, ( ExportedPackage ) o2 );
-                }
-            } );
-
-            StringBuffer val = new StringBuffer();
-            for ( int j = 0; j < exports.length; j++ )
-            {
-                ExportedPackage export = exports[j];
-                printExport( val, export.getName(), export.getVersion() );
-                Bundle[] ubList = export.getImportingBundles();
-                if ( ubList != null )
-                {
-                    for ( int i = 0; i < ubList.length; i++ )
-                    {
-                        Bundle ub = ubList[i];
-                        usingBundles.put( ub.getSymbolicName(), ub );
-                    }
-                }
-            }
-            keyVal( props, "Exported Packages", val.toString() );
-        }
-        else
-        {
-            keyVal( props, "Exported Packages", "None" );
-        }
-
-        exports = packageAdmin.getExportedPackages( ( Bundle ) null );
-        if ( exports != null && exports.length > 0 )
-        {
-            // collect import packages first
-            final List imports = new ArrayList();
-            for ( int i = 0; i < exports.length; i++ )
-            {
-                final ExportedPackage ep = exports[i];
-                final Bundle[] importers = ep.getImportingBundles();
-                for ( int j = 0; importers != null && j < importers.length; j++ )
-                {
-                    if ( importers[j].getBundleId() == bundle.getBundleId() )
-                    {
-                        imports.add( ep );
-
-                        break;
-                    }
-                }
-            }
-            // now sort
-            StringBuffer val = new StringBuffer();
-            if ( imports.size() > 0 )
-            {
-                final ExportedPackage[] packages = ( ExportedPackage[] ) imports.toArray( new ExportedPackage[imports
-                    .size()] );
-                Arrays.sort( packages, new Comparator()
-                {
-                    public int compare( ExportedPackage p1, ExportedPackage p2 )
-                    {
-                        return p1.getName().compareTo( p2.getName() );
-                    }
-
-
-                    public int compare( Object o1, Object o2 )
-                    {
-                        return compare( ( ExportedPackage ) o1, ( ExportedPackage ) o2 );
-                    }
-                } );
-                // and finally print out
-                for ( int i = 0; i < packages.length; i++ )
-                {
-                    ExportedPackage ep = packages[i];
-                    printImport( val, ep.getName(), ep.getVersion(), ep );
-                }
-            }
-            else
-            {
-                // add description if there are no imports
-                val.append( "None" );
-            }
-
-            keyVal( props, "Imported Packages", val.toString() );
-        }
-
-        if ( !usingBundles.isEmpty() )
-        {
-            StringBuffer val = new StringBuffer();
-            for ( Iterator ui = usingBundles.values().iterator(); ui.hasNext(); )
-            {
-                Bundle usingBundle = ( Bundle ) ui.next();
-                val.append( getBundleDescriptor( usingBundle ) );
-                val.append( "<br />" );
-            }
-            keyVal( props, "Importing Bundles", val.toString() );
-        }
-    }
-
-
-    private void listImportExportsUnresolved( JSONArray props, Bundle bundle )
-    {
-        Dictionary dict = bundle.getHeaders();
-
-        String target = ( String ) dict.get( Constants.EXPORT_PACKAGE );
-        if ( target != null )
-        {
-            R4Package[] pkgs = R4Package.parseImportOrExportHeader( target );
-            if ( pkgs != null && pkgs.length > 0 )
-            {
-                // do alphabetical sort
-                Arrays.sort( pkgs, new Comparator()
-                {
-                    public int compare( R4Package p1, R4Package p2 )
-                    {
-                        return p1.getName().compareTo( p2.getName() );
-                    }
-
-
-                    public int compare( Object o1, Object o2 )
-                    {
-                        return compare( ( R4Package ) o1, ( R4Package ) o2 );
-                    }
-                } );
-
-                StringBuffer val = new StringBuffer();
-                for ( int i = 0; i < pkgs.length; i++ )
-                {
-                    R4Export export = new R4Export( pkgs[i] );
-                    printExport( val, export.getName(), export.getVersion() );
-                }
-                keyVal( props, "Exported Packages", val.toString() );
-            }
-            else
-            {
-                keyVal( props, "Exported Packages", "None" );
-            }
-        }
-
-        target = ( String ) dict.get( Constants.IMPORT_PACKAGE );
-        if ( target != null )
-        {
-            R4Package[] pkgs = R4Package.parseImportOrExportHeader( target );
-            if ( pkgs != null && pkgs.length > 0 )
-            {
-                Map imports = new TreeMap();
-                for ( int i = 0; i < pkgs.length; i++ )
-                {
-                    R4Package pkg = pkgs[i];
-                    imports.put( pkg.getName(), new R4Import( pkg ) );
-                }
-
-                // collect import packages first
-                final Map candidates = new HashMap();
-                PackageAdmin packageAdmin = getPackageAdmin();
-                if ( packageAdmin != null )
-                {
-                    ExportedPackage[] exports = packageAdmin.getExportedPackages( ( Bundle ) null );
-                    if ( exports != null && exports.length > 0 )
-                    {
-
-                        for ( int i = 0; i < exports.length; i++ )
-                        {
-                            final ExportedPackage ep = exports[i];
-
-                            R4Import imp = ( R4Import ) imports.get( ep.getName() );
-                            if ( imp != null && imp.isSatisfied( toR4Export( ep ) ) )
-                            {
-                                candidates.put( ep.getName(), ep );
-                            }
-                        }
-                    }
-                }
-
-                // now sort
-                StringBuffer val = new StringBuffer();
-                if ( imports.size() > 0 )
-                {
-                    for ( Iterator ii = imports.values().iterator(); ii.hasNext(); )
-                    {
-                        R4Import r4Import = ( R4Import ) ii.next();
-                        ExportedPackage ep = ( ExportedPackage ) candidates.get( r4Import.getName() );
-
-                        // if there is no matching export, check whether this
-                        // bundle has the package, ignore the entry in this case
-                        if ( ep == null )
-                        {
-                            String path = r4Import.getName().replace( '.', '/' );
-                            if ( bundle.getResource( path ) != null )
-                            {
-                                continue;
-                            }
-                        }
-
-                        printImport( val, r4Import.getName(), r4Import.getVersion(), ep );
-                    }
-                }
-                else
-                {
-                    // add description if there are no imports
-                    val.append( "None" );
-                }
-
-                keyVal( props, "Imported Packages", val.toString() );
-            }
-        }
-    }
-
-
-    private void listServices( JSONArray props, Bundle bundle )
-    {
-        ServiceReference[] refs = bundle.getRegisteredServices();
-        if ( refs == null || refs.length == 0 )
-        {
-            return;
-        }
-
-        for ( int i = 0; i < refs.length; i++ )
-        {
-            String key = "Service ID " + refs[i].getProperty( Constants.SERVICE_ID );
-
-            StringBuffer val = new StringBuffer();
-
-            appendProperty( val, refs[i], Constants.OBJECTCLASS, "Types" );
-            appendProperty( val, refs[i], Constants.SERVICE_PID, "PID" );
-            appendProperty( val, refs[i], ConfigurationAdmin.SERVICE_FACTORYPID, "Factory PID" );
-            appendProperty( val, refs[i], ComponentConstants.COMPONENT_NAME, "Component Name" );
-            appendProperty( val, refs[i], ComponentConstants.COMPONENT_ID, "Component ID" );
-            appendProperty( val, refs[i], ComponentConstants.COMPONENT_FACTORY, "Component Factory" );
-            appendProperty( val, refs[i], Constants.SERVICE_DESCRIPTION, "Description" );
-            appendProperty( val, refs[i], Constants.SERVICE_VENDOR, "Vendor" );
-
-            keyVal( props, key, val.toString() );
-        }
-    }
-
-
-    private void appendProperty( StringBuffer dest, ServiceReference ref, String name, String label )
-    {
-        Object value = ref.getProperty( name );
-        if ( value instanceof Object[] )
-        {
-            Object[] values = ( Object[] ) value;
-            dest.append( label ).append( ": " );
-            for ( int j = 0; j < values.length; j++ )
-            {
-                if ( j > 0 )
-                    dest.append( ", " );
-                dest.append( values[j] );
-            }
-            dest.append( "<br />" ); // assume HTML use of result
-        }
-        else if ( value != null )
-        {
-            dest.append( label ).append( ": " ).append( value ).append( "<br />" );
-        }
-    }
-
-
-    private void keyVal( JSONArray props, String key, Object value )
-    {
-        if ( key != null && value != null )
-        {
-            try
-            {
-                JSONObject obj = new JSONObject();
-                obj.put( "key", key );
-                obj.put( "value", value );
-                props.put( obj );
-            }
-            catch ( JSONException je )
-            {
-                // don't care
-            }
-        }
-    }
-
-
-    private void printExport( StringBuffer val, String name, Version version )
-    {
-        boolean bootDel = isBootDelegated( name );
-        if ( bootDel )
-        {
-            val.append( "<span style=\"color: red\">!! " );
-        }
-
-        val.append( name );
-        val.append( ",version=" );
-        val.append( version );
-
-        if ( bootDel )
-        {
-            val.append( " -- Overwritten by Boot Delegation</span>" );
-        }
-
-        val.append( "<br />" );
-    }
-
-
-    private void printImport( StringBuffer val, String name, Version version, ExportedPackage export )
-    {
-        boolean bootDel = isBootDelegated( name );
-        if ( bootDel || export == null )
-        {
-            val.append( "<span style=\"color: red\">!! " );
-        }
-
-        val.append( name );
-        val.append( ",version=" ).append( version );
-        val.append( " from " );
-
-        if ( export != null )
-        {
-            val.append( getBundleDescriptor( export.getExportingBundle() ) );
-
-            if ( bootDel )
-            {
-                val.append( " -- Overwritten by Boot Delegation</span>" );
-            }
-        }
-        else
-        {
-            val.append( " -- Cannot be resolved" );
-            if ( bootDel )
-            {
-                val.append( " and overwritten by Boot Delegation" );
-            }
-            val.append( "</span>" );
-        }
-
-        val.append( "<br />" );
-    }
-
-
-    // returns true if the package is listed in the bootdelegation property
-    private boolean isBootDelegated( String pkgName )
-    {
-
-        // bootdelegation analysis from Apache Felix R4SearchPolicyCore
-
-        // Only consider delegation if we have a package name, since
-        // we don't want to promote the default package. The spec does
-        // not take a stand on this issue.
-        if ( pkgName.length() > 0 )
-        {
-
-            // Delegate any packages listed in the boot delegation
-            // property to the parent class loader.
-            for ( int i = 0; i < bootPkgs.length; i++ )
-            {
-
-                // A wildcarded boot delegation package will be in the form of
-                // "foo.", so if the package is wildcarded do a startsWith() or
-                // a regionMatches() to ignore the trailing "." to determine if
-                // the request should be delegated to the parent class loader.
-                // If the package is not wildcarded, then simply do an equals()
-                // test to see if the request should be delegated to the parent
-                // class loader.
-                if ( ( bootPkgWildcards[i] && ( pkgName.startsWith( bootPkgs[i] ) || bootPkgs[i].regionMatches( 0,
-                    pkgName, 0, pkgName.length() ) ) )
-                    || ( !bootPkgWildcards[i] && bootPkgs[i].equals( pkgName ) ) )
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-
-    private R4Export toR4Export( ExportedPackage export )
-    {
-        R4Attribute version = new R4Attribute( Constants.VERSION_ATTRIBUTE, export.getVersion().toString(), false );
-        return new R4Export( export.getName(), null, new R4Attribute[]
-            { version } );
-    }
-
-
-    private String getBundleDescriptor( Bundle bundle )
-    {
-        StringBuffer val = new StringBuffer();
-        if ( bundle.getSymbolicName() != null )
-        {
-            // list the bundle name if not null
-            val.append( bundle.getSymbolicName() );
-            val.append( " (" ).append( bundle.getBundleId() );
-            val.append( ")" );
-        }
-        else if ( bundle.getLocation() != null )
-        {
-            // otherwise try the location
-            val.append( bundle.getLocation() );
-            val.append( " (" ).append( bundle.getBundleId() );
-            val.append( ")" );
-        }
-        else
-        {
-            // fallback to just the bundle id
-            // only append the bundle
-            val.append( bundle.getBundleId() );
-        }
-        return val.toString();
-    }
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleAction.java
index f68f46e..5cfb252 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleAction.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleAction.java
@@ -28,7 +28,7 @@
 
     protected long getBundleId( HttpServletRequest request )
     {
-        String bundleIdPar = request.getParameter( BundleListRender.BUNDLE_ID );
+        String bundleIdPar = request.getParameter( BundlesServlet.BUNDLE_ID );
         if ( bundleIdPar != null )
         {
             try
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleListRender.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
similarity index 90%
rename from webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleListRender.java
rename to webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
index 5418352..c775a57 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundleListRender.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
@@ -32,8 +32,6 @@
 import java.util.TreeMap;
 
 import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -43,7 +41,6 @@
 import org.apache.felix.bundlerepository.R4Package;
 import org.apache.felix.webconsole.internal.BaseWebConsolePlugin;
 import org.apache.felix.webconsole.internal.Util;
-import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONWriter;
 import org.osgi.framework.Bundle;
@@ -54,19 +51,19 @@
 import org.osgi.framework.Version;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.component.ComponentConstants;
+import org.osgi.service.log.LogService;
 import org.osgi.service.obr.Repository;
 import org.osgi.service.obr.RepositoryAdmin;
 import org.osgi.service.obr.Resource;
 import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.service.startlevel.StartLevel;
-import org.osgi.util.tracker.ServiceTracker;
 
 
 /**
- * The <code>BundleListRender</code> TODO
+ * The <code>BundlesServlet</code> TODO
  */
-public class BundleListRender extends BaseWebConsolePlugin
+public class BundlesServlet extends BaseWebConsolePlugin
 {
 
     public static final String NAME = "bundles";
@@ -169,12 +166,18 @@
 
     protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException
     {
+        String action = req.getParameter( "action" );
+        if ( "refreshPackages".equals( action ) )
+        {
+            getPackageAdmin().refreshPackages( null );
+        }
+
         boolean success = false;
         Bundle bundle = getBundle( req.getPathInfo() );
         long bundleId = bundle.getBundleId();
+
         if ( bundle != null )
         {
-            String action = req.getParameter( "action" );
             if ( "start".equals( action ) )
             {
                 // start bundle
@@ -185,7 +188,7 @@
                 }
                 catch ( BundleException be )
                 {
-                    // log
+                    getLog().log( LogService.LOG_ERROR, "Cannot start", be );
                 }
             }
             else if ( "stop".equals( action ) )
@@ -198,7 +201,7 @@
                 }
                 catch ( BundleException be )
                 {
-                    // log
+                    getLog().log( LogService.LOG_ERROR, "Cannot stop", be );
                 }
             }
             else if ( "update".equals( action ) )
@@ -217,11 +220,21 @@
                 }
                 catch ( BundleException be )
                 {
-                    // log
+                    getLog().log( LogService.LOG_ERROR, "Cannot uninstall", be );
                 }
             }
         }
 
+        if ( "refreshPackages".equals( action ) )
+        {
+            success = true;
+            getPackageAdmin().refreshPackages( null );
+
+            // refresh completely
+            bundle = null;
+            bundleId = -1;
+        }
+
         if ( success )
         {
             // redirect or 200
@@ -233,13 +246,20 @@
                 {
                     bundleInfo( jw, bundle, true );
                 }
-                else
+                else if ( bundleId >= 0 )
                 {
                     jw.object();
                     jw.key( "bundleId" );
                     jw.value( bundleId );
                     jw.endObject();
                 }
+                else
+                {
+                    jw.object();
+                    jw.key( "reload" );
+                    jw.value( true );
+                    jw.endObject();
+                }
             }
             catch ( JSONException je )
             {
@@ -303,7 +323,7 @@
 
             if ( bundles != null && bundles.length > 0 )
             {
-                sort( bundles );
+                Util.sort( bundles );
 
                 jw.key( "bundles" );
 
@@ -338,7 +358,7 @@
         jw.key( "bundleId" );
         jw.value( bundle.getBundleId() );
         jw.key( "name" );
-        jw.value( getName( bundle ) );
+        jw.value( Util.getName( bundle ) );
         jw.key( "state" );
         jw.value( toStateString( bundle.getState() ) );
         jw.key( "hasStart" );
@@ -448,32 +468,7 @@
     }
 
 
-    private void sort( Bundle[] bundles )
-    {
-        Arrays.sort( bundles, BUNDLE_NAME_COMPARATOR );
-    }
-
-
-    private static String getName( Bundle bundle )
-    {
-        String name = ( String ) bundle.getHeaders().get( Constants.BUNDLE_NAME );
-        if ( name == null || name.length() == 0 )
-        {
-            name = bundle.getSymbolicName();
-            if ( name == null )
-            {
-                name = bundle.getLocation();
-                if ( name == null )
-                {
-                    name = String.valueOf( bundle.getBundleId() );
-                }
-            }
-        }
-        return name;
-    }
-
-
-    public void performAction( JSONWriter jw, Bundle bundle ) throws JSONException
+    private void performAction( JSONWriter jw, Bundle bundle ) throws JSONException
     {
         jw.object();
         jw.key( BUNDLE_ID );
@@ -485,7 +480,7 @@
     }
 
 
-    public void bundleDetails( JSONWriter jw, Bundle bundle ) throws JSONException
+    private void bundleDetails( JSONWriter jw, Bundle bundle ) throws JSONException
     {
         Dictionary headers = bundle.getHeaders();
 
@@ -942,59 +937,4 @@
         return val.toString();
     }
 
-    // ---------- inner classes ------------------------------------------------
-
-    private static final Comparator BUNDLE_NAME_COMPARATOR = new Comparator()
-    {
-        public int compare( Object o1, Object o2 )
-        {
-            return compare( ( Bundle ) o1, ( Bundle ) o2 );
-        }
-
-
-        public int compare( Bundle b1, Bundle b2 )
-        {
-
-            // the same bundles
-            if ( b1 == b2 || b1.getBundleId() == b2.getBundleId() )
-            {
-                return 0;
-            }
-
-            // special case for system bundle, which always is first
-            if ( b1.getBundleId() == 0 )
-            {
-                return -1;
-            }
-            else if ( b2.getBundleId() == 0 )
-            {
-                return 1;
-            }
-
-            // compare the symbolic names
-            int snComp = getName( b1 ).compareToIgnoreCase( getName( b2 ) );
-            if ( snComp != 0 )
-            {
-                return snComp;
-            }
-
-            // same names, compare versions
-            Version v1 = Version.parseVersion( ( String ) b1.getHeaders().get( Constants.BUNDLE_VERSION ) );
-            Version v2 = Version.parseVersion( ( String ) b2.getHeaders().get( Constants.BUNDLE_VERSION ) );
-            int vComp = v1.compareTo( v2 );
-            if ( vComp != 0 )
-            {
-                return vComp;
-            }
-
-            // same version ? Not really, but then, we compare by bundle id
-            if ( b1.getBundleId() < b2.getBundleId() )
-            {
-                return -1;
-            }
-
-            // b1 id must be > b2 id because equality is already checked
-            return 1;
-        }
-    };
 }
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/RefreshPackagesAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/RefreshPackagesAction.java
deleted file mode 100644
index b149d8c..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/RefreshPackagesAction.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.webconsole.internal.core;
-
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-
-/**
- * The <code>RefreshPackagesAction</code> TODO
- */
-public class RefreshPackagesAction extends BundleAction
-{
-
-    public static final String NAME = "refreshPackages";
-
-    public static final String LABEL = "Refresh Packages";
-
-
-    public String getName()
-    {
-        return NAME;
-    }
-
-
-    public String getLabel()
-    {
-        return LABEL;
-    }
-
-
-    public boolean performAction( HttpServletRequest request, HttpServletResponse response )
-    {
-
-        getPackageAdmin().refreshPackages( null );
-
-        return true;
-    }
-
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/StartAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/StartAction.java
deleted file mode 100644
index 321321b..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/StartAction.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.webconsole.internal.core;
-
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.service.log.LogService;
-
-
-public class StartAction extends BundleAction
-{
-
-    public static final String NAME = "start";
-    public static final String LABEL = "Start";
-
-
-    public String getName()
-    {
-        return NAME;
-    }
-
-
-    public String getLabel()
-    {
-        return LABEL;
-    }
-
-
-    public boolean performAction( HttpServletRequest request, HttpServletResponse response )
-    {
-
-        long bundleId = this.getBundleId( request );
-        if ( bundleId > 0 )
-        { // cannot start system bundle !!
-            Bundle bundle = this.getBundleContext().getBundle( bundleId );
-            if ( bundle != null )
-            {
-                try
-                {
-                    bundle.start();
-                }
-                catch ( BundleException be )
-                {
-                    getLog().log( LogService.LOG_ERROR, "Cannot start", be );
-                }
-
-            }
-        }
-        return true;
-    }
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/StopAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/StopAction.java
deleted file mode 100644
index f876d16..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/StopAction.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.webconsole.internal.core;
-
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.service.log.LogService;
-
-
-/**
- * The <code>StopAction</code> TODO
- */
-public class StopAction extends BundleAction
-{
-
-    public static final String NAME = "stop";
-    public static final String LABEL = "Stop";
-
-
-    public String getName()
-    {
-        return NAME;
-    }
-
-
-    public String getLabel()
-    {
-        return LABEL;
-    }
-
-
-    public boolean performAction( HttpServletRequest request, HttpServletResponse response )
-    {
-
-        long bundleId = this.getBundleId( request );
-        if ( bundleId > 0 )
-        { // cannot stop system bundle !!
-            Bundle bundle = this.getBundleContext().getBundle( bundleId );
-            if ( bundle != null )
-            {
-                try
-                {
-                    bundle.stop();
-                }
-                catch ( BundleException be )
-                {
-                    getLog().log( LogService.LOG_ERROR, "Cannot stop", be );
-                }
-
-            }
-        }
-
-        return true;
-    }
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UninstallAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UninstallAction.java
deleted file mode 100644
index 32b451e..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/UninstallAction.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.webconsole.internal.core;
-
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.service.log.LogService;
-
-
-public class UninstallAction extends BundleAction
-{
-
-    public static final String NAME = "uninstall";
-    public static final String LABEL = "Uninstall";
-
-
-    public String getName()
-    {
-        return NAME;
-    }
-
-
-    public String getLabel()
-    {
-        return LABEL;
-    }
-
-
-    public boolean performAction( HttpServletRequest request, HttpServletResponse response )
-    {
-
-        long bundleId = this.getBundleId( request );
-        if ( bundleId > 0 )
-        { // cannot stop system bundle !!
-            Bundle bundle = this.getBundleContext().getBundle( bundleId );
-            if ( bundle != null )
-            {
-                try
-                {
-                    bundle.uninstall();
-                }
-                catch ( BundleException be )
-                {
-                    getLog().log( LogService.LOG_ERROR, "Cannot uninstall", be );
-                }
-
-            }
-        }
-
-        return true;
-    }
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/AssemblyListRender.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/AssemblyListRender.java
index 181c1bb..a4dc873 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/AssemblyListRender.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/AssemblyListRender.java
@@ -20,11 +20,11 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.felix.webconsole.internal.core.BundleListRender;
+import org.apache.felix.webconsole.internal.core.BundlesServlet;
 import org.osgi.framework.Bundle;
 
 
-public class AssemblyListRender extends BundleListRender
+public class AssemblyListRender extends BundlesServlet
 {
 
     public static final String NAME = "assemblyList";
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
index 4b5f4e1..f0b9933 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
@@ -44,9 +44,8 @@
 import org.apache.felix.webconsole.internal.compendium.ComponentConfigurationPrinter;
 import org.apache.felix.webconsole.internal.compendium.ComponentRenderAction;
 import org.apache.felix.webconsole.internal.compendium.ConfigManager;
-import org.apache.felix.webconsole.internal.core.BundleListRender;
+import org.apache.felix.webconsole.internal.core.BundlesServlet;
 import org.apache.felix.webconsole.internal.core.InstallAction;
-import org.apache.felix.webconsole.internal.core.RefreshPackagesAction;
 import org.apache.felix.webconsole.internal.core.SetStartLevelAction;
 import org.apache.felix.webconsole.internal.misc.ConfigurationRender;
 import org.apache.felix.webconsole.internal.obr.BundleRepositoryRender;
@@ -90,42 +89,44 @@
     private static final String PROP_MANAGER_ROOT = "manager.root";
 
     /**
-     * @scr.property value="list"
+     * @scr.property valueRef="DEFAULT_PAGE"
      */
     private static final String PROP_DEFAULT_RENDER = "default.render";
 
     /**
-     * @scr.property value="OSGi Management Console"
+     * @scr.property valueRef="DEFAULT_REALM"
      */
     private static final String PROP_REALM = "realm";
 
     /**
-     * @scr.property value="admin"
+     * @scr.property valueRef="DEFAULT_USER_NAME"
      */
     private static final String PROP_USER_NAME = "username";
 
     /**
-     * @scr.property value="admin"
+     * @scr.property valueRef="DEFAULT_PASSWORD"
      */
     private static final String PROP_PASSWORD = "password";
 
+    private static final String DEFAULT_PAGE = BundlesServlet.NAME;
+
+    private static final String DEFAULT_REALM = "OSGi Management Console";
+
+    private static final String DEFAULT_USER_NAME = "admin";
+
+    private static final String DEFAULT_PASSWORD = "admin";
+
     /**
      * The default value for the {@link #PROP_MANAGER_ROOT} configuration
      * property (value is "/system/console").
      */
     private static final String DEFAULT_MANAGER_ROOT = "/system/console";
 
-    //    private static final Class[] PLUGIN_CLASSES =
-    //        { AjaxConfigManagerAction.class, ComponentConfigurationPrinter.class, ComponentRenderAction.class,
-    //        ConfigManager.class, AjaxBundleDetailsAction.class, BundleListRender.class, InstallAction.class,
-    //        RefreshPackagesAction.class, SetStartLevelAction.class, StartAction.class, StopAction.class,
-    //        UninstallAction.class, UpdateAction.class, ConfigurationRender.class, GCAction.class, ShutdownAction.class,
-    //        ShutdownRender.class, VMStatRender.class };
     private static final Class[] PLUGIN_CLASSES =
         { AjaxConfigManagerAction.class, ComponentConfigurationPrinter.class, ComponentRenderAction.class,
-            ConfigManager.class, BundleListRender.class, InstallAction.class, RefreshPackagesAction.class,
-            SetStartLevelAction.class, ConfigurationRender.class, GCAction.class, ShutdownAction.class,
-            ShutdownRender.class, VMStatRender.class, BundleRepositoryRender.class };
+            ConfigManager.class, BundlesServlet.class, InstallAction.class, SetStartLevelAction.class,
+            ConfigurationRender.class, GCAction.class, ShutdownAction.class, ShutdownRender.class, VMStatRender.class,
+            BundleRepositoryRender.class };
 
     private BundleContext bundleContext;
 
@@ -537,9 +538,9 @@
         Dictionary config = getConfiguration();
 
         // get authentication details
-        String realm = this.getProperty( config, PROP_REALM, "OSGi Management Console" );
-        String userId = this.getProperty( config, PROP_USER_NAME, null );
-        String password = this.getProperty( config, PROP_PASSWORD, null );
+        String realm = this.getProperty( config, PROP_REALM, DEFAULT_REALM );
+        String userId = this.getProperty( config, PROP_USER_NAME, DEFAULT_USER_NAME );
+        String password = this.getProperty( config, PROP_PASSWORD, DEFAULT_PASSWORD );
 
         // register the servlet and resources
         try
@@ -670,7 +671,7 @@
 
         configuration = config;
 
-        defaultRenderName = ( String ) config.get( PROP_DEFAULT_RENDER );
+        defaultRenderName = getProperty( config, PROP_DEFAULT_RENDER, DEFAULT_PAGE );
         if ( defaultRenderName != null && plugins.get( defaultRenderName ) != null )
         {
             defaultRender = ( Servlet ) plugins.get( defaultRenderName );
diff --git a/webconsole/src/main/resources/res/ui/bundles.js b/webconsole/src/main/resources/res/ui/bundles.js
index 537577f..07efab4 100644
--- a/webconsole/src/main/resources/res/ui/bundles.js
+++ b/webconsole/src/main/resources/res/ui/bundles.js
@@ -135,7 +135,7 @@
     document.write( "<td class='content' align='right' colspan='5' noWrap>" );
     document.write( "<input class='submit' style='width:auto' type='submit' value='Install or Update'>" );
     document.write( "&nbsp;" );
-    document.write( "<input class='submit' style='width:auto' type='submit' value='Refresh Packages' onClick='this.form.action.value=\"refreshPackages\"; return true;'>" );
+    document.write( "<input class='submit' style='width:auto' type='button' value='Refresh Packages' onClick='changeBundle(0, \"refreshPackages\");'>" );
     document.write( "</td>" );
     document.write( "</tr>" );
     document.write( "</form>" );
@@ -151,43 +151,47 @@
     
 function bundleChanged(obj)
 {
-    var bundleId = obj.bundleId;
-    if (obj.state)
+    if (obj.reload)
     {
-        // has status, so draw the line
-        var span = document.getElementById('bundle' + bundleId);
-        if (span)
-        {
-            span.innerHTML = bundleInternal( obj );
-        }
-        
-        if (obj.props)
-        {
-            var span = document.getElementById('bundle' + bundleId + '_details');
-            if (span && span.innerHTML)
-            {
-                span.innerHTML = bundleDetails( obj.props );
-            }
-        }
-        
+        document.location = document.location;
     }
     else
     {
-        // no status, bundle has been uninstalled
-        var span = document.getElementById('bundle' + bundleId);
-        if (span)
+        var bundleId = obj.bundleId;
+        if (obj.state)
         {
-            span.parentNode.removeChild(span);
+            // has status, so draw the line
+            var span = document.getElementById('bundle' + bundleId);
+            if (span)
+            {
+                span.innerHTML = bundleInternal( obj );
+            }
+            
+            if (obj.props)
+            {
+                var span = document.getElementById('bundle' + bundleId + '_details');
+                if (span && span.innerHTML)
+                {
+                    span.innerHTML = bundleDetails( obj.props );
+                }
+            }
+            
         }
-        var span = document.getElementById('bundle' + bundleId + '_details');
-        if (span)
+        else
         {
-            span.parentNode.removeChild(span);
+            // no status, bundle has been uninstalled
+            var span = document.getElementById('bundle' + bundleId);
+            if (span)
+            {
+                span.parentNode.removeChild(span);
+            }
+            var span = document.getElementById('bundle' + bundleId + '_details');
+            if (span)
+            {
+                span.parentNode.removeChild(span);
+            }
         }
-    }
-    
-    // reload --- should do better and only update the bundle
-    // document.location = document.location;
+    }    
 }