FELIX-2165 remove Action interface, merge InstallAction into BundlesServlet and add workaround code to support Maven Sling Plugins use of the .../install URL.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@922229 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/Action.java b/webconsole/src/main/java/org/apache/felix/webconsole/Action.java
deleted file mode 100644
index f22c737..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/Action.java
+++ /dev/null
@@ -1,91 +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;
-
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-
-/**
- * The <code>Action</code> interface defines a service interface for actions
- * to be plugged into the web console.
- * <p>
- * <strong>NOTE: This interface is just an intermediate solution for making the
- * web console extensible. Future releases of the web console will remove this
- * interface and use the <code>javax.servlet.Servlet</code> interface with
- * predefined service registration properties instead.</strong>
- *
- * @deprecated This interface will be removed when <a
- *             href="https://issues.apache.org/jira/browse/FELIX-574">FELIX-574</a>
- *             will be implemented.
- */
-public interface Action
-{
-
-    /**
-     * The name of the service used when registered in the OSGi framework.
-     */
-    static final String SERVICE = Action.class.getName();
-
-    /**
-     * The name of a request attribute, which may be set by performAction if
-     * redirecting.
-     */
-    static final String ATTR_REDIRECT_PARAMETERS = "redirectParameters";
-
-
-    /**
-     * Retrieves the name of the action (alias)
-     *
-     * @return the name
-     */
-    String getName();
-
-
-    /**
-     * This method is actually unused
-     *
-     * @return label?
-     */
-    String getLabel();
-
-
-    /**
-     * Performs the action the request data optionally sending a response to
-     * the HTTP Servlet Response.
-     *
-     * @param request
-     * @param response
-     *
-     * @return <code>true</code> the client should be redirected after the
-     *      action has been taken. <code>false</code> if this method also
-     *      provided response to the client and no more processing is
-     *      required.
-     *
-     * @throws IOException May be thrown if an I/O error occurs
-     * @throws ServletException May be thrown if another error occurs while
-     *      processing the action. The <code>rootCause</code> of the exception
-     *      should contain the cause of the error.
-     */
-    boolean performAction( HttpServletRequest request, HttpServletResponse response ) throws IOException,
-        ServletException;
-
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
index 1b6b8a6..03b405f 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
@@ -17,6 +17,7 @@
 package org.apache.felix.webconsole.internal.core;
 
 
+import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -35,15 +36,19 @@
 import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.fileupload.FileItem;
 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.AbstractWebConsolePlugin;
 import org.apache.felix.webconsole.ConfigurationPrinter;
 import org.apache.felix.webconsole.DefaultVariableResolver;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
@@ -65,6 +70,7 @@
 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.packageadmin.ExportedPackage;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.service.startlevel.StartLevel;
@@ -83,6 +89,15 @@
     private static final String TITLE = "Bundles";
     private static final String CSS[] = { "/res/ui/bundles.css" };
 
+    private static final String FIELD_STARTLEVEL = "bundlestartlevel";
+
+    private static final String FIELD_START = "bundlestart";
+
+    private static final String FIELD_BUNDLEFILE = "bundlefile";
+
+    // set to ask for PackageAdmin.refreshPackages() after install/update
+    private static final String FIELD_REFRESH_PACKAGES = "refreshPackages";
+
     // 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.
@@ -257,83 +272,96 @@
      */
     protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException
     {
-        final RequestInfo reqInfo = new RequestInfo(req);
-        if ( reqInfo.bundle == null && reqInfo.bundleRequested ) {
-            resp.sendError(404);
-            return;
-        }
-
         boolean success = false;
-
-        final String action = req.getParameter( "action" );
-
-        Bundle bundle = getBundle( req.getPathInfo() );
-
-        if ( bundle != null )
-        {
-            if ( action == null )
-            {
-                success = true;
-            }
-            else if ( "start".equals( action ) )
-            {
-                // start bundle
-                success = true;
-                try
-                {
-                    bundle.start();
-                }
-                catch ( BundleException be )
-                {
-                    log( "Cannot start", be );
-                }
-            }
-            else if ( "stop".equals( action ) )
-            {
-                // stop bundle
-                success = true;
-                try
-                {
-                    bundle.stop();
-                }
-                catch ( BundleException be )
-                {
-                    log( "Cannot stop", be );
-                }
-            }
-            else if ( "refresh".equals( action ) )
-            {
-                // refresh bundle wiring
-                refresh( bundle );
-                success = true;
-            }
-            else if ( "update".equals( action ) )
-            {
-                // update the bundle
-                update( bundle );
-                success = true;
-            }
-            else if ( "uninstall".equals( action ) )
-            {
-                // uninstall bundle
-                success = true;
-                try
-                {
-                    bundle.uninstall();
-                    bundle = null; // bundle has gone !
-                }
-                catch ( BundleException be )
-                {
-                    log( "Cannot uninstall", be );
-                }
-            }
-        }
-
+        final String action = WebConsoleUtil.getParameter( req, "action" );
         if ( "refreshPackages".equals( action ) )
         {
             getPackageAdmin().refreshPackages( null );
             success = true;
         }
+        else if ( "install".equals( action ) )
+        {
+            installBundles( req );
+
+            if (req.getRequestURI().endsWith( "/install" )) {
+                // just send 200/OK, no content
+                resp.setContentLength( 0 );
+            } else {
+                // redirect to URL
+                resp.sendRedirect( req.getRequestURI() );
+            }
+
+            return;
+        }
+        else
+        {
+            final RequestInfo reqInfo = new RequestInfo( req );
+            if ( reqInfo.bundle == null && reqInfo.bundleRequested )
+            {
+                resp.sendError( 404 );
+                return;
+            }
+
+            final Bundle bundle = reqInfo.bundle;
+            if ( bundle != null )
+            {
+                if ( action == null )
+                {
+                    success = true;
+                }
+                else if ( "start".equals( action ) )
+                {
+                    // start bundle
+                    success = true;
+                    try
+                    {
+                        bundle.start();
+                    }
+                    catch ( BundleException be )
+                    {
+                        log( "Cannot start", be );
+                    }
+                }
+                else if ( "stop".equals( action ) )
+                {
+                    // stop bundle
+                    success = true;
+                    try
+                    {
+                        bundle.stop();
+                    }
+                    catch ( BundleException be )
+                    {
+                        log( "Cannot stop", be );
+                    }
+                }
+                else if ( "refresh".equals( action ) )
+                {
+                    // refresh bundle wiring
+                    refresh( bundle );
+                    success = true;
+                }
+                else if ( "update".equals( action ) )
+                {
+                    // update the bundle
+                    update( bundle );
+                    success = true;
+                }
+                else if ( "uninstall".equals( action ) )
+                {
+                    // uninstall bundle
+                    success = true;
+                    try
+                    {
+                        bundle.uninstall();
+                    }
+                    catch ( BundleException be )
+                    {
+                        log( "Cannot uninstall", be );
+                    }
+                }
+            }
+        }
 
         if ( success )
         {
@@ -346,7 +374,7 @@
             }
             final String pluginRoot = ( String ) req.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT );
             final String servicesRoot = getServicesRoot( req );
-            this.renderJSON(resp, null, pluginRoot, servicesRoot, req.getLocale() );
+            this.renderJSON( resp, null, pluginRoot, servicesRoot, req.getLocale() );
         }
         else
         {
@@ -1369,4 +1397,265 @@
 
         return log;
     }
