FELIX-3784 Refactor support for Configuration Admin Service:
  - Split ConfigurationSupport in ManagedService and meta type support
    and check MetatypeProvider API availability to decide
  - Split ConfigManager to dynamically and controllably make use
    of the Configuration Admin service to access configurations and
    the Metatype Service for configuration descriptors

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1425765 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index 7d492cd..3ffdab8 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -133,7 +133,6 @@
                         </Export-Package>
                         <Import-Package>
                             org.json.*;version=0,
-							org.osgi.service.metatype;resolution:=optional,
                             javax.servlet.*;version=2.4,
                             org.apache.commons.io; version="[1.4,3)",
                             *
@@ -154,8 +153,7 @@
                         </Include-Resource>
                         <Embed-Dependency>
                             org.apache.felix.utils;inline=org/apache/felix/utils/manifest/**,
-                            org.apache.felix.framework;inline=org/apache/felix/framework/util/VersionRange**,
-                            org.osgi.compendium;inline=org/osgi/service/metatype/**
+                            org.apache.felix.framework;inline=org/apache/felix/framework/util/VersionRange**
                         </Embed-Dependency>
                         <_removeheaders>
                             Embed-Dependency,Private-Package,Include-Resource
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
deleted file mode 100644
index 1ee69f4..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
+++ /dev/null
@@ -1,1412 +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.IOException;
-import java.io.PrintWriter;
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.SortedMap;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-import java.util.Vector;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.felix.webconsole.DefaultVariableResolver;
-import org.apache.felix.webconsole.WebConsoleUtil;
-import org.apache.felix.webconsole.internal.Util;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONWriter;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Filter;
-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>
- */
-public class ConfigManager extends ConfigManagerBase
-{
-
-    private static final long serialVersionUID = 5021174538498622428L;
-
-    private static final String LABEL = "configMgr"; // was name //$NON-NLS-1$
-    private static final String TITLE = "%configMgr.pluginTitle"; //$NON-NLS-1$
-    private static final String CSS[] = { "/res/ui/config.css" }; //$NON-NLS-1$
-
-    private static final String PID_FILTER = "pidFilter"; //$NON-NLS-1$
-    private static final String PID = "pid"; //$NON-NLS-1$
-    private static final String factoryPID = "factoryPid"; //$NON-NLS-1$
-
-    private static final String PLACEHOLDER_PID = "[Temporary PID replaced by real PID upon save]";
-
-    /**
-     * Attribute type code for PASSWORD attributes as defined in
-     * Metatype Service Specification 1.2. Since we cannot yet refer
-     * to the 1.2 API package we just replicate the type code here. Once
-     * the API is available and can be referred to, we should use it.
-     */
-    private static final int ATTRIBUTE_TYPE_PASSWORD = 12;
-
-    /**
-     * Marker value of password fields used as dummy values and
-     * indicating unmodified values.
-     */
-    private static final String PASSWORD_PLACEHOLDER_VALUE = "unmodified"; //$NON-NLS-1$
-
-    // templates
-    private final String TEMPLATE;
-
-    /** Default constructor */
-    public ConfigManager()
-    {
-        super(LABEL, TITLE, CATEGORY_OSGI, CSS);
-
-        // load templates
-        TEMPLATE = readTemplateFile( "/templates/config.html" ); //$NON-NLS-1$
-    }
-
-    private static final boolean isAllowedPid(final String pid)
-    {
-        for(int i = 0; i < pid.length(); i++)
-        {
-            final char c = pid.charAt(i);
-            if ( c == '&' || c == '<' || c == '>' || c == '"' || c == '\'' )
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
-    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 = WebConsoleUtil.urlDecode( info.substring( info.lastIndexOf( '/' ) + 1 ) );
-        }
-
-        // the filter to select part of the configurations
-        String pidFilter = request.getParameter( PID_FILTER );
-
-        final ConfigurationAdmin ca = this.getConfigurationAdmin();
-
-        // ignore this request if the PID and/or configuration admin is missing
-        if ( pid == null || pid.length() == 0 || ca == null )
-        {
-            // should log this here !!
-            return;
-        }
-
-        // ignore this request, if the PID is invalid
-        if ( ! isAllowedPid(pid) )
-        {
-            response.sendError(500);
-            return;
-        }
-        if ( pidFilter != null && ! isAllowedPid(pidFilter) )
-        {
-            response.sendError(500);
-            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 ) //$NON-NLS-1$
-        {
-            config = new PlaceholderConfiguration( pid ); // ca.createFactoryConfiguration( pid, null );
-            pid = config.getPid();
-        }
-        else if ( request.getParameter( "apply" ) != null ) //$NON-NLS-1$
-        {
-            String redirect = applyConfiguration( request, ca, pid );
-            if ( redirect != null )
-            {
-                if (pidFilter != null) {
-                    redirect += '?' + PID_FILTER + '=' + pidFilter;
-                }
-
-                WebConsoleUtil.sendRedirect(request, response, redirect);
-            }
-            else
-            {
-                response.setContentType("text/plain"); //$NON-NLS-1$
-                response.getWriter().print("true"); //$NON-NLS-1$
-            }
-
-            return;
-        }
-
-        if ( config == null )
-        {
-            config = getConfiguration( ca, pid );
-        }
-
-        // check for configuration unbinding
-        if ( request.getParameter( "unbind" ) != null ) //$NON-NLS-1$
-        {
-            if ( config != null && config.getBundleLocation() != null )
-            {
-                config.setBundleLocation( null );
-
-                // workaround for Felix Config Admin 1.2.8 not clearing dynamic
-                // bundle location when clearing static bundle location. In
-                // this case we first set the static bundle location to the
-                // dynamic bundle location and then try to set both to null
-                if ( config.getBundleLocation() != null )
-                {
-                    config.setBundleLocation( "??invalid:bundle/location" ); //$NON-NLS-1$
-                    config.setBundleLocation( null );
-                }
-            }
-            response.setContentType( "application/json" ); //$NON-NLS-1$
-            response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
-            response.getWriter().print( "{ \"status\": true }" ); //$NON-NLS-1$
-            return;
-        }
-
-        // send the result
-        response.setContentType( "application/json" ); //$NON-NLS-1$
-        response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
-        final Locale loc = getLocale( request );
-        final String locale = ( loc != null ) ? loc.toString() : null;
-        printConfigurationJson( response.getWriter(), pid, config, pidFilter, locale );
-    }
-
-
-    /**
-     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
-    protected void doGet( HttpServletRequest request, HttpServletResponse response )
-    throws ServletException, IOException
-    {
-        // let's check for a JSON request
-        final String info = request.getPathInfo();
-        if ( info.endsWith( ".json" ) ) //$NON-NLS-1$
-        {
-            response.setContentType( "application/json" ); //$NON-NLS-1$
-            response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
-
-            // after last slash and without extension
-            String pid = info.substring( info.lastIndexOf( '/' ) + 1, info.length() - 5 );
-            // check whether the PID is actually a filter for the selection
-            // of configurations to display, if the filter correctly converts
-            // into an OSGi filter, we use it to select configurations
-            // to display
-            String pidFilter = request.getParameter( PID_FILTER );
-            if ( pidFilter == null )
-            {
-                pidFilter = pid;
-            }
-            try
-            {
-                getBundleContext().createFilter( pidFilter );
-
-                // if the pidFilter was set from the PID, clear the PID
-                if ( pid == pidFilter )
-                {
-                    pid = null;
-                }
-            }
-            catch ( InvalidSyntaxException ise )
-            {
-                // its OK, if the PID is just a single PID
-                pidFilter = null;
-            }
-
-            // check both PID and PID filter
-            if ( pid != null && !isAllowedPid(pid) )
-            {
-                response.sendError(500);
-            }
-            if ( pidFilter != null && !isAllowedPid(pidFilter) )
-            {
-                response.sendError(500);
-            }
-
-            final ConfigurationAdmin ca = this.getConfigurationAdmin();
-
-            final Locale loc = getLocale( request );
-            final String locale = ( loc != null ) ? loc.toString() : null;
-
-            final PrintWriter pw = response.getWriter();
-
-            try
-            {
-                pw.write( "[" ); //$NON-NLS-1$
-                final Map services = this.getServices( pid, pidFilter, locale, false );
-                boolean printColon = false;
-                for ( Iterator spi = services.keySet().iterator(); spi.hasNext(); )
-                {
-                    final String servicePid = ( String ) spi.next();
-                    final Configuration config = getConfiguration( ca, servicePid );
-                    if ( config != null )
-                    {
-                        if ( printColon )
-                        {
-                            pw.print( ',' );
-                        }
-                        this.printConfigurationJson( pw, servicePid, config, pidFilter, locale );
-                        printColon = true;
-                    }
-                }
-                pw.write( "]" ); //$NON-NLS-1$
-            }
-            catch ( InvalidSyntaxException e )
-            {
-                // this should not happened as we checked the filter before
-            }
-
-            // nothing more to do
-            return;
-        }
-
-        super.doGet( request, response );
-    }
-
-    /**
-     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
-    protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
-    {
-
-        // extract the configuration PID from the request path
-        String pid = request.getPathInfo().substring(this.getLabel().length() + 1);
-        if ( pid.length() == 0 ) {
-            pid = null;
-        } else {
-            pid = pid.substring( pid.lastIndexOf( '/' ) + 1 );
-        }
-        // check whether the PID is actually a filter for the selection
-        // of configurations to display, if the filter correctly converts
-        // into an OSGi filter, we use it to select configurations
-        // to display
-        String pidFilter = request.getParameter( PID_FILTER );
-        if ( pidFilter == null )
-        {
-            pidFilter = pid;
-        }
-        if ( pidFilter != null )
-        {
-            try
-            {
-                getBundleContext().createFilter( pidFilter );
-
-                // if the pidFilter was set from the PID, clear the PID
-                if ( pid == pidFilter )
-                {
-                    pid = null;
-                }
-            }
-            catch ( InvalidSyntaxException ise )
-            {
-                // its OK, if the PID is just a single PID
-                pidFilter = null;
-            }
-        }
-
-        // check both PID and PID filter
-        if ( pid != null && !isAllowedPid(pid) )
-        {
-            response.sendError(500);
-        }
-        if ( pidFilter != null && !isAllowedPid(pidFilter) )
-        {
-            response.sendError(500);
-        }
-        final ConfigurationAdmin ca = this.getConfigurationAdmin();
-
-        final Locale loc = getLocale( request );
-        final String locale = ( loc != null ) ? loc.toString() : null;
-
-
-        JSONObject json = new JSONObject();
-        try
-        {
-            json.put("status", ca != null ? Boolean.TRUE : Boolean.FALSE); //$NON-NLS-1$
-            if ( ca != null )
-            {
-                listConfigurations( json, ca, pidFilter, locale, loc );
-                listFactoryConfigurations( json, pidFilter, locale );
-            }
-        }
-        catch (JSONException e)
-        {
-            throw new IOException(e.toString());
-        }
-
-        // if a configuration is addressed, display it immediately
-        if ( request.getParameter( "create" ) != null && pid != null ) //$NON-NLS-1$
-        {
-            pid = new PlaceholderConfiguration( pid ).getPid();
-        }
-
-
-        // prepare variables
-        DefaultVariableResolver vars = ( ( DefaultVariableResolver ) WebConsoleUtil.getVariableResolver( request ) );
-        vars.put( "__data__", json.toString() ); //$NON-NLS-1$
-        vars.put( "selectedPid", pid != null ? pid : ""); //$NON-NLS-1$ //$NON-NLS-2$
-
-        response.getWriter().print(TEMPLATE);
-    }
-
-
-    private static final Configuration getConfiguration( ConfigurationAdmin ca, String pid )
-    {
-        if ( ca != null && pid != null )
-        {
-            try
-            {
-                // 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 )
-                {
-                    return configs[0];
-                }
-            }
-            catch ( InvalidSyntaxException ise )
-            {
-                // should print message
-            }
-            catch ( IOException ioe )
-            {
-                // should print message
-            }
-        }
-
-        // fallback to no configuration at all
-        return null;
-    }
-
-
-    private final void listFactoryConfigurations(JSONObject json, String pidFilter,
-        String locale)
-    {
-        try
-        {
-            Map optionsFactory = getServices(ManagedServiceFactory.class.getName(),
-                pidFilter, locale, true);
-            addMetaTypeNames(optionsFactory, getFactoryPidObjectClasses(locale),
-                pidFilter, ConfigurationAdmin.SERVICE_FACTORYPID);
-            for ( Iterator ii = optionsFactory.keySet().iterator(); ii.hasNext(); )
-            {
-                String id = ( String ) ii.next();
-                Object name = optionsFactory.get( id );
-                json.append( "fpids", new JSONObject() //$NON-NLS-1$
-                    .put( "id", id ) //$NON-NLS-1$
-                    .put( "name", name ) ); //$NON-NLS-1$
-            }
-        }
-        catch (Exception e)
-        {
-            log("listFactoryConfigurations: Unexpected problem encountered", e);
-        }
-    }
-
-    private final void listConfigurations(JSONObject json, ConfigurationAdmin ca,
-        String pidFilter, String locale, Locale loc)
-    {
-        try
-        {
-            // start with ManagedService instances
-            Map optionsPlain = getServices(ManagedService.class.getName(), pidFilter,
-                locale, true);
-
-            // next are the MetaType informations without ManagedService
-            addMetaTypeNames(optionsPlain, getPidObjectClasses(locale), pidFilter,
-                Constants.SERVICE_PID);
-
-            // add in existing configuration (not duplicating ManagedServices)
-            Configuration[] cfgs = ca.listConfigurations(pidFilter);
-            for (int i = 0; cfgs != null && i < cfgs.length; i++)
-            {
-
-                // ignore configuration object if an entry already exists in the map
-                // or if it is invalid
-                final String pid = cfgs[i].getPid();
-                if (optionsPlain.containsKey(pid) || !isAllowedPid(pid) )
-                {
-                    continue;
-                }
-
-                // insert and entry for the PID
-                ObjectClassDefinition ocd = null;
-                try
-                {
-                    ocd = this.getObjectClassDefinition(cfgs[i], locale);
-                }
-                catch (IllegalArgumentException t)
-                {
-                    // MetaTypeProvider.getObjectClassDefinition might throw illegal
-                    // argument exception. So we must catch it here, otherwise the
-                    // other configurations will not be shown
-                    // See https://issues.apache.org/jira/browse/FELIX-2390
-                }
-
-                if (ocd != null)
-                {
-                    optionsPlain.put(pid, ocd.getName());
-                }
-                else
-                {
-                	optionsPlain.put(pid, pid);
-                }
-            }
-
-            for ( Iterator ii = optionsPlain.keySet().iterator(); ii.hasNext(); )
-            {
-                String id = ( String ) ii.next();
-                Object name = optionsPlain.get( id );
-
-                final Configuration config = getConfiguration( ca, id );
-                JSONObject data = new JSONObject() //
-                    .put( "id", id ) //$NON-NLS-1$
-                    .put( "name", name ); //$NON-NLS-1$
-                if ( null != config )
-                {
-                    final String fpid = config.getFactoryPid();
-                    if ( null != fpid )
-                    {
-                        data.put( "fpid", fpid ); //$NON-NLS-1$
-                    }
-
-                    final Bundle bundle = getBoundBundle( config );
-                    if ( null != bundle )
-                    {
-                        data.put( "bundle", bundle.getBundleId() ); //$NON-NLS-1$
-                        data.put( "bundle_name", Util.getName( bundle, loc ) ); //$NON-NLS-1$
-                    }
-                }
-
-                json.append( "pids", data ); //$NON-NLS-1$
-            }
-        }
-        catch (Exception e)
-        {
-            log("listConfigurations: Unexpected problem encountered", e);
-        }
-    }
-
-    private final Bundle getBoundBundle(Configuration config)
-    {
-        if (null == config)
-            return null;
-        final String location = config.getBundleLocation();
-        if (null == location)
-            return null;
-
-        final Bundle bundles[] = getBundleContext().getBundles();
-        for (int i = 0; bundles != null && i < bundles.length; i++)
-        {
-            if (bundles[i].getLocation().equals(location))
-                return bundles[i];
-
-        }
-        return null;
-    }
-
-
-    private SortedMap getServices( String serviceClass, String serviceFilter, String locale,
-        boolean ocdRequired ) 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, serviceFilter );
-        for ( int i = 0; refs != null && i < refs.length; i++ )
-        {
-            Object pidObject = refs[i].getProperty( Constants.SERVICE_PID );
-            // only include valid PIDs
-            if ( pidObject instanceof String && isAllowedPid((String)pidObject) )
-            {
-                String pid = ( String ) pidObject;
-                String name;
-                final ObjectClassDefinition ocd = this.getObjectClassDefinition( refs[i].getBundle(), pid, locale );
-                if ( ocd != null )
-                {
-                    name = ocd.getName();
-                }
-                else
-                {
-                    name = pid;
-                }
-
-                if ( !ocdRequired || ocd != null ) {
-                    optionsFactory.put( pid, name );
-                }
-            }
-        }
-
-        return optionsFactory;
-    }
-
-
-    private void addMetaTypeNames( final Map pidMap, final Map ocdCollection, final String filterSpec, final String type )
-    {
-        Filter filter = null;
-        if ( filterSpec != null )
-        {
-            try
-            {
-                filter = getBundleContext().createFilter( filterSpec );
-            }
-            catch ( InvalidSyntaxException not_expected )
-            {
-                /* filter is correct */
-            }
-        }
-
-        for ( Iterator ei = ocdCollection.entrySet().iterator(); ei.hasNext(); )
-        {
-            Entry ociEntry = ( Entry ) ei.next();
-            final String pid = ( String ) ociEntry.getKey();
-            final ObjectClassDefinition ocd = ( ObjectClassDefinition ) ociEntry.getValue();
-            if ( filter == null )
-            {
-                pidMap.put( pid, ocd.getName() );
-            }
-            else
-            {
-                final Dictionary props = new Hashtable();
-                props.put( type, pid );
-                if ( filter.match( props ) )
-                {
-                    pidMap.put( pid, ocd.getName() );
-                }
-            }
-        }
-    }
-
-
-    private void printConfigurationJson( PrintWriter pw, String pid, Configuration config, String pidFilter,
-        String locale )
-    {
-
-        JSONWriter result = new JSONWriter( pw );
-
-        if ( pid != null )
-        {
-            try
-            {
-                result.object();
-                this.configForm( result, pid, config, pidFilter, locale );
-                result.endObject();
-            }
-            catch ( Exception e )
-            {
-                log( "Error reading configuration PID " + pid, e );
-            }
-        }
-
-    }
-
-
-    private void configForm( JSONWriter json, String pid, Configuration config, String pidFilter, String locale )
-        throws JSONException
-    {
-
-        json.key( ConfigManager.PID );
-        json.value( pid );
-
-        if ( pidFilter != null )
-        {
-            json.key( PID_FILTER );
-            json.value( pidFilter );
-        }
-
-        Dictionary props = null;
-        ObjectClassDefinition ocd = null;
-        if ( config != null )
-        {
-            props = config.getProperties(); // unchecked
-            ocd = this.getObjectClassDefinition( config, locale );
-        }
-        if (ocd == null)
-        {
-            ocd = this.getObjectClassDefinition( pid, locale );
-        }
-
-        if ( props == null )
-        {
-            props = new Hashtable();
-        }
-
-        if ( ocd != null )
-        {
-            mergeWithMetaType( props, ocd, json );
-        }
-        else
-        {
-            json.key( "title" ).value( pid ); //$NON-NLS-1$
-            json.key( "description" ).value( //$NON-NLS-1$
-                "This form is automatically generated from existing properties because no property "
-                    + "descriptors are available for this configuration. This may be cause by the absence "
-                    + "of the OSGi Metatype Service or the absence of a MetaType descriptor for this configuration." );
-
-            json.key( "properties" ).object(); //$NON-NLS-1$
-            for ( Enumeration pe = props.keys(); pe.hasMoreElements(); )
-            {
-                final String id = ( String ) pe.nextElement();
-
-                // ignore well known special properties
-                if ( !id.equals( Constants.SERVICE_PID ) && !id.equals( Constants.SERVICE_DESCRIPTION )
-                    && !id.equals( Constants.SERVICE_ID ) && !id.equals( Constants.SERVICE_VENDOR )
-                    && !id.equals( ConfigurationAdmin.SERVICE_BUNDLELOCATION )
-                    && !id.equals( ConfigurationAdmin.SERVICE_FACTORYPID ) )
-                {
-                    final Object value = props.get( id );
-                    final AttributeDefinition ad = getAttributeDefinition( id, value );
-                    json.key( id );
-                    attributeToJson( json, ad, value );
-                }
-            }
-            json.endObject();
-        }
-
-        if ( config != null )
-        {
-            this.addConfigurationInfo( config, json, locale );
-        }
-    }
-
-
-    private static final void mergeWithMetaType( Dictionary props, ObjectClassDefinition ocd, JSONWriter json ) throws JSONException
-    {
-        json.key( "title" ).value( ocd.getName() ); //$NON-NLS-1$
-
-        if ( ocd.getDescription() != null )
-        {
-            json.key( "description" ).value( ocd.getDescription() ); //$NON-NLS-1$
-        }
-
-        AttributeDefinition[] ad = ocd.getAttributeDefinitions( ObjectClassDefinition.ALL );
-        if ( ad != null )
-        {
-            json.key( "properties" ).object(); //$NON-NLS-1$
-            for ( int i = 0; i < ad.length; i++ )
-            {
-                final AttributeDefinition adi = ad[i];
-                final String attrId = adi.getID();
-                json.key( attrId );
-                attributeToJson( json, adi, props.get( attrId ) );
-            }
-            json.endObject();
-        }
-    }
-
-
-    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 = ""; //$NON-NLS-1$
-        }
-        else
-        {
-            // if the configuration is bound to a bundle location which
-            // is not related to an installed bundle, we just print the
-            // raw bundle location binding
-            Bundle bundle = this.getBundle( config.getBundleLocation() );
-            if ( bundle == null )
-            {
-                location = config.getBundleLocation();
-            }
-            else
-            {
-                Dictionary headers = bundle.getHeaders( locale );
-                String name = ( String ) headers.get( Constants.BUNDLE_NAME );
-                if ( name == null )
-                {
-                    location = bundle.getSymbolicName();
-                }
-                else
-                {
-                    location = name + " (" + bundle.getSymbolicName() + ')'; //$NON-NLS-1$
-                }
-
-                Version v = Version.parseVersion( ( String ) headers.get( Constants.BUNDLE_VERSION ) );
-                location += ", Version " + v.toString();
-            }
-        }
-        json.key( "bundleLocation" ); //$NON-NLS-1$
-        json.value( location );
-        // raw bundle location and service locations
-        final String pid = config.getPid();
-        String serviceLocation = ""; //$NON-NLS-1$
-        try
-        {
-            final ServiceReference[] refs = getBundleContext().getServiceReferences(
-                null,
-                "(&(" + Constants.OBJECTCLASS + '=' + ManagedService.class.getName() //$NON-NLS-1$
-                    + ")(" + Constants.SERVICE_PID + '=' + pid + "))"); //$NON-NLS-1$ //$NON-NLS-2$
-            if ( refs != null && refs.length > 0 )
-            {
-                serviceLocation = refs[0].getBundle().getLocation();
-            }
-        }
-        catch (Throwable t)
-        {
-            log( "Error getting service associated with configuration " + pid, t );
-        }
-        json.key( "bundle_location" ); //$NON-NLS-1$
-        json.value ( config.getBundleLocation() );
-        json.key( "service_location" ); //$NON-NLS-1$
-        json.value ( serviceLocation );
-    }
-
-
-    private String applyConfiguration( HttpServletRequest request, ConfigurationAdmin ca, String pid )
-        throws IOException
-    {
-        if ( request.getParameter( "delete" ) != null ) //$NON-NLS-1$
-        {
-            // only delete if the PID is not our place holder
-            if ( !PLACEHOLDER_PID.equals( pid ) )
-            {
-                log( "applyConfiguration: Deleting configuration " + pid );
-                Configuration config = ca.getConfiguration( pid, null );
-                config.delete();
-            }
-            return null; // return request.getHeader( "Referer" );
-        }
-
-        String factoryPid = request.getParameter( ConfigManager.factoryPID );
-        Configuration config = null;
-
-        String propertyList = request.getParameter( "propertylist" ); //$NON-NLS-1$
-        if ( propertyList == null )
-        {
-            // FIXME: this would be a bug !!
-        }
-        else
-        {
-            config = getConfiguration( ca, pid, factoryPid );
-
-            Dictionary props = config.getProperties();
-            if ( props == null )
-            {
-                props = new Hashtable();
-            }
-
-            Map adMap = this.getAttributeDefinitionMap( config, null );
-            StringTokenizer propTokens = new StringTokenizer( propertyList, "," ); //$NON-NLS-1$
-            while ( propTokens.hasMoreTokens() )
-            {
-                String propName = propTokens.nextToken();
-                AttributeDefinition ad = (AttributeDefinition) adMap.get( propName );
-
-                // try to derive from current value
-                if (ad == null) {
-                    Object currentValue = props.get( propName );
-                    ad = getAttributeDefinition( propName, currentValue );
-                }
-
-                int attributeType = getAttributeType( ad );
-
-                if ( ad == null
-                    || ( ad.getCardinality() == 0 && ( attributeType == AttributeDefinition.STRING || attributeType == ATTRIBUTE_TYPE_PASSWORD ) ) )
-                {
-                    String prop = request.getParameter( propName );
-                    if ( prop != null
-                        && ( attributeType != ATTRIBUTE_TYPE_PASSWORD || !PASSWORD_PLACEHOLDER_VALUE.equals( prop ) ) )
-                    {
-                        props.put( propName, prop );
-                    }
-                }
-                else if ( ad.getCardinality() == 0 )
-                {
-                    // scalar of non-string
-                    String prop = request.getParameter( propName );
-                    if ( prop != null )
-                    {
-                        try
-                        {
-                            props.put( propName, toType( attributeType, prop ) );
-                        }
-                        catch ( NumberFormatException nfe )
-                        {
-                            // don't care
-                        }
-                    }
-                }
-                else
-                {
-                    // array or vector of any type
-                    Vector vec = new Vector();
-
-                    String[] properties = request.getParameterValues( propName );
-                    if ( properties != null )
-                    {
-                        if ( attributeType == ATTRIBUTE_TYPE_PASSWORD )
-                        {
-                            setPasswordProps( vec, properties, props.get( propName ) );
-                        }
-                        else
-                        {
-                            for ( int i = 0; i < properties.length; i++ )
-                            {
-                                try
-                                {
-                                    vec.add( toType( attributeType, properties[i] ) );
-                                }
-                                catch ( NumberFormatException nfe )
-                                {
-                                    // don't care
-                                }
-                            }
-                        }
-                    }
-
-                    // but ensure size (check for positive value since
-                    // abs(Integer.MIN_VALUE) is still INTEGER.MIN_VALUE)
-                    int maxSize = Math.abs( ad.getCardinality() );
-                    if ( vec.size() > maxSize && maxSize > 0 )
-                    {
-                        vec.setSize( maxSize );
-                    }
-
-                    if ( ad.getCardinality() < 0 )
-                    {
-                        // keep the vector, but only add if not empty
-                        if ( vec.isEmpty() )
-                        {
-                            props.remove( propName );
-                        }
-                        else
-                        {
-                            props.put( propName, vec );
-                        }
-                    }
-                    else
-                    {
-                        // convert to an array
-                        props.put( propName, toArray( attributeType, vec ) );
-                    }
-                }
-            }
-
-            config.update( props );
-        }
-
-        // redirect to the new configuration (if existing)
-        return (config != null) ? config.getPid() : ""; //$NON-NLS-1$
-    }
-
-
-    private static final Configuration getConfiguration( ConfigurationAdmin ca, String pid, String factoryPid ) throws IOException
-    {
-        if ( factoryPid != null && ( pid == null || pid.equals( PLACEHOLDER_PID ) ) )
-        {
-            return ca.createFactoryConfiguration( factoryPid, null );
-        }
-
-        return ca.getConfiguration( pid, null );
-    }
-
-
-    private static void attributeToJson( final JSONWriter json, final AttributeDefinition ad, final Object propValue )
-        throws JSONException
-    {
-        json.object();
-
-        Object value;
-        if ( propValue != null )
-        {
-            value = propValue;
-        }
-        else if ( ad.getDefaultValue() != null )
-        {
-            value = ad.getDefaultValue();
-        }
-        else if ( ad.getCardinality() == 0 )
-        {
-            value = ""; //$NON-NLS-1$
-        }
-        else
-        {
-            value = new String[0];
-        }
-
-        json.key( "name" ); //$NON-NLS-1$
-        json.value( ad.getName() );
-
-        // attribute type - overwrite metatype provided type
-        // if the property name contains "password" and the
-        // type is string
-        int propertyType = getAttributeType( ad );
-
-        json.key( "type" ); //$NON-NLS-1$
-        if ( ad.getOptionLabels() != null && ad.getOptionLabels().length > 0 )
-        {
-            json.object();
-            json.key( "labels" ); //$NON-NLS-1$
-            json.value( Arrays.asList( ad.getOptionLabels() ) );
-            json.key( "values" ); //$NON-NLS-1$
-            json.value( Arrays.asList( ad.getOptionValues() ) );
-            json.endObject();
-        }
-        else
-        {
-            json.value( propertyType );
-        }
-
-        // unless the property is of password type, send it
-        final boolean isPassword = propertyType == ATTRIBUTE_TYPE_PASSWORD;
-        if ( ad.getCardinality() == 0 )
-        {
-            // scalar
-            if ( isPassword )
-            {
-                value = PASSWORD_PLACEHOLDER_VALUE;
-            }
-            else if ( value instanceof Vector )
-            {
-                value = ( ( Vector ) value ).get( 0 );
-            }
-            else if ( value.getClass().isArray() )
-            {
-                value = Array.get( value, 0 );
-            }
-            json.key( "value" ); //$NON-NLS-1$
-            json.value( value );
-        }
-        else
-        {
-            value = new JSONArray( toList( value ) );
-            if ( isPassword )
-            {
-                JSONArray tmp = ( JSONArray ) value;
-                for ( int tmpI = 0; tmpI < tmp.length(); tmpI++ )
-                {
-                    tmp.put( tmpI, PASSWORD_PLACEHOLDER_VALUE );
-                }
-            }
-            json.key( "values" ); //$NON-NLS-1$
-            json.value( value );
-        }
-
-        if ( ad.getDescription() != null )
-        {
-            json.key( "description" ); //$NON-NLS-1$
-            json.value( ad.getDescription() + " (" + ad.getID() + ")" ); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-
-        json.endObject();
-    }
-
-
-    private static AttributeDefinition getAttributeDefinition( final String id, final Object value )
-    {
-        int attrType;
-        int attrCardinality;
-        Class type;
-
-        if ( value == null )
-        {
-            attrCardinality = 0;
-            type = String.class;
-        }
-        else if ( value instanceof Collection )
-        {
-            attrCardinality = Integer.MIN_VALUE;
-            Collection coll = ( Collection ) value;
-            if ( coll.isEmpty() )
-            {
-                type = String.class;
-            }
-            else
-            {
-                type = coll.iterator().next().getClass();
-            }
-        }
-        else if ( value.getClass().isArray() )
-        {
-            attrCardinality = Integer.MAX_VALUE;
-            type = value.getClass().getComponentType();
-        }
-        else
-        {
-            attrCardinality = 0;
-            type = value.getClass();
-        }
-
-        if ( type == Boolean.class || type == Boolean.TYPE )
-        {
-            attrType = AttributeDefinition.BOOLEAN;
-        }
-        else if ( type == Byte.class || type == Byte.TYPE )
-        {
-            attrType = AttributeDefinition.BYTE;
-        }
-        else if ( type == Character.class || type == Character.TYPE )
-        {
-            attrType = AttributeDefinition.CHARACTER;
-        }
-        else if ( type == Double.class || type == Double.TYPE )
-        {
-            attrType = AttributeDefinition.DOUBLE;
-        }
-        else if ( type == Float.class || type == Float.TYPE )
-        {
-            attrType = AttributeDefinition.FLOAT;
-        }
-        else if ( type == Long.class || type == Long.TYPE )
-        {
-            attrType = AttributeDefinition.LONG;
-        }
-        else if ( type == Integer.class || type == Integer.TYPE )
-        {
-            attrType = AttributeDefinition.INTEGER;
-        }
-        else if ( type == Short.class || type == Short.TYPE )
-        {
-            attrType = AttributeDefinition.SHORT;
-        }
-        else
-        {
-            attrType = AttributeDefinition.STRING;
-        }
-
-        return new PlaceholderAttributeDefinition( id, attrType, attrCardinality );
-   }
-
-    private static boolean isPasswordProperty(String name)
-    {
-        return name == null ? false : name.toLowerCase().indexOf("password") != -1; //$NON-NLS-1$
-    }
-
-    private static int getAttributeType( final AttributeDefinition ad )
-    {
-        if ( ad.getType() == AttributeDefinition.STRING && isPasswordProperty( ad.getID() ) )
-        {
-            return ATTRIBUTE_TYPE_PASSWORD;
-        }
-        return ad.getType();
-    }
-
-
-    /**
-     * @throws NumberFormatException If the value cannot be converted to
-     *      a number and type indicates a numeric type
-     */
-    private static final 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
-                // includes ATTRIBUTE_TYPE_PASSWORD/AttributeDefinition.PASSWORD
-                return value;
-        }
-    }
-
-
-    private static List toList( Object value )
-    {
-        if ( value instanceof Vector )
-        {
-            return ( Vector ) value;
-        }
-        else if ( value.getClass().isArray() )
-        {
-            if ( value.getClass().getComponentType().isPrimitive() )
-            {
-                final int len = Array.getLength( value );
-                final Object[] tmp = new Object[len];
-                for ( int j = 0; j < len; j++ )
-                {
-                    tmp[j] = Array.get( value, j );
-                }
-                value = tmp;
-            }
-            return Arrays.asList( ( Object[] ) value );
-        }
-        else
-        {
-            return Collections.singletonList( value );
-        }
-    }
-
-
-    private static void setPasswordProps( final Vector vec, final String[] properties, Object props )
-    {
-        List propList = ( props == null ) ? new ArrayList() : toList( props );
-        for ( int i = 0; i < properties.length; i++ )
-        {
-            if ( PASSWORD_PLACEHOLDER_VALUE.equals( properties[i] ) )
-            {
-                if ( i < propList.size() && propList.get( i ) != null )
-                {
-                    vec.add( propList.get( i ) );
-                }
-            }
-            else
-            {
-                vec.add( properties[i] );
-            }
-        }
-    }
-
-
-    private static final Object toArray( int type, Vector values )
-    {
-        int size = values.size();
-
-        // short cut for string array
-        if ( type == AttributeDefinition.STRING || type == ATTRIBUTE_TYPE_PASSWORD )
-        {
-            return values.toArray( new String[size] );
-        }
-
-        Object array;
-        switch ( type )
-        {
-            case AttributeDefinition.BOOLEAN:
-                array = new boolean[size];
-                break;
-            case AttributeDefinition.BYTE:
-                array = new byte[size];
-                break;
-            case AttributeDefinition.CHARACTER:
-                array = new char[size];
-                break;
-            case AttributeDefinition.DOUBLE:
-                array = new double[size];
-                break;
-            case AttributeDefinition.FLOAT:
-                array = new float[size];
-                break;
-            case AttributeDefinition.LONG:
-                array = new long[size];
-                break;
-            case AttributeDefinition.INTEGER:
-                array = new int[size];
-                break;
-            case AttributeDefinition.SHORT:
-                array = new short[size];
-                break;
-            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
-        }
-
-    }
-
-    private static class PlaceholderAttributeDefinition implements AttributeDefinition
-    {
-
-        final String id;
-        final int type;
-        final int cardinality;
-
-
-        public PlaceholderAttributeDefinition( final String id, int type, int cardinality )
-        {
-            this.id = id;
-            this.type = type;
-            this.cardinality = cardinality;
-        }
-
-
-        public String getName()
-        {
-            return id;
-        }
-
-
-        public String getID()
-        {
-            return id;
-        }
-
-
-        public String getDescription()
-        {
-            // no description
-            return null;
-        }
-
-
-        public int getCardinality()
-        {
-            return cardinality;
-        }
-
-
-        public int getType()
-        {
-            return type;
-        }
-
-
-        public String[] getOptionValues()
-        {
-            return null;
-        }
-
-
-        public String[] getOptionLabels()
-        {
-            return null;
-        }
-
-
-        public String validate( String value )
-        {
-            // no validation
-            return null;
-        }
-
-
-        public String[] getDefaultValue()
-        {
-            return null;
-        }
-    }
-}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigAdminSupport.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigAdminSupport.java
new file mode 100644
index 0000000..2458aa7
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigAdminSupport.java
@@ -0,0 +1,729 @@
+/*
+ * 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.configuration;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+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 org.apache.felix.webconsole.internal.Util;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONWriter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+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;
+
+
+class ConfigAdminSupport
+{
+
+    private final BundleContext bundleContext;
+    private final ConfigurationAdmin service;
+
+    private final ConfigManager configManager;
+
+
+    /**
+     *
+     * @param bundleContext
+     * @param service
+     *
+     * @throws ClassCastException if {@code service} is not a MetaTypeService instances
+     */
+    ConfigAdminSupport( final ConfigManager configManager, final BundleContext bundleContext, final Object service )
+    {
+        this.configManager = configManager;
+        this.bundleContext = bundleContext;
+        this.service = ( ConfigurationAdmin ) service;
+    }
+
+
+    public BundleContext getBundleContext()
+    {
+        return bundleContext;
+    }
+
+    private MetaTypeServiceSupport getMetaTypeSupport()
+    {
+        Object metaTypeService = configManager.getService( ConfigManager.META_TYPE_NAME );
+        if ( metaTypeService != null )
+        {
+            return new MetaTypeServiceSupport( this.getBundleContext(), metaTypeService );
+        }
+
+        return null;
+    }
+
+
+    final Configuration getConfiguration( String pid )
+    {
+        if ( pid != null )
+        {
+            try
+            {
+                // we use listConfigurations to not create configuration
+                // objects persistently without the user providing actual
+                // configuration
+                String filter = '(' + Constants.SERVICE_PID + '=' + pid + ')';
+                Configuration[] configs = this.service.listConfigurations( filter );
+                if ( configs != null && configs.length > 0 )
+                {
+                    return configs[0];
+                }
+            }
+            catch ( InvalidSyntaxException ise )
+            {
+                // should print message
+            }
+            catch ( IOException ioe )
+            {
+                // should print message
+            }
+        }
+
+        // fallback to no configuration at all
+        return null;
+    }
+
+
+    final Configuration getConfiguration( String pid, String factoryPid ) throws IOException
+    {
+        if ( factoryPid != null && ( pid == null || pid.equals( ConfigManager.PLACEHOLDER_PID ) ) )
+        {
+            return this.service.createFactoryConfiguration( factoryPid, null );
+        }
+
+        return this.service.getConfiguration( pid, null );
+    }
+
+
+    Configuration getPlaceholderConfiguration( final String factoryPid )
+    {
+        return new PlaceholderConfiguration( factoryPid );
+    }
+
+    String getPlaceholderPid() {
+        return ConfigManager.PLACEHOLDER_PID;
+    }
+
+    String applyConfiguration( HttpServletRequest request, String pid )
+        throws IOException
+    {
+        if ( request.getParameter( "delete" ) != null ) //$NON-NLS-1$
+        {
+            // only delete if the PID is not our place holder
+            if ( !ConfigManager.PLACEHOLDER_PID.equals( pid ) )
+            {
+                configManager.log( "applyConfiguration: Deleting configuration " + pid );
+                Configuration config = service.getConfiguration( pid, null );
+                config.delete();
+            }
+            return null; // return request.getHeader( "Referer" );
+        }
+
+        String factoryPid = request.getParameter( ConfigManager.factoryPID );
+        Configuration config = null;
+
+        String propertyList = request.getParameter( "propertylist" ); //$NON-NLS-1$
+        if ( propertyList == null )
+        {
+            // FIXME: this would be a bug !!
+        }
+        else
+        {
+            config = getConfiguration( pid, factoryPid );
+
+            Dictionary props = config.getProperties();
+            if ( props == null )
+            {
+                props = new Hashtable();
+            }
+
+            final MetaTypeServiceSupport mtss = getMetaTypeSupport();
+            final Map adMap = ( mtss != null ) ? mtss.getAttributeDefinitionMap( config, null ) : new HashMap();
+            final StringTokenizer propTokens = new StringTokenizer( propertyList, "," ); //$NON-NLS-1$
+            while ( propTokens.hasMoreTokens() )
+            {
+                String propName = propTokens.nextToken();
+                PropertyDescriptor ad = (PropertyDescriptor) adMap.get( propName );
+
+                // try to derive from current value
+                if (ad == null) {
+                    Object currentValue = props.get( propName );
+                    ad = MetaTypeSupport.createAttributeDefinition( propName, currentValue );
+                }
+
+                int attributeType = MetaTypeSupport.getAttributeType( ad );
+
+                if ( ad == null
+                    || ( ad.getCardinality() == 0 && ( attributeType == AttributeDefinition.STRING || attributeType == MetaTypeServiceSupport.ATTRIBUTE_TYPE_PASSWORD ) ) )
+                {
+                    String prop = request.getParameter( propName );
+                    if ( prop != null
+                        && ( attributeType != MetaTypeSupport.ATTRIBUTE_TYPE_PASSWORD || !MetaTypeSupport.PASSWORD_PLACEHOLDER_VALUE.equals( prop ) ) )
+                    {
+                        props.put( propName, prop );
+                    }
+                }
+                else if ( ad.getCardinality() == 0 )
+                {
+                    // scalar of non-string
+                    String prop = request.getParameter( propName );
+                    if ( prop != null )
+                    {
+                        try
+                        {
+                            props.put( propName, MetaTypeSupport.toType( attributeType, prop ) );
+                        }
+                        catch ( NumberFormatException nfe )
+                        {
+                            // don't care
+                        }
+                    }
+                }
+                else
+                {
+                    // array or vector of any type
+                    Vector vec = new Vector();
+
+                    String[] properties = request.getParameterValues( propName );
+                    if ( properties != null )
+                    {
+                        if ( attributeType == MetaTypeSupport.ATTRIBUTE_TYPE_PASSWORD )
+                        {
+                            MetaTypeSupport.setPasswordProps( vec, properties, props.get( propName ) );
+                        }
+                        else
+                        {
+                            for ( int i = 0; i < properties.length; i++ )
+                            {
+                                try
+                                {
+                                    vec.add( MetaTypeSupport.toType( attributeType, properties[i] ) );
+                                }
+                                catch ( NumberFormatException nfe )
+                                {
+                                    // don't care
+                                }
+                            }
+                        }
+                    }
+
+                    // but ensure size (check for positive value since
+                    // abs(Integer.MIN_VALUE) is still INTEGER.MIN_VALUE)
+                    int maxSize = Math.abs( ad.getCardinality() );
+                    if ( vec.size() > maxSize && maxSize > 0 )
+                    {
+                        vec.setSize( maxSize );
+                    }
+
+                    if ( ad.getCardinality() < 0 )
+                    {
+                        // keep the vector, but only add if not empty
+                        if ( vec.isEmpty() )
+                        {
+                            props.remove( propName );
+                        }
+                        else
+                        {
+                            props.put( propName, vec );
+                        }
+                    }
+                    else
+                    {
+                        // convert to an array
+                        props.put( propName, MetaTypeSupport.toArray( attributeType, vec ) );
+                    }
+                }
+            }
+
+            config.update( props );
+        }
+
+        // redirect to the new configuration (if existing)
+        return (config != null) ? config.getPid() : ""; //$NON-NLS-1$
+    }
+
+
+    void printConfigurationJson( PrintWriter pw, String pid, Configuration config, String pidFilter,
+        String locale )
+    {
+
+        JSONWriter result = new JSONWriter( pw );
+
+        if ( pid != null )
+        {
+            try
+            {
+                result.object();
+                this.configForm( result, pid, config, pidFilter, locale );
+                result.endObject();
+            }
+            catch ( Exception e )
+            {
+                configManager.log( "Error reading configuration PID " + pid, e );
+            }
+        }
+
+    }
+
+
+    void configForm( JSONWriter json, String pid, Configuration config, String pidFilter, String locale )
+        throws JSONException
+    {
+
+        json.key( ConfigManager.PID );
+        json.value( pid );
+
+        if ( pidFilter != null )
+        {
+            json.key( ConfigManager.PID_FILTER );
+            json.value( pidFilter );
+        }
+
+        Dictionary props = null;
+        if ( config != null )
+        {
+            props = config.getProperties(); // unchecked
+        }
+        if ( props == null )
+        {
+            props = new Hashtable();
+        }
+
+        boolean doSimpleMerge = true;
+        final MetaTypeServiceSupport mtss = getMetaTypeSupport();
+        if ( mtss != null )
+        {
+            ObjectClassDefinition ocd = null;
+            if ( config != null )
+            {
+                ocd = mtss.getObjectClassDefinition( config, locale );
+            }
+            if ( ocd == null )
+            {
+                ocd = mtss.getObjectClassDefinition( pid, locale );
+            }
+            if ( ocd != null )
+            {
+                mtss.mergeWithMetaType( props, ocd, json );
+                doSimpleMerge = false;
+            }
+        }
+
+        if (doSimpleMerge)
+        {
+            json.key( "title" ).value( pid ); //$NON-NLS-1$
+            json.key( "description" ).value( //$NON-NLS-1$
+                "This form is automatically generated from existing properties because no property "
+                    + "descriptors are available for this configuration. This may be cause by the absence "
+                    + "of the OSGi Metatype Service or the absence of a MetaType descriptor for this configuration." );
+
+            json.key( "properties" ).object(); //$NON-NLS-1$
+            for ( Enumeration pe = props.keys(); pe.hasMoreElements(); )
+            {
+                final String id = ( String ) pe.nextElement();
+
+                // ignore well known special properties
+                if ( !id.equals( Constants.SERVICE_PID ) && !id.equals( Constants.SERVICE_DESCRIPTION )
+                    && !id.equals( Constants.SERVICE_ID ) && !id.equals( Constants.SERVICE_VENDOR )
+                    && !id.equals( ConfigurationAdmin.SERVICE_BUNDLELOCATION )
+                    && !id.equals( ConfigurationAdmin.SERVICE_FACTORYPID ) )
+                {
+                    final Object value = props.get( id );
+                    final PropertyDescriptor ad = MetaTypeServiceSupport.createAttributeDefinition( id, value );
+                    json.key( id );
+                    MetaTypeServiceSupport.attributeToJson( json, ad, value );
+                }
+            }
+            json.endObject();
+        }
+
+        if ( config != null )
+        {
+            this.addConfigurationInfo( config, json, locale );
+        }
+    }
+
+
+    void addConfigurationInfo( Configuration config, JSONWriter json, String locale ) throws JSONException
+    {
+
+        if ( config.getFactoryPid() != null )
+        {
+            json.key( ConfigManager.factoryPID );
+            json.value( config.getFactoryPid() );
+        }
+
+        String location;
+        if ( config.getBundleLocation() == null )
+        {
+            location = ""; //$NON-NLS-1$
+        }
+        else
+        {
+            // if the configuration is bound to a bundle location which
+            // is not related to an installed bundle, we just print the
+            // raw bundle location binding
+            Bundle bundle = MetaTypeServiceSupport.getBundle( this.getBundleContext(), config.getBundleLocation() );
+            if ( bundle == null )
+            {
+                location = config.getBundleLocation();
+            }
+            else
+            {
+                Dictionary headers = bundle.getHeaders( locale );
+                String name = ( String ) headers.get( Constants.BUNDLE_NAME );
+                if ( name == null )
+                {
+                    location = bundle.getSymbolicName();
+                }
+                else
+                {
+                    location = name + " (" + bundle.getSymbolicName() + ')'; //$NON-NLS-1$
+                }
+
+                Version v = Version.parseVersion( ( String ) headers.get( Constants.BUNDLE_VERSION ) );
+                location += ", Version " + v.toString();
+            }
+        }
+        json.key( "bundleLocation" ); //$NON-NLS-1$
+        json.value( location );
+        // raw bundle location and service locations
+        final String pid = config.getPid();
+        String serviceLocation = ""; //$NON-NLS-1$
+        try
+        {
+            final ServiceReference[] refs = getBundleContext().getServiceReferences(
+                null,
+                "(&(" + Constants.OBJECTCLASS + '=' + ManagedService.class.getName() //$NON-NLS-1$
+                    + ")(" + Constants.SERVICE_PID + '=' + pid + "))"); //$NON-NLS-1$ //$NON-NLS-2$
+            if ( refs != null && refs.length > 0 )
+            {
+                serviceLocation = refs[0].getBundle().getLocation();
+            }
+        }
+        catch (Throwable t)
+        {
+            configManager.log( "Error getting service associated with configuration " + pid, t );
+        }
+        json.key( "bundle_location" ); //$NON-NLS-1$
+        json.value ( config.getBundleLocation() );
+        json.key( "service_location" ); //$NON-NLS-1$
+        json.value ( serviceLocation );
+    }
+
+
+    private final Bundle getBoundBundle(Configuration config)
+    {
+        if (null == config)
+            return null;
+        final String location = config.getBundleLocation();
+        if (null == location)
+            return null;
+
+        final Bundle bundles[] = getBundleContext().getBundles();
+        for (int i = 0; bundles != null && i < bundles.length; i++)
+        {
+            if (bundles[i].getLocation().equals(location))
+                return bundles[i];
+
+        }
+        return null;
+    }
+
+
+    final void listConfigurations( JSONObject json, String pidFilter, String locale, Locale loc )
+    {
+        try
+        {
+            // start with ManagedService instances
+            Map optionsPlain = getServices(ManagedService.class.getName(), pidFilter,
+                locale, true);
+
+            // next are the MetaType informations without ManagedService
+            final MetaTypeServiceSupport mtss = getMetaTypeSupport();
+            if ( mtss != null )
+            {
+                addMetaTypeNames( optionsPlain, mtss.getPidObjectClasses( locale ), pidFilter, Constants.SERVICE_PID );
+            }
+
+            // add in existing configuration (not duplicating ManagedServices)
+            Configuration[] cfgs = this.service.listConfigurations(pidFilter);
+            for (int i = 0; cfgs != null && i < cfgs.length; i++)
+            {
+
+                // ignore configuration object if an entry already exists in the map
+                // or if it is invalid
+                final String pid = cfgs[i].getPid();
+                if (optionsPlain.containsKey(pid) || !ConfigManager.isAllowedPid(pid) )
+                {
+                    continue;
+                }
+
+                // insert and entry for the PID
+                if ( mtss != null )
+                {
+                    try
+                    {
+                        ObjectClassDefinition ocd = mtss.getObjectClassDefinition( cfgs[i], locale );
+                        if ( ocd != null )
+                        {
+                            optionsPlain.put( pid, ocd.getName() );
+                            continue;
+                        }
+                    }
+                    catch ( IllegalArgumentException t )
+                    {
+                        // MetaTypeProvider.getObjectClassDefinition might throw illegal
+                        // argument exception. So we must catch it here, otherwise the
+                        // other configurations will not be shown
+                        // See https://issues.apache.org/jira/browse/FELIX-2390
+                    }
+                }
+
+                // no object class definition, use plain PID
+                optionsPlain.put( pid, pid );
+            }
+
+            for ( Iterator ii = optionsPlain.keySet().iterator(); ii.hasNext(); )
+            {
+                String id = ( String ) ii.next();
+                Object name = optionsPlain.get( id );
+
+                final Configuration config = this.getConfiguration( id );
+                JSONObject data = new JSONObject() //
+                    .put( "id", id ) //$NON-NLS-1$
+                    .put( "name", name ); //$NON-NLS-1$
+                if ( null != config )
+                {
+                    final String fpid = config.getFactoryPid();
+                    if ( null != fpid )
+                    {
+                        data.put( "fpid", fpid ); //$NON-NLS-1$
+                    }
+
+                    final Bundle bundle = getBoundBundle( config );
+                    if ( null != bundle )
+                    {
+                        data.put( "bundle", bundle.getBundleId() ); //$NON-NLS-1$
+                        data.put( "bundle_name", Util.getName( bundle, loc ) ); //$NON-NLS-1$
+                    }
+                }
+
+                json.append( "pids", data ); //$NON-NLS-1$
+            }
+        }
+        catch (Exception e)
+        {
+            configManager.log("listConfigurations: Unexpected problem encountered", e);
+        }
+    }
+
+
+    final void listFactoryConfigurations(JSONObject json, String pidFilter,
+        String locale)
+    {
+        try
+        {
+            final Map optionsFactory = getServices(ManagedServiceFactory.class.getName(),
+                pidFilter, locale, true);
+            final MetaTypeServiceSupport mtss = getMetaTypeSupport();
+            if ( mtss != null )
+            {
+                addMetaTypeNames( optionsFactory, mtss.getFactoryPidObjectClasses( locale ), pidFilter,
+                    ConfigurationAdmin.SERVICE_FACTORYPID );
+            }
+            for ( Iterator ii = optionsFactory.keySet().iterator(); ii.hasNext(); )
+            {
+                String id = ( String ) ii.next();
+                Object name = optionsFactory.get( id );
+                json.append( "fpids", new JSONObject() //$NON-NLS-1$
+                    .put( "id", id ) //$NON-NLS-1$
+                    .put( "name", name ) ); //$NON-NLS-1$
+            }
+        }
+        catch (Exception e)
+        {
+            configManager.log("listFactoryConfigurations: Unexpected problem encountered", e);
+        }
+    }
+
+    SortedMap getServices( String serviceClass, String serviceFilter, String locale,
+        boolean ocdRequired ) 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, serviceFilter );
+        for ( int i = 0; refs != null && i < refs.length; i++ )
+        {
+            Object pidObject = refs[i].getProperty( Constants.SERVICE_PID );
+            // only include valid PIDs
+            if ( pidObject instanceof String && ConfigManager.isAllowedPid((String)pidObject) )
+            {
+                String pid = ( String ) pidObject;
+                String name = pid;
+                boolean haveOcd = !ocdRequired;
+                final MetaTypeServiceSupport mtss = getMetaTypeSupport();
+                if ( mtss != null )
+                {
+                    final ObjectClassDefinition ocd = mtss.getObjectClassDefinition( refs[i].getBundle(), pid, locale );
+                    if ( ocd != null )
+                    {
+                        name = ocd.getName();
+                        haveOcd = true;
+                    }
+                }
+
+                if ( haveOcd )
+                {
+                    optionsFactory.put( pid, name );
+                }
+            }
+        }
+
+        return optionsFactory;
+    }
+
+    private void addMetaTypeNames( final Map pidMap, final Map ocdCollection, final String filterSpec, final String type )
+    {
+        Filter filter = null;
+        if ( filterSpec != null )
+        {
+            try
+            {
+                filter = getBundleContext().createFilter( filterSpec );
+            }
+            catch ( InvalidSyntaxException not_expected )
+            {
+                /* filter is correct */
+            }
+        }
+
+        for ( Iterator ei = ocdCollection.entrySet().iterator(); ei.hasNext(); )
+        {
+            Entry ociEntry = ( Entry ) ei.next();
+            final String pid = ( String ) ociEntry.getKey();
+            final ObjectClassDefinition ocd = ( ObjectClassDefinition ) ociEntry.getValue();
+            if ( filter == null )
+            {
+                pidMap.put( pid, ocd.getName() );
+            }
+            else
+            {
+                final Dictionary props = new Hashtable();
+                props.put( type, pid );
+                if ( filter.match( props ) )
+                {
+                    pidMap.put( pid, ocd.getName() );
+                }
+            }
+        }
+    }
+
+    private static class PlaceholderConfiguration implements Configuration
+    {
+
+        private final String factoryPid;
+        private String bundleLocation;
+
+
+        PlaceholderConfiguration( String factoryPid )
+        {
+            this.factoryPid = factoryPid;
+        }
+
+
+        public String getPid()
+        {
+            return ConfigManager.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/configuration/ConfigManager.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
new file mode 100644
index 0000000..9fb2bc1
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
@@ -0,0 +1,396 @@
+/*
+ * 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.configuration;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.webconsole.DefaultVariableResolver;
+import org.apache.felix.webconsole.SimpleWebConsolePlugin;
+import org.apache.felix.webconsole.WebConsoleUtil;
+import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+
+
+/**
+ * The <code>ConfigManager</code> class is the Web Console plugin to
+ * manage configurations.
+ */
+public class ConfigManager extends SimpleWebConsolePlugin implements OsgiManagerPlugin
+{
+
+    private static final long serialVersionUID = 5021174538498622428L;
+
+    private static final String LABEL = "configMgr"; // was name //$NON-NLS-1$
+    private static final String TITLE = "%configMgr.pluginTitle"; //$NON-NLS-1$
+    private static final String CSS[] = { "/res/ui/config.css" }; //$NON-NLS-1$
+
+    static final String PID_FILTER = "pidFilter"; //$NON-NLS-1$
+    static final String PID = "pid"; //$NON-NLS-1$
+    static final String factoryPID = "factoryPid"; //$NON-NLS-1$
+
+    static final String CONFIGURATION_ADMIN_NAME = "org.osgi.service.cm.ConfigurationAdmin";
+
+    static final String META_TYPE_NAME = "org.osgi.service.metatype.MetaTypeService";
+
+    static final String PLACEHOLDER_PID = "[Temporary PID replaced by real PID upon save]";
+
+    // templates
+    private final String TEMPLATE;
+
+    /** Default constructor */
+    public ConfigManager()
+    {
+        super(LABEL, TITLE, CATEGORY_OSGI, CSS);
+
+        // load templates
+        TEMPLATE = readTemplateFile( "/templates/config.html" ); //$NON-NLS-1$
+    }
+
+    static final boolean isAllowedPid(final String pid)
+    {
+        for(int i = 0; i < pid.length(); i++)
+        {
+            final char c = pid.charAt(i);
+            if ( c == '&' || c == '<' || c == '>' || c == '"' || c == '\'' )
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    private static final Locale getLocale( HttpServletRequest request )
+    {
+        try
+        {
+            return request.getLocale();
+        }
+        catch ( Throwable t )
+        {
+            // expected in standard OSGi Servlet 2.1 environments
+            // fallback to using the default locale
+            return Locale.getDefault();
+        }
+    }
+
+
+    /**
+     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    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 = WebConsoleUtil.urlDecode( info.substring( info.lastIndexOf( '/' ) + 1 ) );
+        }
+
+        // the filter to select part of the configurations
+        String pidFilter = request.getParameter( PID_FILTER );
+
+        final ConfigAdminSupport cas = getConfigurationAdminSupport();
+
+        // ignore this request if the PID and/or configuration admin is missing
+        if ( pid == null || pid.length() == 0 || cas == null )
+        {
+            // should log this here !!
+            return;
+        }
+
+        // ignore this request, if the PID is invalid
+        if ( ! isAllowedPid(pid) )
+        {
+            response.sendError(500);
+            return;
+        }
+        if ( pidFilter != null && ! isAllowedPid(pidFilter) )
+        {
+            response.sendError(500);
+            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 ) //$NON-NLS-1$
+        {
+            config = cas.getPlaceholderConfiguration( pid ); // ca.createFactoryConfiguration( pid, null );
+            pid = config.getPid();
+        }
+        else if ( request.getParameter( "apply" ) != null ) //$NON-NLS-1$
+        {
+            String redirect = cas.applyConfiguration( request, pid );
+            if ( redirect != null )
+            {
+                if (pidFilter != null) {
+                    redirect += '?' + PID_FILTER + '=' + pidFilter;
+                }
+
+                WebConsoleUtil.sendRedirect(request, response, redirect);
+            }
+            else
+            {
+                response.setContentType("text/plain"); //$NON-NLS-1$
+                response.getWriter().print("true"); //$NON-NLS-1$
+            }
+
+            return;
+        }
+
+        if ( config == null )
+        {
+            config = cas.getConfiguration( pid );
+        }
+
+        // check for configuration unbinding
+        if ( request.getParameter( "unbind" ) != null ) //$NON-NLS-1$
+        {
+            if ( config != null && config.getBundleLocation() != null )
+            {
+                config.setBundleLocation( null );
+
+                // workaround for Felix Config Admin 1.2.8 not clearing dynamic
+                // bundle location when clearing static bundle location. In
+                // this case we first set the static bundle location to the
+                // dynamic bundle location and then try to set both to null
+                if ( config.getBundleLocation() != null )
+                {
+                    config.setBundleLocation( "??invalid:bundle/location" ); //$NON-NLS-1$
+                    config.setBundleLocation( null );
+                }
+            }
+            response.setContentType( "application/json" ); //$NON-NLS-1$
+            response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
+            response.getWriter().print( "{ \"status\": true }" ); //$NON-NLS-1$
+            return;
+        }
+
+        // send the result
+        response.setContentType( "application/json" ); //$NON-NLS-1$
+        response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
+        final Locale loc = getLocale( request );
+        final String locale = ( loc != null ) ? loc.toString() : null;
+        cas.printConfigurationJson( response.getWriter(), pid, config, pidFilter, locale );
+    }
+
+
+    /**
+     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    protected void doGet( HttpServletRequest request, HttpServletResponse response )
+    throws ServletException, IOException
+    {
+        // let's check for a JSON request
+        final String info = request.getPathInfo();
+        if ( info.endsWith( ".json" ) ) //$NON-NLS-1$
+        {
+            response.setContentType( "application/json" ); //$NON-NLS-1$
+            response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
+
+            // after last slash and without extension
+            String pid = info.substring( info.lastIndexOf( '/' ) + 1, info.length() - 5 );
+            // check whether the PID is actually a filter for the selection
+            // of configurations to display, if the filter correctly converts
+            // into an OSGi filter, we use it to select configurations
+            // to display
+            String pidFilter = request.getParameter( PID_FILTER );
+            if ( pidFilter == null )
+            {
+                pidFilter = pid;
+            }
+            try
+            {
+                getBundleContext().createFilter( pidFilter );
+
+                // if the pidFilter was set from the PID, clear the PID
+                if ( pid == pidFilter )
+                {
+                    pid = null;
+                }
+            }
+            catch ( InvalidSyntaxException ise )
+            {
+                // its OK, if the PID is just a single PID
+                pidFilter = null;
+            }
+
+            // check both PID and PID filter
+            if ( pid != null && !isAllowedPid(pid) )
+            {
+                response.sendError(500);
+            }
+            if ( pidFilter != null && !isAllowedPid(pidFilter) )
+            {
+                response.sendError(500);
+            }
+
+
+            final Locale loc = getLocale( request );
+            final String locale = ( loc != null ) ? loc.toString() : null;
+
+            final PrintWriter pw = response.getWriter();
+            pw.write( "[" ); //$NON-NLS-1$
+            final ConfigAdminSupport ca = this.getConfigurationAdminSupport();
+            if ( ca != null )
+            {
+                try
+                {
+                    final Map services = ca.getServices( pid, pidFilter, locale, false );
+                    boolean printComma = false;
+                    for ( Iterator spi = services.keySet().iterator(); spi.hasNext(); )
+                    {
+                        final String servicePid = ( String ) spi.next();
+                        final Configuration config = ca.getConfiguration( servicePid );
+                        if ( config != null )
+                        {
+                            if ( printComma )
+                            {
+                                pw.print( ',' );
+                            }
+                            ca.printConfigurationJson( pw, servicePid, config, pidFilter, locale );
+                            printComma = true;
+                        }
+                    }
+                }
+                catch ( InvalidSyntaxException e )
+                {
+                    // this should not happened as we checked the filter before
+                }
+            }
+            pw.write( "]" ); //$NON-NLS-1$
+
+            // nothing more to do
+            return;
+        }
+
+        super.doGet( request, response );
+    }
+
+    /**
+     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
+    {
+
+        // extract the configuration PID from the request path
+        String pid = request.getPathInfo().substring(this.getLabel().length() + 1);
+        if ( pid.length() == 0 ) {
+            pid = null;
+        } else {
+            pid = pid.substring( pid.lastIndexOf( '/' ) + 1 );
+        }
+        // check whether the PID is actually a filter for the selection
+        // of configurations to display, if the filter correctly converts
+        // into an OSGi filter, we use it to select configurations
+        // to display
+        String pidFilter = request.getParameter( PID_FILTER );
+        if ( pidFilter == null )
+        {
+            pidFilter = pid;
+        }
+        if ( pidFilter != null )
+        {
+            try
+            {
+                getBundleContext().createFilter( pidFilter );
+
+                // if the pidFilter was set from the PID, clear the PID
+                if ( pid == pidFilter )
+                {
+                    pid = null;
+                }
+            }
+            catch ( InvalidSyntaxException ise )
+            {
+                // its OK, if the PID is just a single PID
+                pidFilter = null;
+            }
+        }
+
+        // check both PID and PID filter
+        if ( pid != null && !isAllowedPid(pid) )
+        {
+            response.sendError(500);
+        }
+        if ( pidFilter != null && !isAllowedPid(pidFilter) )
+        {
+            response.sendError(500);
+        }
+
+        final Locale loc = getLocale( request );
+        final String locale = ( loc != null ) ? loc.toString() : null;
+
+
+        JSONObject json = new JSONObject();
+        try
+        {
+            final ConfigAdminSupport ca = getConfigurationAdminSupport();
+            json.put("status", ca != null ? Boolean.TRUE : Boolean.FALSE); //$NON-NLS-1$
+            if ( ca != null )
+            {
+                ca.listConfigurations( json, pidFilter, locale, loc );
+                ca.listFactoryConfigurations( json, pidFilter, locale );
+            }
+        }
+        catch (JSONException e)
+        {
+            throw new IOException(e.toString());
+        }
+
+        // if a configuration is addressed, display it immediately
+        if ( request.getParameter( "create" ) != null && pid != null ) //$NON-NLS-1$
+        {
+            pid = PLACEHOLDER_PID; // new PlaceholderConfiguration( pid ).getPid();
+        }
+
+
+        // prepare variables
+        DefaultVariableResolver vars = ( ( DefaultVariableResolver ) WebConsoleUtil.getVariableResolver( request ) );
+        vars.put( "__data__", json.toString() ); //$NON-NLS-1$
+        vars.put( "selectedPid", pid != null ? pid : ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+        response.getWriter().print(TEMPLATE);
+    }
+
+    private ConfigAdminSupport getConfigurationAdminSupport()
+    {
+        Object configurationAdmin = getService( CONFIGURATION_ADMIN_NAME );
+        if ( configurationAdmin != null )
+        {
+            return new ConfigAdminSupport( this, this.getBundleContext(), configurationAdmin );
+        }
+        return null;
+    }
+
+
+}
+
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigurationAdminConfigurationPrinter.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationAdminConfigurationPrinter.java
similarity index 97%
rename from webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigurationAdminConfigurationPrinter.java
rename to webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationAdminConfigurationPrinter.java
index 8d2abb1..62e1db5 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigurationAdminConfigurationPrinter.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationAdminConfigurationPrinter.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.felix.webconsole.internal.compendium;
+package org.apache.felix.webconsole.internal.configuration;
 
 import java.io.PrintWriter;
 import java.util.Dictionary;
@@ -55,7 +55,7 @@
      */
     public void printConfiguration(PrintWriter pw)
     {
-        ServiceReference sr = getBundleContext().getServiceReference( ConfigManagerBase.CONFIGURATION_ADMIN_NAME );
+        ServiceReference sr = getBundleContext().getServiceReference( ConfigManager.CONFIGURATION_ADMIN_NAME );
         if (sr == null)
         {
             pw.println("Status: Configuration Admin Service not available");
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/configuration/MetaTypeServiceSupport.java
similarity index 77%
rename from webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
rename to webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetaTypeServiceSupport.java
index 81f04f3..5abbe9e 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetaTypeServiceSupport.java
@@ -14,20 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.felix.webconsole.internal.compendium;
+package org.apache.felix.webconsole.internal.configuration;
 
 
+import java.util.Dictionary;
 import java.util.HashMap;
-import java.util.Locale;
 import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.felix.webconsole.SimpleWebConsolePlugin;
-import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
+import org.json.JSONException;
+import org.json.JSONWriter;
 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;
@@ -40,30 +37,35 @@
  * methods mostly with respect to using the MetaTypeService to access
  * configuration descriptions.
  */
-abstract class ConfigManagerBase extends SimpleWebConsolePlugin implements OsgiManagerPlugin
+class MetaTypeServiceSupport extends MetaTypeSupport
 {
 
-    ConfigManagerBase(String label, String title, String category, String[] css)
+    private final BundleContext bundleContext;
+
+    private final MetaTypeService service;
+
+    /**
+     *
+     * @param bundleContext
+     * @param service
+     *
+     * @throws ClassCastException if {@code service} is not a MetaTypeService instances
+     */
+    MetaTypeServiceSupport( final BundleContext bundleContext, final Object service )
     {
-        super(label, title, category, css);
+        super();
+        this.bundleContext = bundleContext;
+        this.service = ( MetaTypeService ) service;
     }
 
-    private static final long serialVersionUID = -6691093960031418130L;
-
-    static final String CONFIGURATION_ADMIN_NAME = "org.osgi.service.cm.ConfigurationAdmin";
-
-    static final String META_TYPE_NAME = "org.osgi.service.metatype.MetaTypeService";
-
-
-    protected ConfigurationAdmin getConfigurationAdmin()
+    public BundleContext getBundleContext()
     {
-        return ( ConfigurationAdmin ) getService( CONFIGURATION_ADMIN_NAME );
+        return bundleContext;
     }
 
-
-    protected MetaTypeService getMetaTypeService()
+    public MetaTypeService getMetaTypeService()
     {
-        return ( MetaTypeService ) getService( META_TYPE_NAME );
+        return service;
     }
 
 
@@ -75,7 +77,7 @@
      * @param locale The name of the locale to get the meta data for.
      * @return see the method description
      */
-    protected Map getPidObjectClasses( final String locale )
+    Map getPidObjectClasses( final String locale )
     {
         return getObjectClassDefinitions( PID_GETTER, locale );
     }
@@ -90,7 +92,7 @@
      * @param locale The name of the locale to get the meta data for.
      * @return see the method description
      */
-    protected Map getFactoryPidObjectClasses( final String locale )
+    Map getFactoryPidObjectClasses( final String locale )
     {
         return getObjectClassDefinitions( FACTORY_PID_GETTER, locale );
     }
@@ -134,7 +136,7 @@
                         {
                             ocd = mti.getObjectClassDefinition( idList[j], locale );
                         }
-                        catch (IllegalArgumentException ignore)
+                        catch ( IllegalArgumentException ignore )
                         {
                             // ignore - just don't show this configuration
                         }
@@ -150,13 +152,13 @@
     }
 
 
-    protected ObjectClassDefinition getObjectClassDefinition( Configuration config, String locale )
+    ObjectClassDefinition getObjectClassDefinition( Configuration config, String locale )
     {
         // if the configuration is bound, try to get the object class
         // definition from the bundle installed from the given location
         if ( config.getBundleLocation() != null )
         {
-            Bundle bundle = this.getBundle( config.getBundleLocation() );
+            Bundle bundle = getBundle( this.getBundleContext(), config.getBundleLocation() );
             if ( bundle != null )
             {
                 String id = config.getFactoryPid();
@@ -164,7 +166,7 @@
                 {
                     id = config.getPid();
                 }
-                return getObjectClassDefinition(bundle, id, locale);
+                return getObjectClassDefinition( bundle, id, locale );
             }
         }
 
@@ -182,7 +184,7 @@
     }
 
 
-    protected ObjectClassDefinition getObjectClassDefinition( Bundle bundle, String pid, String locale )
+    ObjectClassDefinition getObjectClassDefinition( Bundle bundle, String pid, String locale )
     {
         if ( bundle != null )
         {
@@ -197,7 +199,7 @@
                     {
                         return mti.getObjectClassDefinition( pid, locale );
                     }
-                    catch (IllegalArgumentException e)
+                    catch ( IllegalArgumentException e )
                     {
                         // MetaTypeProvider.getObjectClassDefinition might throw illegal
                         // argument exception. So we must catch it here, otherwise the
@@ -214,7 +216,7 @@
     }
 
 
-    protected ObjectClassDefinition getObjectClassDefinition( String pid, String locale )
+    ObjectClassDefinition getObjectClassDefinition( String pid, String locale )
     {
         Bundle[] bundles = this.getBundleContext().getBundles();
         for ( int i = 0; i < bundles.length; i++ )
@@ -236,7 +238,7 @@
     }
 
 
-    protected Map getAttributeDefinitionMap( Configuration config, String locale )
+    Map getAttributeDefinitionMap( Configuration config, String locale )
     {
         Map adMap = new HashMap();
         ObjectClassDefinition ocd = this.getObjectClassDefinition( config, locale );
@@ -247,7 +249,7 @@
             {
                 for ( int i = 0; i < ad.length; i++ )
                 {
-                    adMap.put( ad[i].getID(), ad[i] );
+                    adMap.put( ad[i].getID(), new MetatypePropertyDescriptor( ad[i] ) );
                 }
             }
         }
@@ -255,40 +257,33 @@
     }
 
 
-    protected Bundle getBundle( String bundleLocation )
+    void mergeWithMetaType( Dictionary props, ObjectClassDefinition ocd, JSONWriter json ) throws JSONException
     {
-        if ( bundleLocation == null )
+        json.key( "title" ).value( ocd.getName() ); //$NON-NLS-1$
+
+        if ( ocd.getDescription() != null )
         {
-            return null;
+            json.key( "description" ).value( ocd.getDescription() ); //$NON-NLS-1$
         }
 
-        Bundle[] bundles = this.getBundleContext().getBundles();
-        for ( int i = 0; i < bundles.length; i++ )
+        AttributeDefinition[] ad = ocd.getAttributeDefinitions( ObjectClassDefinition.ALL );
+        if ( ad != null )
         {
-            if ( bundleLocation.equals( bundles[i].getLocation() ) )
+            json.key( "properties" ).object(); //$NON-NLS-1$
+            for ( int i = 0; i < ad.length; i++ )
             {
-                return bundles[i];
+                final AttributeDefinition adi = ad[i];
+                final String attrId = adi.getID();
+                json.key( attrId );
+                attributeToJson( json, new MetatypePropertyDescriptor( adi ), props.get( attrId ) );
             }
-        }
-
-        return null;
-    }
-
-
-    protected static final Locale getLocale( HttpServletRequest request )
-    {
-        try
-        {
-            return request.getLocale();
-        }
-        catch ( Throwable t )
-        {
-            // expected in standard OSGi Servlet 2.1 environments
-            // fallback to using the default locale
-            return Locale.getDefault();
+            json.endObject();
         }
     }
 
+
+
+
     /**
      * The <code>IdGetter</code> interface is an internal helper to abstract
      * retrieving object class definitions from all bundles for either
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetaTypeSupport.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetaTypeSupport.java
new file mode 100644
index 0000000..f58d4bb
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetaTypeSupport.java
@@ -0,0 +1,393 @@
+/*
+ * 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.configuration;
+
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Vector;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONWriter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.metatype.AttributeDefinition;
+
+
+/**
+ * The <code>ConfigManagerBase</code> is the base class for the
+ * ConfigurationAdmin support in the web console. It provides various helper
+ * methods mostly with respect to using the MetaTypeService to access
+ * configuration descriptions.
+ */
+class MetaTypeSupport
+{
+
+    /**
+     * Attribute type code for PASSWORD attributes as defined in
+     * Metatype Service Specification 1.2. Since we cannot yet refer
+     * to the 1.2 API package we just replicate the type code here. Once
+     * the API is available and can be referred to, we should use it.
+     */
+    static final int ATTRIBUTE_TYPE_PASSWORD = 12;
+
+    /**
+     * Marker value of password fields used as dummy values and
+     * indicating unmodified values.
+     */
+    static final String PASSWORD_PLACEHOLDER_VALUE = "unmodified"; //$NON-NLS-1$
+
+
+    static Bundle getBundle( final BundleContext bundleContext, final String bundleLocation )
+    {
+        if ( bundleLocation == null )
+        {
+            return null;
+        }
+
+        Bundle[] bundles = bundleContext.getBundles();
+        for ( int i = 0; i < bundles.length; i++ )
+        {
+            if ( bundleLocation.equals( bundles[i].getLocation() ) )
+            {
+                return bundles[i];
+            }
+        }
+
+        return null;
+    }
+
+
+    static void attributeToJson( final JSONWriter json, final PropertyDescriptor ad, final Object propValue )
+        throws JSONException
+    {
+        json.object();
+
+        Object value;
+        if ( propValue != null )
+        {
+            value = propValue;
+        }
+        else if ( ad.getDefaultValue() != null )
+        {
+            value = ad.getDefaultValue();
+        }
+        else if ( ad.getCardinality() == 0 )
+        {
+            value = ""; //$NON-NLS-1$
+        }
+        else
+        {
+            value = new String[0];
+        }
+
+        json.key( "name" ); //$NON-NLS-1$
+        json.value( ad.getName() );
+
+        // attribute type - overwrite metatype provided type
+        // if the property name contains "password" and the
+        // type is string
+        int propertyType = getAttributeType( ad );
+
+        json.key( "type" ); //$NON-NLS-1$
+        if ( ad.getOptionLabels() != null && ad.getOptionLabels().length > 0 )
+        {
+            json.object();
+            json.key( "labels" ); //$NON-NLS-1$
+            json.value( Arrays.asList( ad.getOptionLabels() ) );
+            json.key( "values" ); //$NON-NLS-1$
+            json.value( Arrays.asList( ad.getOptionValues() ) );
+            json.endObject();
+        }
+        else
+        {
+            json.value( propertyType );
+        }
+
+        // unless the property is of password type, send it
+        final boolean isPassword = propertyType == ATTRIBUTE_TYPE_PASSWORD;
+        if ( ad.getCardinality() == 0 )
+        {
+            // scalar
+            if ( isPassword )
+            {
+                value = PASSWORD_PLACEHOLDER_VALUE;
+            }
+            else if ( value instanceof Vector )
+            {
+                value = ( ( Vector ) value ).get( 0 );
+            }
+            else if ( value.getClass().isArray() )
+            {
+                value = Array.get( value, 0 );
+            }
+            json.key( "value" ); //$NON-NLS-1$
+            json.value( value );
+        }
+        else
+        {
+            value = new JSONArray( toList( value ) );
+            if ( isPassword )
+            {
+                JSONArray tmp = ( JSONArray ) value;
+                for ( int tmpI = 0; tmpI < tmp.length(); tmpI++ )
+                {
+                    tmp.put( tmpI, PASSWORD_PLACEHOLDER_VALUE );
+                }
+            }
+            json.key( "values" ); //$NON-NLS-1$
+            json.value( value );
+        }
+
+        if ( ad.getDescription() != null )
+        {
+            json.key( "description" ); //$NON-NLS-1$
+            json.value( ad.getDescription() + " (" + ad.getID() + ")" ); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        json.endObject();
+    }
+
+
+    private static boolean isPasswordProperty( String name )
+    {
+        return name == null ? false : name.toLowerCase().indexOf( "password" ) != -1; //$NON-NLS-1$
+    }
+
+
+    private static List toList( Object value )
+    {
+        if ( value instanceof Vector )
+        {
+            return ( Vector ) value;
+        }
+        else if ( value.getClass().isArray() )
+        {
+            if ( value.getClass().getComponentType().isPrimitive() )
+            {
+                final int len = Array.getLength( value );
+                final Object[] tmp = new Object[len];
+                for ( int j = 0; j < len; j++ )
+                {
+                    tmp[j] = Array.get( value, j );
+                }
+                value = tmp;
+            }
+            return Arrays.asList( ( Object[] ) value );
+        }
+        else
+        {
+            return Collections.singletonList( value );
+        }
+    }
+
+
+    static PropertyDescriptor createAttributeDefinition( final String id, final Object value )
+    {
+        int attrType;
+        int attrCardinality;
+        Class type;
+
+        if ( value == null )
+        {
+            attrCardinality = 0;
+            type = String.class;
+        }
+        else if ( value instanceof Collection )
+        {
+            attrCardinality = Integer.MIN_VALUE;
+            Collection coll = ( Collection ) value;
+            if ( coll.isEmpty() )
+            {
+                type = String.class;
+            }
+            else
+            {
+                type = coll.iterator().next().getClass();
+            }
+        }
+        else if ( value.getClass().isArray() )
+        {
+            attrCardinality = Integer.MAX_VALUE;
+            type = value.getClass().getComponentType();
+        }
+        else
+        {
+            attrCardinality = 0;
+            type = value.getClass();
+        }
+
+        if ( type == Boolean.class || type == Boolean.TYPE )
+        {
+            attrType = AttributeDefinition.BOOLEAN;
+        }
+        else if ( type == Byte.class || type == Byte.TYPE )
+        {
+            attrType = AttributeDefinition.BYTE;
+        }
+        else if ( type == Character.class || type == Character.TYPE )
+        {
+            attrType = AttributeDefinition.CHARACTER;
+        }
+        else if ( type == Double.class || type == Double.TYPE )
+        {
+            attrType = AttributeDefinition.DOUBLE;
+        }
+        else if ( type == Float.class || type == Float.TYPE )
+        {
+            attrType = AttributeDefinition.FLOAT;
+        }
+        else if ( type == Long.class || type == Long.TYPE )
+        {
+            attrType = AttributeDefinition.LONG;
+        }
+        else if ( type == Integer.class || type == Integer.TYPE )
+        {
+            attrType = AttributeDefinition.INTEGER;
+        }
+        else if ( type == Short.class || type == Short.TYPE )
+        {
+            attrType = AttributeDefinition.SHORT;
+        }
+        else
+        {
+            attrType = AttributeDefinition.STRING;
+        }
+
+        return new PropertyDescriptor( id, attrType, attrCardinality );
+    }
+
+
+    static int getAttributeType( final PropertyDescriptor ad )
+    {
+        if ( ad.getType() == AttributeDefinition.STRING && isPasswordProperty( ad.getID() ) )
+        {
+            return ATTRIBUTE_TYPE_PASSWORD;
+        }
+        return ad.getType();
+    }
+
+
+    /**
+     * @throws NumberFormatException If the value cannot be converted to
+     *      a number and type indicates a numeric type
+     */
+    static final 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
+                // includes ATTRIBUTE_TYPE_PASSWORD/AttributeDefinition.PASSWORD
+                return value;
+        }
+    }
+
+
+    static void setPasswordProps( final Vector vec, final String[] properties, Object props )
+    {
+        List propList = ( props == null ) ? new ArrayList() : toList( props );
+        for ( int i = 0; i < properties.length; i++ )
+        {
+            if ( PASSWORD_PLACEHOLDER_VALUE.equals( properties[i] ) )
+            {
+                if ( i < propList.size() && propList.get( i ) != null )
+                {
+                    vec.add( propList.get( i ) );
+                }
+            }
+            else
+            {
+                vec.add( properties[i] );
+            }
+        }
+    }
+
+
+    static final Object toArray( int type, Vector values )
+    {
+        int size = values.size();
+
+        // short cut for string array
+        if ( type == AttributeDefinition.STRING || type == ATTRIBUTE_TYPE_PASSWORD )
+        {
+            return values.toArray( new String[size] );
+        }
+
+        Object array;
+        switch ( type )
+        {
+            case AttributeDefinition.BOOLEAN:
+                array = new boolean[size];
+                break;
+            case AttributeDefinition.BYTE:
+                array = new byte[size];
+                break;
+            case AttributeDefinition.CHARACTER:
+                array = new char[size];
+                break;
+            case AttributeDefinition.DOUBLE:
+                array = new double[size];
+                break;
+            case AttributeDefinition.FLOAT:
+                array = new float[size];
+                break;
+            case AttributeDefinition.LONG:
+                array = new long[size];
+                break;
+            case AttributeDefinition.INTEGER:
+                array = new int[size];
+                break;
+            case AttributeDefinition.SHORT:
+                array = new short[size];
+                break;
+            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/configuration/MetatypePropertyDescriptor.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetatypePropertyDescriptor.java
new file mode 100644
index 0000000..533c1e5
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetatypePropertyDescriptor.java
@@ -0,0 +1,82 @@
+/*
+ * 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.configuration;
+
+
+import org.osgi.service.metatype.AttributeDefinition;
+
+
+/**
+ * The <code>MetatypePropertyDescriptor</code> extends the
+ * {@link PropertyDescriptor} describing a property based on a real
+ * Metatype Service Attribute Definition.
+ */
+public class MetatypePropertyDescriptor extends PropertyDescriptor
+{
+    private final AttributeDefinition ad;
+
+
+    public MetatypePropertyDescriptor( AttributeDefinition ad )
+    {
+        super( ad.getID(), ad.getType(), ad.getCardinality() );
+        this.ad = ad;
+    }
+
+
+    public String getName()
+    {
+        return ad.getName();
+    }
+
+
+    public String getDescription()
+    {
+        return ad.getDescription();
+    }
+
+
+    public int getType()
+    {
+        return ad.getType();
+    }
+
+
+    public String[] getOptionValues()
+    {
+        return ad.getOptionValues();
+    }
+
+
+    public String[] getOptionLabels()
+    {
+        return ad.getOptionLabels();
+    }
+
+
+    public String validate( String value )
+    {
+        return ad.validate( value );
+    }
+
+
+    public String[] getDefaultValue()
+    {
+        return ad.getDefaultValue();
+    }
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/PropertyDescriptor.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/PropertyDescriptor.java
new file mode 100644
index 0000000..dc412c3
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/PropertyDescriptor.java
@@ -0,0 +1,97 @@
+/*
+ * 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.configuration;
+
+/**
+ * The <code>PropertyDescriptor</code> class describes a property to
+ * generate the Configuration Management UI. It is built after the
+ * AttributeDescriptor but to prevent a hard dependency on the Metatype
+ * Service API, this does not implement it.
+ */
+class PropertyDescriptor
+{
+
+    final String id;
+    final int type;
+    final int cardinality;
+
+
+    public PropertyDescriptor( final String id, int type, int cardinality )
+    {
+        this.id = id;
+        this.type = type;
+        this.cardinality = cardinality;
+    }
+
+
+    public String getName()
+    {
+        return id;
+    }
+
+
+    public String getID()
+    {
+        return id;
+    }
+
+
+    public String getDescription()
+    {
+        // no description
+        return null;
+    }
+
+
+    public int getCardinality()
+    {
+        return cardinality;
+    }
+
+
+    public int getType()
+    {
+        return type;
+    }
+
+
+    public String[] getOptionValues()
+    {
+        return null;
+    }
+
+
+    public String[] getOptionLabels()
+    {
+        return null;
+    }
+
+
+    public String validate( String value )
+    {
+        // no validation
+        return null;
+    }
+
+
+    public String[] getDefaultValue()
+    {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationMetatypeSupport.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationMetatypeSupport.java
new file mode 100644
index 0000000..0d42b0d
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationMetatypeSupport.java
@@ -0,0 +1,288 @@
+/*
+ * 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.servlet;
+
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.TreeMap;
+
+import org.apache.felix.webconsole.internal.Util;
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+
+class ConfigurationMetatypeSupport extends ConfigurationSupport implements MetaTypeProvider
+{
+    private static final String[] CONF_PROPS = new String[]
+        { OsgiManager.PROP_MANAGER_ROOT, OsgiManager.DEFAULT_MANAGER_ROOT, //
+            OsgiManager.PROP_HTTP_SERVICE_SELECTOR, OsgiManager.DEFAULT_HTTP_SERVICE_SELECTOR, //
+            OsgiManager.PROP_DEFAULT_RENDER, OsgiManager.DEFAULT_PAGE, //
+            OsgiManager.PROP_REALM, OsgiManager.DEFAULT_REALM, //
+            OsgiManager.PROP_USER_NAME, OsgiManager.DEFAULT_USER_NAME, //
+            OsgiManager.PROP_PASSWORD, OsgiManager.DEFAULT_PASSWORD, //
+            OsgiManager.PROP_LOCALE, "", //$NON-NLS-1$
+        };
+
+    private final Object ocdLock = new Object();
+    private String ocdLocale;
+    private ObjectClassDefinition ocd;
+
+
+    ConfigurationMetatypeSupport( OsgiManager osgiManager )
+    {
+        super( osgiManager );
+    }
+
+
+    //---------- MetaTypeProvider
+
+    public String[] getLocales()
+    {
+        // there is no locale support here
+        return null;
+    }
+
+
+    public ObjectClassDefinition getObjectClassDefinition( String id, String locale )
+    {
+        if ( !osgiManager.getConfigurationPid().equals( id ) )
+        {
+            return null;
+        }
+
+        if ( locale == null )
+            locale = Locale.ENGLISH.getLanguage();
+
+        // check if OCD is already initialized and it's locale is the same as the requested one
+        synchronized ( ocdLock )
+        {
+            if ( ocd != null && ocdLocale != null && ocdLocale.equals( locale ) )
+            {
+                return ocd;
+            }
+        }
+
+        ObjectClassDefinition xocd = null;
+        final Locale localeObj = Util.parseLocaleString( locale );
+        final ResourceBundle rb = osgiManager.resourceBundleManager.getResourceBundle( osgiManager.getBundleContext()
+            .getBundle(), localeObj );
+        final Map defaultConfig = osgiManager.getDefaultConfiguration();
+
+        // simple configuration properties
+        final ArrayList adList = new ArrayList();
+        for ( int i = 0; i < CONF_PROPS.length; i++ )
+        {
+            final String key = CONF_PROPS[i++];
+            final String defaultValue = ConfigurationUtil.getProperty( defaultConfig, key, CONF_PROPS[i] );
+            final String name = getString( rb, "metadata." + key + ".name", key ); //$NON-NLS-1$ //$NON-NLS-2$
+            final String descr = getString( rb, "metadata." + key + ".description", key ); //$NON-NLS-1$ //$NON-NLS-2$
+            adList.add( new AttributeDefinitionImpl( key, name, descr, defaultValue ) );
+        }
+
+        // log level is select - so no simple default value; requires localized option labels
+        adList.add( new AttributeDefinitionImpl( OsgiManager.PROP_LOG_LEVEL, getString( rb,
+            "metadata.loglevel.name", OsgiManager.PROP_LOG_LEVEL ), //$NON-NLS-1$
+            getString( rb, "metadata.loglevel.description", OsgiManager.PROP_LOG_LEVEL ), //$NON-NLS-1$
+            AttributeDefinition.INTEGER, // type
+            new String[]
+                { String.valueOf( ConfigurationUtil.getProperty( defaultConfig, OsgiManager.PROP_LOG_LEVEL,
+                    OsgiManager.DEFAULT_LOG_LEVEL ) ) }, // default values
+            0, // cardinality
+            new String[]
+                { // option labels
+            getString( rb, "log.level.debug", "Debug" ), //$NON-NLS-1$ //$NON-NLS-2$
+                getString( rb, "log.level.info", "Information" ), //$NON-NLS-1$ //$NON-NLS-2$
+                getString( rb, "log.level.warn", "Warn" ), //$NON-NLS-1$ //$NON-NLS-2$
+                getString( rb, "log.level.error", "Error" ), //$NON-NLS-1$ //$NON-NLS-2$
+            }, new String[]
+            { "4", "3", "2", "1" } ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+
+        // list plugins - requires localized plugin titles
+        final TreeMap namesByClassName = new TreeMap();
+        final String[] defaultPluginsClasses = OsgiManager.PLUGIN_MAP;
+        for ( int i = 0; i < defaultPluginsClasses.length; i++ )
+        {
+            final String clazz = defaultPluginsClasses[i++];
+            final String label = defaultPluginsClasses[i];
+            final String name = getString( rb, label + ".pluginTitle", label ); //$NON-NLS-1$
+            namesByClassName.put( clazz, name );
+        }
+        final String[] classes = ( String[] ) namesByClassName.keySet().toArray( new String[namesByClassName.size()] );
+        final String[] names = ( String[] ) namesByClassName.values().toArray( new String[namesByClassName.size()] );
+
+        adList.add( new AttributeDefinitionImpl( OsgiManager.PROP_ENABLED_PLUGINS, getString( rb,
+            "metadata.plugins.name", OsgiManager.PROP_ENABLED_PLUGINS ), //$NON-NLS-1$
+            getString( rb, "metadata.plugins.description", OsgiManager.PROP_ENABLED_PLUGINS ), //$NON-NLS-1$
+            AttributeDefinition.STRING, classes, Integer.MIN_VALUE, names, classes ) );
+
+        xocd = new ObjectClassDefinition()
+        {
+
+            private final AttributeDefinition[] attrs = ( AttributeDefinition[] ) adList
+                .toArray( new AttributeDefinition[adList.size()] );
+
+
+            public String getName()
+            {
+                return getString( rb, "metadata.name", "Apache Felix OSGi Management Console" ); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+
+
+            public InputStream getIcon( int arg0 )
+            {
+                return null;
+            }
+
+
+            public String getID()
+            {
+                return osgiManager.getConfigurationPid();
+            }
+
+
+            public String getDescription()
+            {
+                return getString( rb,
+                    "metadata.description", "Configuration of the Apache Felix OSGi Management Console." ); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+
+
+            public AttributeDefinition[] getAttributeDefinitions( int filter )
+            {
+                return ( filter == OPTIONAL ) ? null : attrs;
+            }
+        };
+
+        synchronized ( ocdLock )
+        {
+            this.ocd = xocd;
+            this.ocdLocale = locale;
+        }
+
+        return ocd;
+    }
+
+
+    private static final String getString( ResourceBundle rb, String key, String def )
+    {
+        try
+        {
+            return rb.getString( key );
+        }
+        catch ( Throwable t )
+        {
+            return def;
+        }
+    }
+
+    private static class AttributeDefinitionImpl implements AttributeDefinition
+    {
+
+        private final String id;
+        private final String name;
+        private final String description;
+        private final int type;
+        private final String[] defaultValues;
+        private final int cardinality;
+        private final String[] optionLabels;
+        private final String[] optionValues;
+
+
+        AttributeDefinitionImpl( final String id, final String name, final String description, final String defaultValue )
+        {
+            this( id, name, description, STRING, new String[]
+                { defaultValue }, 0, null, null );
+        }
+
+
+        AttributeDefinitionImpl( final String id, final String name, final String description, final int type,
+            final String[] defaultValues, final int cardinality, final String[] optionLabels,
+            final String[] optionValues )
+        {
+            this.id = id;
+            this.name = name;
+            this.description = description;
+            this.type = type;
+            this.defaultValues = defaultValues;
+            this.cardinality = cardinality;
+            this.optionLabels = optionLabels;
+            this.optionValues = optionValues;
+        }
+
+
+        public int getCardinality()
+        {
+            return cardinality;
+        }
+
+
+        public String[] getDefaultValue()
+        {
+            return defaultValues;
+        }
+
+
+        public String getDescription()
+        {
+            return description;
+        }
+
+
+        public String getID()
+        {
+            return id;
+        }
+
+
+        public String getName()
+        {
+            return name;
+        }
+
+
+        public String[] getOptionLabels()
+        {
+            return optionLabels;
+        }
+
+
+        public String[] getOptionValues()
+        {
+            return optionValues;
+        }
+
+
+        public int getType()
+        {
+            return type;
+        }
+
+
+        public String validate( String arg0 )
+        {
+            return null;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationSupport.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationSupport.java
index 31afe70..1bbf26e 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationSupport.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationSupport.java
@@ -19,41 +19,17 @@
 package org.apache.felix.webconsole.internal.servlet;
 
 
-import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.Dictionary;
-import java.util.Locale;
-import java.util.Map;
-import java.util.ResourceBundle;
-import java.util.TreeMap;
-
-import org.apache.felix.webconsole.internal.Util;
 import org.osgi.service.cm.ManagedService;
-import org.osgi.service.metatype.AttributeDefinition;
-import org.osgi.service.metatype.MetaTypeProvider;
-import org.osgi.service.metatype.ObjectClassDefinition;
 
 
-class ConfigurationSupport implements ManagedService, MetaTypeProvider
+class ConfigurationSupport implements ManagedService
 {
-    private static final String[] CONF_PROPS = new String[] {
-        OsgiManager.PROP_MANAGER_ROOT, OsgiManager.DEFAULT_MANAGER_ROOT, //
-        OsgiManager.PROP_HTTP_SERVICE_SELECTOR, OsgiManager.DEFAULT_HTTP_SERVICE_SELECTOR, //
-        OsgiManager.PROP_DEFAULT_RENDER, OsgiManager.DEFAULT_PAGE, //
-        OsgiManager.PROP_REALM, OsgiManager.DEFAULT_REALM, //
-        OsgiManager.PROP_USER_NAME, OsgiManager.DEFAULT_USER_NAME, //
-        OsgiManager.PROP_PASSWORD, OsgiManager.DEFAULT_PASSWORD, //
-        OsgiManager.PROP_LOCALE, "" , //$NON-NLS-1$
-    };
 
-    // used by an inner class, to prevent synthetic methods
-    final OsgiManager osgiManager;
+    protected final OsgiManager osgiManager;
 
-    private final Object ocdLock = new Object();
-    private String ocdLocale;
-    private ObjectClassDefinition ocd;
 
-    ConfigurationSupport( OsgiManager osgiManager )
+    ConfigurationSupport( final OsgiManager osgiManager )
     {
         this.osgiManager = osgiManager;
     }
@@ -65,228 +41,4 @@
     {
         osgiManager.updateConfiguration( config );
     }
-
-    //---------- MetaTypeProvider
-
-    public String[] getLocales()
-    {
-        // there is no locale support here
-        return null;
-    }
-
-    static final String getString(ResourceBundle rb, String key, String def)
-    {
-        try
-        {
-            return rb.getString(key);
-        }
-        catch (Throwable t)
-        {
-            return def;
-        }
-    }
-
-    public ObjectClassDefinition getObjectClassDefinition( String id, String locale )
-    {
-        if ( !osgiManager.getConfigurationPid().equals( id ) )
-        {
-            return null;
-        }
-
-        if (locale == null) locale = Locale.ENGLISH.getLanguage();
-
-        // check if OCD is already initialized and it's locale is the same as the requested one
-        synchronized (ocdLock)
-        {
-            if ( ocd != null && ocdLocale != null && ocdLocale.equals(locale) )
-            {
-                return ocd;
-            }
-        }
-
-        ObjectClassDefinition xocd = null;
-        final Locale localeObj = Util.parseLocaleString(locale);
-        final ResourceBundle rb = osgiManager.resourceBundleManager.getResourceBundle(osgiManager.getBundleContext().getBundle(), localeObj);
-        final Map defaultConfig = osgiManager.getDefaultConfiguration();
-
-        // simple configuration properties
-        final ArrayList adList = new ArrayList();
-        for (int i = 0; i < CONF_PROPS.length; i++)
-        {
-            final String key = CONF_PROPS[i++];
-            final String defaultValue = ConfigurationUtil.getProperty( defaultConfig, key, CONF_PROPS[i] );
-            final String name = getString(rb, "metadata." + key + ".name", key); //$NON-NLS-1$ //$NON-NLS-2$
-            final String descr = getString(rb, "metadata." + key + ".description", key); //$NON-NLS-1$ //$NON-NLS-2$
-            adList.add( new AttributeDefinitionImpl(key, name, descr, defaultValue) );
-        }
-
-        // log level is select - so no simple default value; requires localized option labels
-        adList.add( new AttributeDefinitionImpl( OsgiManager.PROP_LOG_LEVEL,
-            getString(rb, "metadata.loglevel.name", OsgiManager.PROP_LOG_LEVEL), //$NON-NLS-1$
-            getString(rb, "metadata.loglevel.description", OsgiManager.PROP_LOG_LEVEL), //$NON-NLS-1$
-            AttributeDefinition.INTEGER, // type
-            new String[]
-                { String.valueOf( ConfigurationUtil.getProperty( defaultConfig, OsgiManager.PROP_LOG_LEVEL,
-                    OsgiManager.DEFAULT_LOG_LEVEL ) ) }, // default values
-            0, // cardinality
-            new String[] { // option labels
-                getString(rb, "log.level.debug", "Debug"), //$NON-NLS-1$ //$NON-NLS-2$
-                getString(rb, "log.level.info", "Information"), //$NON-NLS-1$ //$NON-NLS-2$
-                getString(rb, "log.level.warn", "Warn"), //$NON-NLS-1$ //$NON-NLS-2$
-                getString(rb, "log.level.error", "Error"), //$NON-NLS-1$ //$NON-NLS-2$
-            },
-            new String[] { "4", "3", "2", "1" } ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
-
-        // list plugins - requires localized plugin titles
-        final TreeMap namesByClassName = new TreeMap();
-        final String[] defaultPluginsClasses = OsgiManager.PLUGIN_MAP;
-        for ( int i = 0; i < defaultPluginsClasses.length; i++ )
-        {
-            final String clazz = defaultPluginsClasses[i++];
-            final String label = defaultPluginsClasses[i];
-            final String name = getString(rb, label + ".pluginTitle", label); //$NON-NLS-1$
-            namesByClassName.put(clazz, name);
-        }
-        final String[] classes = ( String[] ) namesByClassName.keySet().toArray( new String[namesByClassName.size()] );
-        final String[] names = ( String[] ) namesByClassName.values().toArray( new String[namesByClassName.size()] );
-
-        adList.add( new AttributeDefinitionImpl( OsgiManager.PROP_ENABLED_PLUGINS,
-            getString(rb, "metadata.plugins.name", OsgiManager.PROP_ENABLED_PLUGINS), //$NON-NLS-1$
-            getString(rb, "metadata.plugins.description", OsgiManager.PROP_ENABLED_PLUGINS), //$NON-NLS-1$
-            AttributeDefinition.STRING, classes, Integer.MIN_VALUE, names, classes ) );
-
-        xocd = new ObjectClassDefinition()
-        {
-
-            private final AttributeDefinition[] attrs = ( AttributeDefinition[] ) adList
-                .toArray( new AttributeDefinition[adList.size()] );
-
-
-            public String getName()
-            {
-                return getString(rb, "metadata.name", "Apache Felix OSGi Management Console"); //$NON-NLS-1$ //$NON-NLS-2$
-            }
-
-
-            public InputStream getIcon( int arg0 )
-            {
-                return null;
-            }
-
-
-            public String getID()
-            {
-                return osgiManager.getConfigurationPid();
-            }
-
-
-            public String getDescription()
-            {
-                return getString(rb, "metadata.description", "Configuration of the Apache Felix OSGi Management Console."); //$NON-NLS-1$ //$NON-NLS-2$
-            }
-
-
-            public AttributeDefinition[] getAttributeDefinitions( int filter )
-            {
-                return ( filter == OPTIONAL ) ? null : attrs;
-            }
-        };
-
-        synchronized(ocdLock) {
-            this.ocd = xocd;
-            this.ocdLocale = locale;
-        }
-
-        return ocd;
-    }
-
-    private static class AttributeDefinitionImpl implements AttributeDefinition
-    {
-
-        private final String id;
-        private final String name;
-        private final String description;
-        private final int type;
-        private final String[] defaultValues;
-        private final int cardinality;
-        private final String[] optionLabels;
-        private final String[] optionValues;
-
-
-        AttributeDefinitionImpl( final String id, final String name, final String description, final String defaultValue )
-        {
-            this( id, name, description, STRING, new String[]
-                { defaultValue }, 0, null, null );
-        }
-
-
-        AttributeDefinitionImpl( final String id, final String name, final String description, final int type,
-            final String[] defaultValues, final int cardinality, final String[] optionLabels,
-            final String[] optionValues )
-        {
-            this.id = id;
-            this.name = name;
-            this.description = description;
-            this.type = type;
-            this.defaultValues = defaultValues;
-            this.cardinality = cardinality;
-            this.optionLabels = optionLabels;
-            this.optionValues = optionValues;
-        }
-
-
-        public int getCardinality()
-        {
-            return cardinality;
-        }
-
-
-        public String[] getDefaultValue()
-        {
-            return defaultValues;
-        }
-
-
-        public String getDescription()
-        {
-            return description;
-        }
-
-
-        public String getID()
-        {
-            return id;
-        }
-
-
-        public String getName()
-        {
-            return name;
-        }
-
-
-        public String[] getOptionLabels()
-        {
-            return optionLabels;
-        }
-
-
-        public String[] getOptionValues()
-        {
-            return optionValues;
-        }
-
-
-        public int getType()
-        {
-            return type;
-        }
-
-
-        public String validate( String arg0 )
-        {
-            return null;
-        }
-    }
-
 }
\ No newline at end of file
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 e8275f1..8864665 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
@@ -162,7 +162,7 @@
     static final String DEFAULT_MANAGER_ROOT = "/system/console"; //$NON-NLS-1$
 
     static final String[] PLUGIN_CLASSES = {
-            "org.apache.felix.webconsole.internal.compendium.ConfigurationAdminConfigurationPrinter", //$NON-NLS-1$
+            "org.apache.felix.webconsole.internal.configuration.ConfigurationAdminConfigurationPrinter", //$NON-NLS-1$
             "org.apache.felix.webconsole.internal.compendium.PreferencesConfigurationPrinter", //$NON-NLS-1$
             "org.apache.felix.webconsole.internal.compendium.WireAdminConfigurationPrinter", //$NON-NLS-1$
             "org.apache.felix.webconsole.internal.core.BundlesConfigurationPrinter", //$NON-NLS-1$
@@ -172,7 +172,7 @@
             "org.apache.felix.webconsole.internal.misc.ThreadPrinter", }; //$NON-NLS-1$
 
     static final String[] PLUGIN_MAP = {
-            "org.apache.felix.webconsole.internal.compendium.ConfigManager", "configMgr", //$NON-NLS-1$ //$NON-NLS-2$
+            "org.apache.felix.webconsole.internal.configuration.ConfigManager", "configMgr", //$NON-NLS-1$ //$NON-NLS-2$
             "org.apache.felix.webconsole.internal.compendium.LogServlet", "logs", //$NON-NLS-1$ //$NON-NLS-2$
             "org.apache.felix.webconsole.internal.core.BundlesServlet", "bundles", //$NON-NLS-1$ //$NON-NLS-2$
             "org.apache.felix.webconsole.internal.core.ServicesServlet", "services", //$NON-NLS-1$ //$NON-NLS-2$
@@ -323,6 +323,23 @@
             {
                 public Object getService( Bundle bundle, ServiceRegistration registration )
                 {
+                    /*
+                     * Explicitly load the class through the class loader to dynamically
+                     * wire the API if not wired yet. Implicit loading by creating a
+                     * class instance does not seem to properly work wiring the API
+                     * in time.
+                     */
+                    try
+                    {
+                        OsgiManager.this.getClass().getClassLoader()
+                            .loadClass( "org.osgi.service.metatype.MetaTypeProvider" );
+                        return new ConfigurationMetatypeSupport( OsgiManager.this );
+                    }
+                    catch ( ClassNotFoundException cnfe )
+                    {
+                        // ignore
+                    }
+
                     return new ConfigurationSupport( OsgiManager.this );
                 }
 
@@ -421,7 +438,7 @@
         // (we are authorative for our URL space and no other servlet should interfere)
         res.flushBuffer();
     }
-    
+
     private void ensureLocaleCookieSet(HttpServletRequest request, HttpServletResponse response, Locale locale) {
         Cookie[] cookies = request.getCookies();
         boolean hasCookie = false;