blob: a5f79d9c4678ef985e304f664b6fc64b7f8f5f21 [file] [log] [blame]
/*
* 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.annotation.api;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a method or a field for injecting a Service Dependency. When applied on a class
* field, optional unavailable dependencies are injected with a NullObject.
*
* <p> For "add", "change", "remove" callbacks, the following method signatures are supported:
*
* <pre>{@code
* (Component comp, ServiceReference ref, Service service)
* (Component comp, ServiceReference ref, Object service)
* (Component comp, ServiceReference ref)
* (Component comp, Service service)
* (Component comp, Object service)
* (Component comp)
* (Component comp, Map properties, Service service)
* (ServiceReference ref, Service service)
* (ServiceReference ref, Object service)
* (ServiceReference ref)
* (Service service)
* (Service service, Map properties)
* (Map properties, Service, service)
* (Service service, Dictionary properties)
* (Dictionary properties, Service service)
* (Object service)
* (ServiceReference<T> service)
* (ServiceObjects<T> service)
* }</pre>
*
* <p> For "swap" callbacks, the following method signatures are supported:
*
* <pre>{@code
* (Service old, Service replace)
* (Object old, Object replace)
* (ServiceReference old, Service old, ServiceReference replace, Service replace)
* (ServiceReference old, Object old, ServiceReference replace, Object replace)
* (Component comp, Service old, Service replace)
* (Component comp, Object old, Object replace)
* (Component comp, ServiceReference old, Service old, ServiceReference replace, Service replace)
* (Component comp, ServiceReference old, Object old, ServiceReference replace, Object replace)
* (ServiceReference old, ServiceReference replace)
* (Component comp, ServiceReference old, ServiceReference replace)
* (ServiceObjects old, ServiceObjects replace)
* (Component comp, ServiceObjects old, ServiceObjects replace)
* }</pre>
*
* <p> When the dependency is injected on a class field, the following field types are supported:
*
* <ul>
* <li> a field having the same type as the dependency. If the field may be accessed by any thread, then the field should be declared volatile, in order to ensure visibility
* when the field is auto injected concurrently.
* <li> a field which is assignable to an {@literal Iterable<T>} where T must match the dependency type. In this case, an Iterable will be injected by DependencyManager before the start
* callback is called. The Iterable field may then be traversed to inspect the currently available dependency services. The Iterable can possibly be set to a final value
* so you can choose the Iterable implementation of your choice (for example, a CopyOnWrite ArrayList, or a ConcurrentLinkedQueue).
* <li> a {@literal Map<K,V>} where K must match the dependency type and V must exactly equals Dictionary class. In this case, a ConcurrentHashMap will be injected by DependencyManager
* before the start callback is called. The Map may then be consulted to lookup current available dependency services, including the dependency service properties
* (the map key holds the dependency services, and the map value holds the dependency service properties). The Map field may be set to a final value so you can choose a Map of your choice (Typically a ConcurrentHashMap). A ConcurrentHashMap is "weakly consistent", meaning that when traversing the elements, you may or may not see any concurrent updates made on the map. So, take care to traverse the map using an iterator on the map entry set, which allows to atomically lookup pairs of Dependency service/Service properties.
* </ul>
*
* <h3>Usage Examples</h3>
* Here, the MyComponent component is injected with a dependency over a "MyDependency" service
*
* <blockquote><pre>
* &#64;Component
* class MyComponent {
* &#64;ServiceDependency
* volatile MyDependency dependency;
* }
* </pre></blockquote>
*
* The dependency can also be injected using a method callback:
*
* <blockquote><pre>
* &#64;Component
* class MyComponent {
* &#64;ServiceDependency
* void bind(MyDependency dependency) {}
* }
* </pre></blockquote>
*
* Same example as before, but the callback signatures includes service properties:
*
* <blockquote><pre>
* &#64;Component
* class MyComponent {
* &#64;ServiceDependency
* void bind(MyDependency dependency, Map&lt;String, Object&gt; serviceProperties) {}
* }
* </pre></blockquote>
*
* Another example, were we inject multiple dependencies to an Iterable field
*
* <blockquote><pre>
* &#64;Component
* class MyComponent {
* &#64;ServiceDependency
* final Iterable&lt;Dependency&gt; dependencies = new CopyOnWriteArrayList&lt;&gt;();
* }
* </pre></blockquote>
*
* Another example, were we inject multiple dependencies to a Map field, allowing to inspect service dependency properties
* (the keys hold the services, and the values hold the associated service properties):
*
* <blockquote><pre>
* &#64;Component
* class MyComponent {
* &#64;ServiceDependency
* final Map&lt;MyDependency, Dictionary&lt;String, Object&gt;&gt; dependencies = new ConcurrentHashMap&lt;&gt;;
* }
* </pre></blockquote>
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface ServiceDependency
{
/**
* Marker interface used to match any service types. When you set the {@link ServiceDependency#service} attribute to this class,
* it means that the dependency will return any services (matching the {@link ServiceDependency#filter()} attribute if it is specified).
*/
public interface Any { }
/**
* The type if the service this dependency is applying on. By default, the method parameter
* (or the class field) is used as the type. If you want to match all available services, you can set
* this attribute to the {@link Any} class. In this case, all services (matching the {@link ServiceDependency#filter()} attribute if it is specified) will
* be returned.
* @return the service dependency
*/
Class<?> service() default Object.class;
/**
* The Service dependency OSGi filter.
* @return the service filter
*/
String filter() default "";
/**
* Sets the default implementation for an <code>optional</code> service dependency. You can use this to supply
* your own implementation that will be used instead of a Null Object when the dependency is
* not available. This is also convenient if the service dependency is not an interface
* (which would cause the Null Object creation to fail) but a class.
* Only use this attribute on an optional service dependency applied on a class field.
*
* @return the default implementation class
*/
Class<?> defaultImpl() default Object.class;
/**
* Whether the Service dependency is required or not. When the dependency is optional and if the annotation is declared on a class field, then
* a Null Object will be injected in case the dependency is unavailable.
* @return the required flag
*/
boolean required() default true;
/**
* The callback method to be invoked when the service is available. This attribute is only meaningful when
* the annotation is applied on a class field.
*
* @return the add callback
*/
String added() default "";
/**
* The callback method to be invoked when the service properties have changed.
* @return the change callback
*/
String changed() default "";
/**
* The callback method to invoke when the service is lost.
* @return the remove callback
*/
String removed() default "";
/**
* the method to call when the service was swapped due to addition or removal of an aspect
* @return the swap callback
*/
String swap() default "";
/**
* The max time in millis to wait for the dependency availability.
* Specifying a positive number allow to block the caller thread between service updates. Only
* useful for required stateless dependencies that can be replaced transparently.
* A Dynamic Proxy is used to wrap the actual service dependency (which must be an interface).
* When the dependency goes away, an attempt is made to replace it with another one which satisfies
* the service dependency criteria. If no service replacement is available, then any method invocation
* (through the dynamic proxy) will block during a configurable timeout. On timeout, an unchecked
* <code>IllegalStateException</code> exception is raised (but the service is not deactivated).<p>
* Notice that the changed/removed callbacks are not used when the timeout parameter is greater than -1.
*
* -1 means no timeout at all (default). 0 means that invocation on a missing service will fail
* immediately. A positive number represents the max timeout in millis to wait for the service availability.
*
* Sample Code:
* <blockquote><pre>
* &#64;Component
* class MyServer implements Runnable {
* &#64;ServiceDependency(timeout=15000)
* MyDependency dependency;.
*
* &#64;Start
* void start() {
* (new Thread(this)).start();
* }
*
* public void run() {
* try {
* dependency.doWork();
* } catch (IllegalStateException e) {
* t.printStackTrace();
* }
* }
* </pre></blockquote>
* @return the wait time when the dependency is unavailable
*/
long timeout() default -1;
/**
* 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 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> See {@link Init} annotation for an example usage of a dependency dynamically configured from the init method.
* @return the dependency name used to dynamically configure the filter and required flag from the init callback.
*/
String name() default "";
/**
* Returns true if the dependency service properties must be published along with the service.
* Any additional service properties specified directly are merged with these.
* The component provided service properties take precedence over the propagated service dependency properties, meaning
* that a give component service property overrides the same property included in the propagated service dependency properties.
* @return true if dependency service properties must be published along with the service, false if not.
*/
boolean propagate() default false;
}