+
+
+    //---------- Bundle Installation handler (former InstallAction)
+
+    private void installBundles( HttpServletRequest request ) throws IOException
+    {
+
+        // get the uploaded data
+        final Map params = ( Map ) request.getAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD );
+        if ( params == null )
+        {
+            return;
+        }
+
+        final FileItem startItem = getParameter( params, FIELD_START );
+        final FileItem startLevelItem = getParameter( params, FIELD_STARTLEVEL );
+        final FileItem[] bundleItems = getFileItems( params, FIELD_BUNDLEFILE );
+        final FileItem refreshPackagesItem = getParameter( params, FIELD_REFRESH_PACKAGES );
+
+        // don't care any more if no bundle item
+        if ( bundleItems.length == 0 )
+        {
+            return;
+        }
+
+        // default values
+        // it exists
+        int startLevel = -1;
+        String bundleLocation = "inputstream:";
+
+        // convert the start level value
+        if ( startLevelItem != null )
+        {
+            try
+            {
+                startLevel = Integer.parseInt( startLevelItem.getString() );
+            }
+            catch ( NumberFormatException nfe )
+            {
+                getLog().log( LogService.LOG_INFO,
+                    "Cannot parse start level parameter " + startLevelItem + " to a number, not setting start level" );
+            }
+        }
+
+        for ( int i = 0; i < bundleItems.length; i++ )
+        {
+            final FileItem bundleItem = bundleItems[i];
+            // write the bundle data to a temporary file to ease processing
+            File tmpFile = null;
+            try
+            {
+                // copy the data to a file for better processing
+                tmpFile = File.createTempFile( "install", ".tmp" );
+                bundleItem.write( tmpFile );
+            }
+            catch ( Exception e )
+            {
+                getLog().log( LogService.LOG_ERROR, "Problem accessing uploaded bundle file: " + bundleItem.getName(),
+                    e );
+
+                // remove the tmporary file
+                if ( tmpFile != null )
+                {
+                    tmpFile.delete();
+                    tmpFile = null;
+                }
+            }
+
+            // install or update the bundle now
+            if ( tmpFile != null )
+            {
+                // start, refreshPackages just needs to exist, don't care for value
+                boolean start = startItem != null;
+                boolean refreshPackages = refreshPackagesItem != null;
+
+                bundleLocation = "inputstream:" + bundleItem.getName();
+                installBundle( bundleLocation, tmpFile, startLevel, start, refreshPackages );
+            }
+        }
+    }
+
+
+    private FileItem getParameter( Map params, String name )
+    {
+        FileItem[] items = ( FileItem[] ) params.get( name );
+        if ( items != null )
+        {
+            for ( int i = 0; i < items.length; i++ )
+            {
+                if ( items[i].isFormField() )
+                {
+                    return items[i];
+                }
+            }
+        }
+
+        // nothing found, fail
+        return null;
+    }
+
+
+    private FileItem[] getFileItems( Map params, String name )
+    {
+        final List files = new ArrayList();
+        FileItem[] items = ( FileItem[] ) params.get( name );
+        if ( items != null )
+        {
+            for ( int i = 0; i < items.length; i++ )
+            {
+                if ( !items[i].isFormField() && items[i].getSize() > 0 )
+                {
+                    files.add( items[i] );
+                }
+            }
+        }
+
+        return ( FileItem[] ) files.toArray( new FileItem[files.size()] );
+    }
+
+
+    private void installBundle( String location, File bundleFile, int startLevel, boolean start, boolean refreshPackages )
+        throws IOException
+    {
+        if ( bundleFile != null )
+        {
+
+            // try to get the bundle name, fail if none
+            String symbolicName = getSymbolicName( bundleFile );
+            if ( symbolicName == null )
+            {
+                bundleFile.delete();
+                throw new IOException( Constants.BUNDLE_SYMBOLICNAME + " header missing, cannot install bundle" );
+            }
+
+            // check for existing bundle first
+            Bundle updateBundle = null;
+            if ( Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals( symbolicName ) )
+            {
+                updateBundle = getBundleContext().getBundle( 0 );
+            }
+            else
+            {
+                Bundle[] bundles = getBundleContext().getBundles();
+                for ( int i = 0; i < bundles.length; i++ )
+                {
+                    if ( ( bundles[i].getLocation() != null && bundles[i].getLocation().equals( location ) )
+                        || ( bundles[i].getSymbolicName() != null && bundles[i].getSymbolicName().equals( symbolicName ) ) )
+                    {
+                        updateBundle = bundles[i];
+                        break;
+                    }
+                }
+            }
+
+            if ( updateBundle != null )
+            {
+
+                updateBackground( updateBundle, bundleFile, refreshPackages );
+
+            }
+            else
+            {
+
+                installBackground( bundleFile, location, startLevel, start, refreshPackages );
+
+            }
+        }
+    }
+
+
+    private String getSymbolicName( File bundleFile )
+    {
+        JarFile jar = null;
+        try
+        {
+            jar = new JarFile( bundleFile );
+            Manifest m = jar.getManifest();
+            if ( m != null )
+            {
+                return m.getMainAttributes().getValue( Constants.BUNDLE_SYMBOLICNAME );
+            }
+        }
+        catch ( IOException ioe )
+        {
+            getLog().log( LogService.LOG_WARNING, "Cannot extract symbolic name of bundle file " + bundleFile, ioe );
+        }
+        finally
+        {
+            if ( jar != null )
+            {
+                try
+                {
+                    jar.close();
+                }
+                catch ( IOException ioe )
+                {
+                    // ignore
+                }
+            }
+        }
+
+        // fall back to "not found"
+        return null;
+    }
+
+
+    private void installBackground( final File bundleFile, final String location, final int startlevel,
+        final boolean doStart, final boolean refreshPackages )
+    {
+
+        InstallHelper t = new InstallHelper( getBundleContext(), bundleFile, location, startlevel, doStart,
+            refreshPackages )
+        {
+            protected Logger getLog()
+            {
+                return BundlesServlet.this.getLog();
+            }
+
+
+            protected Object getService( String serviceName )
+            {
+                if ( serviceName.equals( PackageAdmin.class.getName() ) )
+                {
+                    return BundlesServlet.this.getPackageAdmin();
+                }
+                else if ( serviceName.equals( StartLevel.class.getName() ) )
+                {
+                    return BundlesServlet.this.getStartLevel();
+                }
+
+                return null;
+            }
+        };
+
+        t.start();
+    }
+
+
+    private void updateBackground( final Bundle bundle, final File bundleFile, final boolean refreshPackages )
+    {
+        UpdateHelper t = new UpdateHelper( bundle, bundleFile, refreshPackages )
+        {
+            protected Logger getLog()
+            {
+                return BundlesServlet.this.getLog();
+            }
+
+
+            protected Object getService( String serviceName )
+            {
+                if ( serviceName.equals( PackageAdmin.class.getName() ) )
+                {
+                    return BundlesServlet.this.getPackageAdmin();
+                }
+
+                return null;
+            }
+        };
+
+        t.start();
+    }
 }
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java
deleted file mode 100644
index 76f2aea..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/InstallAction.java
+++ /dev/null
@@ -1,328 +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.*;
-import java.util.*;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.fileupload.FileItem;
-import org.apache.felix.webconsole.AbstractWebConsolePlugin;
-import org.apache.felix.webconsole.Action;
-import org.apache.felix.webconsole.internal.BaseManagementPlugin;
-import org.apache.felix.webconsole.internal.Logger;
-import org.osgi.framework.*;
-import org.osgi.service.log.LogService;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.service.startlevel.StartLevel;
-
-
-/**
- * The <code>InstallAction</code> TODO
- */
-public class InstallAction extends BaseManagementPlugin implements Action
-{
-
-    public static final String NAME = "install";
-
-    public static final String LABEL = "Install or Update";
-
-    public static final String FIELD_STARTLEVEL = "bundlestartlevel";
-
-    public static final String FIELD_START = "bundlestart";
-
-    public static final String FIELD_BUNDLEFILE = "bundlefile";
-
-    // set to ask for PackageAdmin.refreshPackages() after install/update
-    public static final String FIELD_REFRESH_PACKAGES = "refreshPackages";
-
-
-    public String getName()
-    {
-        return NAME;
-    }
-
-
-    public String getLabel()
-    {
-        return LABEL;
-    }
-
-
-    public boolean performAction( HttpServletRequest request, HttpServletResponse response )
-    throws IOException
-    {
-
-        // get the uploaded data
-        final Map params = ( Map ) request.getAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD );
-        if ( params == null )
-        {
-            return true;
-        }
-
-        final FileItem startItem = getParameter( params, FIELD_START );
-        final FileItem startLevelItem = getParameter( params, FIELD_STARTLEVEL );
-        final FileItem[] bundleItems = getFileItems( params, FIELD_BUNDLEFILE );
-        final FileItem refreshPackagesItem = getParameter( params, FIELD_REFRESH_PACKAGES );
-
-        // don't care any more if not bundle item
-        if ( bundleItems.length == 0 )
-        {
-            return true;
-        }
-
-        // default values
-        // it exists
-        int startLevel = -1;
-        String bundleLocation = "inputstream:";
-
-        // convert the start level value
-        if ( startLevelItem != null )
-        {
-            try
-            {
-                startLevel = Integer.parseInt( startLevelItem.getString() );
-            }
-            catch ( NumberFormatException nfe )
-            {
-                getLog().log( LogService.LOG_INFO,
-                    "Cannot parse start level parameter " + startLevelItem + " to a number, not setting start level" );
-            }
-        }
-
-        for(int i = 0; i < bundleItems.length; i++ )
-        {
-            final FileItem bundleItem = bundleItems[i];
-            // write the bundle data to a temporary file to ease processing
-            File tmpFile = null;
-            try
-            {
-                // copy the data to a file for better processing
-                tmpFile = File.createTempFile( "install", ".tmp" );
-                bundleItem.write( tmpFile );
-            }
-            catch ( Exception e )
-            {
-                getLog().log( LogService.LOG_ERROR, "Problem accessing uploaded bundle file: " + bundleItem.getName(), e );
-
-                // remove the tmporary file
-                if ( tmpFile != null )
-                {
-                    tmpFile.delete();
-                    tmpFile = null;
-                }
-            }
-
-            // install or update the bundle now
-            if ( tmpFile != null )
-            {
-                // start, refreshPackages just needs to exist, don't care for value
-                boolean start = startItem != null;
-                boolean refreshPackages = refreshPackagesItem != null;
-
-                bundleLocation = "inputstream:" + bundleItem.getName();
-                installBundle( bundleLocation, tmpFile, startLevel, start, refreshPackages );
-            }
-        }
-
-        return true;
-    }
-
-
-    private FileItem getParameter( Map params, String name )
-    {
-        FileItem[] items = ( FileItem[] ) params.get( name );
-        if ( items != null )
-        {
-            for ( int i = 0; i < items.length; i++ )
-            {
-                if ( items[i].isFormField() )
-                {
-                    return items[i];
-                }
-            }
-        }
-
-        // nothing found, fail
-        return null;
-    }
-
-    private FileItem[] getFileItems( Map params, String name )
-    {
-        final List files = new ArrayList();
-        FileItem[] items = ( FileItem[] ) params.get( name );
-        if ( items != null )
-        {
-            for ( int i = 0; i < items.length; i++ )
-            {
-                if ( !items[i].isFormField() && items[i].getSize() > 0 )
-                {
-                    files.add(items[i]);
-                }
-            }
-        }
-
-        return (FileItem[])files.toArray(new FileItem[files.size()]);
-    }
-
-    private void installBundle( String location, File bundleFile, int startLevel, boolean start, boolean refreshPackages )
-    throws IOException
-    {
-        if ( bundleFile != null )
-        {
-
-            // try to get the bundle name, fail if none
-            String symbolicName = getSymbolicName( bundleFile );
-            if ( symbolicName == null )
-            {
-                bundleFile.delete();
-                throw new IOException(Constants.BUNDLE_SYMBOLICNAME + " header missing, cannot install bundle");
-            }
-
-            // check for existing bundle first
-            Bundle updateBundle = null;
-            if ( Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals( symbolicName ) )
-            {
-                updateBundle = getBundleContext().getBundle( 0 );
-            }
-            else
-            {
-                Bundle[] bundles = getBundleContext().getBundles();
-                for ( int i = 0; i < bundles.length; i++ )
-                {
-                    if ( ( bundles[i].getLocation() != null && bundles[i].getLocation().equals( location ) )
-                        || ( bundles[i].getSymbolicName() != null && bundles[i].getSymbolicName().equals( symbolicName ) ) )
-                    {
-                        updateBundle = bundles[i];
-                        break;
-                    }
-                }
-            }
-
-            if ( updateBundle != null )
-            {
-
-                updateBackground( updateBundle, bundleFile, refreshPackages );
-
-            }
-            else
-            {
-
-                installBackground( bundleFile, location, startLevel, start, refreshPackages );
-
-            }
-        }
-    }
-
-
-    private String getSymbolicName( File bundleFile )
-    {
-        JarFile jar = null;
-        try
-        {
-            jar = new JarFile( bundleFile );
-            Manifest m = jar.getManifest();
-            if ( m != null )
-            {
-                return m.getMainAttributes().getValue( Constants.BUNDLE_SYMBOLICNAME );
-            }
-        }
-        catch ( IOException ioe )
-        {
-            getLog().log( LogService.LOG_WARNING, "Cannot extract symbolic name of bundle file " + bundleFile, ioe );
-        }
-        finally
-        {
-            if ( jar != null )
-            {
-                try
-                {
-                    jar.close();
-                }
-                catch ( IOException ioe )
-                {
-                    // ignore
-                }
-            }
-        }
-
-        // fall back to "not found"
-        return null;
-    }
-
-
-    private void installBackground( final File bundleFile, final String location, final int startlevel,
-        final boolean doStart, final boolean refreshPackages )
-    {
-
-        InstallHelper t = new InstallHelper( getBundleContext(), bundleFile, location, startlevel, doStart,
-            refreshPackages )
-        {
-            protected Logger getLog()
-            {
-                return InstallAction.this.getLog();
-            }
-
-
-            protected Object getService( String serviceName )
-            {
-                if ( serviceName.equals( PackageAdmin.class.getName() ) )
-                {
-                    return InstallAction.this.getPackageAdmin();
-                }
-                else if ( serviceName.equals( StartLevel.class.getName() ) )
-                {
-                    return InstallAction.this.getStartLevel();
-                }
-
-                return null;
-            }
-        };
-
-        t.start();
-    }
-
-
-    private void updateBackground( final Bundle bundle, final File bundleFile, final boolean refreshPackages )
-    {
-        UpdateHelper t = new UpdateHelper( bundle, bundleFile, refreshPackages )
-        {
-            protected Logger getLog()
-            {
-                return InstallAction.this.getLog();
-            }
-
-
-            protected Object getService( String serviceName )
-            {
-                if ( serviceName.equals( PackageAdmin.class.getName() ) )
-                {
-                    return InstallAction.this.getPackageAdmin();
-                }
-
-                return null;
-            }
-        };
-
-        t.start();
-    }
-}
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 4891b7e..83d5ac9 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
@@ -29,7 +29,6 @@
 import java.util.Map;
 import java.util.ResourceBundle;
 import java.util.Set;
