FELIX-2117 Reduce the static dependencies
  - Import most Compendium services dynamically
    (still require Http, Startlevel, and PackageAdmin)
  - Embed Metatype service package and import optionally
    (allows to synthesize descriptors but still use imported
    package if available)
  - Make sure existing factory configurations are displayed even
    in the absence of a Metatype service

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1220723 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index 45708fa..8fd55e2 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -130,22 +130,28 @@
                             org.apache.felix.webconsole.internal.OsgiManagerActivator
                         </Bundle-Activator>
                         <Import-Package>
-                            org.osgi.service.http,
-                            org.apache.felix.shell;
-                            org.osgi.service.*;resolution:=optional,
+                            org.osgi.service.metatype;resolution:=optional,
                             javax.servlet.*;version=2.4,
                             *
                         </Import-Package>
                         <DynamicImport-Package>
-                            org.apache.felix.bundlerepository,
-                            org.osgi.service.obr
+                            org.apache.felix.bundlerepository;version="[2.0,3)",
+                            org.osgi.service.obr;version="[1.0,2)",
+                            org.osgi.service.cm;version="[1.2,2)",
+                            org.osgi.service.condpermadmin;version="[1.0,2)",
+                            org.osgi.service.log;version="[1.3,2)",
+                            org.osgi.service.metatype;version="[1.1,2)",
+                            org.osgi.service.permissionadmin;version="[1.2,2)",
+                            org.osgi.service.prefs;version="[1.1,2)",
+                            org.osgi.service.wireadmin;version="[1.0,2)"
                         </DynamicImport-Package>
                         <Include-Resource>
                             {maven-resources},META-INF=src/main/bare-resources
                         </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.apache.felix.framework;inline=org/apache/felix/framework/util/VersionRange**,
