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;