-
 import javax.servlet.GenericServlet;
 import javax.servlet.Servlet;
 import javax.servlet.ServletException;
@@ -39,13 +38,10 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.webconsole.AbstractWebConsolePlugin;
-import org.apache.felix.webconsole.Action;
 import org.apache.felix.webconsole.BrandingPlugin;
 import org.apache.felix.webconsole.WebConsoleConstants;
-import org.apache.felix.webconsole.WebConsoleUtil;
 import org.apache.felix.webconsole.internal.Logger;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
-import org.apache.felix.webconsole.internal.Util;
 import org.apache.felix.webconsole.internal.WebConsolePluginAdapter;
 import org.apache.felix.webconsole.internal.core.BundlesServlet;
 import org.apache.felix.webconsole.internal.filter.FilteringResponseWrapper;
@@ -132,7 +128,6 @@
             "org.apache.felix.webconsole.internal.compendium.PreferencesConfigurationPrinter",
             "org.apache.felix.webconsole.internal.core.BundlesServlet",
             "org.apache.felix.webconsole.internal.core.ServicesServlet",
-            "org.apache.felix.webconsole.internal.core.InstallAction",
             "org.apache.felix.webconsole.internal.deppack.DepPackServlet",
             "org.apache.felix.webconsole.internal.misc.LicenseServlet",
             "org.apache.felix.webconsole.internal.misc.ConfigurationRender",
