FELIX-583, FELIX-584, FELIX-588: Separate the lists of the ManagedServiceFactory
and ManagedService with existing configuration objects.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@667498 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/AjaxConfigManagerAction.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/AjaxConfigManagerAction.java
index 9dc7aad..52fcd97 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/AjaxConfigManagerAction.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/AjaxConfigManagerAction.java
@@ -34,9 +34,11 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.webconsole.Action;
+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.InvalidSyntaxException;
@@ -71,26 +73,43 @@
     public boolean performAction( HttpServletRequest request, HttpServletResponse response ) throws IOException
     {
 
+        // needed multiple times below
+        String pid = request.getParameter( ConfigManager.PID );
+
         // should actually apply the configuration before redirecting
-        if ( request.getParameter( "apply" ) != null )
+        if ( request.getParameter( "create" ) != null && pid != null )
+        {
+            ConfigurationAdmin ca = this.getConfigurationAdmin();
+            if ( ca != null )
+            {
+                Configuration config = ca.createFactoryConfiguration( pid, null );
+                pid = config.getPid();
+            }
+        }
+        else if ( request.getParameter( "apply" ) != null )
         {
             return applyConfiguration( request );
         }
 
-        JSONObject result = new JSONObject();
-
-        String pid = request.getParameter( ConfigManager.PID );
         boolean isFactory = pid == null;
         if ( isFactory )
         {
             pid = request.getParameter( "factoryPid" );
         }
 
+        // send the result
+        response.setContentType( "text/javascript" );
+        response.setCharacterEncoding( "UTF-8" );
+
+        JSONWriter result = new JSONWriter( response.getWriter() );
+
         if ( pid != null )
         {
             try
             {
+                result.object();
                 this.configForm( result, pid, isFactory, getLocale( request ) );
+                result.endObject();
             }
             catch ( Exception e )
             {
@@ -98,15 +117,11 @@
             }
         }
 
-        // send the result
-        response.setContentType( "text/javascript" );
-        response.getWriter().print( result.toString() );
-
         return false;
     }
 
 
-    private void configForm( JSONObject json, String pid, boolean isFactory, Locale loc ) throws IOException,
+    private void configForm( JSONWriter json, String pid, boolean isFactory, Locale loc ) throws IOException,
         JSONException
     {
         String locale = ( loc == null ) ? null : loc.toString();
@@ -133,8 +148,10 @@
             return;
         }
 
-        json.put( ConfigManager.PID, pid );
-        json.put( "isFactory", isFactory );
+        json.key( ConfigManager.PID );
+        json.value( pid );
+        json.key( "isFactory" );
+        json.value( isFactory );
 
         Dictionary props = null;
         ObjectClassDefinition ocd;
@@ -152,7 +169,18 @@
 
         if ( props != null )
         {
-            JSONObject properties = new JSONObject();
+
+            json.key( "title" );
+            json.value( pid );
+            json.key( "description" );
+            json
+                .value( "Please enter configuration properties for this configuration in the field below. This configuration has no associated description" );
+
+            json.key( "propertylist" );
+            json.value( "properties" );
+
+            json.key( "properties" );
+            json.object();
             for ( Enumeration pe = props.keys(); pe.hasMoreElements(); )
             {
                 Object key = pe.nextElement();
@@ -164,19 +192,11 @@
                     && !key.equals( ConfigurationAdmin.SERVICE_BUNDLELOCATION )
                     && !key.equals( ConfigurationAdmin.SERVICE_FACTORYPID ) )
                 {
-                    properties.put( String.valueOf( key ), props.get( key ) );
+                    json.key( String.valueOf( key ) );
+                    json.value( props.get( key ) );
                 }
-
             }
-
-            json.put( "title", pid );
-            json
-                .put(
-                    "description",
-                    "Please enter configuration properties for this configuration in the field below. This configuration has no associated description" );
-
-            json.put( "propertylist", "properties" );
-            json.put( "properties", properties );
+            json.endObject();
 
         }
 
@@ -187,7 +207,7 @@
     }
 
 
