| /* |
| * 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.lang.reflect.Array; |
| import java.util.ArrayList; |
| import java.util.Dictionary; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| 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.Set; |
| import java.util.SortedMap; |
| import java.util.StringTokenizer; |
| import java.util.TreeMap; |
| import java.util.Vector; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import javax.servlet.http.HttpServletRequest; |
| |
| import org.apache.felix.utils.json.JSONWriter; |
| import org.apache.felix.webconsole.internal.Util; |
| 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 static final String PROPERTY_FACTORYCONFIG_NAMEHINT = "webconsole.configurationFactory.nameHint"; |
| private static final Set<String> CONFIG_PROPERTIES_HIDE = new HashSet<>(); |
| static { |
| CONFIG_PROPERTIES_HIDE.add(PROPERTY_FACTORYCONFIG_NAMEHINT); |
| CONFIG_PROPERTIES_HIDE.add(ConfigurationAdmin.SERVICE_BUNDLELOCATION); |
| CONFIG_PROPERTIES_HIDE.add(ConfigurationAdmin.SERVICE_FACTORYPID); |
| CONFIG_PROPERTIES_HIDE.add(Constants.SERVICE_PID); |
| } |
| private static final Pattern NAMEHINT_PLACEHOLER_REGEXP = Pattern.compile("\\{([^\\{\\}]*)}"); |
| |
| 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( ConfigManager.ACTION_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.FACTORY_PID ); |
| Configuration config = null; |
| |
| String propertyList = request.getParameter( ConfigManager.PROPERTY_LIST ); //$NON-NLS-1$ |
| if ( propertyList == null ) |
| { |
| // FIXME: this would be a bug !! |
| } |
| else |
| { |
| config = getConfiguration( pid, factoryPid ); |
| |
| Dictionary<String, Object> props = config.getProperties(); |
| if ( props == null ) |
| { |
| props = new Hashtable<>(); |
| } |
| |
| final MetaTypeServiceSupport mtss = getMetaTypeSupport(); |
| final Map<String, MetatypePropertyDescriptor> adMap = ( mtss != null ) ? mtss.getAttributeDefinitionMap( config, null ) : new HashMap<>(); |
| final StringTokenizer propTokens = new StringTokenizer( propertyList, "," ); //$NON-NLS-1$ |
| final List<String> propsToKeep = new ArrayList<>(); |
| while ( propTokens.hasMoreTokens() ) |
| { |
| String propName = propTokens.nextToken(); |
| String paramName = "action".equals(propName) //$NON-NLS-1$ |
| || ConfigManager.ACTION_DELETE.equals(propName) |
| || ConfigManager.ACTION_APPLY.equals(propName) |
| || ConfigManager.PROPERTY_LIST.equals(propName) |
| ? '$' + propName : propName; |
| propsToKeep.add(propName); |
| |
| PropertyDescriptor ad = 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 == AttributeDefinition.PASSWORD ) ) ) |
| { |
| String prop = request.getParameter( paramName ); |
| if ( prop != null |
| && ( attributeType != AttributeDefinition.PASSWORD || !MetaTypeSupport.PASSWORD_PLACEHOLDER_VALUE.equals( prop ) ) ) |
| { |
| props.put( propName, prop ); |
| } |
| } |
| else if ( ad.getCardinality() == 0 ) |
| { |
| // scalar of non-string |
| String prop = request.getParameter( paramName ); |
| if ( prop != null ) |
| { |
| try |
| { |
| props.put( propName, MetaTypeSupport.toType( attributeType, prop ) ); |
| } |
| catch ( NumberFormatException nfe ) |
| { |
| // the value is put as a string, for example this could be a placeholder etc |
| props.put( propName, prop); |
| } |
| } |
| } |
| else |
| { |
| // array or vector of any type |
| Vector<Object> vec = new Vector<>(); |
| boolean formatError = false; |
| |
| String[] properties = request.getParameterValues( paramName ); |
| if ( properties != null ) |
| { |
| if ( attributeType == AttributeDefinition.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 ) |
| { |
| // the value is put as a string, for example this could be a placeholder etc |
| vec.add( properties[i] ); |
| formatError = true; |
| } |
| } |
| } |
| } |
| |
| // if a format error occurred revert to String! |
| if ( formatError ) |
| { |
| Vector<Object> newVec = new Vector<Object>(); |
| for(final Object v : vec) |
| { |
| newVec.add(v.toString()); |
| } |
| vec = newVec; |
| } |
| |
| // 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( formatError ? AttributeDefinition.STRING : attributeType, vec ) ); |
| } |
| } |
| } |
| |
| // remove the properties that are not specified in the request |
| final Dictionary<String, Object> updateProps = new Hashtable<>(props.size()); |
| for ( Enumeration<String> e = props.keys(); e.hasMoreElements(); ) |
| { |
| final String key = e.nextElement(); |
| if ( propsToKeep.contains(key) ) |
| { |
| updateProps.put(key, props.get(key)); |
| } |
| } |
| |
| final String location = request.getParameter(ConfigManager.LOCATION); |
| if ( location == null || location.trim().length() == 0 || ConfigManager.UNBOUND_LOCATION.equals(location) ) |
| { |
| if ( 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 ); |
| } |
| } |
| } else |
| { |
| if ( config.getBundleLocation() == null || !config.getBundleLocation().equals(location) ) |
| { |
| config.setBundleLocation(location); |
| } |
| } |
| config.update( updateProps ); |
| } |
| |
| // 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 IOException |
| { |
| |
| json.key( ConfigManager.PID ); |
| json.value( pid ); |
| |
| if ( pidFilter != null ) |
| { |
| json.key( ConfigManager.PID_FILTER ); |
| json.value( pidFilter ); |
| } |
| |
| Dictionary<String, Object> props = null; |
| if ( config != null ) |
| { |
| props = config.getProperties(); |
| } |
| 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, CONFIG_PROPERTIES_HIDE ); |
| 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<String> pe = props.keys(); pe.hasMoreElements(); ) |
| { |
| final String id = 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 IOException |
| { |
| |
| if ( config.getFactoryPid() != null ) |
| { |
| json.key( ConfigManager.FACTORY_PID ); |
| json.value( config.getFactoryPid() ); |
| } |
| |
| String bundleLocation = config.getBundleLocation(); |
| if ( ConfigManager.UNBOUND_LOCATION.equals(bundleLocation) ) |
| { |
| bundleLocation = null; |
| } |
| String location; |
| if ( bundleLocation == 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(), bundleLocation ); |
| if ( bundle == null ) |
| { |
| location = bundleLocation; |
| } |
| else |
| { |
| Dictionary<String, String> headers = bundle.getHeaders( locale ); |
| String name = headers.get( Constants.BUNDLE_NAME ); |
| if ( name == null ) |
| { |
| location = bundle.getSymbolicName(); |
| } |
| else |
| { |
| location = name + " (" + bundle.getSymbolicName() + ')'; //$NON-NLS-1$ |
| } |
| |
| Version v = Version.parseVersion( 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( |
| (String)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 ( bundleLocation ); |
| 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( JSONWriter jw, String pidFilter, String locale, Locale loc ) |
| { |
| try |
| { |
| // start with ManagedService instances |
| Map<String, String> 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 ); |
| } |
| |
| jw.key("pids");//$NON-NLS-1$ |
| jw.array(); |
| for ( Iterator<String> ii = optionsPlain.keySet().iterator(); ii.hasNext(); ) |
| { |
| String id = ii.next(); |
| Object name = optionsPlain.get( id ); |
| |
| final Configuration config = this.getConfiguration( id ); |
| jw.object(); |
| jw.key("id").value( id ); //$NON-NLS-1$ |
| jw.key( "name").value( name ); //$NON-NLS-1$ |
| if ( null != config ) |
| { |
| // FELIX-3848 |
| jw.key("has_config").value( true ); //$NON-NLS-1$ |
| |
| final String fpid = config.getFactoryPid(); |
| if ( null != fpid ) |
| { |
| jw.key("fpid").value( fpid ); //$NON-NLS-1$ |
| final String val = getConfigurationFactoryNameHint(config, mtss); |
| if ( val != null ) |
| { |
| jw.key( "nameHint").value(val ); //$NON-NLS-1$ |
| } |
| } |
| |
| final Bundle bundle = getBoundBundle( config ); |
| if ( null != bundle ) |
| { |
| jw.key( "bundle").value( bundle.getBundleId() ); //$NON-NLS-1$ |
| jw.key( "bundle_name").value( Util.getName( bundle, loc ) ); //$NON-NLS-1$ |
| } |
| } |
| jw.endObject(); |
| |
| } |
| jw.endArray(); |
| } |
| catch (Exception e) |
| { |
| configManager.log("listConfigurations: Unexpected problem encountered", e); |
| } |
| } |
| |
| /** |
| * Builds a "name hint" for factory configuration based on other property |
| * values of the config and a "name hint template" defined as hidden |
| * property in the service. |
| * @param props Service properties. |
| * @return Name hint or null if none is defined. |
| */ |
| private static final String getConfigurationFactoryNameHint(Configuration config, MetaTypeServiceSupport mtss) |
| { |
| Dictionary<String, Object> props = config.getProperties(); |
| Map<String, MetatypePropertyDescriptor> adMap = (mtss != null) ? mtss.getAttributeDefinitionMap(config, null) : null; |
| if (null == adMap) |
| { |
| return null; |
| } |
| |
| // check for configured name hint template |
| String nameHint = getConfigurationPropertyValueOrDefault(PROPERTY_FACTORYCONFIG_NAMEHINT, props, adMap); |
| if (nameHint == null) |
| { |
| return null; |
| } |
| |
| // search for all variable patterns in name hint and replace them with configured/default values |
| Matcher matcher = NAMEHINT_PLACEHOLER_REGEXP.matcher(nameHint); |
| StringBuffer sb = new StringBuffer(); |
| while (matcher.find()) |
| { |
| String propertyName = matcher.group(1); |
| String value = getConfigurationPropertyValueOrDefault(propertyName, props, adMap); |
| if (value == null) { |
| value = ""; |
| } |
| matcher.appendReplacement(sb, matcherQuoteReplacement(value)); |
| } |
| matcher.appendTail(sb); |
| |
| // make sure name hint does not only contain whitespaces |
| nameHint = sb.toString().trim(); |
| if (nameHint.length() == 0) { |
| return null; |
| } |
| else { |
| return nameHint; |
| } |
| } |
| |
| /** |
| * Gets configured service property value, or default value if no value is configured. |
| * @param propertyName Property name |
| * @param props Service configuration properties map |
| * @param adMap Attribute definitions map |
| * @return Value or null if none found |
| */ |
| private static String getConfigurationPropertyValueOrDefault(String propertyName, Dictionary<String, Object> props, Map<String, MetatypePropertyDescriptor> adMap) { |
| // get configured property value |
| Object value = props.get(propertyName); |
| |
| if (value != null) |
| { |
| // convert array to string |
| if (value.getClass().isArray()) |
| { |
| StringBuffer valueString = new StringBuffer(); |
| for (int i = 0; i < Array.getLength(value); i++) |
| { |
| if (i > 0) |
| { |
| valueString.append(", "); |
| } |
| valueString.append(Array.get(value, i)); |
| } |
| return valueString.toString(); |
| } |
| else |
| { |
| return value.toString(); |
| } |
| } |
| else |
| { |
| // if not set try to get default value |
| PropertyDescriptor ad = adMap.get(propertyName); |
| if (ad != null && ad.getDefaultValue() != null && ad.getDefaultValue().length == 1) |
| { |
| return ad.getDefaultValue()[0]; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Replacement for Matcher.quoteReplacement which is only available in JDK 1.5 and up. |
| * @param str Unquoted string |
| * @return Quoted string |
| */ |
| private static String matcherQuoteReplacement(String str) |
| { |
| StringBuffer sb = new StringBuffer(); |
| for (int i = 0; i < str.length(); i++) { |
| char c = str.charAt(i); |
| if (c == '$' || c == '\\') { |
| sb.append('\\'); |
| } |
| sb.append(c); |
| } |
| return sb.toString(); |
| } |
| |
| final void listFactoryConfigurations(JSONWriter jw, String pidFilter, |
| String locale) |
| { |
| try |
| { |
| final Map<String, String> optionsFactory = getServices(ManagedServiceFactory.class.getName(), |
| pidFilter, locale, true); |
| final MetaTypeServiceSupport mtss = getMetaTypeSupport(); |
| if ( mtss != null ) |
| { |
| addMetaTypeNames( optionsFactory, mtss.getFactoryPidObjectClasses( locale ), pidFilter, |
| ConfigurationAdmin.SERVICE_FACTORYPID ); |
| } |
| jw.key("fpids"); |
| jw.array(); |
| for ( Iterator<String> ii = optionsFactory.keySet().iterator(); ii.hasNext(); ) |
| { |
| String id = ii.next(); |
| Object name = optionsFactory.get( id ); |
| jw.object(); |
| jw.key("id").value(id ); //$NON-NLS-1$ |
| jw.key("name").value( name ); //$NON-NLS-1$ |
| jw.endObject(); |
| } |
| jw.endArray(); |
| } |
| catch (Exception e) |
| { |
| configManager.log("listFactoryConfigurations: Unexpected problem encountered", e); |
| } |
| } |
| |
| SortedMap<String, String> getServices( String serviceClass, String serviceFilter, String locale, |
| boolean ocdRequired ) throws InvalidSyntaxException |
| { |
| // sorted map of options |
| SortedMap<String, String> 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<String, String> pidMap, final Map<String, ObjectClassDefinition> 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<Map.Entry<String, ObjectClassDefinition>> ei = ocdCollection.entrySet().iterator(); ei.hasNext(); ) |
| { |
| Entry<String, ObjectClassDefinition> ociEntry = ei.next(); |
| final String pid = ociEntry.getKey(); |
| final ObjectClassDefinition ocd = ociEntry.getValue(); |
| if ( filter == null ) |
| { |
| pidMap.put( pid, ocd.getName() ); |
| } |
| else |
| { |
| final Dictionary<String, Object> 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; |
| } |
| |
| |
| @Override |
| public String getPid() |
| { |
| return ConfigManager.PLACEHOLDER_PID; |
| } |
| |
| |
| @Override |
| public String getFactoryPid() |
| { |
| return factoryPid; |
| } |
| |
| |
| @Override |
| public void setBundleLocation( String bundleLocation ) |
| { |
| this.bundleLocation = bundleLocation; |
| } |
| |
| |
| @Override |
| public String getBundleLocation() |
| { |
| return bundleLocation; |
| } |
| |
| |
| @Override |
| public Dictionary<String, Object> getProperties() |
| { |
| // dummy configuration has no properties |
| return null; |
| } |
| |
| |
| @Override |
| public void update() |
| { |
| // dummy configuration cannot be updated |
| } |
| |
| |
| @Override |
| public void update( Dictionary<String, ?> properties ) |
| { |
| // dummy configuration cannot be updated |
| } |
| |
| |
| @Override |
| public void delete() |
| { |
| // dummy configuration cannot be deleted |
| } |
| |
| |
| @Override |
| public long getChangeCount() { |
| // dummy configuration always returns 0 |
| return 0; |
| } |
| } |
| |
| public Configuration[] listConfigurations(String filter) throws IOException, InvalidSyntaxException |
| { |
| return this.service.listConfigurations(filter); |
| } |
| } |