@@ -149,8 +144,6 @@
 
     private HttpService httpService;
 
-    private ServiceTracker operationsTracker;
-
     private ServiceTracker pluginsTracker;
 
     private ServiceTracker brandingTracker;
@@ -165,8 +158,6 @@
     // are plugin titles
     private Map labelMap = new HashMap();
 
-    private Map operations = new HashMap();
-
     private AbstractWebConsolePlugin defaultPlugin;
 
     private String defaultRenderName;
@@ -277,16 +268,9 @@
                 {
                     bindServlet( ( AbstractWebConsolePlugin ) plugin );
                 }
-                else
+                else if ( plugin instanceof BrandingPlugin )
                 {
-                    if ( plugin instanceof Action )
-                    {
-                        bindOperation( ( Action ) plugin );
-                    }
-                    if ( plugin instanceof BrandingPlugin )
-                    {
-                        AbstractWebConsolePlugin.setBrandingPlugin((BrandingPlugin) plugin);
-                    }
+                    AbstractWebConsolePlugin.setBrandingPlugin( ( BrandingPlugin ) plugin );
                 }
             }
             catch ( NoClassDefFoundError ncdfe )
@@ -311,8 +295,6 @@
         }
 
         // start tracking external plugins after setting up our own plugins
-        operationsTracker = new OperationServiceTracker( this );
-        operationsTracker.open();
         pluginsTracker = new PluginServiceTracker( this );
         pluginsTracker.open();
         brandingTracker = new BrandingServiceTracker(this);