-    private Dictionary mergeWithMetaType( Dictionary props, ObjectClassDefinition ocd, JSONObject json )
+    private Dictionary mergeWithMetaType( Dictionary props, ObjectClassDefinition ocd, JSONWriter json )
         throws JSONException
     {
 
@@ -199,11 +219,13 @@
         if ( ocd != null )
         {
 
-            json.put( "title", ocd.getName() );
+            json.key( "title" );
+            json.value( ocd.getName() );
 
             if ( ocd.getDescription() != null )
             {
-                json.put( "description", ocd.getDescription() );
+                json.key( "description" );
+                json.value( ocd.getDescription() );
             }
 
             AttributeDefinition[] ad = ocd.getAttributeDefinitions( ObjectClassDefinition.ALL );
@@ -214,7 +236,8 @@
 
                 for ( int i = 0; i < ad.length; i++ )
                 {
-                    JSONObject entry = new JSONObject();
+                    json.key( ad[i].getID() );
+                    json.object();
 
                     Object value = props.get( ad[i].getID() );
                     if ( value == null )
@@ -233,18 +256,22 @@
                         }
                     }
 
-                    entry.put( "name", ad[i].getName() );
+                    json.key( "name" );
+                    json.value( ad[i].getName() );
 
+                    json.key( "type" );
                     if ( ad[i].getOptionLabels() != null && ad[i].getOptionLabels().length > 0 )
                     {
-                        JSONObject type = new JSONObject();
-                        type.put( "labels", Arrays.asList( ad[i].getOptionLabels() ) );
-                        type.put( "values", Arrays.asList( ad[i].getOptionValues() ) );
-                        entry.put( "type", type );
+                        json.object();
+                        json.key( "labels" );
+                        json.value( Arrays.asList( ad[i].getOptionLabels() ) );
+                        json.key( "values" );
+                        json.value( Arrays.asList( ad[i].getOptionValues() ) );
+                        json.endObject();
                     }
                     else
                     {
-                        entry.put( "type", ad[i].getType() );
+                        json.value( ad[i].getType() );
                     }
 
                     if ( ad[i].getCardinality() == 0 )
@@ -258,7 +285,8 @@
                         {
                             value = Array.get( value, 0 );
                         }
-                        entry.put( "value", value );
+                        json.key( "value" );
+                        json.value( value );
                     }
                     else
                     {
@@ -276,19 +304,22 @@
                             tmp.put( value );
                             value = tmp;
                         }
-                        entry.put( "values", value );
+                        json.key( "values" );
+                        json.value( value );
                     }
 
                     if ( ad[i].getDescription() != null )
                     {
-                        entry.put( "description", ad[i].getDescription() );
+                        json.key( "description" );
+                        json.value( ad[i].getDescription() );
                     }
 
-                    json.put( ad[i].getID(), entry );
+                    json.endObject();
                     propertyList.put( ad[i].getID() );
                 }
 
-                json.put( "propertylist", propertyList );
+                json.key( "propertylist" );
+                json.value( propertyList );
             }
 
             // nothing more to display
@@ -299,12 +330,13 @@
     }
 
 
-    private void addConfigurationInfo( Configuration config, JSONObject json, String locale ) throws JSONException
+    private void addConfigurationInfo( Configuration config, JSONWriter json, String locale ) throws JSONException
     {
 
         if ( config.getFactoryPid() != null )
         {
-            json.put( "factoryPID", config.getFactoryPid() );
+            json.key( "factoryPID" );
+            json.value( config.getFactoryPid() );
         }
 
         String location;
@@ -330,7 +362,8 @@
             Version v = Version.parseVersion( ( String ) headers.get( Constants.BUNDLE_VERSION ) );
             location += ", Version " + v.toString();
         }
-        json.put( "bundleLocation", location );
+        json.key( "bundleLocation" );
+        json.value( location );
     }
 
 
@@ -360,7 +393,7 @@
 
             // request.setAttribute(ATTR_REDIRECT_PARAMETERS, "pid=" +
             // config.getPid());
-            return true;
+            return false;
         }
 
         String propertyList = request.getParameter( "propertylist" );
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
index 0a43e07..839e038 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManager.java
@@ -22,7 +22,6 @@
 import java.util.Dictionary;
 import java.util.Iterator;
 import java.util.Locale;
-import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.Map.Entry;
@@ -31,13 +30,12 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.webconsole.Render;
-import org.apache.felix.webconsole.internal.Util;
-import org.apache.felix.webconsole.internal.core.SetStartLevelAction;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 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.ObjectClassDefinition;
 
@@ -86,6 +84,13 @@
         pw.println( "</td>" );
         pw.println( "</tr>" );
 
+        pw.println( "<tr class='content' id='factoryField'>" );
+        pw.println( "<td class='content'>Factory Configurations</th>" );
+        pw.println( "<td class='content'>" );
+        this.listFactoryConfigurations( pw, optionalMetaType, getLocale( request ) );
+        pw.println( "</td>" );
+        pw.println( "</tr>" );
+        
         pw.println( "</table>" );
     }
 
