Added two new publisher/unpublisher attributes in the Service annotation in order to allow a Service to take control over service exposition. Allow Start annotation to return an optional Map of properties which can be appended into the provided service properties
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@984213 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Service.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Service.java
index ce16e40..29c7a10 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Service.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Service.java
@@ -174,4 +174,80 @@
* Sets the static method used to create the Service implementation instance.
*/
String factoryMethod() default "";
+
+ /**
+ * Injects a <code>Runnable</code> object to invoke for manually registering the Service into the OSGi registry.
+ * By default, a Service is implicitly registered into the OSGi registry when the service's bundle is
+ * started and when all required dependencies are satisfied. However, it is sometimes required to programatically
+ * take control of when the service is registered. In this case, this attribute can be used and provides a
+ * </code>Runnable</code> object that can be invoked in order to register a Service at any time.
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * /**
+ * * This Service will be registered programatically into the OSGi registry, using the publisher attribute.
+ * */
+ * @Service(publisher="m_publisher")
+ * class X implements Z {
+ * Runnable m_publisher;
+ *
+ * @Start
+ * void start() {
+ * // Our Z Service is started but won't be registered into the OSGi registry once this method returns,
+ * // because we are using the publisher attribute. The service will be registered only when we
+ * // decide to invoke the Runnable injected by the publisher attribute.
+ * }
+ *
+ * public void registerServiceWheneverIWant() {
+ * m_publisher.run(); // register our service into the osgi registry.
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ */
+ String publisher() default "";
+
+ /**
+ * Injects a <code>Runnable</code> object for manually unregistering the Service from the OSGi registry.
+ * By default, a Service is implicitly unregistered from the OSGi registry when the service's bundle is
+ * stopped, or when the Service has lost one of its required dependencies. However, it is sometimes required to programatically
+ * take control of when the service is unregistered. In this case, this attribute can be used and provides a
+ * </code>Runnable</code> object that can be invoked in order to unregister a Service at any time.
+ *
+ * <p> Notice that this attribute is generally used with the {@link #publisher()} attribute.
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * /**
+ * * This Service will be registered programatically into the OSGi registry, using the publisher attribute.
+ * * It will also be unregistered unsing the Runnable injected by the unpublisher attribute.
+ * */
+ * @Service(publisher="m_publisher", unpublisher="m_unpublisher")
+ * class X implements Z {
+ * Runnable m_publisher;
+ * Runnable m_unpublisher;
+ *
+ * @Start
+ * void start() {
+ * // Our Z Service is started but won't be registered into the OSGi registry once this method returns,
+ * // because we are using the publisher attribute. The service will be registered only when we
+ * // decide to invoke the Runnable injected by the publisher attribute.
+ * }
+ *
+ * public void registerServiceWheneverIWant() {
+ * m_publisher.run(); // register our service into the OSGi registry.
+ * }
+ *
+ * public void unregisterServiceWheneverIWant() {
+ * m_unpublisher.run(); // unregister our Z service from the OSGi registry.
+ * }
+ * }
+ * </pre>
+ * </blockquote>
+ */
+ String unpublisher() default "";
}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ServiceDependency.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ServiceDependency.java
index 4a8270d..0ad12f1 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ServiceDependency.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/ServiceDependency.java
@@ -110,8 +110,8 @@
* The name used when dynamically configuring this dependency from the init method.
* Specifying this attribute allows to dynamically configure the dependency
* <code>filter</code> and <code>required</code> flag from the Service's init method.
- * All unamed dependencies will be injected before the init() method; so from the init() method, you can
- * then pick up whatever information needed from already injected (unamed) dependencies, and configure dynamically
+ * All unnamed dependencies will be injected before the init() method; so from the init() method, you can
+ * then pick up whatever information needed from already injected (unnamed) dependencies, and configure dynamically
* your named dependencies, which will then be calculated once the init() method returns.
*
* <p> Usage example of a Service whose dependency filter is configured from ConfigAdmin:
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Start.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Start.java
index 4106c0b..60af798 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Start.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/api/Start.java
@@ -25,7 +25,31 @@
/**
* Annotates a method which will be invoked when the Service is started.
- * If you don't supply this annotation, then no "start" method will be invoked.
+ * The annotated method will be called when all required dependencies have been injected, and
+ * just before registering the service into the OSGi registry (if the service provides an interface).
+ * Notice that the start method may optionally return a Map which will be propagated to the provided
+ * service properties.
+ *
+ * <p>
+ * <h3>Usage Examples</h3>
+ * <blockquote>
+ *
+ * <pre>
+ * @Service(properties={@Property(name="foo", value="bar")})
+ * class X implements Z {
+ * @ServiceDependency
+ * OtherService m_dependency;
+ *
+ * @Start
+ * Map start() {
+ * // Our Z Service is ready (all required dependencies have been satisfied), and is about to be
+ * // registered into the OSGi registry. We return here an optional Map containing some extra-properties
+ * // which will be appended to the properties supplied in the Service annotation.
+ * return new HashMap() {{ put("foo2", "bar2"); }};
+ * }
+ * }
+ * </pre>
+ * </blockquote>
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
index 13d43b6..689e922 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
@@ -208,26 +208,18 @@
}
else if (annotation.getName().equals(A_INIT))
{
- //Patterns.parseMethod(m_method, m_descriptor, Patterns.VOID);
- // TODO check if method takes optional params like Service, DependencyManager, etc ...
m_initMethod = m_method;
}
else if (annotation.getName().equals(A_START))
{
- //Patterns.parseMethod(m_method, m_descriptor, Patterns.VOID);
- // TODO check if method takes optional params like Service, DependencyManager, etc ...
m_startMethod = m_method;
}
else if (annotation.getName().equals(A_STOP))
{
- //Patterns.parseMethod(m_method, m_descriptor, Patterns.VOID);
- // TODO check if method takes optional params like Service, DependencyManager, etc ...
m_stopMethod = m_method;
}
else if (annotation.getName().equals(A_DESTROY))
{
- //Patterns.parseMethod(m_method, m_descriptor, Patterns.VOID);
- // TODO check if method takes optional params like Service, DependencyManager, etc ...
m_destroyMethod = m_method;
}
else if (annotation.getName().equals(A_COMPOSITION))
@@ -282,6 +274,10 @@
// factoryMethod attribute
writer.putString(annotation, EntryParam.factoryMethod, null);
+
+ // Parse publisher/unpublisher attributes.
+ writer.putString(annotation, EntryParam.publisher, null);
+ writer.putString(annotation, EntryParam.unpublisher, null);
}
private void addCommonServiceParams(EntryWriter writer)
@@ -306,11 +302,10 @@
writer.put(EntryParam.destroy, m_destroyMethod);
}
- // Register Composition method
if (m_compositionMethod != null)
{
writer.put(EntryParam.composition, m_compositionMethod);
- }
+ }
}
/**
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
index c9072f3..12a5cec 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryParam.java
@@ -36,5 +36,7 @@
factoryConfigure,
factoryMethod,
field,
- name
+ name,
+ publisher,
+ unpublisher
}
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java
index 7e9b15b..c976d7e 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/Patterns.java
@@ -11,11 +11,14 @@
// Pattern used to check if a method returns an array of Objects
public final static Pattern COMPOSITION = Pattern.compile("\\(\\)\\[Ljava/lang/Object;");
- // Pattern used to parse the class parameter from the bind methods ("bind(Type)" or "bind(Map, Type)" or "bind(BundleContext, Type)"
+ // Pattern used to parse the class parameter from the bind methods ("bind(Type)" or "bind(Map, Type)" or "bind(BundleContext, Type)"
public final static Pattern BIND_CLASS = Pattern.compile("\\((L[^;]+;)?L([^;]+);\\)V");
// Pattern used to parse classes from class descriptors;
public final static Pattern CLASS = Pattern.compile("L([^;]+);");
+
+ // Pattern used to parse the field on which a Publisher annotation may be applied on
+ public final static Pattern PUBLISHER = Pattern.compile("Ljava/lang/Runnable;");
/**
* Parses a class.
@@ -39,6 +42,7 @@
/**
* Checks if a method descriptor matches a given pattern.
+ * @param the method whose signature descriptor is checked
* @param pattern the pattern used to check the method signature descriptor
* @throws IllegalArgumentException if the method signature descriptor does not match the given pattern.
*/
@@ -51,4 +55,20 @@
+ descriptor);
}
}
+
+ /**
+ * Checks if a field descriptor matches a given pattern.
+ * @param field the field whose type descriptor is checked
+ * @param descriptor the field descriptor to be checked
+ * @param pattern the pattern to use
+ * @throws IllegalArgumentException if the method signature descriptor does not match the given pattern.
+ */
+ public static void parseField(String field, String descriptor, Pattern pattern) {
+ Matcher matcher = pattern.matcher(descriptor);
+ if (!matcher.matches())
+ {
+ throw new IllegalArgumentException("Invalid field " + field + ", wrong signature: "
+ + descriptor);
+ }
+ }
}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactorySet.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactorySet.java
index b0d179e..cb54c1f 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactorySet.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/FactorySet.java
@@ -365,9 +365,30 @@
s.setImplementation(m_impl);
if (m_provide != null)
{
- // Merge service properties with the configuration provided by the factory.
+ // Merge service properties with the configuration provided by the factory.
Dictionary serviceProperties = mergeSettings(m_serviceProperties, configuration);
- s.setInterface(m_provide, serviceProperties);
+
+ // Set the exposed service, unless a Publisher field is present.
+ // If present, the publisher field means that we have to inject
+ // a Runnable that will be called back by the service for firing a service
+ // registration.
+
+ String publisherField = m_srvMeta.getString(Params.publisher, null);
+ String unpublisherField = m_srvMeta.getString(Params.unpublisher, null);
+ if (publisherField == null)
+ {
+ s.setInterface(m_provide, serviceProperties);
+ } else
+ {
+ // Services will be manually provided by the service itself.
+ ServicePublisher publisher = new ServicePublisher(publisherField,
+ unpublisherField,
+ s,
+ m_bundle.getBundleContext(),
+ m_provide,
+ serviceProperties);
+ publisher.register(m_dm);
+ }
}
s.setComposition(m_srvMeta.getString(Params.composition, null));
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Log.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Log.java
index 68999c5..dcd8bea 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Log.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Log.java
@@ -26,7 +26,7 @@
public class Log
{
/** The log service */
- private LogService m_logService;
+ private volatile LogService m_logService;
/** Our sole instance */
private static Log m_instance = new Log();
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Params.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Params.java
index 445b5d4..afaca7e 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Params.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/Params.java
@@ -55,5 +55,7 @@
factoryConfigure,
factoryMethod,
name,
- field
+ field,
+ publisher,
+ unpublisher
}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceBuilder.java
index bd0a990..f0c9891 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceBuilder.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceBuilder.java
@@ -55,8 +55,6 @@
String impl = srvMeta.getString(Params.impl);
String composition = srvMeta.getString(Params.composition, null);
- Dictionary<String, Object> serviceProperties = srvMeta.getDictionary(Params.properties, null);
- String[] provide = srvMeta.getStrings(Params.provide, null);
String factoryMethod = srvMeta.getString(Params.factoryMethod, null);
if (factoryMethod == null)
{
@@ -66,12 +64,33 @@
service.setFactory(b.loadClass(impl), factoryMethod);
}
service.setComposition(composition);
- service.setInterface(provide, serviceProperties);
+
// Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
addUnamedDependencies(b, dm, service, srvMeta, depsMeta);
// Creates a ServiceHandler, which will filter all service lifecycle callbacks.
ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(service, b, dm, srvMeta, depsMeta);
service.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
+
+ // Set the provided services
+ Dictionary<String, Object> properties = srvMeta.getDictionary(Params.properties, null);
+ String[] services = srvMeta.getStrings(Params.provide, null);
+ String publisherField = srvMeta.getString(Params.publisher, null);
+ String unpublisherField = srvMeta.getString(Params.unpublisher, null);
+ if (publisherField == null)
+ {
+ service.setInterface(services, properties);
+ }
+ else if (services != null)
+ {
+ // Services will be manually provided by the service itself.
+ ServicePublisher publisher = new ServicePublisher(publisherField,
+ unpublisherField,
+ service,
+ b.getBundleContext(),
+ services,
+ properties);
+ publisher.register(dm);
+ }
}
else
{
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceComponentBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceComponentBuilder.java
index af33da3..18b75e8 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceComponentBuilder.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceComponentBuilder.java
@@ -18,12 +18,15 @@
*/
package org.apache.felix.dm.runtime;
+import java.util.Arrays;
+import java.util.Dictionary;
import java.util.List;
import org.apache.felix.dm.Dependency;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.Service;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
import org.osgi.service.log.LogService;
/**
@@ -64,7 +67,7 @@
}
/**
- * Registers all unamed dependencies into a given service. Named dependencies are
+ * Registers all unnamed dependencies into a given service. Named dependencies are
* handled differently, and are managed by the ServiceLifecycleHandler class.
* @throws Exception
*/
@@ -84,6 +87,5 @@
s.add(d);
}
}
-
}
}
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
index 2783d65..3a6356d 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServiceLifecycleHandler.java
@@ -20,7 +20,10 @@
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
import java.util.HashMap;
+import java.util.Hashtable;
import java.util.List;
import java.util.Map;
@@ -182,11 +185,47 @@
/**
* Handles the Service's start lifecycle callback. We just invoke the service "start" service callback on
* the service instance, as well as on all eventual service composites.
+ * We take care to check if a start callback returns a Map, which is meant to contain
+ * some additional properties which must be appended to existing service properties.
+ * Such extra properties takes precedence over existing service properties.
*/
+ @SuppressWarnings("unchecked")
public void start(Service service)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
- callbackComposites(service, m_start);
+ DependencyManager dm = service.getDependencyManager();
+ Map<String, String> extraProperties = new HashMap<String, String>();
+ Object[] composites = service.getCompositionInstances();
+ for (Object composite: composites)
+ {
+ Object o = invokeMethod(composite, m_start, dm, service);
+ if (o != null && Map.class.isAssignableFrom(o.getClass()))
+ {
+ extraProperties.putAll((Map) o);
+ }
+ }
+
+ if (extraProperties.size() > 0)
+ {
+ // Store extra properties returned by start callbacks into existing service properties
+ Dictionary existingProperties = service.getServiceProperties();
+ if (existingProperties != null)
+ {
+ Hashtable props = new Hashtable();
+ Enumeration e = existingProperties.keys();
+ while (e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ props.put(key, existingProperties.get(key));
+ }
+ props.putAll(extraProperties);
+ service.setServiceProperties(props);
+ }
+ else
+ {
+ service.setServiceProperties(new Hashtable(extraProperties));
+ }
+ }
}
/**
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServicePublisher.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServicePublisher.java
new file mode 100644
index 0000000..e177dd0
--- /dev/null
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/ServicePublisher.java
@@ -0,0 +1,165 @@
+/*
+ * 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.runtime;
+
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.Service;
+import org.apache.felix.dm.ServiceStateListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+/**
+ * This class is injected in Service's Runnable fields which are annotated with the Publisher annotation.
+ * This Runnable acts as a Service publisher, allowing the Service to take control of when the Service
+ * is actually exposed from the OSGi registry.
+ */
+public class ServicePublisher
+{
+ private final AtomicBoolean m_published = new AtomicBoolean(false);
+ private Service m_srv;
+ private BundleContext m_bc;
+ private String[] m_services;
+ private Dictionary<String, Object> m_properties;
+ private volatile ServiceRegistration m_registration;
+ private String m_publisherField;
+ private String m_unpublisherField;
+
+ /**
+ * Class constructor.
+ * @param publisherField The Service field name annotated with the Publisher annotation
+ * @param unpublisherField the Servicel field where to inject a Runnable for unregistering the Service
+ * @param srv the Service object where to inject the Runnables (we'll use defaultImplementation ...)
+ * @param bc the Service bundle context
+ * @param services the list of provided services which will be registered once our Runnable is invoked
+ * @param props the published service properties.
+ */
+ public ServicePublisher(String publisherField, String unpublisherField, Service srv, BundleContext bc, String[] services, Dictionary<String, Object> props)
+ {
+ m_publisherField = publisherField;
+ m_unpublisherField = unpublisherField;
+ m_srv = srv;
+ m_bc = bc;
+ m_services = services;
+ m_properties = props;
+ }
+
+ public void register(DependencyManager dm)
+ {
+ Log.instance().log(LogService.LOG_DEBUG, "registering Publisher for services %s",
+ Arrays.toString(m_services));
+ Publisher publisher = new Publisher();
+ m_srv.add(dm.createServiceDependency()
+ .setService(Runnable.class, "(dm.publisher=" + System.identityHashCode(this) + ")")
+ .setRequired(false)
+ .setAutoConfig(m_publisherField)
+ .setDefaultImplementation(publisher));
+ m_srv.addStateListener(publisher);
+
+ if (m_unpublisherField != null)
+ {
+ Unpublisher unpublisher = new Unpublisher();
+ m_srv.add(dm.createServiceDependency()
+ .setService(Runnable.class, "(dm.unpublisher=" + System.identityHashCode(this) + ")")
+ .setRequired(false)
+ .setAutoConfig(m_unpublisherField)
+ .setDefaultImplementation(unpublisher));
+ }
+ }
+
+ private class Publisher implements Runnable, ServiceStateListener
+ {
+ public void run()
+ {
+ if (m_published.compareAndSet(false, true))
+ {
+ try
+ {
+ Log.instance().log(LogService.LOG_DEBUG, "publishing services %s",
+ Arrays.toString(m_services));
+
+ m_registration = m_bc.registerService(m_services, m_srv.getService(), m_properties);
+ }
+ catch (Throwable t)
+ {
+ m_published.set(false);
+ if (t instanceof RuntimeException)
+ {
+ throw (RuntimeException) t;
+ }
+ else
+ {
+ throw new RuntimeException("Could not register services", t);
+ }
+ }
+ }
+ }
+
+ public void starting(Service service)
+ {
+ }
+
+ public void started(Service service)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void stopping(Service service)
+ {
+ if (m_published.compareAndSet(true, false))
+ {
+ if (m_registration != null)
+ {
+ Log.instance().log(LogService.LOG_DEBUG, "unpublishing services %s (service is stopping)",
+ Arrays.toString(m_services));
+
+ m_registration.unregister();
+ m_registration = null;
+ }
+ }
+ }
+
+ public void stopped(Service service)
+ {
+ }
+ }
+
+ private class Unpublisher implements Runnable
+ {
+ public void run()
+ {
+ if (m_published.compareAndSet(true, false))
+ {
+ if (m_registration != null)
+ {
+ Log.instance().log(LogService.LOG_DEBUG, "unpublishing services %s",
+ Arrays.toString(m_services));
+
+ m_registration.unregister();
+ m_registration = null;
+ }
+ }
+ }
+ }
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraAdapterServiceProperties.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraAdapterServiceProperties.java
new file mode 100644
index 0000000..0fa83ff
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraAdapterServiceProperties.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.test.bundle.annotation.extraproperties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.AdapterService;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.Service;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+
+/**
+ * This test validates that an adapter Service may specify some extra service properties
+ * from it's start callback
+ */
+public class ExtraAdapterServiceProperties
+{
+ public interface Provider
+ {
+ }
+
+ public interface Provider2
+ {
+ }
+
+
+ @Service(properties={@Property(name="foo", value="bar")})
+ public static class ProviderImpl implements Provider
+ {
+ }
+
+ @AdapterService(adapteeService=Provider.class, adapterProperties={@Property(name="foo2", value="bar2")})
+ public static class Provider2Impl implements Provider2
+ {
+ protected Provider m_adaptee;
+
+ @Start
+ Map<String, String> start()
+ {
+ return new HashMap<String, String>() {{ put("foo3", "bar3"); }};
+ }
+ }
+
+ @Service
+ public static class Consumer
+ {
+ @ServiceDependency(filter="(test=ExtraAdapterServiceProperties)")
+ Sequencer m_sequencer;
+
+ private Map m_properties;
+
+ @ServiceDependency
+ void bind(Map properties, Provider2 provider2)
+ {
+ m_properties = properties;
+ }
+
+ @Start
+ void start()
+ {
+ System.out.println("provider2 service properties: " + m_properties);
+ if ("bar".equals(m_properties.get("foo")))
+ {
+ m_sequencer.step(1);
+ }
+
+ if ("bar2".equals(m_properties.get("foo2")))
+ {
+ m_sequencer.step(2);
+ }
+
+ if ("bar3".equals(m_properties.get("foo3")))
+ {
+ m_sequencer.step(3);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraAspectServiceProperties.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraAspectServiceProperties.java
new file mode 100644
index 0000000..30ab097
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraAspectServiceProperties.java
@@ -0,0 +1,90 @@
+/*
+ * 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.test.bundle.annotation.extraproperties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.AspectService;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.Service;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+
+/**
+ * This test validates that an adapter Service may specify some extra service properties
+ * from it's start callback
+ */
+public class ExtraAspectServiceProperties
+{
+ public interface Provider
+ {
+ }
+
+ @Service(properties={@Property(name="foo", value="bar")})
+ public static class ProviderImpl implements Provider
+ {
+ }
+
+ @AspectService(ranking=10, properties={@Property(name="foo2", value="bar2")})
+ public static class ProviderAspectImpl implements Provider
+ {
+ @Start
+ Map<String, String> start()
+ {
+ return new HashMap<String, String>() {{ put("foo3", "aspect"); }};
+ }
+ }
+
+ @Service
+ public static class Consumer
+ {
+ @ServiceDependency(filter="(test=ExtraAspectServiceProperties)")
+ Sequencer m_sequencer;
+
+ private Map m_properties;
+
+ @ServiceDependency
+ void bind(Map properties, Provider provider)
+ {
+ m_properties = properties;
+ }
+
+ @Start
+ void start()
+ {
+ System.out.println("provider aspect service properties: " + m_properties);
+ if ("bar".equals(m_properties.get("foo")))
+ {
+ m_sequencer.step(1);
+ }
+
+ if ("bar2".equals(m_properties.get("foo2")))
+ {
+ m_sequencer.step(2);
+ }
+
+ if ("aspect".equals(m_properties.get("foo3")))
+ {
+ m_sequencer.step(3);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraFactoryServiceProperties.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraFactoryServiceProperties.java
new file mode 100644
index 0000000..3d9385f
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraFactoryServiceProperties.java
@@ -0,0 +1,96 @@
+/*
+ * 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.test.bundle.annotation.extraproperties;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.Service;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+
+public class ExtraFactoryServiceProperties
+{
+ public interface Provider
+ {
+ }
+
+ @Service(properties={@Property(name="foo", value="bar")}, factorySet="MyFactory")
+ public static class ProviderImpl implements Provider
+ {
+ @Start
+ Map<String, String> start()
+ {
+ return new HashMap<String, String>() {{ put("foo2", "bar2"); }};
+ }
+ }
+
+ @Service
+ public static class ProviderImplFactory
+ {
+ @ServiceDependency
+ Set<Dictionary> m_factory;
+
+ @Start
+ void start()
+ {
+ m_factory.add(new Hashtable() {{ put("foo3", "bar3"); }});
+ }
+ }
+
+ @Service
+ public static class Consumer
+ {
+ @ServiceDependency(filter="(test=ExtraFactoryServiceProperties)")
+ Sequencer m_sequencer;
+
+ private Map m_properties;
+
+ @ServiceDependency
+ void bindProvider(Map properties, Provider m_provider)
+ {
+ m_properties = properties;
+ }
+
+ @Start
+ void start()
+ {
+ System.out.println("provider service properties: " + m_properties);
+ if ("bar".equals(m_properties.get("foo")))
+ {
+ m_sequencer.step(1);
+ }
+
+ if ("bar2".equals(m_properties.get("foo2")))
+ {
+ m_sequencer.step(2);
+ }
+
+ if ("bar3".equals(m_properties.get("foo3")))
+ {
+ m_sequencer.step(3);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraServiceProperties.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraServiceProperties.java
new file mode 100644
index 0000000..d7172e6
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/extraproperties/ExtraServiceProperties.java
@@ -0,0 +1,79 @@
+/*
+ * 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.test.bundle.annotation.extraproperties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.dm.annotation.api.Service;
+import org.apache.felix.dm.annotation.api.Property;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+
+/**
+ * This test validates that a basic Service may specify some extra service properties
+ * from it's start callback
+ */
+public class ExtraServiceProperties
+{
+ public interface Provider
+ {
+ }
+
+ @Service(properties={@Property(name="foo", value="bar")})
+ public static class ProviderImpl implements Provider
+ {
+ @Start
+ Map<String, String> start()
+ {
+ return new HashMap<String, String>() {{ put("foo2", "bar2"); }};
+ }
+ }
+
+ @Service
+ public static class Consumer
+ {
+ @ServiceDependency(filter="(test=ExtraServiceProperties)")
+ Sequencer m_sequencer;
+
+ private Map m_properties;
+
+ @ServiceDependency
+ void bindProvider(Map properties, Provider m_provider)
+ {
+ m_properties = properties;
+ }
+
+ @Start
+ void start()
+ {
+ System.out.println("provider service properties: " + m_properties);
+ if ("bar".equals(m_properties.get("foo")))
+ {
+ m_sequencer.step(1);
+ }
+
+ if ("bar2".equals(m_properties.get("foo2")))
+ {
+ m_sequencer.step(2);
+ }
+ }
+ }
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/FactoryServiceTestWthPublisher.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/FactoryServiceTestWthPublisher.java
new file mode 100644
index 0000000..fd82fa4
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/FactoryServiceTestWthPublisher.java
@@ -0,0 +1,109 @@
+/*
+ * 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.test.bundle.annotation.publisher;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Service;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+
+/**
+ * This test validate that a basic "ProviderImpl" which is instantiated from a FactorySet can register/unregister its
+ * service using the Publisher annotation.
+ */
+public class FactoryServiceTestWthPublisher
+{
+ @Service
+ public static class Consumer
+ {
+ @ServiceDependency(filter="(test=testFactoryService)")
+ Sequencer m_sequencer;
+
+ @ServiceDependency(required=false, removed = "unbind")
+ void bind(Provider provider)
+ {
+ m_sequencer.step(1);
+ }
+
+ void unbind(Provider provider)
+ {
+ m_sequencer.step(2);
+ }
+
+ @Destroy
+ void destroy()
+ {
+ m_sequencer.step(3);
+ }
+ }
+
+ @Service(publisher="m_publisher", unpublisher="m_unpublisher", factorySet="MyFactory")
+ public static class ProviderImpl implements Provider
+ {
+ Runnable m_publisher; // injected and used to register our service
+ Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter="(test=testFactoryService)")
+ Sequencer m_sequencer;
+
+ @Start
+ void start()
+ {
+ // register service in 1 second
+ schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ schedule(m_unpublisher, 2000);
+ }
+
+ private void schedule(final Runnable task, final long n)
+ {
+ Thread t = new Thread() {
+ public void run()
+ {
+ try
+ {
+ sleep(n);
+ }
+ catch (InterruptedException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ task.run();
+ }
+ };
+ t.start();
+ }
+ }
+
+ @Service
+ public static class ProviderImplFactory
+ {
+ @ServiceDependency(filter="(dm.factory.name=MyFactory)")
+ void bind(Set<Dictionary> m_providerImplFactory)
+ {
+ m_providerImplFactory.add(new Hashtable() {{ put("foo", "bar"); }});
+ }
+ }
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/Provider.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/Provider.java
new file mode 100644
index 0000000..8606b80
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/Provider.java
@@ -0,0 +1,23 @@
+/*
+ * 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.test.bundle.annotation.publisher;
+
+public interface Provider
+{
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/ServiceTestWthPublisher.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/ServiceTestWthPublisher.java
new file mode 100644
index 0000000..7ed0731
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/publisher/ServiceTestWthPublisher.java
@@ -0,0 +1,95 @@
+/*
+ * 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.test.bundle.annotation.publisher;
+
+import org.apache.felix.dm.annotation.api.Destroy;
+import org.apache.felix.dm.annotation.api.Service;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+
+/**
+ * This test validate that a basic "ProviderImpl" services can register/unregister its
+ * service using the Publisher annotation.
+ */
+public class ServiceTestWthPublisher
+{
+ @Service
+ public static class Consumer
+ {
+ @ServiceDependency(filter="(test=testService)")
+ Sequencer m_sequencer;
+
+ @ServiceDependency(required=false, removed = "unbind")
+ void bind(Provider provider)
+ {
+ m_sequencer.step(1);
+ }
+
+ void unbind(Provider provider)
+ {
+ m_sequencer.step(2);
+ }
+
+ @Destroy
+ void destroy()
+ {
+ m_sequencer.step(3);
+ }
+ }
+
+ @Service(publisher="m_publisher", unpublisher="m_unpublisher")
+ public static class ProviderImpl implements Provider
+ {
+ Runnable m_publisher; // injected and used to register our service
+ Runnable m_unpublisher; // injected and used to unregister our service
+
+ @ServiceDependency(filter="(test=testService)")
+ Sequencer m_sequencer;
+
+ @Start
+ void start()
+ {
+ // register service in 1 second
+ schedule(m_publisher, 1000);
+ // unregister the service in 2 seconds
+ schedule(m_unpublisher, 2000);
+ }
+
+ private void schedule(final Runnable task, final long n)
+ {
+ Thread t = new Thread() {
+ public void run()
+ {
+ try
+ {
+ sleep(n);
+ }
+ catch (InterruptedException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ task.run();
+ }
+ };
+ t.start();
+ }
+ }
+}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/ExtraServicePropertiesTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/ExtraServicePropertiesTest.java
new file mode 100644
index 0000000..16ab1e6
--- /dev/null
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/ExtraServicePropertiesTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.test.annotation;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.test.BundleGenerator;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Use case: Verify the a Service may provide its service properties dynamically from its start method.
+ */
+@RunWith(JUnit4TestRunner.class)
+public class ExtraServicePropertiesTest extends AnnotationBase
+{
+ @Configuration
+ public static Option[] configuration()
+ {
+ return options(
+ systemProperty(DMLOG_PROPERTY).value( "true" ),
+ provision(
+ mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.1.0"),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject(),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager.runtime").versionAsInProject()),
+ provision(
+ new BundleGenerator()
+ .set(Constants.BUNDLE_SYMBOLICNAME, "ExtraPropertiesTest")
+ .set("Export-Package", "org.apache.felix.dm.test.bundle.annotation.sequencer")
+ .set("Private-Package", "org.apache.felix.dm.test.bundle.annotation.extraproperties")
+ .set("Import-Package", "*")
+ .set("-plugin", "org.apache.felix.dm.annotation.plugin.bnd.AnnotationPlugin")
+ .build()));
+ }
+
+ /**
+ * Tests if a Service can provide its service properties from its start method.
+ */
+ @Test
+ public void testExtraServiceProperties(BundleContext context)
+ {
+ DependencyManager m = new DependencyManager(context);
+ // Provide the Sequencer service to the "Component" service.
+ m.add(m.createService().setImplementation(this).setInterface(Sequencer.class.getName(),
+ new Hashtable() {{ put("test", "ExtraServiceProperties"); }}));
+ m_ensure.waitForStep(2, 10000);
+ }
+
+ /**
+ * Tests if a Service instantiated by a Factory can provide its service properties from its start method.
+ */
+ @Test
+ public void testExtraFactoryServiceProperties(BundleContext context)
+ {
+ DependencyManager m = new DependencyManager(context);
+ // Provide the Sequencer service to the "Component" service.
+ m.add(m.createService().setImplementation(this).setInterface(Sequencer.class.getName(),
+ new Hashtable() {{ put("test", "ExtraFactoryServiceProperties"); }}));
+ m_ensure.waitForStep(3, 10000);
+ }
+
+ /**
+ * Tests if an AdapterService can provide its service properties from its start method.
+ */
+ @Test
+ public void testExtraAdapterServiceProperties(BundleContext context)
+ {
+ DependencyManager m = new DependencyManager(context);
+ // Provide the Sequencer service to the "Component" service.
+ m.add(m.createService().setImplementation(this).setInterface(Sequencer.class.getName(),
+ new Hashtable() {{ put("test", "ExtraAdapterServiceProperties"); }}));
+ m_ensure.waitForStep(3, 10000);
+ }
+
+ /**
+ * Tests if an AspectService can provide its service properties from its start method.
+ */
+ @Test
+ public void testExtraAspectServiceProperties(BundleContext context)
+ {
+ DependencyManager m = new DependencyManager(context);
+ // Provide the Sequencer service to the "Component" service.
+ m.add(m.createService().setImplementation(this).setInterface(Sequencer.class.getName(),
+ new Hashtable() {{ put("test", "ExtraAspectServiceProperties"); }}));
+ m_ensure.waitForStep(3, 10000);
+ }
+}
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/PublisherAnnotationTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/PublisherAnnotationTest.java
new file mode 100644
index 0000000..f1d5c7c
--- /dev/null
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/PublisherAnnotationTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.test.annotation;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+import java.util.Hashtable;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.test.BundleGenerator;
+import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Use case: Verify the Publisher annotation, which allows a component to register/unregister
+ * its service programatically.
+ */
+@RunWith(JUnit4TestRunner.class)
+public class PublisherAnnotationTest extends AnnotationBase
+{
+ @Configuration
+ public static Option[] configuration()
+ {
+ return options(
+ systemProperty(DMLOG_PROPERTY).value( "true" ),
+ provision(
+ mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.1.0"),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject(),
+ mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager.runtime").versionAsInProject()),
+ provision(
+ new BundleGenerator()
+ .set(Constants.BUNDLE_SYMBOLICNAME, "PublisherAnnotationsTest")
+ .set("Export-Package", "org.apache.felix.dm.test.bundle.annotation.sequencer")
+ .set("Private-Package", "org.apache.felix.dm.test.bundle.annotation.publisher")
+ .set("Import-Package", "*")
+ .set("-plugin", "org.apache.felix.dm.annotation.plugin.bnd.AnnotationPlugin")
+ .build()));
+ }
+
+ /**
+ * A Provider that just registers/unregisters its service, using the Publisher annotation.
+ */
+ @Test
+ public void testServiceWithPublisher(BundleContext context)
+ {
+ DependencyManager m = new DependencyManager(context);
+ // Provide the Sequencer service to the "Component" service.
+ m.add(m.createService().setImplementation(this).setInterface(Sequencer.class.getName(),
+ new Hashtable() {{ put("test", "testService"); }}));
+ // Check if the Provider has seen the Provider.
+ m_ensure.waitForStep(2, 10000);
+ // Stop the bundle
+ stopBundle("PublisherAnnotationsTest", context);
+ // And check if the Consumer has been destroyed.
+ m_ensure.waitForStep(3, 10000);
+ }
+
+ /**
+ * A Provider instantiated from a FactorySet, and which registers/unregisters its service, using the Publisher annotation.
+ */
+ @Test
+ public void testFactoryServiceWithPublisher(BundleContext context)
+ {
+ DependencyManager m = new DependencyManager(context);
+ // Provide the Sequencer service to the "Component" service.
+ m.add(m.createService().setImplementation(this).setInterface(Sequencer.class.getName(),
+ new Hashtable() {{ put("test", "testFactoryService"); }}));
+ // Check if the Provider has seen the Provider.
+ m_ensure.waitForStep(2, 10000);
+ // Stop the bundle
+ stopBundle("PublisherAnnotationsTest", context);
+ // And check if the Consumer has been destroyed.
+ m_ensure.waitForStep(3, 10000);
+ }
+}