@@ -328,14 +310,9 @@
         HttpServletRequest request = ( HttpServletRequest ) req;
         HttpServletResponse response = ( HttpServletResponse ) res;
 
-        // handle the request action, terminate if done
-        if ( this.handleAction( request, response ) )
-        {
-            return;
-        }
-
         // check whether we are not at .../{webManagerRoot}
-        if ( request.getPathInfo() == null || request.getPathInfo().equals( "/" ) )
+        final String pathInfo = request.getPathInfo();
+        if ( pathInfo == null || pathInfo.equals( "/" ) )
         {
             String path = request.getRequestURI();
             if ( !path.endsWith( "/" ) )
@@ -347,15 +324,23 @@
             return;
         }
 
-        String label = request.getPathInfo();
-        int slash = label.indexOf( "/", 1 );
+        int slash = pathInfo.indexOf( "/", 1 );
         if ( slash < 2 )
         {
-            slash = label.length();
+            slash = pathInfo.length();
         }
 
-        label = label.substring( 1, slash );
+        final String label = pathInfo.substring( 1, slash );
         AbstractWebConsolePlugin plugin = ( AbstractWebConsolePlugin ) plugins.get( label );
+
+        if ( plugin == null )
+        {
+            if ( "install".equals( label ) )
+            {
+                plugin = ( AbstractWebConsolePlugin ) plugins.get( BundlesServlet.NAME );
+            }
+        }
+
         if ( plugin != null )
         {
             // the official request attributes
@@ -393,11 +378,6 @@
         }
 
         // stop listening for plugins
