FELIX-1988 Apply abstr-simple-web-console2.patch by Valentin Valchev (thanks)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@908927 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
index 4bbb13d..0626667 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
@@ -27,24 +27,41 @@
 import javax.servlet.ServletException;
 import javax.servlet.http.*;
 
-import org.apache.commons.fileupload.FileItem;
-import org.apache.commons.fileupload.FileUploadException;
-import org.apache.commons.fileupload.disk.DiskFileItemFactory;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
-import org.apache.commons.fileupload.servlet.ServletRequestContext;
+import org.apache.commons.io.IOUtils;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
 
+/**
+ * The Web Console can be extended by registering an OSGi service for the interface
+ * {@link javax.servlet.Servlet} with the service property
+ * <code>felix.webconsole.label</code> set to the label (last segment in the URL)
+ * of the page. The respective service is called a Web Console Plugin or a plugin
+ * for short.
+ *
+ * To help rendering the response the Apache Felix Web Console bundle provides two
+ * options. One of the options is to extend the AbstractWebConsolePlugin overwriting
+ * the {@link #renderContent(HttpServletRequest, HttpServletResponse)} method.
+ */
 public abstract class AbstractWebConsolePlugin extends HttpServlet
 {
 
     /** Pseudo class version ID to keep the IDE quite. */
     private static final long serialVersionUID = 1L;
 
-    /** The name of the request attribute containig the map of FileItems from the POST request */
+    /** The name of the request attribute containing the map of FileItems from the POST request */
     public static final String ATTR_FILEUPLOAD = "org.apache.felix.webconsole.fileupload";
 
+    /**
+     * Web Console Plugin typically consists of servlet and resources such as images,
+     * scripts or style sheets.
+     *
+     * To load resources, a Resource Provider is used. The resource provider is an object,
+     * that provides a method which name is specified by this constants and it is
+     * 'getResource'.
+     *
+     *  @see #getResourceProvider()
+     */
     public static final String GET_RESOURCE_METHOD_NAME = "getResource";
 
     /**
@@ -72,6 +89,8 @@
 
     /**
      * Returns the title for this plugin as returned by {@link #getTitle()}
+     *
+     * @see javax.servlet.GenericServlet#getServletName()
      */
     public String getServletName()
     {
@@ -93,6 +112,9 @@
      * <b>Note</b>: If a resource is sent back for the request only the first
      * step is executed. Otherwise the first step is a null-operation actually
      * and the latter four steps are executed in order.
+     * @see javax.servlet.http.HttpServlet#doGet(
+     *  javax.servlet.http.HttpServletRequest,
+     *  javax.servlet.http.HttpServletResponse)
      */
     protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException,
         IOException
@@ -129,6 +151,9 @@
      * footers of this plugin be rendered or not. This method always returns
      * <code>true</true> but has been overwritten in the
      * {@link WebConsolePluginAdapter} for the plugins.
+     *
+     * @param request the original request passed from the HTTP server
+     * @return <code>true</code> if the page should have headers and footers rendered
      */
     protected boolean isHtmlRequest( final HttpServletRequest request )
     {
@@ -138,27 +163,65 @@
 
     //---------- AbstractWebConsolePlugin API ----------------------------------
 
+    /**
+     * This method is called from the Felix Web Console to ensure the
+     * AbstractWebConsolePlugin is correctly setup.
+     *
+     * It is called right after the Web Console receives notification for
+     * plugin registration.
+     *
+     * @param bundleContext the context of the plugin bundle
+     */
     public void activate( BundleContext bundleContext )
     {
         this.bundleContext = bundleContext;
     }
 
 
+    /**
+     * This method is called, by the Web Console to de-activate the plugin and release
+     * all used resources.
+     */
     public void deactivate()
     {
         this.bundleContext = null;
     }
 
 
-    public abstract String getTitle();
-
-
-    public abstract String getLabel();
-
-
+    /**
+     * This method is used to render the content of the plug-in. It is called internally
+     * from the Web Console.
+     *
+     * @param req the HTTP request send from the user
+     * @param res the HTTP response object, where to render the plugin data.
+     * @throws IOException if an input or output error is
+     *  detected when the servlet handles the request
+     * @throws ServletException  if the request for the GET
+     *  could not be handled
+     */
     protected abstract void renderContent( HttpServletRequest req, HttpServletResponse res ) throws ServletException,
         IOException;
 
+    /**
+     * Retrieves the label. This is the last component in the servlet path.
+     *
+     * This method MUST be overridden, if the {@link #AbstractWebConsolePlugin()}
+     * constructor is used.
+     *
+     * @return the label.
+     */
+    public abstract String getLabel();
+
+    /**
+     * Retrieves the title of the plug-in. It is displayed in the page header
+     * and is also included in the title of the HTML document.
+     *
+     * This method MUST be overridden, if the {@link #AbstractWebConsolePlugin()}
+     * constructor is used.
+     *
+     * @return the plugin title.
+     */
+    public abstract String getTitle();
 
     /**
      * Returns a list of CSS reference paths or <code>null</code> if no
@@ -178,12 +241,13 @@
         return null;
     }
 
-
     /**
      * Returns the <code>BundleContext</code> with which this plugin has been
      * activated. If the plugin has not be activated by calling the
      * {@link #activate(BundleContext)} method, this method returns
      * <code>null</code>.
+     *
+     * @return the bundle context or <code>null</code> if the bundle is not activated.
      */
     protected BundleContext getBundleContext()
     {
@@ -197,6 +261,8 @@
      * been activated. If the plugin has not be activated by calling the
      * {@link #activate(BundleContext)} method, this method returns
      * <code>null</code>.
+     *
+     * @return the bundle or <code>null</code> if the plugin is not activated.
      */
     public final Bundle getBundle()
     {
@@ -204,7 +270,6 @@
         return ( bundleContext != null ) ? bundleContext.getBundle() : null;
     }
 
-
     /**
      * Returns the object which might provide resources. The class of this
      * object is used to find the <code>getResource</code> method.
@@ -223,7 +288,7 @@
 
     /**
      * Returns a method which is called on the
-     * {@link #getResourceProvider() resource provder} class to return an URL
+     * {@link #getResourceProvider() resource provider} class to return an URL
      * to a resource which may be spooled when requested. The method has the
      * following signature:
      * <pre>
@@ -240,7 +305,7 @@
      *      if the {@link #getResourceProvider() resource provider} is
      *      <code>null</code> or does not provide such a method.
      */
-    private Method getGetResourceMethod()
+    private final Method getGetResourceMethod()
     {
         // return what we know of the getResourceMethod, if we already checked
         if (getResourceMethodChecked) {
@@ -309,9 +374,9 @@
      * @param response The response object
      * @return <code>true</code> if the request causes a resource to be sent back.
      *
-     * @throws IOException If an error occurrs accessing or spooling the resource.
+     * @throws IOException If an error occurs accessing or spooling the resource.
      */
-    private boolean spoolResource( HttpServletRequest request, HttpServletResponse response ) throws IOException
+    private final boolean spoolResource( HttpServletRequest request, HttpServletResponse response ) throws IOException
     {
         // no resource if no resource accessor
         Method getResourceMethod = getGetResourceMethod();
@@ -392,22 +457,22 @@
         }
       finally
         {
-            if ( ins != null )
-            {
-                try
-                {
-                    ins.close();
-                }
-                catch ( IOException ignore )
-                {
-                }
-            }
+            IOUtils.closeQuietly(ins);
         }
 
         return false;
     }
 
 
+    /**
+     * This method is responsible for generating the top heading of the page.
+     *
+     * @param request the HTTP request coming from the user
+     * @param response the HTTP response, where data is rendered
+     * @return the writer that was used for generating the response.
+     * @throws IOException on I/O error
+     * @see #endResponse(PrintWriter)
+     */
     protected PrintWriter startResponse( HttpServletRequest request, HttpServletResponse response ) throws IOException
     {
         response.setCharacterEncoding( "utf-8" );
@@ -428,6 +493,12 @@
     }
 
 
+    /**
+     * This method is called to generate the top level links with the available plug-ins.
+     *
+     * @param request the HTTP request coming from the user
+     * @param pw the writer, where the HTML data is rendered
+     */
     protected void renderTopNavigation( HttpServletRequest request, PrintWriter pw )
     {
         // assume pathInfo to not be null, else this would not be called
@@ -489,105 +560,57 @@
     }
 
 
+    /**
+     * This method is responsible for generating the footer of the page.
+     *
+     * @param pw the writer, where the HTML data is rendered
+     * @see #startResponse(HttpServletRequest, HttpServletResponse)
+     */
     protected void endResponse( PrintWriter pw )
     {
         pw.println(getFooter());
     }
 
 
-    public static String getParameter( HttpServletRequest request, String name )
+    /**
+     * An utility method, that is used to filter out simple parameter from file
+     * parameter when multipart transfer encoding is used.
+     *
+     * This method processes the request and sets a request attribute
+     * {@link #ATTR_FILEUPLOAD}. The attribute value is a {@link Map}
+     * where the key is a String specifying the field name and the value
+     * is a {@link org.apache.commons.fileupload.FileItem}.
+     *
+     * @param request the HTTP request coming from the user
+     * @param name the name of the parameter
+     * @return if not multipart transfer encoding is used - the value is the
+     *  parameter value or <code>null</code> if not set. If multipart is used,
+     *  and the specified parameter is field - then the value of the parameter
+     *  is returned.
+     * @deprecated use {@link WebConsoleUtil#getParameter(HttpServletRequest, String)}
+     */
+    public static final String getParameter( HttpServletRequest request, String name )
     {
-        // just get the parameter if not a multipart/form-data POST
-        if ( !ServletFileUpload.isMultipartContent( new ServletRequestContext( request ) ) )
-        {
-            return request.getParameter( name );
-        }
-
-        // check, whether we alread have the parameters
-        Map params = ( Map ) request.getAttribute( ATTR_FILEUPLOAD );
-        if ( params == null )
-        {
-            // parameters not read yet, read now
-            // Create a factory for disk-based file items
-            DiskFileItemFactory factory = new DiskFileItemFactory();
-            factory.setSizeThreshold( 256000 );
-
-            // Create a new file upload handler
-            ServletFileUpload upload = new ServletFileUpload( factory );
-            upload.setSizeMax( -1 );
-
-            // Parse the request
-            params = new HashMap();
-            try
-            {
-                List items = upload.parseRequest( request );
-                for ( Iterator fiter = items.iterator(); fiter.hasNext(); )
-                {
-                    FileItem fi = ( FileItem ) fiter.next();
-                    FileItem[] current = ( FileItem[] ) params.get( fi.getFieldName() );
-                    if ( current == null )
-                    {
-                        current = new FileItem[]
-                            { fi };
-                    }
-                    else
-                    {
-                        FileItem[] newCurrent = new FileItem[current.length + 1];
-                        System.arraycopy( current, 0, newCurrent, 0, current.length );
-                        newCurrent[current.length] = fi;
-                        current = newCurrent;
-                    }
-                    params.put( fi.getFieldName(), current );
-                }
-            }
-            catch ( FileUploadException fue )
-            {
-                // TODO: log
-            }
-            request.setAttribute( ATTR_FILEUPLOAD, params );
-        }
-
-        FileItem[] param = ( FileItem[] ) params.get( name );
-        if ( param != null )
-        {
-            for ( int i = 0; i < param.length; i++ )
-            {
-                if ( param[i].isFormField() )
-                {
-                    return param[i].getString();
-                }
-            }
-        }
-
-        // no valid string parameter, fail
-        return null;
+        return WebConsoleUtil.getParameter(request, name);
     }
 
     /**
      * Utility method to handle relative redirects.
-     * Some app servers like web sphere handle relative redirects differently
-     * therefore we should make an absolute url before invoking send redirect.
+     * Some application servers like Web Sphere handle relative redirects differently
+     * therefore we should make an absolute URL before invoking send redirect.
+     *
+     * @param request the HTTP request coming from the user
+     * @param response the HTTP response, where data is rendered
+     * @param redirectUrl the redirect URI.
+     * @throws IOException If an input or output exception occurs
+     * @throws IllegalStateException   If the response was committed or if a partial
+     *  URL is given and cannot be converted into a valid URL
+     * @deprecated use {@link WebConsoleUtil#sendRedirect(HttpServletRequest, HttpServletResponse, String)}
      */
     protected void sendRedirect(final HttpServletRequest request,
                                 final HttpServletResponse response,
                                 String redirectUrl) throws IOException {
-        // check for relative url
-        if ( !redirectUrl.startsWith("/") ) {
-            String base = request.getContextPath() + request.getServletPath() + request.getPathInfo();
-            int i = base.lastIndexOf('/');
-            if (i > -1) {
-                base = base.substring(0, i);
-            } else {
-                i = base.indexOf(':');
-                base = (i > -1) ? base.substring(i + 1, base.length()) : "";
-            }
-            if (!base.startsWith("/")) {
-                base = '/' + base;
-            }
-            redirectUrl = base + '/' + redirectUrl;
-
-        }
-        response.sendRedirect(redirectUrl);
+        WebConsoleUtil.sendRedirect(request, response, redirectUrl);
     }
 
     /**
@@ -600,7 +623,7 @@
     /**
      * @param brandingPlugin the brandingPlugin to set
      */
-    public static void setBrandingPlugin(BrandingPlugin brandingPlugin) {
+    public static final void setBrandingPlugin(BrandingPlugin brandingPlugin) {
         if(brandingPlugin == null){
             AbstractWebConsolePlugin.brandingPlugin = DefaultBrandingPlugin.getInstance();
         } else {
@@ -609,7 +632,7 @@
     }
 
 
-    private String getHeader()
+    private final String getHeader()
     {
         // MessageFormat pattern place holder
         //  0 main title (brand name)
@@ -663,7 +686,7 @@
     }
 
 
-    private String getFooter()
+    private final String getFooter()
     {
         // close <div id="main">, body and html
         final String footer = "    </div>"
@@ -673,7 +696,7 @@
     }
 
 
-    private String getCssLinks( final String appRoot )
+    private final String getCssLinks( final String appRoot )
     {
         // get the CSS references and return nothing if there are none
         final String[] cssRefs = getCssReferences();
@@ -707,7 +730,7 @@
      *          the url.
      * @throws NullPointerException if <code>url</code> is <code>null</code>.
      */
-    private String toUrl( final String url, final String appRoot )
+    private static final String toUrl( final String url, final String appRoot )
     {
         if ( url.startsWith( "/" ) )
         {
@@ -715,4 +738,5 @@
         }
         return url;
     }
+
 }
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
new file mode 100644
index 0000000..f41ba98
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
@@ -0,0 +1,224 @@
+/*
+ * 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.net.URL;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+/**
+ * SimpleWebConsolePlugin is an utility class that provides default
+ * implementation of the {@link AbstractWebConsolePlugin} and supports the
+ * following features:
+ * <ul>
+ * <li>Methods for (un)registering the web console plugin service</li>
+ * <li>Default implementation for resource loading</li>
+ * </ul>
+ */
+public abstract class SimpleWebConsolePlugin extends AbstractWebConsolePlugin
+{
+
+    // Serializable UID
+    private static final long serialVersionUID = 1500463493078823878L;
+
+    // used for standard AbstractWebConsolePlugin implementation
+    private final String label;
+    private final String title;
+    private final String css[];
+    private final String labelRes;
+    private final int labelResLen;
+
+    // used for service registration
+    private final Object regLock = new Object();
+    private ServiceRegistration reg;
+
+    // used to obtain services. Structure is: service name -> ServiceTracker
+    private final Map services = new HashMap();
+
+
+    /**
+     * Creates new Simple Web Console Plugin.
+     *
+     * @param label the front label. See
+     *          {@link AbstractWebConsolePlugin#getLabel()}
+     * @param title the plugin title . See
+     *          {@link AbstractWebConsolePlugin#getTitle()}
+     * @param css the additional plugin CSS. See
+     *          {@link AbstractWebConsolePlugin#getCssReferences()}
+     */
+    public SimpleWebConsolePlugin( String label, String title, String css[] )
+    {
+        if ( label == null )
+        {
+            throw new NullPointerException( "Null label" );
+        }
+        if ( title == null )
+        {
+            throw new NullPointerException( "Null title" );
+        }
+        this.label = label;
+        this.title = title;
+        this.css = css;
+        this.labelRes = '/' + label + '/';
+        this.labelResLen = labelRes.length() - 1;
+    }
+
+
+    /**
+     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getLabel()
+     */
+    public final String getLabel()
+    {
+        return label;
+    }
+
+
+    /**
+     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getTitle()
+     */
+    public final String getTitle()
+    {
+        return title;
+    }
+
+
+    /**
+     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getCssReferences()
+     */
+    protected final String[] getCssReferences()
+    {
+        return css;
+    }
+
+
+    /**
+     * Called internally by {@link AbstractWebConsolePlugin} to load resources.
+     *
+     * This particular implementation depends on the label. As example, if the
+     * plugin is accessed as <code>/system/console/abc</code>, and the plugin
+     * resources are accessed like <code>/system/console/abc/res/logo.gif</code>,
+     * the code here will try load resource <code>/res/logo.gif</code> from the
+     * bundle, providing the plugin.
+     *
+     *
+     * @param path the path to read.
+     * @return the URL of the resource or <code>null</code> if not found.
+     */
+    protected URL getResource( String path )
+    {
+        return ( path != null && path.startsWith( labelRes ) ) ? //
+        getClass().getResource( path.substring( labelResLen ) )
+            : null;
+    }
+
+
+    // -- begin methods for plugin registration/unregistration
+    /**
+     * This is an utility method. It is used to register the plugin service. Don't
+     * forget to call the {@link #unregister()} when the plugin is no longer
+     * needed.
+     *
+     * @param bc the bundle context used for service registration.
+     * @return self
+     */
+    public final SimpleWebConsolePlugin register( BundleContext bc )
+    {
+        synchronized ( regLock )
+        {
+            activate( bc ); // don't know why this is needed!
+
+            Hashtable props = new Hashtable();
+            props.put( WebConsoleConstants.PLUGIN_LABEL, label );
+            props.put( WebConsoleConstants.PLUGIN_TITLE, title );
+            reg = bc.registerService( "javax.servlet.Servlet", this, props ); //$NON-NLS-1$
+        }
+        return this;
+    }
+
+
+    /**
+     * An utility method that removes the service, registered by the
+     * {@link #register(BundleContext)} method.
+     */
+    public final void unregister()
+    {
+        synchronized ( regLock )
+        {
+            deactivate(); // is this needed?
+
+            if ( reg != null )
+                reg.unregister();
+            reg = null;
+        }
+    }
+
+
+    // -- end methods for plugin registration/unregistration
+
+    // -- begin methods for obtaining services
+
+    /**
+     * Gets the service with the specified class name. Will create a new
+     * {@link ServiceTracker} if the service is not already got.
+     *
+     * @param serviceName the service name to obtain
+     * @return the service or <code>null</code> if missing.
+     */
+    protected final Object getService( String serviceName )
+    {
+        ServiceTracker serviceTracker = ( ServiceTracker ) services.get( serviceName );
+        if ( serviceTracker == null )
+        {
+            serviceTracker = new ServiceTracker( getBundleContext(), serviceName, null );
+            serviceTracker.open();
+
+            services.put( serviceName, serviceTracker );
+        }
+
+        return serviceTracker.getService();
+    }
+
+
+    /**
+     * This method will close all service trackers, created by
+     * {@link #getService(String)} method. If you override this method, don't
+     * forget to call the super.
+     *
+     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#deactivate()
+     */
+    public void deactivate()
+    {
+        for ( Iterator ti = services.values().iterator(); ti.hasNext(); )
+        {
+            ServiceTracker tracker = ( ServiceTracker ) ti.next();
+            tracker.close();
+            ti.remove();
+        }
+        super.deactivate();
+    }
+
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java
index d634727..a0950b8 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleConstants.java
@@ -18,7 +18,10 @@
  */
 package org.apache.felix.webconsole;
 
-
+/**
+ * WebConsoleConstants provides some common constants that are used by plugin
+ * developers.
+ */
 public interface WebConsoleConstants
 {
 
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleUtil.java b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleUtil.java
index 5eaf7e3..fafd9d2 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleUtil.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleUtil.java
@@ -19,7 +19,24 @@
 package org.apache.felix.webconsole;
 
 
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadBase;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.fileupload.servlet.ServletRequestContext;
+import org.json.JSONException;
+import org.json.JSONWriter;
 
 
 /**
@@ -29,11 +46,16 @@
 public final class WebConsoleUtil
 {
 
+    private WebConsoleUtil()
+    {
+        /* no instantiation */
+    }
+
     /**
      * Returns the {@link VariableResolver} for the given request.
      * <p>
      *  If not resolver
-     * has yet be created for the requets, an instance of the
+     * has yet be created for the requests, an instance of the
      * {@link DefaultVariableResolver} is created, placed into the request and
      * returned.
      * <p>
@@ -75,4 +97,216 @@
         request.setAttribute( WebConsoleConstants.ATTR_CONSOLE_VARIABLE_RESOLVER, resolver );
     }
 
+
+    /**
+     * An utility method, that is used to filter out simple parameter from file
+     * parameter when multipart transfer encoding is used.
+     * 
+     * This method processes the request and sets a request attribute 
+     * {@link AbstractWebConsolePlugin#ATTR_FILEUPLOAD}. The attribute value is a {@link Map}
+     * where the key is a String specifying the field name and the value
+     * is a {@link org.apache.commons.fileupload.FileItem}.
+     *
+     * @param request the HTTP request coming from the user
+     * @param name the name of the parameter
+     * @return if not multipart transfer encoding is used - the value is the
+     *  parameter value or <code>null</code> if not set. If multipart is used,
+     *  and the specified parameter is field - then the value of the parameter
+     *  is returned.
+     */
+    public static final String getParameter( HttpServletRequest request, String name )
+    {
+        // just get the parameter if not a multipart/form-data POST
+        if ( !FileUploadBase.isMultipartContent( new ServletRequestContext( request ) ) )
+        {
+            return request.getParameter( name );
+        }
+
+        // check, whether we already have the parameters
+        Map params = ( Map ) request.getAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD );
+        if ( params == null )
+        {
+            // parameters not read yet, read now
+            // Create a factory for disk-based file items
+            DiskFileItemFactory factory = new DiskFileItemFactory();
+            factory.setSizeThreshold( 256000 );
+
+            // Create a new file upload handler
+            ServletFileUpload upload = new ServletFileUpload( factory );
+            upload.setSizeMax( -1 );
+
+            // Parse the request
+            params = new HashMap();
+            try
+            {
+                List items = upload.parseRequest( request );
+                for ( Iterator fiter = items.iterator(); fiter.hasNext(); )
+                {
+                    FileItem fi = ( FileItem ) fiter.next();
+                    FileItem[] current = ( FileItem[] ) params.get( fi.getFieldName() );
+                    if ( current == null )
+                    {
+                        current = new FileItem[]
+                            { fi };
+                    }
+                    else
+                    {
+                        FileItem[] newCurrent = new FileItem[current.length + 1];
+                        System.arraycopy( current, 0, newCurrent, 0, current.length );
+                        newCurrent[current.length] = fi;
+                        current = newCurrent;
+                    }
+                    params.put( fi.getFieldName(), current );
+                }
+            }
+            catch ( FileUploadException fue )
+            {
+                // TODO: log
+            }
+            request.setAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD, params );
+        }
+
+        FileItem[] param = ( FileItem[] ) params.get( name );
+        if ( param != null )
+        {
+            for ( int i = 0; i < param.length; i++ )
+            {
+                if ( param[i].isFormField() )
+                {
+                    return param[i].getString();
+                }
+            }
+        }
+
+        // no valid string parameter, fail
+        return null;
+    }
+
+    /**
+     * Utility method to handle relative redirects.
+     * Some application servers like Web Sphere handle relative redirects differently
+     * therefore we should make an absolute URL before invoking send redirect.
+     * 
+     * @param request the HTTP request coming from the user
+     * @param response the HTTP response, where data is rendered
+     * @param redirectUrl the redirect URI.
+     * @throws IOException If an input or output exception occurs
+     * @throws IllegalStateException   If the response was committed or if a partial 
+     *  URL is given and cannot be converted into a valid URL
+     */
+    public static final void sendRedirect(final HttpServletRequest request,
+                                final HttpServletResponse response,
+                                String redirectUrl) throws IOException {
+        // check for relative URL
+        if ( !redirectUrl.startsWith("/") ) {
+            String base = request.getContextPath() + request.getServletPath() + request.getPathInfo();
+            int i = base.lastIndexOf('/');
+            if (i > -1) {
+                base = base.substring(0, i);
+            } else {
+                i = base.indexOf(':');
+                base = (i > -1) ? base.substring(i + 1, base.length()) : "";
+            }
+            if (!base.startsWith("/")) {
+                base = '/' + base;
+            }
+            redirectUrl = base + '/' + redirectUrl;
+
+        }
+        response.sendRedirect(redirectUrl);
+    }
+
+    /**
+     * Escapes HTML special chars like: <>&\r\n and space
+     * 
+     * 
+     * @param text the text to escape
+     * @return the escaped text
+     */
+    public static final String escapeHtml(String text)
+    {
+        StringBuffer sb = new StringBuffer(text.length() * 4 / 3);
+        synchronized (sb) // faster buffer operations
+        {
+            char ch, oldch = '_';
+            for (int i = 0; i < text.length(); i++)
+            {
+                switch (ch = text.charAt(i))
+                {
+                    case '<':
+                        sb.append("&lt;"); //$NON-NLS-1$
+                        break;
+                    case '>':
+                        sb.append("&gt;"); //$NON-NLS-1$
+                        break;
+                    case '&':
+                        sb.append("&amp;"); //$NON-NLS-1$
+                        break;
+                    case ' ':
+                        sb.append("&nbsp;"); //$NON-NLS-1$
+                        break;
+                    case '\r':
+                    case '\n':
+                        if (oldch != '\r' && oldch != '\n') // don't add twice <br>
+                            sb.append("<br/>\n"); //$NON-NLS-1$
+                        break;
+                    default:
+                        sb.append(ch);
+                }
+                oldch = ch;
+            }
+
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Retrieves a request parameter and converts it to int.
+     * 
+     * @param request the HTTP request
+     * @param name the name of the request parameter
+     * @param _default the default value returned if the parameter is not set or is not a valid integer.
+     * @return the request parameter if set and is valid integer, or the default value
+     */
+    public static final int getParameterInt(HttpServletRequest request, String name,
+        int _default)
+    {
+        int ret = _default;
+        String param = request.getParameter(name);
+        try
+        {
+            if (param != null)
+                ret = Integer.parseInt(param);
+        }
+        catch (NumberFormatException nfe)
+        {
+            // don't care, will return default
+        }
+
+        return ret;
+    }
+
+    /**
+     * Writes a key-value pair in a JSON writer. Write is performed only if both key and 
+     * value are not null.
+     * 
+     * @param jw the writer, where to write the data
+     * @param key the key value, stored under 'key'
+     * @param value the value stored under 'value'
+     * @throws JSONException if the value cannot be serialized.
+     */
+    public static final void keyVal(JSONWriter jw, String key, Object value)
+        throws JSONException
+    {
+        if (key != null && value != null)
+        {
+            jw.object();
+            jw.key("key"); //$NON-NLS-1$
+            jw.value(key);
+            jw.key("value"); //$NON-NLS-1$
+            jw.value(value);
+            jw.endObject();
+        }
+    }
+
 }