FELIX-3651 implement r5 location binding
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1480103 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java b/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java
index 6d10c76..c3e4241 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java
@@ -452,8 +452,9 @@
* @param pid the pid candidate
* @return the set of ComponentHolders matching the singleton pid supplied
*/
- public final Collection<ComponentHolder> getComponentHoldersByPid(String pid)
+ public final Collection<ComponentHolder> getComponentHoldersByPid(TargetedPID targetedPid)
{
+ String pid = targetedPid.getServicePid();
Set<ComponentHolder> componentHoldersUsingPid = new HashSet<ComponentHolder>();
synchronized (m_componentHoldersByPid)
{
@@ -461,7 +462,13 @@
// only return the entry if non-null and not a reservation
if (set != null)
{
- componentHoldersUsingPid.addAll(set);
+ for (ComponentHolder holder: set)
+ {
+ if (targetedPid.matchesTarget(holder))
+ {
+ componentHoldersUsingPid.add( holder );
+ }
+ }
}
}
return componentHoldersUsingPid;
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/TargetedPID.java b/scr/src/main/java/org/apache/felix/scr/impl/TargetedPID.java
new file mode 100644
index 0000000..abf1f9d
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/TargetedPID.java
@@ -0,0 +1,264 @@
+/*
+ * 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;
+
+import org.apache.felix.scr.impl.config.ComponentHolder;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+
+/**
+ * Copied with modifications from felix configadmin.
+ *
+ * The <code>TargetedPID</code> class represents a targeted PID as read
+ * from a configuration object.
+ * <p>
+ * For a factory configuration the <code>TargetedPID</code> represents
+ * the factory PID of the configuration. Otherwise it represents the
+ * PID itself of the configuration.
+ */
+public class TargetedPID
+{
+
+ private final String rawPid;
+
+ private final String servicePid;
+
+ private final String symbolicName;
+ private final String version;
+ private final String location;
+
+ /**
+ * The level of binding of this targeted PID:
+ * <ul>
+ * <li><code>0</code> -- this PID is not targeted at all</li>
+ * <li><code>1</code> -- this PID is targeted by the symbolic name</li>
+ * <li><code>2</code> -- this PID is targeted by the symbolic name and version</li>
+ * <li><code>3</code> -- this PID is targeted by the symoblic name, version, and location</li>
+ * </ul>
+ */
+ private final short bindingLevel;
+
+
+ /**
+ * Returns the bundle's version as required for targeted PIDs: If the
+ * bundle has a version the string representation of the version
+ * string converted to a Version object is returned. Otherwise the
+ * string representation of <code>Version.emptyVersion</code> is
+ * returned.
+ * <p>
+ * To remain compatible with pre-R4.2 (Framework API < 1.5) we cannot
+ * use the <code>Bundle.getVersion()</code> method.
+ *
+ * @param bundle The bundle whose version is to be returned.
+ */
+ public static String getBundleVersion( final Bundle bundle )
+ {
+ Object vHeader = bundle.getHeaders().get( Constants.BUNDLE_VERSION );
+ Version version = ( vHeader == null ) ? Version.emptyVersion : new Version( vHeader.toString() );
+ return version.toString();
+ }
+
+
+ public TargetedPID( final String rawPid )
+ {
+ this.rawPid = rawPid;
+
+ if ( rawPid.indexOf( '|' ) < 0 )
+ {
+ this.servicePid = rawPid;
+ this.symbolicName = null;
+ this.version = null;
+ this.location = null;
+ this.bindingLevel = 0;
+ }
+ else
+ {
+ int start = 0;
+ int end = rawPid.indexOf( '|' );
+ this.servicePid = rawPid.substring( start, end );
+
+ start = end + 1;
+ end = rawPid.indexOf( '|', start );
+ if ( end >= 0 )
+ {
+ this.symbolicName = rawPid.substring( start, end );
+ start = end + 1;
+ end = rawPid.indexOf( '|', start );
+ if ( end >= 0 )
+ {
+ this.version = rawPid.substring( start, end );
+ this.location = rawPid.substring( end + 1 );
+ this.bindingLevel = 3;
+ }
+ else
+ {
+ this.version = rawPid.substring( start );
+ this.location = null;
+ this.bindingLevel = 2;
+ }
+ }
+ else
+ {
+ this.symbolicName = rawPid.substring( start );
+ this.version = null;
+ this.location = null;
+ this.bindingLevel = 1;
+ }
+ }
+ }
+
+
+ /**
+ * Returns true if the target of this PID (bundle symbolic name,
+ * version, and location) match the bundle registering the referenced
+ * service.
+ * <p>
+ * This method just checks the target not the PID value itself, so
+ * this method returning <code>true</code> does not indicate whether
+ * the service actually is registered with a service PID equal to the
+ * raw PID of this targeted PID.
+ * <p>
+ * This method also returns <code>false</code> if the service has
+ * concurrently been unregistered and the registering bundle is now
+ * <code>null</code>.
+ *
+ * @param reference <code>ServiceReference</code> to the registered
+ * service
+ * @return <code>true</code> if the referenced service matches the
+ * target of this PID.
+ */
+ public boolean matchesTarget( ComponentHolder holder )
+ {
+ // already unregistered
+ final Bundle serviceBundle = holder.getActivator().getBundleContext().getBundle();
+ if ( serviceBundle == null )
+ {
+ return false;
+ }
+
+ // This is not really targeted
+ if ( this.symbolicName == null )
+ {
+ return true;
+ }
+
+ // bundle symbolic names don't match
+ if ( !this.symbolicName.equals( serviceBundle.getSymbolicName() ) )
+ {
+ return false;
+ }
+
+ // no more specific target
+ if ( this.version == null )
+ {
+ return true;
+ }
+
+ // bundle version does not match
+
+ if ( !this.version.equals( getBundleVersion( serviceBundle ) ) )
+ {
+ return false;
+ }
+
+ // assert bundle location match
+ return this.location == null || this.location.equals( serviceBundle.getLocation() );
+ }
+
+
+ /**
+ * Gets the raw PID with which this instance has been created.
+ * <p>
+ * If an actual service PID contains pipe symbols that PID might be
+ * considered being targeted PID without it actually being one. This
+ * method provides access to the raw PID to allow for such services to
+ * be configured.
+ */
+ public String getRawPid()
+ {
+ return rawPid;
+ }
+
+
+ /**
+ * Returns the service PID of this targeted PID which basically is
+ * the targeted PID without the targeting information.
+ */
+ public String getServicePid()
+ {
+ return servicePid;
+ }
+
+
+ /**
+ * Returns <code>true</code> if this targeted PID binds stronger than
+ * the <code>other</code> {@link TargetedPID}.
+ * <p>
+ * This method assumes both targeted PIDs have already been checked for
+ * suitability for the bundle encoded in the targetting.
+ *
+ * @param other The targeted PID to check whether it is binding stronger
+ * or not.
+ * @return <code>true</code> if the <code>other</code> targeted PID
+ * is binding strong.
+ */
+ public boolean bindsStronger( final TargetedPID other )
+ {
+ return other == null || this.bindingLevel > other.bindingLevel;
+ }
+
+
+ @Override
+ public int hashCode()
+ {
+ return this.rawPid.hashCode();
+ }
+
+
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( obj == null )
+ {
+ return false;
+ }
+ else if ( obj == this )
+ {
+ return true;
+ }
+
+ // assume equality if same class and raw PID equals
+ if ( this.getClass() == obj.getClass() )
+ {
+ return this.rawPid.equals( ( ( TargetedPID ) obj ).rawPid );
+ }
+
+ // not the same class or different raw PID
+ return false;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ return this.rawPid;
+ }
+}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurationSupport.java b/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurationSupport.java
index 7df6535..3fe70ec 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurationSupport.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/config/ConfigurationSupport.java
@@ -22,13 +22,20 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
+import java.util.Collections;
import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
import org.apache.felix.scr.impl.Activator;
import org.apache.felix.scr.impl.BundleComponentActivator;
import org.apache.felix.scr.impl.ComponentRegistry;
+import org.apache.felix.scr.impl.TargetedPID;
+import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
@@ -38,6 +45,7 @@
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationEvent;
import org.osgi.service.cm.ConfigurationListener;
+import org.osgi.service.cm.ConfigurationPermission;
import org.osgi.service.log.LogService;
public class ConfigurationSupport implements ConfigurationListener
@@ -69,7 +77,7 @@
// the service m_registration of the ConfigurationListener service
private ServiceRegistration m_registration;
-
+
public ConfigurationSupport(final BundleContext bundleContext, final ComponentRegistry registry)
{
this.m_registry = registry;
@@ -114,26 +122,25 @@
if ( cao instanceof ConfigurationAdmin )
{
final ConfigurationAdmin ca = ( ConfigurationAdmin ) cao;
- final Configuration[] factory = findFactoryConfigurations(ca, confPid);
- if (factory != null)
+ final Collection<Configuration> factory = findFactoryConfigurations(ca, confPid, bundleContext.getBundle());
+ if (!factory.isEmpty())
{
- for (int i = 0; i < factory.length; i++)
+ for (Configuration config: factory)
{
- final String pid = factory[i].getPid();
- final Configuration config = getConfiguration(ca, pid, bundleLocation);
+ config = getConfiguration( ca, config.getPid(), bundleContext.getBundle() );
long changeCount = changeCounter.getChangeCount( config, false, -1 );
- holder.configurationUpdated(pid, config.getProperties(), changeCount);
+ holder.configurationUpdated(config.getPid(), config.getProperties(), changeCount);
}
}
else
{
// check for configuration and configure the holder
- final Configuration singleton = findSingletonConfiguration(ca, confPid);
+ Configuration singleton = findSingletonConfiguration(ca, confPid, bundleContext.getBundle());
if (singleton != null)
{
- final Configuration config = getConfiguration(ca, confPid, bundleLocation);
- long changeCount = changeCounter.getChangeCount( config, false, -1 );
- holder.configurationUpdated(confPid, config.getProperties(), changeCount);
+ singleton = getConfiguration( ca, singleton.getPid(), bundleContext.getBundle() );
+ long changeCount = changeCounter.getChangeCount( singleton, false, -1 );
+ holder.configurationUpdated(confPid, singleton.getProperties(), changeCount);
}
}
}
@@ -203,8 +210,9 @@
*/
public void configurationEvent(ConfigurationEvent event)
{
- final String pid = event.getPid();
- final String factoryPid = event.getFactoryPid();
+ 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)
@@ -229,7 +237,7 @@
{
switch (event.getType()) {
case ConfigurationEvent.CM_DELETED:
- componentHolder.configurationDeleted(pid);
+ componentHolder.configurationDeleted(pid.getServicePid());
break;
case ConfigurationEvent.CM_UPDATED:
@@ -259,12 +267,11 @@
if ( cao instanceof ConfigurationAdmin )
{
final ConfigurationAdmin ca = ( ConfigurationAdmin ) cao;
- final Configuration config = getConfiguration( ca, pid, bundleContext
- .getBundle().getLocation() );
+ final Configuration config = getConfiguration( ca, pid.getRawPid(), bundleContext.getBundle() );
if ( config != null )
{
- long changeCount = changeCounter.getChangeCount( config, true, componentHolder.getChangeCount( pid ) );
- componentHolder.configurationUpdated( pid, config.getProperties(), changeCount );
+ long changeCount = changeCounter.getChangeCount( config, true, componentHolder.getChangeCount( pid.getServicePid() ) );
+ componentHolder.configurationUpdated( pid.getServicePid(), config.getProperties(), changeCount );
}
}
else
@@ -294,8 +301,7 @@
break;
case ConfigurationEvent.CM_LOCATION_CHANGED:
- // FELIX-3650: Don't log WARNING message
- // FELIX-3584: Implement event support
+ // FELIX-3584: Implement event support
break;
default:
@@ -306,19 +312,19 @@
}
}
- private Configuration getConfiguration(final ConfigurationAdmin ca, final String pid, final String bundleLocation)
+ private Configuration getConfiguration(final ConfigurationAdmin ca, final String pid, final Bundle bundle)
{
try
{
final Configuration cfg = ca.getConfiguration(pid);
- if (bundleLocation.equals(cfg.getBundleLocation()))
+ if (checkBundleLocation( cfg, bundle ))
{
return cfg;
}
// configuration belongs to another bundle, cannot be used here
- Activator.log(LogService.LOG_ERROR, null, "Cannot use configuration pid=" + pid + " for bundle "
- + bundleLocation + " because it belongs to bundle " + cfg.getBundleLocation(), null);
+ Activator.log(LogService.LOG_INFO, null, "Cannot use configuration pid=" + pid + " for bundle "
+ + bundle.getLocation() + " because it belongs to bundle " + cfg.getBundleLocation(), null);
}
catch (IOException ioe)
{
@@ -334,13 +340,33 @@
*
* @param ca Configuration Admin service
* @param pid Pid for desired configuration
+ * @param bundle TODO
* @return configuration with the specified Pid
*/
- public Configuration findSingletonConfiguration(final ConfigurationAdmin ca, final String pid)
+ public Configuration findSingletonConfiguration(final ConfigurationAdmin ca, final String pid, Bundle bundle)
{
- final String filter = "(service.pid=" + pid + ")";
+ final String filter = getTargetedPidFilter( pid, bundle, Constants.SERVICE_PID );
final Configuration[] cfg = findConfigurations(ca, filter);
- return (cfg == null || cfg.length == 0) ? null : cfg[0];
+ if (cfg == null)
+ {
+ return null;
+ }
+ String longest = null;
+ Configuration best = null;
+ for (Configuration config: cfg)
+ {
+ if ( checkBundleLocation( config, bundle ) )
+ {
+ String testPid = config.getPid();
+ if ( longest == null || testPid.length() > longest.length())
+ {
+ longest = testPid;
+ best = config;
+ }
+ }
+
+ }
+ return best;
}
/**
@@ -349,12 +375,54 @@
*
* @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.
*/
- public Configuration[] findFactoryConfigurations(final ConfigurationAdmin ca, final String factoryPid)
+ private Collection<Configuration> findFactoryConfigurations(final ConfigurationAdmin ca, final String factoryPid, Bundle bundle)
{
- final String filter = "(service.factoryPid=" + factoryPid + ")";
- return findConfigurations(ca, filter);
+ 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<String, Configuration>();
+ for (Configuration config: configs)
+ {
+ if ( checkBundleLocation( config, bundle ) )
+ {
+ 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 );
+ }
+ }
+ }
+ }
+ return configsByPid.values();
+ }
+
+ private boolean checkBundleLocation(Configuration config, Bundle bundle)
+ {
+ String configBundleLocation = config.getBundleLocation();
+ if ( configBundleLocation == null)
+ {
+ return true;
+ }
+ if (configBundleLocation.startsWith( "?" ))
+ {
+ //multilocation
+ return bundle.hasPermission(new ConfigurationPermission(configBundleLocation, ConfigurationPermission.TARGET));
+ }
+ return configBundleLocation.equals(bundle.getLocation());
}
private Configuration[] findConfigurations(final ConfigurationAdmin ca, final String filter)
@@ -376,6 +444,17 @@
return null;
}
+ private String getTargetedPidFilter(String pid, Bundle bundle, String key)
+ {
+ String bsn = bundle.getSymbolicName();
+ String version = bundle.getVersion().toString();
+ String location = bundle.getLocation();
+ String f = String.format(
+ "(|(%1$s=%2$s)(%1$s=%2$s|%3$s)(%1$s=%2$s|%3$s|%4$s)(%1$s=%2$s|%3$s|%4$s|%5$s))",
+ key, pid, bsn, version, location );
+ return f;
+ }
+
private interface ChangeCount {
long getChangeCount( Configuration configuration, boolean fromEvent, long previous );