FELIX-5177: Committed the patch from Jan-Willem (:-)) which implements config proxy for factory conf adapters.
Updated the "dictionary.api" sample code that is now using a proxy config when using factory conf adapter.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1729508 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java
index c4cc264..13083bc 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/Activator.java
@@ -33,7 +33,7 @@
@Override
public void init(BundleContext context, DependencyManager dm) throws Exception {
// Create the factory configuration for our DictionaryImpl service.
- dm.add(createFactoryConfigurationAdapterService(DictionaryConfiguration.class.getName(), "updated", true)
+ dm.add(createFactoryConfigurationAdapterService(DictionaryConfiguration.class.getName(), "updated", true, DictionaryConfiguration.class)
.setInterface(DictionaryService.class.getName(), null)
.setImplementation(DictionaryImpl.class)
.add(createServiceDependency().setService(LogService.class))); // NullObject
diff --git a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java
index 4b361e1..da33f55 100644
--- a/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/dictionary/api/DictionaryImpl.java
@@ -61,16 +61,11 @@
* Our service will be initialized from ConfigAdmin.
* @param config The configuration where we'll lookup our words list (key=".words").
*/
- protected void updated(Dictionary<String, ?> config) {
- if (config != null) {
- // We use the bnd "Configurable" helper in order to get an implementation for our DictionaryConfiguration interface.
- DictionaryConfiguration cnf = Configurable.createConfigurable(DictionaryConfiguration.class, config);
-
- m_lang = cnf.lang();
- m_words.clear();
- for (String word : cnf.words()) {
- m_words.add(word);
- }
+ protected void updated(DictionaryConfiguration cnf) {
+ m_lang = cnf.lang();
+ m_words.clear();
+ for (String word : cnf.words()) {
+ m_words.add(word);
}
}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java
index 07b0b98..a3674b8 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyActivatorBase.java
@@ -364,6 +364,26 @@
}
/**
+ * Creates a new factory configuration adapter service, using a specific callback instance
+ *
+ * @return the factory configuration adapter service
+ * @see DependencyManager#createFactoryConfigurationAdapterService(String, String, boolean, Class)
+ */
+ public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Class<?> configType) {
+ return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, propagate, configType);
+ }
+
+ /**
+ * Creates a new factory configuration adapter service, using a specific callback instance
+ *
+ * @return the factory configuration adapter service
+ * @see DependencyManager#createFactoryConfigurationAdapterService(String, String, boolean, Object, Class)
+ */
+ public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Object callbackInstance, Class<?> configType) {
+ return m_manager.createFactoryConfigurationAdapterService(factoryPid, update, propagate, callbackInstance, configType);
+ }
+
+ /**
* Creates a new factory configuration adapter service.
*
* @return the factory configuration adapter service
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
index d26a10f..8a21037 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/DependencyManager.java
@@ -416,7 +416,7 @@
* @return a service that acts as a factory for generating the managed service factory configuration adapter
*/
public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate) {
- return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, null);
+ return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, null, null);
}
/**
@@ -439,7 +439,63 @@
* @return a service that acts as a factory for generating the managed service factory configuration adapter
*/
public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Object callbackInstance) {
- return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, callbackInstance);
+ return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, callbackInstance, null);
+ }
+
+ /**
+ * Creates a new Managed Service Factory Configuration Adapter. For each new Config Admin factory configuration matching
+ * the factoryPid, an adapter will be created based on the adapter implementation class.
+ * The adapter will be registered with the specified interface, and with the specified adapter service properties.
+ * Depending on the <code>propagate</code> parameter, every public factory configuration properties
+ * (which don't start with ".") will be propagated along with the adapter service properties.
+ * It will also inherit all dependencies.
+ *
+ * <h3>Usage Example</h3>
+ *
+ * <blockquote><pre>
+ * manager.createFactoryConfigurationAdapterService("MyFactoryPid", "update", true)
+ * // The interface to use when registering adapter
+ * .setInterface(AdapterService.class.getName(), new Hashtable() {{ put("foo", "bar"); }})
+ * // the implementation of the adapter
+ * .setImplementation(AdapterServiceImpl.class);
+ * </pre></blockquote>
+ *
+ * @param factoryPid the pid matching the factory configuration
+ * @param update the adapter method name that will be notified when the factory configuration is created/updated.<p>
+ * The following signatures are supported:<p>
+ * <ul><li> updated(Dictionary)
+ * <li> updated(Component, Dictionary)
+ * </ul>
+ * @param propagate true if public factory configuration should be propagated to the adapter service properties
+ * @param configType the configuration type to use instead of a dictionary or map.
+ * @return a service that acts as a factory for generating the managed service factory configuration adapter
+ */
+ public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Class<?> configType) {
+ return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, null, configType);
+ }
+
+ /**
+ * Creates a new Managed Service Factory Configuration Adapter using a specific update callback instance.
+ * For each new Config Admin factory configuration matching the factoryPid, an adapter will be created
+ * based on the adapter implementation class.
+ * The adapter will be registered with the specified interface, and with the specified adapter service properties.
+ * Depending on the <code>propagate</code> parameter, every public factory configuration properties
+ * (which don't start with ".") will be propagated along with the adapter service properties.
+ * It will also inherit all dependencies.
+ *
+ * @param factoryPid the pid matching the factory configuration
+ * @param update the adapter method name that will be notified when the factory configuration is created/updated.<p>
+ * The following signatures are supported:<p>
+ * <ul><li> updated(Dictionary)
+ * <li> updated(Component, Dictionary)
+ * </ul>
+ * @param propagate true if public factory configuration should be propagated to the adapter service properties
+ * @param callbackInstance the object on which the updated callback will be invoked.
+ * @param configType the configuration type to use instead of a dictionary or map.
+ * @return a service that acts as a factory for generating the managed service factory configuration adapter
+ */
+ public Component createFactoryConfigurationAdapterService(String factoryPid, String update, boolean propagate, Object callbackInstance, Class<?> configType) {
+ return new FactoryConfigurationAdapterImpl(this, factoryPid, update, propagate, callbackInstance, configType);
}
/**
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java
index 92086b4..0649b95 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AdapterServiceImpl.java
@@ -25,7 +25,6 @@
import org.apache.felix.dm.Component;
import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.Dependency;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.ServiceDependency;
import org.apache.felix.dm.context.DependencyContext;
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java
index 5adce5b..d89cd33 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/AspectServiceImpl.java
@@ -27,7 +27,6 @@
import org.apache.felix.dm.Component;
import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.Dependency;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.ServiceDependency;
import org.apache.felix.dm.context.DependencyContext;
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java
index 166af93..b28d2e0 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleAdapterImpl.java
@@ -24,7 +24,6 @@
import org.apache.felix.dm.Component;
import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.Dependency;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.context.DependencyContext;
import org.osgi.framework.Bundle;
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java
index 4aad446..f424e17 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/BundleDependencyImpl.java
@@ -234,7 +234,8 @@
Bundle bundle = (Bundle) event.getEvent();
if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
try {
- return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ Bundle.class }}, new Object[][] {{ bundle }});
+ CallbackTypeDef callbackInfo = new CallbackTypeDef(Bundle.class, bundle);
+ return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, callbackInfo.m_sigs, callbackInfo.m_args);
}
catch (InvocationTargetException e) {
m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/CallbackTypeDef.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/CallbackTypeDef.java
new file mode 100644
index 0000000..597fcb4
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/CallbackTypeDef.java
@@ -0,0 +1,39 @@
+/*
+ * 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.dm.impl;
+
+/**
+ * Small wrapper to hold the various signatures and arguments for callback methods.
+ */
+public class CallbackTypeDef {
+
+ public final Class<?>[][] m_sigs;
+ public final Object[][] m_args;
+
+ public CallbackTypeDef(Class<?> sig, Object arg) {
+ this(new Class[][] { { sig } }, new Object[][] { { arg } });
+ }
+
+ public CallbackTypeDef(Class<?>[][] sigs, Object[][] args) {
+ m_sigs = sigs;
+ m_args = args;
+ }
+
+}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
index face758..d129e42 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ConfigurationDependencyImpl.java
@@ -347,6 +347,38 @@
}
}
+ /**
+ * Creates the various signatures and arguments combinations used for the configuration-type style callbacks.
+ *
+ * @param service the service for which the callback should be applied;
+ * @param configType the configuration type to use (can be <code>null</code>);
+ * @param settings the actual configuration settings.
+ */
+ static CallbackTypeDef createCallbackType(Logger logger, Component service, Class<?> configType, Dictionary<?, ?> settings) {
+ Class<?>[][] sigs = new Class[][] { { Dictionary.class }, { Component.class, Dictionary.class }, {} };
+ Object[][] args = new Object[][] { { settings }, { service, settings }, {} };
+
+ if (configType != null) {
+ try {
+ // if the configuration is null, it means we are losing it, and since we pass a null dictionary for other callback
+ // (that accepts a Dictionary), then we should have the same behavior and also pass a null conf proxy object when
+ // the configuration is lost.
+ Object configurable = settings != null ? Configurable.create(configType, settings) : null;
+
+ logger.debug("Using configuration-type injecting using %s as possible configType.", configType.getSimpleName());
+
+ sigs = new Class[][] { { Dictionary.class }, { Component.class, Dictionary.class }, { Component.class, configType }, { configType }, {} };
+ args = new Object[][] { { settings }, { service, settings }, { service, configurable }, { configurable }, {} };
+ }
+ catch (Exception e) {
+ // This is not something we can recover from, use the defaults above...
+ logger.warn("Failed to create configurable for configuration type %s!", e, configType);
+ }
+ }
+
+ return new CallbackTypeDef(sigs, args);
+ }
+
private void invokeUpdated(Dictionary<?, ?> settings) throws ConfigurationException {
if (m_updateInvokedCache.compareAndSet(false, true)) {
Object[] instances = super.getInstances(); // either the callback instance or the component instances
@@ -354,29 +386,11 @@
return;
}
- Class<?>[][] sigs = new Class[][] { { Dictionary.class }, { Component.class, Dictionary.class }, {} };
- Object[][] args = new Object[][] { { settings }, { m_component, settings }, {} };
-
- if (m_configType != null) {
- Object configurable;
- try {
- // if the configuration is null, it means we are losing it, and since we pass a null dictionary for other callback
- // (that accepts a Dictionary), then we should have the same behavior and also pass a null conf proxy object when
- // the configuration is lost.
- configurable = settings != null ? Configurable.create(m_configType, settings) : null;
-
- sigs = new Class[][] { { Dictionary.class }, { Component.class, Dictionary.class }, { Component.class, m_configType }, { m_configType }, {} };
- args = new Object[][] { { settings }, { m_component, settings }, { m_component, configurable }, { configurable }, {} };
- }
- catch (Exception e) {
- // This is not something we can recover from, use the defaults above...
- m_component.getLogger().warn("Failed to create configurable for method %s and configuration type %s!", e, m_add, m_configType);
- }
- }
+ CallbackTypeDef callbackInfo = createCallbackType(m_logger, m_component, m_configType, settings);
for (int i = 0; i < instances.length; i++) {
try {
- InvocationUtil.invokeCallbackMethod(instances[i], m_add, sigs, args);
+ InvocationUtil.invokeCallbackMethod(instances[i], m_add, callbackInfo.m_sigs, callbackInfo.m_args);
}
catch (InvocationTargetException e) {
// The component has thrown an exception during it's callback invocation.
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
index 6ad328d..646ffeb 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/FactoryConfigurationAdapterImpl.java
@@ -24,11 +24,9 @@
import java.util.Hashtable;
import org.apache.felix.dm.Component;
-import org.apache.felix.dm.Dependency;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.Logger;
import org.apache.felix.dm.PropertyMetaData;
-import org.apache.felix.dm.context.DependencyContext;
import org.apache.felix.dm.impl.metatype.MetaTypeProviderImpl;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -36,6 +34,8 @@
import org.osgi.service.metatype.MetaTypeProvider;
import org.osgi.service.metatype.ObjectClassDefinition;
+import static org.apache.felix.dm.impl.ConfigurationDependencyImpl.createCallbackType;
+
/**
* Factory configuration adapter service implementation. This class extends the FilterService in order to catch
* some Service methods for configuring actual adapter service implementation.
@@ -49,7 +49,7 @@
// Our logger
protected final Logger m_logger;
- public FactoryConfigurationAdapterImpl(DependencyManager dm, String factoryPid, String update, boolean propagate, Object updateCallbackInstance) {
+ public FactoryConfigurationAdapterImpl(DependencyManager dm, String factoryPid, String update, boolean propagate, Object updateCallbackInstance, Class<?> configType) {
super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
m_factoryPid = factoryPid;
m_logger = ((ComponentImpl) m_component).getLogger();
@@ -58,7 +58,7 @@
props.put(Constants.SERVICE_PID, factoryPid);
m_component
.setInterface(ManagedServiceFactory.class.getName(), props)
- .setImplementation(new AdapterImpl(update, propagate, updateCallbackInstance))
+ .setImplementation(new AdapterImpl(update, propagate, updateCallbackInstance, configType))
.setCallbacks("init", null, "stop", null);
}
@@ -80,7 +80,7 @@
public String getName() {
return "Adapter for factory pid " + m_factoryPid;
}
-
+
/**
* Creates, updates, or removes a service, when a ConfigAdmin factory configuration is created/updated or deleted.
*/
@@ -93,6 +93,9 @@
// A specific callback instance where the update callback is invoked on (or null if default component instances should be used).
protected final Object m_updateCallbackInstance;
+
+ // the configuration type to use as alternative to the dictionary/map when propagating the configuration to the service
+ protected final Class<?> m_configType;
/**
* Creates a new CM factory configuration adapter.
@@ -105,10 +108,11 @@
* @param propagate
* @param updateCallbackObject null if update should be called on all component instances (composition), or a specific update callback instance.
*/
- public AdapterImpl(String updateMethod, boolean propagate, Object updateCallbackObject) {
+ public AdapterImpl(String updateMethod, boolean propagate, Object updateCallbackObject, Class<?> configType) {
m_update = updateMethod;
m_propagate = propagate;
m_updateCallbackInstance = updateCallbackObject;
+ m_configType = configType;
}
/**
@@ -142,23 +146,23 @@
// Instantiate the component, because we need to invoke the updated callback synchronously, in the CM calling thread.
((ComponentImpl) newService).instantiateComponent();
-
- try {
- for (Object instance : getCompositionInstances(newService)) {
- InvocationUtil.invokeCallbackMethod(instance, m_update,
- new Class[][] {
- {Dictionary.class},
- {Component.class, Dictionary.class},
- {}},
- new Object[][] {
- {settings},
- {newService, settings},
- {}});
+
+ CallbackTypeDef callbackInfo = createCallbackType(m_logger, newService, m_configType, settings);
+
+ for (Object instance : getCompositionInstances(newService)) {
+ try {
+ InvocationUtil.invokeCallbackMethod(instance, m_update, callbackInfo.m_sigs, callbackInfo.m_args);
}
- }
-
- catch (Throwable t) {
- handleException(t); // will rethrow a runtime exception.
+ catch (InvocationTargetException e) {
+ // The component has thrown an exception during it's callback invocation.
+ handleException(e.getTargetException());
+ }
+ catch (NoSuchMethodException e) {
+ // if the method does not exist, ignore it
+ }
+ catch (Throwable t) {
+ handleException(t); // will rethrow a runtime exception.
+ }
}
return newService;
@@ -174,18 +178,11 @@
Component service = (Component) properties[1];
Object[] instances = getUpdateCallbackInstances(service);
+ CallbackTypeDef callbackInfo = createCallbackType(m_logger, service, m_configType, cmSettings);
+
try {
for (Object instance : instances) {
- InvocationUtil.invokeCallbackMethod(instance, m_update,
- new Class[][] {
- { Dictionary.class },
- { Component.class, Dictionary.class },
- {}},
- new Object[][] {
- { cmSettings },
- { service, cmSettings },
- {}
- });
+ InvocationUtil.invokeCallbackMethod(instance, m_update, callbackInfo.m_sigs, callbackInfo.m_args);
}
if (m_serviceInterfaces != null && m_propagate == true) {
Dictionary<String, ?> serviceProperties = getServiceProperties(cmSettings);
@@ -251,11 +248,10 @@
}
}
}
-
return props;
}
-
+
private void handleException(Throwable t) {
m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for factory pid " + m_factoryPid, t);
if (t instanceof InvocationTargetException) {
@@ -285,7 +281,7 @@
BundleContext bctx, Logger logger, String heading,
String description, String localization,
PropertyMetaData[] properyMetaData) {
- super(updateMethod, propagate, updateCallbackInstance);
+ super(updateMethod, propagate, updateCallbackInstance, null /* configType */);
m_metaType = new MetaTypeProviderImpl(m_factoryPid, bctx, logger, null, this);
m_metaType.setName(heading);
m_metaType.setDescription(description);
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java
index 1115884..3cfc117 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceAdapterImpl.java
@@ -25,7 +25,6 @@
import org.apache.felix.dm.Component;
import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.Dependency;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.ResourceDependency;
import org.apache.felix.dm.context.DependencyContext;
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java
index effd588..834ee55 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ResourceDependencyImpl.java
@@ -194,7 +194,8 @@
Dictionary<String, Object> resourceProperties = re.getProperties();
if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
try {
- return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ URL.class }}, new Object[][] {{ resource }});
+ CallbackTypeDef callbackInfo = new CallbackTypeDef(URL.class, resource);
+ return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, callbackInfo.m_sigs, callbackInfo.m_args);
}
catch (InvocationTargetException e) {
m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
diff --git a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java
index ab4daf4..4e9df67 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/ServiceDependencyImpl.java
@@ -356,9 +356,9 @@
if (se != null) {
if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
try {
- return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod,
- new Class[][]{{ServiceReference.class, Object.class}, {ServiceReference.class}}, new Object[][]{
- {se.getReference(), se.getEvent()}, {se.getReference()}});
+ CallbackTypeDef callbackInfo = new CallbackTypeDef(new Class[][] { { ServiceReference.class, Object.class }, { ServiceReference.class } },
+ new Object[][] { { se.getReference(), se.getEvent() }, { se.getReference() } });
+ return (Dictionary<String, Object>) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, callbackInfo.m_sigs, callbackInfo.m_args);
} catch (InvocationTargetException e) {
m_component.getLogger().warn("Exception while invoking callback method", e.getCause());
} catch (Throwable e) {
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
index c455a77..af82ff0 100644
--- a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/ConfigurationDependencyImplTest.java
@@ -20,8 +20,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -29,71 +28,53 @@
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.felix.dm.Logger;
import org.apache.felix.dm.context.ComponentContext;
import org.junit.Test;
+import org.osgi.framework.BundleContext;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
+import test.Ensure;
+
/**
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class ConfigurationDependencyImplTest {
- public static interface MyMap {
- String getFoo();
-
- int getQux();
-
- String[] getQuu();
- }
-
public static interface MyConfiguration {
- boolean isTrue();
-
- int getValue();
-
- long getLongValue();
-
- double getPi();
-
String[] getArgArray();
List<String> getArgList();
+ long getLongValue();
+
MyMap getMap();
String getMessage();
+
+ double getPi();
+
+ int getValue();
+
+ boolean isTrue();
}
- static class PlainService {
- final CountDownLatch m_latch = new CountDownLatch(1);
+ public static interface MyMap {
+ String getFoo();
- public void updated(Dictionary config) throws ConfigurationException {
- if (config != null) {
- m_latch.countDown();
- }
- assertConfiguration(config);
- }
+ String[] getQuu();
- private void assertConfiguration(Dictionary cfg) {
- assertEquals("isTrue", "true", cfg.get("true"));
- assertEquals("getValue", "42", cfg.get("value"));
- assertEquals("getLongValue", "1234567890", cfg.get("longValue"));
- assertEquals("getPi", "3.141", cfg.get("pi"));
- assertEquals("getArgArray", "[a, b, c]", cfg.get("argArray"));
- assertEquals("getArgList", "[d, e, f]", cfg.get("argList"));
- assertEquals("getMap.foo", "bar", cfg.get("map.foo"));
- assertEquals("getMap.qux", "123", cfg.get("map.qux"));
- assertEquals("getMap.quu", "[x, y, z]", cfg.get("map.quu"));
- assertEquals("getMessage", "hello world!", cfg.get("message"));
- }
+ int getQux();
}
static class AManagedService extends PlainService implements ManagedService {
+ public AManagedService(Ensure ensure) {
+ super(ensure);
+ }
+
@Override
public void updated(Dictionary config) throws ConfigurationException {
super.updated(config);
@@ -101,13 +82,25 @@
}
static class FancyService {
- final CountDownLatch m_latch = new CountDownLatch(1);
+ final Ensure m_ensure;
+
+ public FancyService(Ensure ensure) {
+ m_ensure = ensure;
+ }
public void updated(MyConfiguration config) throws ConfigurationException {
+ m_ensure.step();
+
if (config != null) {
- m_latch.countDown();
+ assertConfiguration(config);
}
- assertConfiguration(config);
+ else {
+ assertNull(config);
+ }
+ }
+
+ public void stop() {
+ m_ensure.step();
}
private void assertConfiguration(MyConfiguration cfg) {
@@ -126,46 +119,117 @@
}
}
- @Test
- public void testInvokeManagedServiceUpdatedMethodOk() throws Exception {
- AManagedService service = new AManagedService();
+ static class PlainService {
+ final Ensure m_ensure;
- ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
- cdi.updated(createDictionary());
+ public PlainService(Ensure ensure) {
+ m_ensure = ensure;
+ }
- assertTrue(service.m_latch.await(1, TimeUnit.SECONDS));
+ public void updated(Dictionary config) throws ConfigurationException {
+ m_ensure.step();
+
+ if (config != null) {
+ assertConfiguration(config);
+ }
+ else {
+ assertNull(config);
+ }
+ }
+
+ public void stop() {
+ m_ensure.step();
+ }
+
+ private void assertConfiguration(Dictionary cfg) {
+ assertEquals("isTrue", "true", cfg.get("true"));
+ assertEquals("getValue", "42", cfg.get("value"));
+ assertEquals("getLongValue", "1234567890", cfg.get("longValue"));
+ assertEquals("getPi", "3.141", cfg.get("pi"));
+ assertEquals("getArgArray", "[a, b, c]", cfg.get("argArray"));
+ assertEquals("getArgList", "[d, e, f]", cfg.get("argList"));
+ assertEquals("getMap.foo", "bar", cfg.get("map.foo"));
+ assertEquals("getMap.qux", "123", cfg.get("map.qux"));
+ assertEquals("getMap.quu", "[x, y, z]", cfg.get("map.quu"));
+ assertEquals("getMessage", "hello world!", cfg.get("message"));
+ }
}
@Test
- public void testInvokePlainUpdatedMethodOk() throws Exception {
- PlainService service = new PlainService();
+ public void testDoNotInvokeFancyUpdatedMethodWithWrongSignatureOk() throws Exception {
+ Ensure ensure = createEnsure();
+ FancyService service = new FancyService(ensure);
ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
+ cdi.setCallback(service, "updated", Dictionary.class);
+ ensure.step(1);
+
cdi.updated(createDictionary());
- assertTrue(service.m_latch.await(1, TimeUnit.SECONDS));
+ TimeUnit.SECONDS.sleep(1L);
+
+ // Our step shouldn't be changed...
+ ensure.waitForStep(1, 1000);
}
@Test
public void testInvokeFancyUpdatedMethodOk() throws Exception {
- FancyService service = new FancyService();
+ Ensure ensure = createEnsure();
+ FancyService service = new FancyService(ensure);
ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
cdi.setCallback(service, "updated", MyConfiguration.class);
cdi.updated(createDictionary());
- assertTrue(service.m_latch.await(1, TimeUnit.SECONDS));
+ ensure.waitForStep(1, 1000);
+
+ cdi.updated(null);
+
+ ensure.waitForStep(2, 1000);
}
@Test
- public void testDoNotInvokeFancyUpdatedMethodWithWrongSignatureOk() throws Exception {
- FancyService service = new FancyService();
+ public void testInvokeManagedServiceUpdatedMethodOk() throws Exception {
+ Ensure ensure = createEnsure();
+ AManagedService service = new AManagedService(ensure);
ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
- cdi.setCallback(service, "updated", Dictionary.class);
cdi.updated(createDictionary());
- assertFalse(service.m_latch.await(1, TimeUnit.SECONDS));
+ ensure.waitForStep(1, 1000);
+
+ cdi.updated(null);
+
+ ensure.waitForStep(2, 1000);
+ }
+
+ @Test
+ public void testInvokePlainUpdatedMethodOk() throws Exception {
+ Ensure ensure = createEnsure();
+ PlainService service = new PlainService(ensure);
+
+ ConfigurationDependencyImpl cdi = createConfigurationDependency(service);
+ cdi.updated(createDictionary());
+
+ ensure.waitForStep(1, 1000);
+
+ cdi.updated(null);
+
+ ensure.waitForStep(2, 1000);
+ }
+
+ private ConfigurationDependencyImpl createConfigurationDependency(Object service) {
+ BundleContext bc = mock(BundleContext.class);
+ Logger mockLogger = mock(Logger.class);
+
+ ComponentContext component = mock(ComponentContext.class);
+ when(component.getExecutor()).thenReturn(Executors.newSingleThreadExecutor());
+ when(component.getLogger()).thenReturn(mockLogger);
+
+ ConfigurationDependencyImpl result = new ConfigurationDependencyImpl(bc, mockLogger);
+ result.setCallback(service, "updated").setPid("does.not.matter");
+ result.setComponentContext(component);
+ return result;
}
private Dictionary createDictionary() {
@@ -183,16 +247,7 @@
return result;
}
- private ConfigurationDependencyImpl createConfigurationDependency(Object service) {
- Logger mockLogger = mock(Logger.class);
-
- ComponentContext component = mock(ComponentContext.class);
- when(component.getExecutor()).thenReturn(Executors.newSingleThreadExecutor());
- when(component.getLogger()).thenReturn(mockLogger);
-
- ConfigurationDependencyImpl result = new ConfigurationDependencyImpl();
- result.setCallback(service, "updated").setPid("does.not.matter");
- result.setComponentContext(component);
- return result;
+ private Ensure createEnsure() {
+ return new Ensure(false);
}
}
diff --git a/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/FactoryConfigurationAdapterImplTest.java b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/FactoryConfigurationAdapterImplTest.java
new file mode 100644
index 0000000..51d6edb
--- /dev/null
+++ b/dependencymanager/org.apache.felix.dependencymanager/test/org/apache/felix/dm/impl/FactoryConfigurationAdapterImplTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.dm.impl;
+
+import static org.mockito.Mockito.mock;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.impl.ConfigurationDependencyImplTest.AManagedService;
+import org.apache.felix.dm.impl.ConfigurationDependencyImplTest.FancyService;
+import org.apache.felix.dm.impl.ConfigurationDependencyImplTest.MyConfiguration;
+import org.apache.felix.dm.impl.ConfigurationDependencyImplTest.PlainService;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ManagedServiceFactory;
+
+import test.Ensure;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FactoryConfigurationAdapterImplTest {
+
+ private static final String CONF_PID = "bogus";
+
+ @Test
+ public void testDoNotInvokeFancyUpdatedMethodWithWrongSignatureOk() throws Exception {
+ Ensure ensure = createEnsure();
+ FancyService service = new FancyService(ensure);
+
+ FactoryConfigurationAdapterImpl cdi = createConfigurationDependency(service, Map.class);
+ ensure.step(1);
+
+ ((ManagedServiceFactory) cdi.m_component.getInstance()).updated(CONF_PID, createDictionary());
+
+ TimeUnit.SECONDS.sleep(1L);
+
+ // Our step shouldn't be changed...
+ ensure.waitForStep(1, 1000);
+ }
+
+ @Test
+ public void testInvokeFancyUpdatedMethodOk() throws Exception {
+ Ensure ensure = createEnsure();
+ FancyService service = new FancyService(ensure);
+
+ FactoryConfigurationAdapterImpl cdi = createConfigurationDependency(service, MyConfiguration.class);
+
+ ((ManagedServiceFactory) cdi.m_component.getInstance()).updated(CONF_PID, createDictionary());
+
+ ensure.waitForStep(1, 1000);
+
+ ((ManagedServiceFactory) cdi.m_component.getInstance()).deleted(CONF_PID);
+
+ ensure.waitForStep(2, 1000);
+ }
+
+ @Test
+ public void testInvokeManagedServiceUpdatedMethodOk() throws Exception {
+ Ensure ensure = createEnsure();
+ AManagedService service = new AManagedService(ensure);
+
+ FactoryConfigurationAdapterImpl cdi = createConfigurationDependency(service);
+
+ ((ManagedServiceFactory) cdi.m_component.getInstance()).updated(CONF_PID, createDictionary());
+
+ ensure.waitForStep(1, 1000);
+
+ ((ManagedServiceFactory) cdi.m_component.getInstance()).deleted(CONF_PID);
+
+ ensure.waitForStep(2, 1000);
+ }
+
+ @Test
+ public void testInvokePlainUpdatedMethodOk() throws Exception {
+ Ensure ensure = createEnsure();
+ PlainService service = new PlainService(ensure);
+
+ FactoryConfigurationAdapterImpl cdi = createConfigurationDependency(service);
+
+ ((ManagedServiceFactory) cdi.m_component.getInstance()).updated(CONF_PID, createDictionary());
+
+ ensure.waitForStep(1, 1000);
+
+ ((ManagedServiceFactory) cdi.m_component.getInstance()).deleted(CONF_PID);
+
+ ensure.waitForStep(2, 1000);
+ }
+
+
+ private FactoryConfigurationAdapterImpl createConfigurationDependency(Object service) {
+ return createConfigurationDependency(service, null);
+ }
+
+ private FactoryConfigurationAdapterImpl createConfigurationDependency(Object service, Class<?> configType) {
+ BundleContext bc = mock(BundleContext.class);
+
+ DependencyManager dm = new DependencyManager(bc);
+
+ Component result = dm.createFactoryConfigurationAdapterService("does.not.matter", "updated", false, service, configType);
+
+ // Normally, when creating a factory pid adapter, you specify the class of the adapter implementation which will be instantiated
+ // for each created factory pid. To do so, you invoke the setImplementation(Object impl) method, and this methods
+ // accepts a class parameter, or an object instance. Usually, you always pass a class, because the intent of a factory pid adapter is to
+ // create a component instance for each created factory pid. But in our case, the "service" parameter represents our adapter instance,
+ // so just use it as the factory adapter implementation instance:
+ result.setImplementation(service);
+
+ // *Important note:* the semantic of the factory conf pid adapter is really similar to a ManagedServiceFactory:
+ // - when the factory pid is created, a component is created; called in updated; and called in start().
+ // - when the factory pid is updated, the component is called in updated().
+ // - but when the factory pid is removed, updated(null) is not invoked (unlike in case of ConfigurationDependency), and the component is simply
+ // stopped. This is actually the same semantic as ManagedServiceFactory: when factory pid is removed, ManagedServiceFactory.deleted() is called
+ // and the deleted() method is assumed to stop and unregister the service that was registered for the pid being removed.
+ dm.add(result);
+ return (FactoryConfigurationAdapterImpl) result;
+ }
+
+ private Dictionary<?,?> createDictionary() {
+ Dictionary<String, Object> result = new Hashtable<>();
+ result.put("true", "true");
+ result.put("value", "42");
+ result.put("longValue", "1234567890");
+ result.put("pi", "3.141");
+ result.put("argArray", "[a, b, c]");
+ result.put("argList", "[d, e, f]");
+ result.put("map.foo", "bar");
+ result.put("map.qux", "123");
+ result.put("map.quu", "[x, y, z]");
+ result.put("message", "hello world!");
+ return result;
+ }
+
+ private Ensure createEnsure() {
+ return new Ensure(false);
+ }
+}