@@ -104,11 +109,106 @@
 
         try
         {
-            // get a list of all pids for which MetaData exists
-            Map metaDataPids = this.getMetadataPids();
-
             // sorted map of options
-            SortedMap options = new TreeMap( String.CASE_INSENSITIVE_ORDER );
+            SortedMap optionsPlain = new TreeMap( String.CASE_INSENSITIVE_ORDER );
+
+            // find all ManagedServices to get the PIDs
+            ServiceReference[] refs = this.getBundleContext().getServiceReferences( ManagedService.class.getName(),
+                null );
+            for ( int i = 0; refs != null && i < refs.length; i++ )
+            {
+                Object pidObject = refs[i].getProperty( Constants.SERVICE_PID );
+                if ( pidObject instanceof String )
+                {
+                    String pid = ( String ) pidObject;
+                    String name;
+                    ObjectClassDefinition ocd = this.getObjectClassDefinition( refs[i].getBundle(), pid, locale );
+                    if ( ocd != null )
+                    {
+                        name = ocd.getName() + " (";
+                        name += pid + ")";
+                    }
+                    else
+                    {
+                        name = pid;
+                    }
+
+                    if ( ocd != null || optionalMetaType )
+                    {
+                        optionsPlain.put( pid, name );
+                    }
+                }
+            }
+
+            // get a sorted list of configuration PIDs
+            Configuration[] cfgs = ca.listConfigurations( null );
+            for ( int i = 0; cfgs != null && i < cfgs.length; i++ )
+            {
+
+                // ignore configuration object if an entry already exists in the
+                // map
+                String pid = cfgs[i].getPid();
+                if ( optionsPlain.containsKey( pid ) )
+                {
+                    continue;
+                }
+
+                // insert and entry for the pid
+                ObjectClassDefinition ocd = this.getObjectClassDefinition( cfgs[i], locale );
+                String name;
+                if ( ocd != null )
+                {
+                    name = ocd.getName() + " (";
+                    name += pid + ")";
+                }
+                else
+                {
+                    name = pid;
+                }
+
+                if ( ocd != null || optionalMetaType )
+                {
+                    optionsPlain.put( pid, name );
+                }
+            }
+
+            //            pw.println( "<form method='post' name='configSelection' onSubmit='configure(); return false;'" );
+            pw.println( "<select class='select' name='pid' id='configSelection_pid' onChange='configure();'>" );
+            for ( Iterator ei = optionsPlain.entrySet().iterator(); ei.hasNext(); )
+            {
+                Entry entry = ( Entry ) ei.next();
+                pw.print( "<option value='" + entry.getKey() + "'>" );
+                pw.print( entry.getValue() );
+                pw.println( "</option>" );
+            }
+            pw.println( "</select>" );
+            pw.println( "&nbsp;&nbsp;" );
+            pw.println( "<input class='submit' type='button' value='Configure' onClick='configure();' />" );
+            //            pw.println( "</form>" );
+        }
+        catch ( Exception e )
+        {
+            // write a message or ignore
+        }
+    }
+
+
+    private void listFactoryConfigurations( PrintWriter pw, boolean optionalMetaType, Locale loc )
+    {
+
+        ConfigurationAdmin ca = this.getConfigurationAdmin();
+        if ( ca == null )
+        {
+            pw.print( "Configuration Admin Service not available" );
+            return;
+        }
+
+        String locale = ( loc != null ) ? loc.toString() : null;
+
+        try
+        {
+            // sorted map of options
+            SortedMap optionsFactory = new TreeMap( String.CASE_INSENSITIVE_ORDER );
 
             // find all ManagedServiceFactories to get the factoryPIDs
             ServiceReference[] refs = this.getBundleContext().getServiceReferences(
@@ -133,97 +233,13 @@
 
                     if ( ocd != null || optionalMetaType )
                     {
-                        options.put( "factoryPid=" + pid, name );
+                        optionsFactory.put( pid, name );
                     }
                 }
             }
 
-            // get a sorted list of configuration PIDs
-            Configuration[] cfgs = ca.listConfigurations( null );
-            for ( int i = 0; cfgs != null && i < cfgs.length; i++ )
-            {
-
-                // ignore configuration object if an entry already exists in the
-                // map
-                String pid = cfgs[i].getPid();
-                if ( options.containsKey( "pid=" + pid ) || options.containsKey( "factoryPid=" + pid ) )
-                {
-                    continue;
-                }
-
-                Dictionary props = cfgs[i].getProperties();
-
-                // insert and entry for the pid
-                ObjectClassDefinition ocd = this.getObjectClassDefinition( cfgs[i], locale );
-                String name;
-                if ( ocd != null )
-                {
-                    name = ocd.getName() + " (";
-                    name += pid + ")";
-
-                    // remove from the list of known pids
-                    metaDataPids.remove( pid );
-
-                }
-                else
-                {
-                    name = pid;
-                }
-
-                if ( ocd != null || optionalMetaType )
-                {
-                    options.put( "pid=" + pid, name );
-                }
-
-                // if the configuration is part of a factory, ensure an entry
-                // for the factory
-                if ( cfgs[i].getFactoryPid() != null )
-                {
-                    pid = cfgs[i].getFactoryPid();
-                    if ( options.containsValue( "factoryPid=" + pid ) )
-                    {
-                        continue;
-                    }
-
-                    String existing = ( String ) options.remove( "pid=" + pid );
-                    if ( existing != null )
-                    {
-                        options.put( "factoryPid=" + pid, existing );
-                    }
-                    else
-                    {
-                        Bundle bundle = this.getBundle( cfgs[i].getBundleLocation() );
-                        ocd = this.getObjectClassDefinition( bundle, pid, locale );
-                        if ( ocd != null )
-                        {
-                            options.put( "factoryPid=" + pid, ocd.getName() );
-                        }
-                        else if ( optionalMetaType )
-                        {
-                            options.put( "factoryPid=" + pid, pid );
-                        }
-                    }
-                }
-            }
-
-            // If there are any meta data PIDs for which there is no existing
-            // configuration, we add them to the list to create configuration
-            if ( !metaDataPids.isEmpty() )
-            {
-                for ( Iterator mdpi = metaDataPids.entrySet().iterator(); mdpi.hasNext(); )
-                {
-                    Entry mdp = ( Entry ) mdpi.next();
-                    ObjectClassDefinition ocd = this.getObjectClassDefinition( ( Bundle ) mdp.getValue(),
-                        ( String ) mdp.getKey(), locale );
-                    options.put( "pid=" + mdp.getKey(), ocd.getName() + " (" + mdp.getKey() + ")" );
-                }
-            }
-
-            pw.println( "<form method='post' name='configSelection' onSubmit='configure(); return false;'" );
-            pw.println( "<input type='hidden' name='" + Util.PARAM_ACTION + "' value='" + SetStartLevelAction.NAME
-                + "'>" );
-            pw.println( "<select class='select' name='pid' onChange='configure();'>" );
-            for ( Iterator ei = options.entrySet().iterator(); ei.hasNext(); )
+            pw.println( "<select class='select' name='pid' id='configSelection_factory' onChange='create();'>" );
+            for ( Iterator ei = optionsFactory.entrySet().iterator(); ei.hasNext(); )
             {
                 Entry entry = ( Entry ) ei.next();
                 pw.print( "<option value='" + entry.getKey() + "'>" );
@@ -232,9 +248,7 @@
             }
             pw.println( "</select>" );
             pw.println( "&nbsp;&nbsp;" );
-            pw.println( "<input class='submit' type='submit' value='Configure' />" );
-            pw.println( "</form>" );
-
+            pw.println( "<input class='submit' type='button' value='Create' onClick='create();' />" );
         }
         catch ( Exception e )
         {
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
index 225268b..53e701b 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/ConfigManagerBase.java
@@ -118,17 +118,14 @@
         // if the configuration is not bound, search in the bundles
         if ( config.getBundleLocation() == null )
         {
-            ObjectClassDefinition ocd = this.getObjectClassDefinition( config.getPid(), locale );
-            if ( ocd != null )
-            {
-                return ocd;
-            }
-
-            // if none, check whether there might be one for the factory PID
+            // if the configuration is a factory one, use the factory PID
             if ( config.getFactoryPid() != null )
             {
                 return this.getObjectClassDefinition( config.getFactoryPid(), locale );
             }
+
+            // otherwise use the configuration PID
+            return this.getObjectClassDefinition( config.getPid(), locale );
         }
 
         MetaTypeService mts = this.getMetaTypeService();
@@ -140,19 +137,14 @@
                 MetaTypeInformation mti = mts.getMetaTypeInformation( bundle );
                 if ( mti != null )
                 {
-                    // try OCD by PID first
-                    ObjectClassDefinition ocd = mti.getObjectClassDefinition( config.getPid(), locale );
-                    if ( ocd != null )
-                    {
-                        return ocd;
-                    }
-
-                    // if none, check whether there might be one for the factory
-                    // PID
+                    // check by factory PID
                     if ( config.getFactoryPid() != null )
                     {
                         return mti.getObjectClassDefinition( config.getFactoryPid(), locale );
                     }
+                    
+                    // otherwise check by configuration PID
+                    return mti.getObjectClassDefinition( config.getPid(), locale );
                 }
             }
         }
