FELIX-566 Support restful URLs for Configuration support

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@668622 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/AjaxConfigManagerAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/AjaxConfigManagerAction.java
deleted file mode 100644
index d19eda2..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/AjaxConfigManagerAction.java
+++ /dev/null
@@ -1,544 +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.compendium;
-
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.StringTokenizer;
-import java.util.Vector;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.felix.webconsole.Action;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONWriter;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.Version;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.metatype.AttributeDefinition;
-import org.osgi.service.metatype.ObjectClassDefinition;
-
-
-/**
- * The <code>AjaxConfigManagerAction</code> TODO
- */
-public class AjaxConfigManagerAction extends ConfigManagerBase implements Action
-{
-
-    public static final String NAME = "ajaxConfigManager";
-
-
-    public String getName()
-    {
-        return NAME;
-    }
-
-
-    public String getLabel()
-    {
-        return NAME;
-    }
-
-
-    public boolean performAction( HttpServletRequest request, HttpServletResponse response ) throws IOException
-    {
-
-        // needed multiple times below
-        String pid = request.getParameter( ConfigManager.PID );
-        ConfigurationAdmin ca = this.getConfigurationAdmin();
-
-        // ignore this request if the pid and/or configuration admin is missing
-        if (pid == null || ca == null) {
-            // should log this here !!
-            return true;
-        }
-        
-        // the configuration to operate on (to be created or "missing")
-        Configuration config = null;
-
-        // should actually apply the configuration before redirecting
-        if ( request.getParameter( "create" ) != null && pid != null )
-        {
-            if ( ca != null )
-            {
-                config = ca.createFactoryConfiguration( pid, null );
-                pid = config.getPid();
-            }
-        }
-        else if ( request.getParameter( "apply" ) != null )
-        {
-            return applyConfiguration( request );
-        }
-
-        if (config == null) {
-            try
-            {
-                // we use listConfigurations to not create configuration
-                // objects persistently without the user providing actual
-                // configuration
-                Configuration[] configs = ca.listConfigurations( "(" + Constants.SERVICE_PID + "=" + pid + ")" );
-                if ( configs != null && configs.length > 0 )
-                {
-                    config = configs[0];
-                }
-            }
-            catch ( InvalidSyntaxException ise )
-            {
-                // should print message
-            }
-        }
-        
-        // send the result
-        response.setContentType( "text/javascript" );
-        response.setCharacterEncoding( "UTF-8" );
-
-        JSONWriter result = new JSONWriter( response.getWriter() );
-
-        if ( pid != null )
-        {
-            try
-            {
-                result.object();
-                this.configForm( result, pid, config, getLocale( request ) );
-                result.endObject();
-            }
-            catch ( Exception e )
-            {
-                // add message
-            }
-        }
-
-        return false;
-    }
-
-
-    private void configForm( JSONWriter json, String pid, Configuration config, Locale loc ) throws IOException,
-        JSONException
-    {
-        String locale = ( loc == null ) ? null : loc.toString();
-
-        json.key( ConfigManager.PID );
-        json.value( pid );
-
-        Dictionary props = null;
-        ObjectClassDefinition ocd;
-        if ( config != null )
-        {
-            props = config.getProperties();
-            ocd = this.getObjectClassDefinition( config, locale );
-        }
-        else
-        {
-            ocd = this.getObjectClassDefinition( pid, locale );
-        }
-
-        props = this.mergeWithMetaType( props, ocd, json );
-
-        if ( props != null )
-        {
-
-            json.key( "title" );
-            json.value( pid );
-            json.key( "description" );
-            json
-                .value( "Please enter configuration properties for this configuration in the field below. This configuration has no associated description" );
-
-            json.key( "propertylist" );
-            json.value( "properties" );
-
-            json.key( "properties" );
-            json.object();
-            for ( Enumeration pe = props.keys(); pe.hasMoreElements(); )
-            {
-                Object key = pe.nextElement();
-
-                // ignore well known special properties
-                if ( !key.equals( Constants.SERVICE_PID ) && !key.equals( Constants.SERVICE_DESCRIPTION )
-                    && !key.equals( Constants.SERVICE_ID ) && !key.equals( Constants.SERVICE_RANKING )
-                    && !key.equals( Constants.SERVICE_VENDOR )
-                    && !key.equals( ConfigurationAdmin.SERVICE_BUNDLELOCATION )
-                    && !key.equals( ConfigurationAdmin.SERVICE_FACTORYPID ) )
-                {
-                    json.key( String.valueOf( key ) );
-                    json.value( props.get( key ) );
-                }
-            }
-            json.endObject();
-
-        }
-
-        if ( config != null )
-        {
-            this.addConfigurationInfo( config, json, locale );
-        }
-    }
-
-
-    private Dictionary mergeWithMetaType( Dictionary props, ObjectClassDefinition ocd, JSONWriter json )
-        throws JSONException
-    {
-
-        if ( props == null )
-        {
-            props = new Hashtable();
-        }
-
-        if ( ocd != null )
-        {
-
-            json.key( "title" );
-            json.value( ocd.getName() );
-
-            if ( ocd.getDescription() != null )
-            {
-                json.key( "description" );
-                json.value( ocd.getDescription() );
-            }
-
-            AttributeDefinition[] ad = ocd.getAttributeDefinitions( ObjectClassDefinition.ALL );
-            if ( ad != null )
-            {
-
-                JSONArray propertyList = new JSONArray();
-
-                for ( int i = 0; i < ad.length; i++ )
-                {
-                    json.key( ad[i].getID() );
-                    json.object();
-
-                    Object value = props.get( ad[i].getID() );
-                    if ( value == null )
-                    {
-                        value = ad[i].getDefaultValue();
-                        if ( value == null )
-                        {
-                            if ( ad[i].getCardinality() == 0 )
-                            {
-                                value = "";
-                            }
-                            else
-                            {
-                                value = new String[0];
-                            }
-                        }
-                    }
-
-                    json.key( "name" );
-                    json.value( ad[i].getName() );
-
-                    json.key( "type" );
-                    if ( ad[i].getOptionLabels() != null && ad[i].getOptionLabels().length > 0 )
-                    {
-                        json.object();
-                        json.key( "labels" );
-                        json.value( Arrays.asList( ad[i].getOptionLabels() ) );
-                        json.key( "values" );
-                        json.value( Arrays.asList( ad[i].getOptionValues() ) );
-                        json.endObject();
-                    }
-                    else
-                    {
-                        json.value( ad[i].getType() );
-                    }
-
-                    if ( ad[i].getCardinality() == 0 )
-                    {
-                        // scalar
-                        if ( value instanceof Vector )
-                        {
-                            value = ( ( Vector ) value ).get( 0 );
-                        }
-                        else if ( value.getClass().isArray() )
-                        {
-                            value = Array.get( value, 0 );
-                        }
-                        json.key( "value" );
-                        json.value( value );
-                    }
-                    else
-                    {
-                        if ( value instanceof Vector )
-                        {
-                            value = new JSONArray( ( Vector ) value );
-                        }
-                        else if ( value.getClass().isArray() )
-                        {
-                            value = new JSONArray( Arrays.asList( ( Object[] ) value ) );
-                        }
-                        else
-                        {
-                            JSONArray tmp = new JSONArray();
-                            tmp.put( value );
-                            value = tmp;
-                        }
-                        json.key( "values" );
-                        json.value( value );
-                    }
-
-                    if ( ad[i].getDescription() != null )
-                    {
-                        json.key( "description" );
-                        json.value( ad[i].getDescription() );
-                    }
-
-                    json.endObject();
-                    propertyList.put( ad[i].getID() );
-                }
-
-                json.key( "propertylist" );
-                json.value( propertyList );
-            }
-
-            // nothing more to display
-            props = null;
-        }
-
-        return props;
-    }
-
-
-    private void addConfigurationInfo( Configuration config, JSONWriter json, String locale ) throws JSONException
-    {
-
-        if ( config.getFactoryPid() != null )
-        {
-            json.key( "factoryPID" );
-            json.value( config.getFactoryPid() );
-        }
-
-        String location;
-        if ( config.getBundleLocation() == null )
-        {
-            location = "None";
-        }
-        else
-        {
-            Bundle bundle = this.getBundle( config.getBundleLocation() );
-
-            Dictionary headers = bundle.getHeaders( locale );
-            String name = ( String ) headers.get( Constants.BUNDLE_NAME );
-            if ( name == null )
-            {
-                location = bundle.getSymbolicName();
-            }
-            else
-            {
-                location = name + " (" + bundle.getSymbolicName() + ")";
-            }
-
-            Version v = Version.parseVersion( ( String ) headers.get( Constants.BUNDLE_VERSION ) );
-            location += ", Version " + v.toString();
-        }
-        json.key( "bundleLocation" );
-        json.value( location );
-    }
-
-
-    private boolean applyConfiguration( HttpServletRequest request ) throws IOException
-    {
-
-        ConfigurationAdmin ca = this.getConfigurationAdmin();
-        if ( ca == null )
-        {
-            return false;
-        }
-
-        String pid = request.getParameter( "pid" );
-
-        if ( request.getParameter( "delete" ) != null )
-        {
-            // TODO: should log this here !!
-            Configuration config = ca.getConfiguration( pid, null );
-            config.delete();
-            return true;
-        }
-
-        String propertyList = request.getParameter( "propertylist" );
-        if ( propertyList == null )
-        {
-            String propertiesString = request.getParameter( "properties" );
-
-            if ( propertiesString != null )
-            {
-                byte[] propBytes = propertiesString.getBytes( "ISO-8859-1" );
-                ByteArrayInputStream bin = new ByteArrayInputStream( propBytes );
-                Properties props = new Properties();
-                props.load( bin );
-
-                Configuration config = ca.getConfiguration( pid, null );
-                config.update( props );
-            }
-        }
-        else
-        {
-            Configuration config = ca.getConfiguration( pid, null );
-            Dictionary props = config.getProperties();
-            if ( props == null )
-            {
-                props = new Hashtable();
-            }
-
-            Map adMap = ( Map ) this.getAttributeDefinitionMap( config, null );
-            if ( adMap != null )
-            {
-                StringTokenizer propTokens = new StringTokenizer( propertyList, "," );
-                while ( propTokens.hasMoreTokens() )
-                {
-                    String propName = propTokens.nextToken();
-                    AttributeDefinition ad = ( AttributeDefinition ) adMap.get( propName );
-                    if ( ad == null || ( ad.getCardinality() == 0 && ad.getType() == AttributeDefinition.STRING ) )
-                    {
-                        String prop = request.getParameter( propName );
-                        if ( prop != null )
-                        {
-                            props.put( propName, prop );
-                        }
-                    }
-                    else if ( ad.getCardinality() == 0 )
-                    {
-                        // scalar of non-string
-                        String prop = request.getParameter( propName );
-                        props.put( propName, this.toType( ad.getType(), prop ) );
-                    }
-                    else
-                    {
-                        // array or vector of any type
-                        Vector vec = new Vector();
-
-                        String[] properties = request.getParameterValues( propName );
-                        if ( properties != null )
-                        {
-                            for ( int i = 0; i < properties.length; i++ )
-                            {
-                                vec.add( this.toType( ad.getType(), properties[i] ) );
-                            }
-                        }
-
-                        // but ensure size
-                        int maxSize = Math.abs( ad.getCardinality() );
-                        if ( vec.size() > maxSize )
-                        {
-                            vec.setSize( maxSize );
-                        }
-
-                        if ( ad.getCardinality() < 0 )
-                        {
-                            // keep the vector
-                            props.put( propName, vec );
-                        }
-                        else
-                        {
-                            // convert to an array
-                            props.put( propName, this.toArray( ad.getType(), vec ) );
-                        }
-                    }
-                }
-            }
-
-            config.update( props );
-        }
-
-        // request.setAttribute(ATTR_REDIRECT_PARAMETERS, "pid=" + pid);
-        return true;
-    }
-
-
-    private Object toType( int type, String value )
-    {
-        switch ( type )
-        {
-            case AttributeDefinition.BOOLEAN:
-                return Boolean.valueOf( value );
-            case AttributeDefinition.BYTE:
-                return Byte.valueOf( value );
-            case AttributeDefinition.CHARACTER:
-                char c = ( value.length() > 0 ) ? value.charAt( 0 ) : 0;
-                return new Character( c );
-            case AttributeDefinition.DOUBLE:
-                return Double.valueOf( value );
-            case AttributeDefinition.FLOAT:
-                return Float.valueOf( value );
-            case AttributeDefinition.LONG:
-                return Long.valueOf( value );
-            case AttributeDefinition.INTEGER:
-                return Integer.valueOf( value );
-            case AttributeDefinition.SHORT:
-                return Short.valueOf( value );
-
-            default:
-                // includes AttributeDefinition.STRING
-                return value;
-        }
-    }
-
-
-    private Object toArray( int type, Vector values )
-    {
-        int size = values.size();
-
-        // short cut for string array
-        if ( type == AttributeDefinition.STRING )
-        {
-            return values.toArray( new String[size] );
-        }
-
-        Object array;
-        switch ( type )
-        {
-            case AttributeDefinition.BOOLEAN:
-                array = new boolean[size];
-            case AttributeDefinition.BYTE:
-                array = new byte[size];
-            case AttributeDefinition.CHARACTER:
-                array = new char[size];
-            case AttributeDefinition.DOUBLE:
-                array = new double[size];
-            case AttributeDefinition.FLOAT:
-                array = new float[size];
-            case AttributeDefinition.LONG:
-                array = new long[size];
-            case AttributeDefinition.INTEGER:
-                array = new int[size];
-            case AttributeDefinition.SHORT:
-                array = new short[size];
-            default:
-                // unexpected, but assume string
-                array = new String[size];
-        }
-
-        for ( int i = 0; i < size; i++ )
-        {
-            Array.set( array, i, values.get( i ) );
-        }
-
-        return array;
-    }
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
index a925fc2..08bd8ee 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
@@ -17,31 +17,49 @@
 package org.apache.felix.webconsole.internal.compendium;
 
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
 import java.util.SortedMap;
+import java.util.StringTokenizer;
 import java.util.TreeMap;
+import java.util.Vector;
 import java.util.Map.Entry;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.felix.webconsole.Render;
+import org.apache.felix.webconsole.internal.Util;
+import org.apache.felix.webconsole.internal.servlet.OsgiManager;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONWriter;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.cm.ManagedService;
 import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.metatype.AttributeDefinition;
 import org.osgi.service.metatype.ObjectClassDefinition;
 
 
 /**
  * The <code>ConfigManager</code> TODO
  */
-public class ConfigManager extends ConfigManagerBase implements Render
+public class ConfigManager extends ConfigManagerBase
 {
 
     public static final String NAME = "configMgr";
@@ -50,95 +68,181 @@
 
     public static final String PID = "pid";
 
+    public static final String factoryPID = "factoryPid";
 
-    public String getLabel()
+    private static final String PLACEHOLDER_PID = "[Temporary PID replaced by real PID upon save]";
+
+
+    public String getTitle()
     {
         return LABEL;
     }
 
 
-    public String getName()
+    public String getLabel()
     {
         return NAME;
     }
 
 
-    public void render( HttpServletRequest request, HttpServletResponse response ) throws IOException
+    protected void doPost( HttpServletRequest request, HttpServletResponse response ) throws IOException
+    {
+        // needed multiple times below
+        String pid = request.getParameter( ConfigManager.PID );
+        if ( pid == null )
+        {
+            String info = request.getPathInfo();
+            pid = info.substring( info.lastIndexOf( '/' ) + 1 );
+        }
+
+        ConfigurationAdmin ca = this.getConfigurationAdmin();
+
+        // ignore this request if the pid and/or configuration admin is missing
+        if ( pid == null || ca == null )
+        {
+            // should log this here !!
+            return;
+        }
+
+        // the configuration to operate on (to be created or "missing")
+        Configuration config = null;
+
+        // should actually apply the configuration before redirecting
+        if ( request.getParameter( "create" ) != null )
+        {
+            if ( ca != null )
+            {
+                config = new PlaceholderConfiguration( pid ); // ca.createFactoryConfiguration( pid, null );
+                pid = config.getPid();
+            }
+        }
+        else if ( request.getParameter( "apply" ) != null )
+        {
+            String redirect = applyConfiguration( request, ca, pid );
+            if ( redirect != null )
+            {
+                response.sendRedirect( redirect );
+            }
+
+            return;
+        }
+
+        if ( config == null )
+        {
+            config = getConfiguration( ca, pid );
+        }
+
+        // send the result
+        response.setContentType( "text/javascript" );
+        response.setCharacterEncoding( "UTF-8" );
+        printConfigurationJson( response.getWriter(), pid, config, getLocale( request ) );
+    }
+
+
+    public void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
     {
 
         // true if MetaType service information is not required
         boolean optionalMetaType = false;
 
+        Locale loc = getLocale( request );
+        String locale = ( loc != null ) ? loc.toString() : null;
+
         PrintWriter pw = response.getWriter();
 
-        pw.println( "<script type='text/javascript' src='res/ui/configmanager.js'></script>" );
+        String appRoot = (String) request.getAttribute( OsgiManager.ATTR_APP_ROOT );
+        pw.println( "<script src='" + appRoot + "/res/ui/configmanager.js' language='JavaScript'></script>" );
 
         pw.println( "<table class='content' cellpadding='0' cellspacing='0' width='100%'>" );
 
-        pw.println( "<tr class='content' id='configField'>" );
-        pw.println( "<td class='content'>Configurations</th>" );
-        pw.println( "<td class='content'>" );
-        this.listConfigurations( pw, optionalMetaType, getLocale( request ) );
-        pw.println( "</td>" );
-        pw.println( "</tr>" );
-
-        pw.println( "<tr class='content' id='factoryField'>" );
-        pw.println( "<td class='content'>Factory Configurations</th>" );
-        pw.println( "<td class='content'>" );
-        this.listFactoryConfigurations( pw, optionalMetaType, getLocale( request ) );
-        pw.println( "</td>" );
-        pw.println( "</tr>" );
-        
-        pw.println( "</table>" );
-    }
-
-
-    private void listConfigurations( PrintWriter pw, boolean optionalMetaType, Locale loc )
-    {
-
         ConfigurationAdmin ca = this.getConfigurationAdmin();
         if ( ca == null )
         {
+            pw.println( "<tr class='content' id='configField'>" );
+            pw.println( "<td class='content'>&nbsp;</th>" );
+            pw.println( "<td class='content'>" );
             pw.print( "Configuration Admin Service not available" );
-            return;
+            pw.println( "</td>" );
+            pw.println( "</tr>" );
+        }
+        else
+        {
+            pw.println( "<tr class='content' id='configField'>" );
+            pw.println( "<td class='content'>Configurations</th>" );
+            pw.println( "<td class='content'>" );
+            this.listConfigurations( pw, ca, optionalMetaType, locale );
+            pw.println( "</td>" );
+            pw.println( "</tr>" );
+
+            pw.println( "<tr class='content' id='factoryField'>" );
+            pw.println( "<td class='content'>Factory Configurations</th>" );
+            pw.println( "<td class='content'>" );
+            this.listFactoryConfigurations( pw, ca, optionalMetaType, locale );
+            pw.println( "</td>" );
+            pw.println( "</tr>" );
         }
 
-        String locale = ( loc != null ) ? loc.toString() : null;
+        pw.println( "</table>" );
 
-        try
+        // if a configuration is addressed, display it immediately
+        Configuration config = getConfiguration( getConfigurationAdmin(), request.getPathInfo() );
+        if ( config != null )
         {
-            // sorted map of options
-            SortedMap optionsPlain = new TreeMap( String.CASE_INSENSITIVE_ORDER );
+            Util.startScript( pw );
 
-            // find all ManagedServices to get the PIDs
-            ServiceReference[] refs = this.getBundleContext().getServiceReferences( ManagedService.class.getName(),
-                null );
-            for ( int i = 0; refs != null && i < refs.length; i++ )
+            pw.println( "var configuration=" );
+            printConfigurationJson( pw, config.getPid(), config, getLocale( request ) );
+            pw.println( ";" );
+
+            pw.println( "displayConfigForm(configuration);" );
+
+            Util.endScript( pw );
+        }
+    }
+
+
+    private Configuration getConfiguration( ConfigurationAdmin ca, String pid )
+    {
+        if ( ca != null )
+        {
+            // only use last part of the pathInfo
+            pid = pid.substring( pid.lastIndexOf( '/' ) + 1 );
+
+            try
             {
-                Object pidObject = refs[i].getProperty( Constants.SERVICE_PID );
-                if ( pidObject instanceof String )
+                // we use listConfigurations to not create configuration
+                // objects persistently without the user providing actual
+                // configuration
+                String filter = "(" + Constants.SERVICE_PID + "=" + pid + ")";
+                Configuration[] configs = ca.listConfigurations( filter );
+                if ( configs != null && configs.length > 0 )
                 {
-                    String pid = ( String ) pidObject;
-                    String name;
-                    ObjectClassDefinition ocd = this.getObjectClassDefinition( refs[i].getBundle(), pid, locale );
-                    if ( ocd != null )
-                    {
-                        name = ocd.getName() + " (";
-                        name += pid + ")";
-                    }
-                    else
-                    {
-                        name = pid;
-                    }
-
-                    if ( ocd != null || optionalMetaType )
-                    {
-                        optionsPlain.put( pid, name );
-                    }
+                    return configs[0];
                 }
             }
+            catch ( InvalidSyntaxException ise )
+            {
+                // should print message
+            }
+            catch ( IOException ioe )
+            {
+                // should print message
+            }
+        }
 
-            // get a sorted list of configuration PIDs
+        // fallback to no configuration at all
+        return null;
+    }
+
+
+    private void listConfigurations( PrintWriter pw, ConfigurationAdmin ca, boolean optionalMetaType, String locale )
+    {
+        try
+        {
+            // start with ManagedService instances
+            SortedMap optionsPlain = getServices( ManagedService.class.getName(), optionalMetaType, locale );
+
+            // add in existing configuration (not duplicating ManagedServices)
             Configuration[] cfgs = ca.listConfigurations( null );
             for ( int i = 0; cfgs != null && i < cfgs.length; i++ )
             {
@@ -170,19 +274,7 @@
                 }
             }
 
-            //            pw.println( "<form method='post' name='configSelection' onSubmit='configure(); return false;'" );
-            pw.println( "<select class='select' name='pid' id='configSelection_pid' onChange='configure();'>" );
-            for ( Iterator ei = optionsPlain.entrySet().iterator(); ei.hasNext(); )
-            {
-                Entry entry = ( Entry ) ei.next();
-                pw.print( "<option value='" + entry.getKey() + "'>" );
-                pw.print( entry.getValue() );
-                pw.println( "</option>" );
-            }
-            pw.println( "</select>" );
-            pw.println( "&nbsp;&nbsp;" );
-            pw.println( "<input class='submit' type='button' value='Configure' onClick='configure();' />" );
-            //            pw.println( "</form>" );
+            printOptionsForm( pw, optionsPlain, "configSelection_pid", "configure", "Configure" );
         }
         catch ( Exception e )
         {
@@ -191,66 +283,580 @@
     }
 
 
-    private void listFactoryConfigurations( PrintWriter pw, boolean optionalMetaType, Locale loc )
+    private void listFactoryConfigurations( PrintWriter pw, ConfigurationAdmin ca, boolean optionalMetaType,
+        String locale )
     {
-
-        ConfigurationAdmin ca = this.getConfigurationAdmin();
-        if ( ca == null )
-        {
-            pw.print( "Configuration Admin Service not available" );
-            return;
-        }
-
-        String locale = ( loc != null ) ? loc.toString() : null;
-
         try
         {
-            // sorted map of options
-            SortedMap optionsFactory = new TreeMap( String.CASE_INSENSITIVE_ORDER );
+            SortedMap optionsFactory = getServices( ManagedServiceFactory.class.getName(), optionalMetaType, locale );
+            printOptionsForm( pw, optionsFactory, "configSelection_factory", "create", "Create" );
+        }
+        catch ( Exception e )
+        {
+            // write a message or ignore
+        }
+    }
 
-            // find all ManagedServiceFactories to get the factoryPIDs
-            ServiceReference[] refs = this.getBundleContext().getServiceReferences(
-                ManagedServiceFactory.class.getName(), null );
-            for ( int i = 0; refs != null && i < refs.length; i++ )
+
+    private SortedMap getServices( String serviceClass, boolean optionalMetaType, String locale )
+        throws InvalidSyntaxException
+    {
+        // sorted map of options
+        SortedMap optionsFactory = new TreeMap( String.CASE_INSENSITIVE_ORDER );
+
+        // find all ManagedServiceFactories to get the factoryPIDs
+        ServiceReference[] refs = this.getBundleContext().getServiceReferences( serviceClass, null );
+        for ( int i = 0; refs != null && i < refs.length; i++ )
+        {
+            Object pidObject = refs[i].getProperty( Constants.SERVICE_PID );
+            if ( pidObject instanceof String )
             {
-                Object factoryPid = refs[i].getProperty( Constants.SERVICE_PID );
-                if ( factoryPid instanceof String )
+                String pid = ( String ) pidObject;
+                String name;
+                ObjectClassDefinition ocd = this.getObjectClassDefinition( refs[i].getBundle(), pid, locale );
+                if ( ocd != null )
                 {
-                    String pid = ( String ) factoryPid;
-                    String name;
-                    ObjectClassDefinition ocd = this.getObjectClassDefinition( refs[i].getBundle(), pid, locale );
-                    if ( ocd != null )
+                    name = ocd.getName() + " (";
+                    name += pid + ")";
+                }
+                else
+                {
+                    name = pid;
+                }
+
+                if ( ocd != null || optionalMetaType )
+                {
+                    optionsFactory.put( pid, name );
+                }
+            }
+        }
+
+        return optionsFactory;
+    }
+
+
+    private void printOptionsForm( PrintWriter pw, SortedMap options, String formId, String submitMethod,
+        String submitLabel )
+    {
+        pw.println( "<select class='select' name='pid' id='" + formId + "' onChange='" + submitMethod + "();'>" );
+        for ( Iterator ei = options.entrySet().iterator(); ei.hasNext(); )
+        {
+            Entry entry = ( Entry ) ei.next();
+            pw.print( "<option value='" + entry.getKey() + "'>" );
+            pw.print( entry.getValue() );
+            pw.println( "</option>" );
+        }
+        pw.println( "</select>" );
+        pw.println( "&nbsp;&nbsp;" );
+        pw.println( "<input class='submit' type='button' value='" + submitLabel + "' onClick='" + submitMethod
+            + "();' />" );
+
+    }
+
+
+    private void printConfigurationJson( PrintWriter pw, String pid, Configuration config, Locale locale )
+        throws IOException
+    {
+
+        JSONWriter result = new JSONWriter( pw );
+
+        if ( pid != null )
+        {
+            try
+            {
+                result.object();
+                this.configForm( result, pid, config, locale );
+                result.endObject();
+            }
+            catch ( Exception e )
+            {
+                // add message
+            }
+        }
+
+    }
+
+
+    private void configForm( JSONWriter json, String pid, Configuration config, Locale loc ) throws IOException,
+        JSONException
+    {
+        String locale = ( loc == null ) ? null : loc.toString();
+
+        json.key( ConfigManager.PID );
+        json.value( pid );
+
+        if ( pid == PLACEHOLDER_PID )
+        {
+            json.key( ConfigManager.factoryPID );
+            json.value( config.getFactoryPid() );
+        }
+
+        Dictionary props = null;
+        ObjectClassDefinition ocd;
+        if ( config != null )
+        {
+            props = config.getProperties();
+            ocd = this.getObjectClassDefinition( config, locale );
+        }
+        else
+        {
+            ocd = this.getObjectClassDefinition( pid, locale );
+        }
+
+        props = this.mergeWithMetaType( props, ocd, json );
+
+        if ( props != null )
+        {
+
+            json.key( "title" );
+            json.value( pid );
+            json.key( "description" );
+            json
+                .value( "Please enter configuration properties for this configuration in the field below. This configuration has no associated description" );
+
+            json.key( "propertylist" );
+            json.value( "properties" );
+
+            json.key( "properties" );
+            json.object();
+            for ( Enumeration pe = props.keys(); pe.hasMoreElements(); )
+            {
+                Object key = pe.nextElement();
+
+                // ignore well known special properties
+                if ( !key.equals( Constants.SERVICE_PID ) && !key.equals( Constants.SERVICE_DESCRIPTION )
+                    && !key.equals( Constants.SERVICE_ID ) && !key.equals( Constants.SERVICE_RANKING )
+                    && !key.equals( Constants.SERVICE_VENDOR )
+                    && !key.equals( ConfigurationAdmin.SERVICE_BUNDLELOCATION )
+                    && !key.equals( ConfigurationAdmin.SERVICE_FACTORYPID ) )
+                {
+                    json.key( String.valueOf( key ) );
+                    json.value( props.get( key ) );
+                }
+            }
+            json.endObject();
+
+        }
+
+        if ( config != null )
+        {
+            this.addConfigurationInfo( config, json, locale );
+        }
+    }
+
+
+    private Dictionary mergeWithMetaType( Dictionary props, ObjectClassDefinition ocd, JSONWriter json )
+        throws JSONException
+    {
+
+        if ( props == null )
+        {
+            props = new Hashtable();
+        }
+
+        if ( ocd != null )
+        {
+
+            json.key( "title" );
+            json.value( ocd.getName() );
+
+            if ( ocd.getDescription() != null )
+            {
+                json.key( "description" );
+                json.value( ocd.getDescription() );
+            }
+
+            AttributeDefinition[] ad = ocd.getAttributeDefinitions( ObjectClassDefinition.ALL );
+            if ( ad != null )
+            {
+
+                JSONArray propertyList = new JSONArray();
+
+                for ( int i = 0; i < ad.length; i++ )
+                {
+                    json.key( ad[i].getID() );
+                    json.object();
+
+                    Object value = props.get( ad[i].getID() );
+                    if ( value == null )
                     {
-                        name = ocd.getName() + " (";
-                        name += pid + ")";
+                        value = ad[i].getDefaultValue();
+                        if ( value == null )
+                        {
+                            if ( ad[i].getCardinality() == 0 )
+                            {
+                                value = "";
+                            }
+                            else
+                            {
+                                value = new String[0];
+                            }
+                        }
+                    }
+
+                    json.key( "name" );
+                    json.value( ad[i].getName() );
+
+                    json.key( "type" );
+                    if ( ad[i].getOptionLabels() != null && ad[i].getOptionLabels().length > 0 )
+                    {
+                        json.object();
+                        json.key( "labels" );
+                        json.value( Arrays.asList( ad[i].getOptionLabels() ) );
+                        json.key( "values" );
+                        json.value( Arrays.asList( ad[i].getOptionValues() ) );
+                        json.endObject();
                     }
                     else
                     {
-                        name = pid;
+                        json.value( ad[i].getType() );
                     }
 
-                    if ( ocd != null || optionalMetaType )
+                    if ( ad[i].getCardinality() == 0 )
                     {
-                        optionsFactory.put( pid, name );
+                        // scalar
+                        if ( value instanceof Vector )
+                        {
+                            value = ( ( Vector ) value ).get( 0 );
+                        }
+                        else if ( value.getClass().isArray() )
+                        {
+                            value = Array.get( value, 0 );
+                        }
+                        json.key( "value" );
+                        json.value( value );
+                    }
+                    else
+                    {
+                        if ( value instanceof Vector )
+                        {
+                            value = new JSONArray( ( Vector ) value );
+                        }
+                        else if ( value.getClass().isArray() )
+                        {
+                            value = new JSONArray( Arrays.asList( ( Object[] ) value ) );
+                        }
+                        else
+                        {
+                            JSONArray tmp = new JSONArray();
+                            tmp.put( value );
+                            value = tmp;
+                        }
+                        json.key( "values" );
+                        json.value( value );
+                    }
+
+                    if ( ad[i].getDescription() != null )
+                    {
+                        json.key( "description" );
+                        json.value( ad[i].getDescription() );
+                    }
+
+                    json.endObject();
+                    propertyList.put( ad[i].getID() );
+                }
+
+                json.key( "propertylist" );
+                json.value( propertyList );
+            }
+
+            // nothing more to display
+            props = null;
+        }
+
+        return props;
+    }
+
+
+    private void addConfigurationInfo( Configuration config, JSONWriter json, String locale ) throws JSONException
+    {
+
+        if ( config.getFactoryPid() != null )
+        {
+            json.key( "factoryPID" );
+            json.value( config.getFactoryPid() );
+        }
+
+        String location;
+        if ( config.getBundleLocation() == null )
+        {
+            location = "None";
+        }
+        else
+        {
+            Bundle bundle = this.getBundle( config.getBundleLocation() );
+
+            Dictionary headers = bundle.getHeaders( locale );
+            String name = ( String ) headers.get( Constants.BUNDLE_NAME );
+            if ( name == null )
+            {
+                location = bundle.getSymbolicName();
+            }
+            else
+            {
+                location = name + " (" + bundle.getSymbolicName() + ")";
+            }
+
+            Version v = Version.parseVersion( ( String ) headers.get( Constants.BUNDLE_VERSION ) );
+            location += ", Version " + v.toString();
+        }
+        json.key( "bundleLocation" );
+        json.value( location );
+    }
+
+
+    private String applyConfiguration( HttpServletRequest request, ConfigurationAdmin ca, String pid )
+        throws IOException
+    {
+        if ( request.getParameter( "delete" ) != null )
+        {
+            // only delete if the PID is not our place holder
+            if ( !PLACEHOLDER_PID.equals( pid ) )
+            {
+                // TODO: should log this here !!
+                Configuration config = ca.getConfiguration( pid, null );
+                config.delete();
+            }
+            return ""; // up a level
+        }
+
+        String factoryPid = request.getParameter( ConfigManager.factoryPID );
+        Configuration config = null;
+        
+        String propertyList = request.getParameter( "propertylist" );
+        if ( propertyList == null )
+        {
+            String propertiesString = request.getParameter( "properties" );
+
+            if ( propertiesString != null )
+            {
+                byte[] propBytes = propertiesString.getBytes( "ISO-8859-1" );
+                ByteArrayInputStream bin = new ByteArrayInputStream( propBytes );
+                Properties props = new Properties();
+                props.load( bin );
+
+                config = getConfiguration( ca, pid, factoryPid );
+                config.update( props );
+            }
+        }
+        else
+        {
+            config = getConfiguration( ca, pid, factoryPid );
+
+            Dictionary props = config.getProperties();
+            if ( props == null )
+            {
+                props = new Hashtable();
+            }
+
+            Map adMap = this.getAttributeDefinitionMap( config, null );
+            if ( adMap != null )
+            {
+                StringTokenizer propTokens = new StringTokenizer( propertyList, "," );
+                while ( propTokens.hasMoreTokens() )
+                {
+                    String propName = propTokens.nextToken();
+                    AttributeDefinition ad = ( AttributeDefinition ) adMap.get( propName );
+                    if ( ad == null || ( ad.getCardinality() == 0 && ad.getType() == AttributeDefinition.STRING ) )
+                    {
+                        String prop = request.getParameter( propName );
+                        if ( prop != null )
+                        {
+                            props.put( propName, prop );
+                        }
+                    }
+                    else if ( ad.getCardinality() == 0 )
+                    {
+                        // scalar of non-string
+                        String prop = request.getParameter( propName );
+                        props.put( propName, this.toType( ad.getType(), prop ) );
+                    }
+                    else
+                    {
+                        // array or vector of any type
+                        Vector vec = new Vector();
+
+                        String[] properties = request.getParameterValues( propName );
+                        if ( properties != null )
+                        {
+                            for ( int i = 0; i < properties.length; i++ )
+                            {
+                                vec.add( this.toType( ad.getType(), properties[i] ) );
+                            }
+                        }
+
+                        // but ensure size
+                        int maxSize = Math.abs( ad.getCardinality() );
+                        if ( vec.size() > maxSize )
+                        {
+                            vec.setSize( maxSize );
+                        }
+
+                        if ( ad.getCardinality() < 0 )
+                        {
+                            // keep the vector
+                            props.put( propName, vec );
+                        }
+                        else
+                        {
+                            // convert to an array
+                            props.put( propName, this.toArray( ad.getType(), vec ) );
+                        }
                     }
                 }
             }
 
-            pw.println( "<select class='select' name='pid' id='configSelection_factory' onChange='create();'>" );
-            for ( Iterator ei = optionsFactory.entrySet().iterator(); ei.hasNext(); )
-            {
-                Entry entry = ( Entry ) ei.next();
-                pw.print( "<option value='" + entry.getKey() + "'>" );
-                pw.print( entry.getValue() );
-                pw.println( "</option>" );
-            }
-            pw.println( "</select>" );
-            pw.println( "&nbsp;&nbsp;" );
-            pw.println( "<input class='submit' type='button' value='Create' onClick='create();' />" );
+            config.update( props );
         }
-        catch ( Exception e )
+
+        // redirect to the new configuration (if existing)
+        return (config != null) ? config.getPid() : "";
+    }
+
+
+    private Configuration getConfiguration( ConfigurationAdmin ca, String pid, String factoryPid ) throws IOException
+    {
+        if ( factoryPid != null )
         {
-            // write a message or ignore
+            return ca.createFactoryConfiguration( factoryPid, null );
+        }
+
+        return ca.getConfiguration( pid, null );
+    }
+
+
+    private Object toType( int type, String value )
+    {
+        switch ( type )
+        {
+            case AttributeDefinition.BOOLEAN:
+                return Boolean.valueOf( value );
+            case AttributeDefinition.BYTE:
+                return Byte.valueOf( value );
+            case AttributeDefinition.CHARACTER:
+                char c = ( value.length() > 0 ) ? value.charAt( 0 ) : 0;
+                return new Character( c );
+            case AttributeDefinition.DOUBLE:
+                return Double.valueOf( value );
+            case AttributeDefinition.FLOAT:
+                return Float.valueOf( value );
+            case AttributeDefinition.LONG:
+                return Long.valueOf( value );
+            case AttributeDefinition.INTEGER:
+                return Integer.valueOf( value );
+            case AttributeDefinition.SHORT:
+                return Short.valueOf( value );
+
+            default:
+                // includes AttributeDefinition.STRING
+                return value;
         }
     }
+
+
+    private Object toArray( int type, Vector values )
+    {
+        int size = values.size();
+
+        // short cut for string array
+        if ( type == AttributeDefinition.STRING )
+        {
+            return values.toArray( new String[size] );
+        }
+
+        Object array;
+        switch ( type )
+        {
+            case AttributeDefinition.BOOLEAN:
+                array = new boolean[size];
+            case AttributeDefinition.BYTE:
+                array = new byte[size];
+            case AttributeDefinition.CHARACTER:
+                array = new char[size];
+            case AttributeDefinition.DOUBLE:
+                array = new double[size];
+            case AttributeDefinition.FLOAT:
+                array = new float[size];
+            case AttributeDefinition.LONG:
+                array = new long[size];
+            case AttributeDefinition.INTEGER:
+                array = new int[size];
+            case AttributeDefinition.SHORT:
+                array = new short[size];
+            default:
+                // unexpected, but assume string
+                array = new String[size];
+        }
+
+        for ( int i = 0; i < size; i++ )
+        {
+            Array.set( array, i, values.get( i ) );
+        }
+
+        return array;
+    }
+
+    private static class PlaceholderConfiguration implements Configuration
+    {
+
+        private final String factoryPid;
+        private String bundleLocation;
+
+
+        PlaceholderConfiguration( String factoryPid )
+        {
+            this.factoryPid = factoryPid;
+        }
+
+
+        public String getPid()
+        {
+            return PLACEHOLDER_PID;
+        }
+
+
+        public String getFactoryPid()
+        {
+            return factoryPid;
+        }
+
+
+        public void setBundleLocation( String bundleLocation )
+        {
+            this.bundleLocation = bundleLocation;
+        }
+
+
+        public String getBundleLocation()
+        {
+            return bundleLocation;
+        }
+
+
+        public Dictionary getProperties()
+        {
+            // dummy configuration has no properties
+            return null;
+        }
+
+
+        public void update()
+        {
+            // dummy configuration cannot be updated
+        }
+
+
+        public void update( Dictionary properties )
+        {
+            // dummy configuration cannot be updated
+        }
+
+
+        public void delete()
+        {
+            // dummy configuration cannot be deleted
+        }
+
+    }
+
 }
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
index 53e701b..0aae6f4 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
@@ -23,68 +23,37 @@
 
 import javax.servlet.http.HttpServletRequest;
 
-import org.apache.felix.webconsole.internal.BaseManagementPlugin;
+import org.apache.felix.webconsole.internal.BaseWebConsolePlugin;
 import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.metatype.AttributeDefinition;
 import org.osgi.service.metatype.MetaTypeInformation;
 import org.osgi.service.metatype.MetaTypeService;
 import org.osgi.service.metatype.ObjectClassDefinition;
-import org.osgi.util.tracker.ServiceTracker;
 
 
 /**
  * The <code>ConfigManagerBase</code> TODO
  * 
  */
-abstract class ConfigManagerBase extends BaseManagementPlugin
+abstract class ConfigManagerBase extends BaseWebConsolePlugin
 {
     private static final String CONFIGURATION_ADMIN_NAME = ConfigurationAdmin.class.getName();
-    
+
     private static final String META_TYPE_NAME = MetaTypeService.class.getName();
 
-    private ServiceTracker configurationAdmin;
-
-    private ServiceTracker metaTypeService;
-
-
-    public void activate( BundleContext bundleContext )
-    {
-        super.activate( bundleContext );
-
-        configurationAdmin = new ServiceTracker( bundleContext, ConfigurationAdmin.class.getName(), null );
-        configurationAdmin.open();
-        metaTypeService = new ServiceTracker( bundleContext, MetaTypeService.class.getName(), null );
-        metaTypeService.open();
-    }
-
-
-    public void destroy()
-    {
-        if ( configurationAdmin != null )
-        {
-            configurationAdmin.close();
-        }
-        if ( metaTypeService != null )
-        {
-            metaTypeService.close();
-        }
-    }
-
 
     protected ConfigurationAdmin getConfigurationAdmin()
     {
-        //TODO: getService(CONFIGURATION_ADMIN_NAME)
-        return ( ConfigurationAdmin ) configurationAdmin.getService();
+        return ( ConfigurationAdmin ) getService( CONFIGURATION_ADMIN_NAME );
     }
 
 
     protected MetaTypeService getMetaTypeService()
     {
-        //TODO: getService(META_TYPE_NAME)
-        return ( MetaTypeService ) metaTypeService.getService();
+        //TODO: 
+        return ( MetaTypeService ) getService( META_TYPE_NAME );
     }
 
 
@@ -142,7 +111,7 @@
                     {
                         return mti.getObjectClassDefinition( config.getFactoryPid(), locale );
                     }
-                    
+
                     // otherwise check by configuration PID
                     return mti.getObjectClassDefinition( config.getPid(), locale );
                 }
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 421fedc..b0c28c4 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
@@ -40,7 +40,6 @@
 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.compendium.AjaxConfigManagerAction;
 import org.apache.felix.webconsole.internal.compendium.ComponentConfigurationPrinter;
 import org.apache.felix.webconsole.internal.compendium.ComponentRenderAction;
 import org.apache.felix.webconsole.internal.compendium.ConfigManager;
@@ -77,6 +76,8 @@
 
     public static final String ATTR_LABEL_MAP = OsgiManager.class.getName() + ".labelMap";
 
+    public static final String ATTR_APP_ROOT = OsgiManager.class.getName() + ".appRoot";
+
     /**
      * The name and value of a parameter which will prevent redirection to a
      * render after the action has been executed (value is "_noredir_"). This
@@ -124,10 +125,10 @@
     private static final String DEFAULT_MANAGER_ROOT = "/system/console";
 
     private static final Class[] PLUGIN_CLASSES =
-        { AjaxConfigManagerAction.class, ComponentConfigurationPrinter.class, ComponentRenderAction.class,
-            ConfigManager.class, BundlesServlet.class, InstallAction.class, SetStartLevelAction.class,
-            ConfigurationRender.class, GCAction.class, ShutdownAction.class, ShutdownRender.class, VMStatRender.class,
-            BundleRepositoryRender.class, LicenseServlet.class };
+        { ComponentConfigurationPrinter.class, ComponentRenderAction.class, ConfigManager.class, BundlesServlet.class,
+            InstallAction.class, SetStartLevelAction.class, ConfigurationRender.class, GCAction.class,
+            ShutdownAction.class, ShutdownRender.class, VMStatRender.class, BundleRepositoryRender.class,
+            LicenseServlet.class };
 
     private BundleContext bundleContext;
 
@@ -317,6 +318,8 @@
         if ( plugin != null )
         {
             req.setAttribute( ATTR_LABEL_MAP, labelMap );
+            req.setAttribute( ATTR_APP_ROOT, request.getContextPath() + request.getServletPath() );
+            
             plugin.service( req, res );
         }
         else
diff --git a/webconsole/src/main/resources/res/ui/configmanager.js b/webconsole/src/main/resources/res/ui/configmanager.js
index 7c4ce5a..0af2182 100644
--- a/webconsole/src/main/resources/res/ui/configmanager.js
+++ b/webconsole/src/main/resources/res/ui/configmanager.js
@@ -23,8 +23,8 @@
     }
     var select = document.getElementById('configSelection_pid');
     var pid = select.options[select.selectedIndex].value;
-    var parm = '?action=ajaxConfigManager&pid=' + pid;
-    sendRequest('GET', parm, displayConfigForm);
+    var parm = pluginRoot + '/' + pid;
+    sendRequest('POST', parm, displayConfigForm);
 }
 
 
@@ -35,8 +35,8 @@
     }
     var select = document.getElementById('configSelection_factory');
     var pid = select.options[select.selectedIndex].value;
-    var parm = '?action=ajaxConfigManager&create=true&pid=' + pid;
-    sendRequest('GET', parm, displayConfigForm);
+    var parm = pluginRoot + '/' + pid + '?create=true';
+    sendRequest('POST', parm, displayConfigForm);
 }
 
 function displayConfigForm(obj) {
@@ -61,9 +61,15 @@
     innerHtml += '<tr class="content">';
     innerHtml += '<td class="content">&nbsp;</td>';
     innerHtml += '<td class="content">';
-    innerHtml += '<form method="post">';
+    innerHtml += '<form method="post" action="' + pluginRoot + '/' + obj.pid + '">';
     innerHtml += '<input type="hidden" name="apply" value="true" />';
-    innerHtml += '<input type="hidden" name="pid" value="' + obj.pid + '" />';
+    
+    // add the factory PID as a hidden form field if present
+    if (obj.factoryPid)
+    {
+        innerHtml += '<input type="hidden" name="factoryPid" value="' + obj.factoryPid + '" />';
+    }
+    
     innerHtml += '<input type="hidden" name="action" value="ajaxConfigManager" />';
     innerHtml += '<table border="0" width="100%">';
     if (obj.description) {