| /* |
| * 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.scr.impl.manager; |
| |
| import java.io.IOException; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import org.apache.felix.scr.impl.ComponentRegistry; |
| import org.apache.felix.scr.impl.logger.ScrLogger; |
| import org.apache.felix.scr.impl.metadata.TargetedPID; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.service.cm.Configuration; |
| import org.osgi.service.cm.ConfigurationAdmin; |
| import org.osgi.service.cm.ConfigurationEvent; |
| import org.osgi.service.cm.ConfigurationException; |
| import org.osgi.service.cm.ConfigurationListener; |
| import org.osgi.service.cm.ConfigurationPermission; |
| import org.osgi.service.cm.ManagedService; |
| import org.osgi.service.log.LogService; |
| |
| public abstract class RegionConfigurationSupport |
| { |
| |
| private final ScrLogger logger; |
| private final ServiceReference<ConfigurationAdmin> caReference; |
| private final BundleContext caBundleContext; |
| private final Long bundleId; |
| |
| private final AtomicInteger referenceCount = new AtomicInteger( 1 ); |
| |
| // the service registration of the ConfigurationListener service |
| private volatile ServiceRegistration<ConfigurationListener> m_registration; |
| |
| /** |
| * |
| * @param bundleContext of the ConfigurationAdmin we are tracking |
| * @param registry |
| */ |
| public RegionConfigurationSupport(final ScrLogger logger, final ServiceReference<ConfigurationAdmin> reference, Bundle bundle) |
| { |
| this.logger = logger; |
| this.caReference = reference; |
| this.bundleId = bundle.getBundleId(); |
| this.caBundleContext = bundle.getBundleContext(); |
| } |
| |
| public void start() |
| { |
| // register as listener for configurations |
| final Dictionary<String, Object> props = new Hashtable<>(); |
| props.put( Constants.SERVICE_DESCRIPTION, "Declarative Services Configuration Support Listener" ); |
| props.put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" ); |
| |
| // If RegionConfigurationSupport *directly* implements ConfigurationListener then we get NoClassDefFoundError |
| // when SCR is started without a wiring to an exporter of Config Admin API. This construction allows the |
| // class loading exception to be caught and confined. |
| final ConfigurationListener serviceDelegator; |
| if ( System.getSecurityManager() != null ) { |
| serviceDelegator = new ConfigurationListener() |
| { |
| @Override |
| public void configurationEvent(final ConfigurationEvent event) |
| { |
| AccessController.doPrivileged( |
| new PrivilegedAction<Object>() |
| { |
| @Override |
| public Void run() |
| { |
| RegionConfigurationSupport.this.configurationEvent(event); |
| return null; |
| } |
| }); |
| } |
| }; |
| } |
| else |
| { |
| serviceDelegator = new ConfigurationListener() |
| { |
| |
| @Override |
| public void configurationEvent(final ConfigurationEvent event) |
| { |
| RegionConfigurationSupport.this.configurationEvent(event); |
| } |
| }; |
| } |
| this.m_registration = caBundleContext.registerService(ConfigurationListener.class, serviceDelegator, props ); |
| } |
| |
| public Long getBundleId() |
| { |
| return bundleId; |
| } |
| |
| public boolean reference() |
| { |
| if ( referenceCount.get() == 0 ) |
| { |
| return false; |
| } |
| referenceCount.incrementAndGet(); |
| return true; |
| } |
| |
| public boolean dereference() |
| { |
| if ( referenceCount.decrementAndGet() == 0 ) |
| { |
| this.m_registration.unregister(); |
| this.m_registration = null; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * The return value is only relevant for the call from {@link #configurationEvent(ConfigurationEvent)} |
| * in the case of a deleted configuration which is not a factory configuration! |
| */ |
| public boolean configureComponentHolder(final ComponentHolder<?> holder) |
| { |
| |
| // 112.7 configure unless configuration not required |
| if ( !holder.getComponentMetadata().isConfigurationIgnored() ) |
| { |
| final BundleContext bundleContext = holder.getActivator().getBundleContext(); |
| if ( bundleContext == null ) |
| { |
| return false;// bundle was stopped concurrently with configuration deletion |
| } |
| final List<String> confPids = holder.getComponentMetadata().getConfigurationPid(); |
| |
| final ConfigurationAdmin ca = getConfigAdmin( bundleContext ); |
| try |
| { |
| for ( final String confPid : confPids ) |
| { |
| final Collection<Configuration> factory = findFactoryConfigurations( ca, confPid, |
| bundleContext.getBundle() ); |
| if ( !factory.isEmpty() ) |
| { |
| boolean created = false; |
| for (Configuration config : factory) |
| { |
| try |
| { |
| logger.log(LogService.LOG_DEBUG, |
| "Configuring holder {0} with change count {1}", null, |
| holder, config.getChangeCount()); |
| if (checkBundleLocation(config, |
| bundleContext.getBundle())) |
| { |
| long changeCount = config.getChangeCount(); |
| ServiceReference<ManagedService> ref = getManagedServiceReference( |
| bundleContext); |
| created |= holder.configurationUpdated( |
| new TargetedPID(config.getPid()), |
| new TargetedPID(config.getFactoryPid()), |
| config.getProcessedProperties(ref), changeCount); |
| } |
| } |
| catch (IllegalStateException e) |
| { |
| continue; |
| } |
| |
| } |
| if ( !created ) |
| { |
| return false; |
| } |
| } |
| else |
| { |
| // check for configuration and configure the holder |
| Configuration singleton = findSingletonConfiguration( ca, confPid, bundleContext.getBundle() ); |
| if (singleton != null) |
| { |
| try |
| { |
| logger.log(LogService.LOG_DEBUG, |
| "Configuring holder {0} with change count {1}", null, |
| holder, singleton.getChangeCount()); |
| if (singleton != null && checkBundleLocation(singleton, |
| bundleContext.getBundle())) |
| { |
| long changeCount = singleton.getChangeCount(); |
| ServiceReference<ManagedService> ref = getManagedServiceReference( |
| bundleContext); |
| holder.configurationUpdated( |
| new TargetedPID(singleton.getPid()), null, |
| singleton.getProcessedProperties(ref), |
| changeCount); |
| } |
| else |
| { |
| return false; |
| } |
| } |
| catch (IllegalStateException e) |
| { |
| return false; |
| } |
| } |
| else |
| { |
| return false; |
| } |
| } |
| } |
| return !confPids.isEmpty(); |
| } |
| finally |
| { |
| try |
| { |
| bundleContext.ungetService( caReference ); |
| } |
| catch ( IllegalStateException e ) |
| { |
| // ignore, bundle context was shut down during the above. |
| } |
| } |
| } |
| return false; |
| } |
| |
| // ---------- ConfigurationListener |
| |
| /** |
| * Called by the Configuration Admin service if a configuration is updated |
| * or removed. |
| * <p> |
| * This method is really only called upon configuration changes; it is not |
| * called for existing configurations upon startup of the Configuration |
| * Admin service. To bridge this gap, the |
| * {@link ComponentRegistry#serviceChanged(org.osgi.framework.ServiceEvent)} method called when the |
| * Configuration Admin service is registered calls #configureComponentHolders which calls this method for all |
| * existing configurations to be able to forward existing configurations to |
| * components. |
| * |
| * @param event The configuration change event |
| */ |
| public void configurationEvent(ConfigurationEvent event) |
| { |
| final TargetedPID pid = new TargetedPID( event.getPid() ); |
| String rawFactoryPid = event.getFactoryPid(); |
| final TargetedPID factoryPid = rawFactoryPid == null? null: new TargetedPID( rawFactoryPid ); |
| |
| // iterate over all components which must be configured with this pid |
| // (since DS 1.2, components may specify a specific configuration PID (112.4.4 configuration-pid) |
| Collection<ComponentHolder<?>> holders = getComponentHolders( factoryPid != null? factoryPid: pid ); |
| |
| logger.log( LogService.LOG_DEBUG, |
| "configurationEvent: Handling {0} of Configuration PID={1} for component holders {2}", null, |
| getEventType( event ), pid, holders ); |
| |
| for ( ComponentHolder<?> componentHolder : holders ) |
| { |
| if ( !componentHolder.getComponentMetadata().isConfigurationIgnored() ) |
| { |
| switch (event.getType()) |
| { |
| case ConfigurationEvent.CM_DELETED: |
| if ( factoryPid != null || !configureComponentHolder( componentHolder ) ) |
| { |
| componentHolder.configurationDeleted( pid, factoryPid ); |
| } |
| break; |
| |
| case ConfigurationEvent.CM_UPDATED: |
| { |
| final ComponentActivator activator = componentHolder.getActivator(); |
| if ( activator == null ) |
| { |
| break; |
| } |
| |
| final BundleContext bundleContext = activator.getBundleContext(); |
| if ( bundleContext == null ) |
| { |
| break; |
| } |
| |
| TargetedPID targetedPid = factoryPid == null? pid: factoryPid; |
| TargetedPID oldTargetedPID = componentHolder.getConfigurationTargetedPID( pid, factoryPid ); |
| if ( factoryPid != null || targetedPid.equals( oldTargetedPID ) |
| || targetedPid.bindsStronger( oldTargetedPID ) ) |
| { |
| final ConfigurationInfo configInfo = getConfigurationInfo( pid, targetedPid, |
| componentHolder, bundleContext ); |
| if ( configInfo != null ) |
| { |
| if ( checkBundleLocation( configInfo.getBundleLocation(), bundleContext.getBundle() ) ) |
| { |
| // The below seems to be unnecessary - and if put in, the behaviour is not spec compliant anymore: |
| // if a component has a required configuration and a modified method, the component must not be |
| // reactivated |
| // If this is replacing a weaker targetedPID delete the old one. |
| // if ( factoryPid == null && !targetedPid.equals(oldTargetedPID) && oldTargetedPID != null) |
| //{ |
| //componentHolder.configurationDeleted( pid, factoryPid ); |
| //} |
| componentHolder.configurationUpdated( pid, factoryPid, configInfo.getProps(), |
| configInfo.getChangeCount() ); |
| } |
| } |
| } |
| |
| break; |
| } |
| case ConfigurationEvent.CM_LOCATION_CHANGED: |
| { |
| //TODO is this logic correct for factory pids???? |
| final ComponentActivator activator = componentHolder.getActivator(); |
| if ( activator == null ) |
| { |
| break; |
| } |
| |
| final BundleContext bundleContext = activator.getBundleContext(); |
| if ( bundleContext == null ) |
| { |
| break; |
| } |
| |
| TargetedPID targetedPid = factoryPid == null? pid: factoryPid; |
| TargetedPID oldTargetedPID = componentHolder.getConfigurationTargetedPID( pid, factoryPid ); |
| if ( targetedPid.equals( oldTargetedPID ) ) |
| { |
| //this sets the location to this component's bundle if not already set. OK here |
| //since it used to be set to this bundle, ok to reset it |
| final ConfigurationInfo configInfo = getConfigurationInfo( pid, targetedPid, |
| componentHolder, bundleContext ); |
| if ( configInfo != null ) |
| { |
| logger.log( LogService.LOG_DEBUG, |
| "LocationChanged event, same targetedPID {0}, location now {1}, change count {2}", null, |
| targetedPid, configInfo.getBundleLocation(), |
| configInfo.getChangeCount() ); |
| if ( configInfo.getProps() == null ) |
| { |
| throw new IllegalStateException( "Existing Configuration with pid " + pid |
| + " has had its properties set to null and location changed. We expected a delete event first." ); |
| } |
| //this config was used on this component. Does it still match? |
| if ( !checkBundleLocation( configInfo.getBundleLocation(), bundleContext.getBundle() ) ) |
| { |
| //no, delete it |
| componentHolder.configurationDeleted( pid, factoryPid ); |
| //maybe there's another match |
| configureComponentHolder( componentHolder ); |
| |
| } |
| //else still matches |
| } |
| break; |
| } |
| boolean better = targetedPid.bindsStronger( oldTargetedPID ); |
| if ( better ) |
| { |
| //this sets the location to this component's bundle if not already set. OK here |
| //because if it is set to this bundle we will use it. |
| final ConfigurationInfo configInfo = getConfigurationInfo( pid, targetedPid, |
| componentHolder, bundleContext ); |
| if ( configInfo != null ) |
| { |
| logger.log( LogService.LOG_DEBUG, |
| "LocationChanged event, better targetedPID {0} compared to {1}, location now {2}, change count {3}", null, |
| targetedPid, oldTargetedPID, configInfo.getBundleLocation(), |
| configInfo.getChangeCount()); |
| if ( configInfo.getProps() == null ) |
| { |
| //location has been changed before any properties are set. We don't care. Wait for an updated event with the properties |
| break; |
| } |
| //this component was not configured with this config. Should it be now? |
| if ( checkBundleLocation( configInfo.getBundleLocation(), bundleContext.getBundle() ) ) |
| { |
| if ( oldTargetedPID != null ) |
| { |
| //this is a better match, delete old before setting new |
| componentHolder.configurationDeleted( pid, factoryPid ); |
| } |
| componentHolder.configurationUpdated( pid, factoryPid, configInfo.getProps(), |
| configInfo.getChangeCount() ); |
| } |
| } |
| } |
| //else worse match, do nothing |
| else |
| { |
| logger.log( LogService.LOG_DEBUG, |
| "LocationChanged event, worse targetedPID {0} compared to {1}, do nothing", null, |
| targetedPid, oldTargetedPID ); |
| } |
| break; |
| } |
| default: |
| logger.log( LogService.LOG_WARNING, "Unknown ConfigurationEvent type {0}", null, |
| event.getType() ); |
| } |
| } |
| } |
| } |
| |
| protected abstract Collection<ComponentHolder<?>> getComponentHolders(TargetedPID pid); |
| |
| private String getEventType(ConfigurationEvent event) |
| { |
| switch (event.getType()) |
| { |
| case ConfigurationEvent.CM_UPDATED: |
| return "UPDATED"; |
| case ConfigurationEvent.CM_DELETED: |
| return "DELETED"; |
| case ConfigurationEvent.CM_LOCATION_CHANGED: |
| return "LOCATION CHANGED"; |
| default: |
| return "Unkown event type: " + event.getType(); |
| } |
| |
| } |
| |
| private static class ConfigurationInfo |
| { |
| private final Dictionary<String, Object> props; |
| private final String bundleLocation; |
| private final long changeCount; |
| |
| public ConfigurationInfo(Dictionary<String, Object> props, String bundleLocation, long changeCount) |
| { |
| this.props = props; |
| this.bundleLocation = bundleLocation; |
| this.changeCount = changeCount; |
| } |
| |
| public long getChangeCount() |
| { |
| return changeCount; |
| } |
| |
| public Dictionary<String, Object> getProps() |
| { |
| return props; |
| } |
| |
| public String getBundleLocation() |
| { |
| return bundleLocation; |
| } |
| |
| } |
| |
| /** |
| * This gets config admin, gets the requested configuration, extracts the info we need from it, and ungets config admin. |
| * Some versions of felix config admin do not allow access to configurations after the config admin instance they were obtained from |
| * are ungot. Extracting the info we need into "configInfo" solves this problem. |
| * |
| * @param pid TargetedPID for the desired configuration |
| * @param targetedPid the targeted factory pid for a factory configuration or the pid for a singleton configuration |
| * @param componentHolder ComponentHolder that holds the old change count (for r4 fake change counting) |
| * @param bundleContext BundleContext to get the CA from |
| * @return ConfigurationInfo object containing the info we need from the configuration. |
| */ |
| private ConfigurationInfo getConfigurationInfo(final TargetedPID pid, TargetedPID targetedPid, |
| ComponentHolder<?> componentHolder, final BundleContext bundleContext) |
| { |
| try |
| { |
| final ConfigurationAdmin ca = getConfigAdmin( bundleContext ); |
| try |
| { |
| Configuration[] configs = ca.listConfigurations( filter( pid.getRawPid() ) ); |
| if ( configs != null && configs.length > 0 ) |
| { |
| for (Configuration config : configs) |
| { |
| try |
| { |
| ServiceReference<ManagedService> ref = getManagedServiceReference( |
| bundleContext); |
| return new ConfigurationInfo( |
| config.getProcessedProperties(ref), |
| config.getBundleLocation(), config.getChangeCount()); |
| } |
| catch (IllegalStateException e) |
| { |
| continue; |
| } |
| } |
| } |
| } |
| catch ( IOException e ) |
| { |
| logger.log( LogService.LOG_WARNING, "Failed reading configuration for pid={0}", e, pid); |
| } |
| catch ( InvalidSyntaxException e ) |
| { |
| logger.log( LogService.LOG_WARNING, "Failed reading configuration for pid={0}", e, pid); |
| } |
| finally |
| { |
| bundleContext.ungetService( caReference ); |
| } |
| } |
| catch ( IllegalStateException ise ) |
| { |
| // If the bundle has been stopped concurrently |
| logger.log( LogService.LOG_WARNING, "Bundle in unexpected state", ise ); |
| } |
| return null; |
| } |
| |
| private ServiceReference<ManagedService> getManagedServiceReference(BundleContext bundleContext) |
| { |
| try { |
| Collection<ServiceReference<ManagedService>> refs = bundleContext.getServiceReferences(ManagedService.class, |
| "(&(service.bundleid=" + String.valueOf(bundleContext.getBundle().getBundleId()) + ")(!(service.pid=*)))"); |
| if ( !refs.isEmpty() ) { |
| return refs.iterator().next(); |
| } |
| } catch (InvalidSyntaxException e) { |
| // this should never happen, |
| } |
| return bundleContext.registerService(ManagedService.class, new ManagedService() { |
| |
| @Override |
| public void updated(Dictionary<String, ?> properties) throws ConfigurationException { |
| // nothing to do |
| |
| } |
| }, null).getReference(); |
| } |
| |
| private String filter(String rawPid) |
| { |
| return "(service.pid=" + rawPid + ")"; |
| } |
| |
| /** |
| * Returns the configuration whose PID equals the given pid. If no such |
| * configuration exists, <code>null</code> is returned. |
| * |
| * @param ca Configuration Admin service |
| * @param pid Pid for desired configuration |
| * @param bundle bundle of the component we are configuring (used in targeted pids) |
| * @return configuration with the specified Pid |
| */ |
| public Configuration findSingletonConfiguration(final ConfigurationAdmin ca, final String pid, Bundle bundle) |
| { |
| final String filter = getTargetedPidFilter( pid, bundle, Constants.SERVICE_PID ); |
| final Configuration[] cfg = findConfigurations( ca, filter ); |
| if ( cfg == null ) |
| { |
| return null; |
| } |
| String longest = null; |
| Configuration best = null; |
| for ( Configuration config : cfg ) |
| { |
| if (checkBundleLocation(config, bundle)) |
| { |
| try |
| { |
| String testPid = config.getPid(); |
| if (longest == null || testPid.length() > longest.length()) |
| { |
| longest = testPid; |
| best = config; |
| } |
| } |
| catch (IllegalStateException e) |
| { |
| continue; |
| } |
| } |
| |
| } |
| return best; |
| } |
| |
| /** |
| * Returns all configurations whose factory PID equals the given factory PID |
| * or <code>null</code> if no such configurations exist |
| * |
| * @param ca ConfigurationAdmin service |
| * @param factoryPid factory Pid we want the configurations for |
| * @param bundle bundle we're working for (for location and location permission) |
| * @return the configurations specifying the supplied factory Pid. |
| */ |
| private Collection<Configuration> findFactoryConfigurations(final ConfigurationAdmin ca, final String factoryPid, |
| Bundle bundle) |
| { |
| final String filter = getTargetedPidFilter( factoryPid, bundle, ConfigurationAdmin.SERVICE_FACTORYPID ); |
| Configuration[] configs = findConfigurations( ca, filter ); |
| if ( configs == null ) |
| { |
| return Collections.emptyList(); |
| } |
| Map<String, Configuration> configsByPid = new HashMap<>(); |
| for ( Configuration config : configs ) |
| { |
| if (checkBundleLocation(config, bundle)) |
| { |
| try |
| { |
| Configuration oldConfig = configsByPid.get(config.getPid()); |
| if (oldConfig == null) |
| { |
| configsByPid.put(config.getPid(), config); |
| } |
| else |
| { |
| String newPid = config.getFactoryPid(); |
| String oldPid = oldConfig.getFactoryPid(); |
| if (newPid.length() > oldPid.length()) |
| { |
| configsByPid.put(config.getPid(), config); |
| } |
| } |
| } |
| catch (IllegalStateException e) |
| { |
| continue; |
| } |
| |
| } |
| } |
| return configsByPid.values(); |
| } |
| |
| private boolean checkBundleLocation(Configuration config, Bundle bundle) |
| { |
| if ( config == null ) |
| { |
| return false; |
| } |
| |
| String configBundleLocation = null; |
| try |
| { |
| configBundleLocation = config.getBundleLocation(); |
| } |
| catch (IllegalStateException e) |
| { |
| return false; |
| } |
| |
| return checkBundleLocation( configBundleLocation, bundle ); |
| } |
| |
| private boolean checkBundleLocation(String configBundleLocation, Bundle bundle) |
| { |
| boolean result; |
| if ( configBundleLocation == null ) |
| { |
| result = true; |
| } |
| else if ( configBundleLocation.startsWith( "?" ) ) |
| { |
| //multilocation |
| if ( System.getSecurityManager() != null ) |
| { |
| result = bundle.hasPermission( |
| new ConfigurationPermission(configBundleLocation, ConfigurationPermission.TARGET)); |
| } |
| else |
| { |
| result = true; |
| } |
| } |
| else |
| { |
| result = configBundleLocation.equals( bundle.getLocation() ); |
| } |
| logger.log( LogService.LOG_DEBUG, "checkBundleLocation: location {0}, returning {1}", null, |
| configBundleLocation, result ); |
| return result; |
| } |
| |
| private Configuration[] findConfigurations(final ConfigurationAdmin ca, final String filter) |
| { |
| try |
| { |
| return ca.listConfigurations( filter ); |
| } |
| catch ( IOException ioe ) |
| { |
| logger.log( LogService.LOG_WARNING, "Problem listing configurations for filter={0}", ioe, |
| filter ); |
| } |
| catch ( InvalidSyntaxException ise ) |
| { |
| logger.log( LogService.LOG_ERROR, "Invalid Configuration selection filter {0}", ise, filter); |
| } |
| |
| // no factories in case of problems |
| return null; |
| } |
| |
| private String getTargetedPidFilter(String pid, Bundle bundle, String key) |
| { |
| String bsn = bundle.getSymbolicName(); |
| String version = bundle.getVersion().toString(); |
| String location = escape( bundle.getLocation() ); |
| |
| StringBuilder sb = new StringBuilder(); |
| |
| sb.append("(|("); |
| |
| sb.append(key); |
| sb.append('='); |
| sb.append(pid); |
| |
| sb.append(")("); |
| |
| sb.append(key); |
| sb.append('='); |
| sb.append(pid); |
| sb.append('|'); |
| sb.append(bsn); |
| |
| sb.append(")("); |
| |
| sb.append(key); |
| sb.append('='); |
| sb.append(pid); |
| sb.append('|'); |
| sb.append(bsn); |
| sb.append('|'); |
| sb.append(version); |
| |
| sb.append(")("); |
| |
| sb.append(key); |
| sb.append('='); |
| sb.append(pid); |
| sb.append('|'); |
| sb.append(bsn); |
| sb.append('|'); |
| sb.append(version); |
| sb.append('|'); |
| sb.append(location); |
| |
| sb.append("))"); |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * see core spec 3.2.7. Escape \*() with preceding \ |
| * @param value |
| * @return escaped string |
| */ |
| static final String escape(String value) |
| { |
| StringBuilder sb = null; |
| |
| int index = 0; |
| |
| for (int i = 0; i < value.length(); i++) { |
| char c = value.charAt(i); |
| |
| switch (c) { |
| case '\\': |
| case '*': |
| case '(': |
| case ')': |
| if (sb == null) { |
| sb = new StringBuilder(); |
| } |
| |
| sb.append(value, index, i); |
| sb.append('\\'); |
| sb.append(c); |
| |
| index = i + 1; |
| |
| break; |
| } |
| } |
| |
| if (sb == null) { |
| return value; |
| } |
| |
| if (index < value.length()) { |
| sb.append(value, index, value.length()); |
| } |
| |
| return sb.toString(); |
| } |
| |
| private ConfigurationAdmin getConfigAdmin(BundleContext bundleContext) |
| { |
| return bundleContext.getService( caReference ); |
| } |
| } |