-        if ( operationsTracker != null )
-        {
-            operationsTracker.close();
-            operationsTracker = null;
-        }
         if ( pluginsTracker != null )
         {
             pluginsTracker.close();
@@ -422,62 +402,10 @@
         // simply remove all operations, we should not be used anymore
         this.plugins.clear();
         this.labelMap.clear();
-        this.operations.clear();
     }
 
     //---------- internal
 
-    protected boolean handleAction( HttpServletRequest req, HttpServletResponse resp ) throws IOException, ServletException
-    {
-        // check action
-        String actionName = WebConsoleUtil.getParameter( req, Util.PARAM_ACTION );
-        if ( actionName != null )
-        {
-            Action action = ( Action ) this.operations.get( actionName );
-            if ( action != null )
-            {
-                boolean redirect = true;
-                try
-                {
-                    redirect = action.performAction( req, resp );
-                }
-                catch ( IOException ioe )
-                {
-                    log.log( LogService.LOG_WARNING, ioe.getMessage(), ioe );
-                }
-                catch ( ServletException se )
-                {
-                    log.log( LogService.LOG_WARNING, se.getMessage(), se.getRootCause() );
-                }
-
-                // maybe overwrite redirect
-                if ( PARAM_NO_REDIRECT_AFTER_ACTION.equals( WebConsoleUtil.getParameter( req,
-                    PARAM_NO_REDIRECT_AFTER_ACTION ) ) )
-                {
-                    resp.setStatus( HttpServletResponse.SC_OK );
-                    resp.setContentType( "text/html" );
-                    resp.getWriter().println( "Ok" );
-                    return true;
-                }
-
-                if ( redirect )
-                {
-                    String uri = req.getRequestURI();
-                    // Object pars =
-                    // req.getAttribute(Action.ATTR_REDIRECT_PARAMETERS);
-                    // if (pars instanceof String) {
-                    // uri += "?" + pars;
-                    // }
-                    resp.sendRedirect( uri );
-                }
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
     BundleContext getBundleContext()
     {
         return bundleContext;
@@ -537,41 +465,6 @@
         }
     }
 
-    private static class OperationServiceTracker extends ServiceTracker
-    {
-
-        private final OsgiManager osgiManager;
-
-
-        OperationServiceTracker( OsgiManager osgiManager )
-        {
-            super( osgiManager.getBundleContext(), Action.SERVICE, null );
-            this.osgiManager = osgiManager;
-        }
-
-
-        public Object addingService( ServiceReference reference )
-        {
-            Object operation = super.addingService( reference );
-            if ( operation instanceof Action )
-            {
-                osgiManager.bindOperation( ( Action ) operation );
-            }
-            return operation;
-        }
-
-
-        public void removedService( ServiceReference reference, Object service )
-        {
-            if ( service instanceof Action )
-            {
-                osgiManager.bindOperation( ( Action ) service );
-            }
-
-            super.removedService( reference, service );
-        }
-    }
-
     private static class PluginServiceTracker extends ServiceTracker
     {
 
@@ -819,18 +712,6 @@
     }
 
 
-    protected void bindOperation( Action operation )
-    {
-        operations.put( operation.getName(), operation );
-    }
-
-
-    protected void unbindOperation( Action operation )
-    {
-        operations.remove( operation.getName() );
-    }
-
-
     private Dictionary getConfiguration()
     {
         return configuration;
diff --git a/webconsole/src/main/resources/templates/bundles_upload.html b/webconsole/src/main/resources/templates/bundles_upload.html
index 7202ef3..c7399b6 100644
--- a/webconsole/src/main/resources/templates/bundles_upload.html
+++ b/webconsole/src/main/resources/templates/bundles_upload.html
@@ -1,6 +1,6 @@
 <script type="text/javascript" src="${appRoot}/res/lib/jquery.multifile-1.4.6.js"></script>
 
-<form method="post" enctype="multipart/form-data" action="../" style="width:50%">
+<form method="post" enctype="multipart/form-data" action="${pluginRoot}" style="width:50%">
 
 	<!-- top heading -->
 	<div class="ui-widget-header ui-corner-top buttonGroup">