diff --git a/webconsole/src/main/resources/res/ui/configmanager.js b/webconsole/src/main/resources/res/ui/configmanager.js
index e0e0027..a538a84 100644
--- a/webconsole/src/main/resources/res/ui/configmanager.js
+++ b/webconsole/src/main/resources/res/ui/configmanager.js
@@ -21,20 +21,43 @@
     if (!span) {
         return;
     }
-    var select = document.configSelection.pid;
+    var select = document.getElementById('configSelection_pid');
     var pid = select.options[select.selectedIndex].value;
-    var parm = '?action=ajaxConfigManager&' + pid;
+    var parm = '?action=ajaxConfigManager&pid=' + pid;
     sendRequest('GET', parm, displayConfigForm);
 }
 
-function displayConfigForm(obj) {
+
+function create() {
     var span = document.getElementById('configField');
     if (!span) {
         return;
     }
-    var innerHtml = '<tr class="content" id="configField">' + span.innerHTML + '</tr>';
+    var select = document.getElementById('configSelection_factory');
+    var pid = select.options[select.selectedIndex].value;
+    var parm = '?action=ajaxConfigManager&create=true&pid=' + pid;
+    sendRequest('GET', parm, displayConfigForm);
+}
+
+function displayConfigForm(obj) {
+    var span1 = document.getElementById('configField');
+    var span2 = document.getElementById('factoryField');
+    if (!span1 && !span2) {
+        return;
+    }
+    
+    var innerHtml = "";
+    
+    if (span1) {
+        innerHtml += '<tr class="content" id="configField">' + span1.innerHTML + '</tr>';
+    }
+    if (span2) {
+        innerHtml += '<tr class="content" id="factoryField">' + span2.innerHTML + '</tr>';
+    }
+    
     innerHtml += '<tr class="content">';
     innerHtml += '<th colspan="2" class="content" >' + obj.title + '</th></tr>';
+    
     innerHtml += '<tr class="content">';
     innerHtml += '<td class="content">&nbsp;</td>';
     innerHtml += '<td class="content">';
@@ -68,8 +91,10 @@
     innerHtml += '</table>';
     innerHtml += '</form>';
     innerHtml += '</td></tr>';
+    
     innerHtml += printConfigurationInfo(obj);
-    span.parentNode.innerHTML = innerHtml;
+    
+    span1.parentNode.innerHTML = innerHtml;
 }
 
 function printTextArea(props) {
@@ -101,6 +126,11 @@
         	// assume attr.values and multiselect
         	innerHtml += createMultiSelect(prop, attr.values, attr.type, '99%');
             innerHtml += '<br />';
+        } else if (attr.values.length == 0) {
+            var spanElement = createSpan(prop, "", attr.type);
+            innerHtml += '<span id="' + spanElement.id + '">';
+            innerHtml += spanElement.innerHTML;
+            innerHtml += '</span>';
         } else {
             for (var vidx in attr.values) {
                 var spanElement = createSpan(prop, attr.values[vidx], attr.type);
@@ -129,14 +159,22 @@
     innerHtml += '<tr class="content">';
     innerHtml += '<td class="content">Persistent Identity (PID)</td>';
     innerHtml += '<td class="content">' + obj.pid + '</td></tr>';
+    
     if (obj.factoryPID) {
         innerHtml += '<tr class="content">';
         innerHtml += '<td class="content">Factory Peristent Identifier (Factory PID)</td>';
         innerHtml += '<td class="content">' + obj.factoryPID + '</td></tr>';
     }
+    
+    var binding = obj.bundleLocation;
+    if (!binding) {
+        binding = "Unbound or new configuration";
+    }
+    
     innerHtml += '<tr class="content">';
     innerHtml += '<td class="content">Configuration Binding</td>';
-    innerHtml += '<td class="content">' + obj.bundleLocation + '</td></tr>';
+    innerHtml += '<td class="content">' + binding + '</td></tr>';
+    
     return innerHtml;
 }