+                            org.osgi.compendium;inline=org/osgi/service/metatype/**
                         </Embed-Dependency>
                         <_removeheaders>
                             Embed-Dependency,Private-Package,Include-Resource
@@ -199,7 +205,6 @@
             <artifactId>commons-fileupload</artifactId>
             <version>1.2.1</version>
             <scope>provided</scope>
-            <optional>true</optional>
         </dependency>
 
         <dependency>
@@ -207,7 +212,6 @@
             <artifactId>commons-io</artifactId>
             <version>1.4</version>
             <scope>provided</scope>
-            <optional>true</optional>
         </dependency>
 
         <dependency>
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationListener.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationListener.java
deleted file mode 100644
index 35f114b..0000000
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationListener.java
+++ /dev/null
@@ -1,70 +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.servlet;
-
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.cm.ManagedService;
-
-
-class ConfigurationListener implements ManagedService
-{
-
-    private final OsgiManager osgiManager;
-
-
-    static ServiceRegistration create( OsgiManager osgiManager )
-    {
-        ConfigurationListener cl = new ConfigurationListener( osgiManager );
-        return registerService( cl, new String[]
-            { ManagedService.class.getName() } );
-    }
-
-
-    static ServiceRegistration registerService( ConfigurationListener listener, final String[] serviceNames )
-    {
-        final OsgiManager osgiManager = listener.osgiManager;
-
-        Dictionary<String, String> props = new Hashtable<String, String>();
-        props.put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" );
-        props.put( Constants.SERVICE_DESCRIPTION, "OSGi Management Console Configuration Receiver" );
-        props.put( Constants.SERVICE_PID, osgiManager.getConfigurationPid() );
-
-        return osgiManager.getBundleContext().registerService( serviceNames, listener, props );
-    }
-
-
-    protected ConfigurationListener( OsgiManager osgiManager )
-    {
-        this.osgiManager = osgiManager;
-    }
-
-
-    //---------- ManagedService
-
-    @SuppressWarnings("unchecked")
-    public void updated( @SuppressWarnings("rawtypes") Dictionary config )
-    {
-        osgiManager.updateConfiguration( config );
-    }
-
-}
\ No newline at end of file
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationListener2.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationSupport.java
similarity index 91%
rename from webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationListener2.java
rename to webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationSupport.java
index 60d5bb9..6536fcc 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationListener2.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationSupport.java
@@ -21,29 +21,21 @@
 
 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.framework.ServiceRegistration;
 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 ConfigurationListener2 extends ConfigurationListener implements MetaTypeProvider
+class ConfigurationSupport implements ManagedService, MetaTypeProvider
 {
-
-    final String pid; // reduces visibility because access to this was made though synthetic accessor method
-
-    private final Object ocdLock = new Object();
-    private String ocdLocale;
-    private ObjectClassDefinition ocd;
-    private final OsgiManager osgiManager;
-
     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, //
@@ -51,28 +43,30 @@
         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$
+        OsgiManager.PROP_LOCALE, "" , //$NON-NLS-1$
     };
 
+    // used by an inner class, to prevent synthetic methods
+    final OsgiManager osgiManager;
 
+    private final Object ocdLock = new Object();
+    private String ocdLocale;
+    private ObjectClassDefinition ocd;
 
-
-    static ServiceRegistration create( OsgiManager osgiManager )
+    ConfigurationSupport( OsgiManager osgiManager )
     {
-        ConfigurationListener2 cl = new ConfigurationListener2( osgiManager );
-        return registerService( cl, new String[]
-            { ManagedService.class.getName(), MetaTypeProvider.class.getName() } );
-    }
-
-
-    private ConfigurationListener2( OsgiManager osgiManager )
-    {
-        super( osgiManager );
-        this.pid = osgiManager.getConfigurationPid();
         this.osgiManager = osgiManager;
     }
 
 
+    //---------- ManagedService
+
+    @SuppressWarnings("unchecked")
+    public void updated( @SuppressWarnings("rawtypes") Dictionary config )
+    {
+        osgiManager.updateConfiguration( config );
+    }
+
     //---------- MetaTypeProvider
 
     public String[] getLocales()
@@ -95,7 +89,7 @@
 
     public ObjectClassDefinition getObjectClassDefinition( String id, String locale )
     {
-        if ( !pid.equals( id ) )
+        if ( !osgiManager.getConfigurationPid().equals( id ) )
         {
             return null;
         }
@@ -184,7 +178,7 @@
 
             public String getID()
             {
-                return pid;
+                return osgiManager.getConfigurationPid();
             }
 
 
@@ -296,4 +290,5 @@
             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 ef01403..5379d20 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
@@ -35,6 +35,8 @@
 import java.util.Set;
 
 import javax.servlet.GenericServlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
@@ -59,6 +61,7 @@
 import org.osgi.framework.Constants;
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.http.HttpContext;
@@ -218,6 +221,7 @@
 
     private int logLevel = DEFAULT_LOG_LEVEL;
 
+    @SuppressWarnings("serial")
     public OsgiManager(BundleContext bundleContext)
     {
         this.bundleContext = bundleContext;
@@ -314,23 +318,28 @@
         // configure and start listening for configuration
         updateConfiguration(null);
 
-        try
-        {
-            this.configurationListener = ConfigurationListener2.create(this);
-        }
-        catch (Throwable t2)
-        {
-            // might be caused by Metatype API not available
-            // try without MetaTypeProvider
-            try
+        // register managed service as a service factory
+        this.configurationListener = bundleContext.registerService( "org.osgi.service.cm.ManagedService", //$NON-NLS-1$
+            new ServiceFactory()
             {
-                this.configurationListener = ConfigurationListener.create(this);
-            }
-            catch (Throwable t)
+                public Object getService( Bundle bundle, ServiceRegistration registration )
+                {
+                    return new ConfigurationSupport( OsgiManager.this );
+                }
+
+
+                public void ungetService( Bundle bundle, ServiceRegistration registration, Object service )
+                {
+                    // do nothing
+                }
+            }, new Hashtable<String, String>()
             {
-                // might be caused by CM API not available
-            }
-        }
+                {
+                    put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" ); //$NON-NLS-1$
+                    put( Constants.SERVICE_DESCRIPTION, "OSGi Management Console Configuration Receiver" ); //$NON-NLS-1$
+                    put( Constants.SERVICE_PID, getConfigurationPid() );
+                }
+            } );
     }
 
     public void dispose()
@@ -551,14 +560,18 @@
         return getClass().getName();
     }
 
+
     /**
-     * Calls the <code>GenericServlet.log(String)</code> method if the
+     * Calls the <code>ServletContext.log(String)</code> method if the
      * configured log level is less than or equal to the given <code>level</code>.
      * <p>
      * Note, that the <code>level</code> parameter is only used to decide whether
      * the <code>GenericServlet.log(String)</code> method is called or not. The
      * actual implementation of the <code>GenericServlet.log</code> method is
      * outside of the control of this method.
+     * <p>
+     * If the servlet has not been initialized yet or has already been destroyed
+     * the message is printed to stderr.
      *
      * @param level The log level at which to log the message
      * @param message The message to log
@@ -567,12 +580,23 @@
     {
         if (logLevel >= level)
         {
-            log(message);
+            ServletConfig config = getServletConfig();
+            if ( config != null )
+            {
+                ServletContext context = config.getServletContext();
+                if ( context != null )
+                {
+                    context.log( message );
+                    return;
+                }
+            }
+
+            System.err.println( message );
         }
     }
 
     /**
-     * Calls the <code>GenericServlet.log(String, Throwable)</code> method if
+     * Calls the <code>ServletContext.log(String, Throwable)</code> method if
      * the configured log level is less than or equal to the given
      * <code>level</code>.
      * <p>
@@ -580,6 +604,9 @@
      * the <code>GenericServlet.log(String, Throwable)</code> method is called
      * or not. The actual implementation of the <code>GenericServlet.log</code>
      * method is outside of the control of this method.
+     * <p>
+     * If the servlet has not been initialized yet or has already been destroyed
+     * the message is printed to stderr.
      *
      * @param level The log level at which to log the message
      * @param message The message to log
@@ -589,7 +616,22 @@
     {
         if (logLevel >= level)
         {
-            log(message, t);
+            ServletConfig config = getServletConfig();
+            if ( config != null )
+            {
+                ServletContext context = config.getServletContext();
+                if ( context != null )
+                {
+                    context.log( message, t );
+                    return;
+                }
+            }
+
+            System.err.println( message );
+            if ( t != null )
+            {
+                t.printStackTrace( System.err );
+            }
         }
     }
 
diff --git a/webconsole/src/main/resources/res/ui/config.js b/webconsole/src/main/resources/res/ui/config.js
index faa8e08..e0baaaf 100644
--- a/webconsole/src/main/resources/res/ui/config.js
+++ b/webconsole/src/main/resources/res/ui/config.js
@@ -541,11 +541,22 @@
 		for(var i in configData.fpids) {
 			addFactoryConfig(configData.fpids[i]);
 
-			var confs = factories[ configData.fpids[i].id ];
-			if (confs) for (var j in confs) {
-				addConfig(confs[j]);
+			var fpid = configData.fpids[i].id;
+			var confs = factories[ fpid ];
+			if (confs) {
+			    for (var j in confs) {
+			        addConfig(confs[j]);
+			    }
+			    delete factories[ fpid ];
 			}
 		}
+		for(var fpid in factories) {
+		    var flist = factories[fpid];
+		    for(var i in flist) {
+		        delete flist[i].fpid; // render as regular config
+		        addConfig(flist[i]);
+		    }
+		}
 		initStaticWidgets(configTable);
 
 		// init tablesorte