diff options
author | Thomas Watson | 2016-12-07 16:02:27 +0000 |
---|---|---|
committer | Thomas Watson | 2017-01-11 16:08:16 +0000 |
commit | 86ac868ae86d1ff21292919d47a6dbf46075d3a8 (patch) | |
tree | 54e19a5e50b418afc9c8ecd4d21d775c257ffee6 | |
parent | dd171a7ac305c0b5df769731037a76bf645d797e (diff) | |
download | rt.equinox.framework-86ac868ae86d1ff21292919d47a6dbf46075d3a8.tar.gz rt.equinox.framework-86ac868ae86d1ff21292919d47a6dbf46075d3a8.tar.xz rt.equinox.framework-86ac868ae86d1ff21292919d47a6dbf46075d3a8.zip |
Bug 501950 - [ds] replace Equinox DS implementation with Felix SCR (DS)I20170112-0245I20170112-0200I20170111-2000
implementation
Update the DS API for R6
Change-Id: I49f46a8474a4b434ad20fe54f2ad8f0f4d649e99
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
49 files changed, 3295 insertions, 200 deletions
diff --git a/bundles/org.eclipse.osgi.services/.settings/.api_filters b/bundles/org.eclipse.osgi.services/.settings/.api_filters new file mode 100644 index 000000000..ab34f1b57 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/.settings/.api_filters @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.osgi.services" version="2">
+ <resource path="META-INF/MANIFEST.MF">
+ <filter id="923795461">
+ <message_arguments>
+ <message_argument value="3.6.0"/>
+ <message_argument value="3.5.100"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="META-INF/MANIFEST.MF" type="org.osgi.service.component.annotations.Component">
+ <filter id="286375965">
+ <message_arguments>
+ <message_argument value="org.osgi.service.component.annotations.Component"/>
+ <message_argument value="reference()"/>
+ </message_arguments>
+ </filter>
+ <filter id="288469092">
+ <message_arguments>
+ <message_argument value="org.osgi.service.component.annotations.Component"/>
+ <message_argument value="configurationPid()"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/osgi/service/component/ComponentConstants.java" type="org.osgi.service.component.ComponentConstants">
+ <filter id="403767336">
+ <message_arguments>
+ <message_argument value="org.osgi.service.component.ComponentConstants"/>
+ <message_argument value="COMPONENT_CAPABILITY_NAME"/>
+ </message_arguments>
+ </filter>
+ <filter id="1209008130">
+ <message_arguments>
+ <message_argument value="1.3"/>
+ <message_argument value="3.6"/>
+ <message_argument value="COMPONENT_CAPABILITY_NAME"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/osgi/service/component/ComponentServiceObjects.java" type="org.osgi.service.component.ComponentServiceObjects">
+ <filter id="1108344834">
+ <message_arguments>
+ <message_argument value="1.3"/>
+ <message_argument value="3.6"/>
+ <message_argument value="org.osgi.service.component.ComponentServiceObjects"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/bundles/org.eclipse.osgi.services/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.services/META-INF/MANIFEST.MF index b71f95ab0..6527349a5 100644 --- a/bundles/org.eclipse.osgi.services/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.osgi.services/META-INF/MANIFEST.MF @@ -2,36 +2,40 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %osgiServices Bundle-SymbolicName: org.eclipse.osgi.services -Bundle-Version: 3.5.100.qualifier +Bundle-Version: 3.6.0.qualifier Bundle-Description: %osgiServicesDes Bundle-Localization: plugin Bundle-Vendor: %eclipse.org Bundle-DocUrl: http://www.eclipse.org Bundle-ContactAddress: www.eclipse.org -Export-Package: org.osgi.service.cm; version="1.5", - org.osgi.service.component; version="1.2.2", - org.osgi.service.component.annotations; version="1.2", - org.osgi.service.device; version="1.1", - org.osgi.service.event; version="1.3.1", - org.osgi.service.http;version="1.2.1", - org.osgi.service.http.context;version="1.0", - org.osgi.service.http.runtime;version="1.0", - org.osgi.service.http.runtime.dto;version="1.0", +Export-Package: org.osgi.service.cm;version="1.5";uses:="org.osgi.framework", + org.osgi.service.component;version="1.3";uses:="org.osgi.framework", + org.osgi.service.component.annotations;version="1.3", + org.osgi.service.component.runtime;version="1.3";uses:="org.osgi.framework,org.osgi.util.promise,org.osgi.service.component.runtime.dto", + org.osgi.service.component.runtime.dto;version="1.3";uses:="org.osgi.dto,org.osgi.framework.dto", + org.osgi.service.device;version="1.1";uses:="org.osgi.framework", + org.osgi.service.event;version="1.3.1";uses:="org.osgi.framework", + org.osgi.service.http;version="1.2.1";uses:="javax.servlet,javax.servlet.http", + org.osgi.service.http.context;version="1.0";uses:="org.osgi.framework,javax.servlet.http", + org.osgi.service.http.runtime;version="1.0";uses:="org.osgi.service.http.runtime.dto", + org.osgi.service.http.runtime.dto;version="1.0";uses:="org.osgi.dto,org.osgi.framework.dto", org.osgi.service.http.whiteboard;version="1.0", - org.osgi.service.log; version="1.3", - org.osgi.service.metatype; version="1.3", - org.osgi.service.provisioning; version="1.2", - org.osgi.service.upnp; version="1.2", - org.osgi.service.useradmin; version="1.1", - org.osgi.service.wireadmin; version="1.0.1" + org.osgi.service.log;version="1.3";uses:="org.osgi.framework", + org.osgi.service.metatype;version="1.3";uses:="org.osgi.framework", + org.osgi.service.provisioning;version="1.2", + org.osgi.service.upnp;version="1.2", + org.osgi.service.useradmin;version="1.1";uses:="org.osgi.framework", + org.osgi.service.wireadmin;version="1.0.1";uses:="org.osgi.framework" Import-Package: javax.servlet;resolution:=optional, javax.servlet.http;resolution:=optional, org.osgi.dto;version="1.0", org.osgi.framework;version="1.6", org.osgi.framework.dto;version="1.8.0", org.osgi.service.cm;version="[1.5,1.6)", - org.osgi.service.component;version="[1.2,1.3)", - org.osgi.service.component.annotations;version="[1.2,1.3)", + org.osgi.service.component;version="[1.3,1.4)", + org.osgi.service.component.annotations;version="[1.3,1.4)", + org.osgi.service.component.runtime; version="[1.3,1.4)", + org.osgi.service.component.runtime.dto; version="[1.3,1.4)", org.osgi.service.device;version="[1.1,1.2)", org.osgi.service.event;version="[1.3,1.4)", org.osgi.service.http;version="[1.2,1.3)", @@ -40,7 +44,9 @@ Import-Package: javax.servlet;resolution:=optional, org.osgi.service.provisioning;version="[1.2,1.3)", org.osgi.service.upnp;version="[1.2,1.3)", org.osgi.service.useradmin;version="[1.1,1.2)", - org.osgi.service.wireadmin;version="[1.0,1.1)" + org.osgi.service.wireadmin;version="[1.0,1.1)", + org.osgi.util.function;version="[1.0,2.0)", + org.osgi.util.promise;version="[1.0,2.0)" DynamicImport-Package: javax.servlet, javax.servlet.http Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/bundles/org.eclipse.osgi.services/pom.xml b/bundles/org.eclipse.osgi.services/pom.xml index bead54ccb..167274b43 100644 --- a/bundles/org.eclipse.osgi.services/pom.xml +++ b/bundles/org.eclipse.osgi.services/pom.xml @@ -19,7 +19,7 @@ </parent> <groupId>org.eclipse.osgi</groupId> <artifactId>org.eclipse.osgi.services</artifactId> - <version>3.5.100-SNAPSHOT</version> + <version>3.6.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> <build> <plugins> diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentConstants.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentConstants.java index ac753054f..624eb5610 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentConstants.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2004, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2004, 2015). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,11 +47,11 @@ public interface ComponentConstants { * configuration. The value of this property must be of type {@code Long}. * * <p> - * The value of this property is assigned by the Service Component Runtime - * when a component configuration is created. The Service Component Runtime - * assigns a unique value that is larger than all previously assigned values - * since the Service Component Runtime was started. These values are NOT - * persistent across restarts of the Service Component Runtime. + * The value of this property is assigned by Service Component Runtime when + * a component configuration is created. Service Component Runtime assigns a + * unique value that is larger than all previously assigned values since + * Service Component Runtime was started. These values are NOT persistent + * across restarts of Service Component Runtime. */ public final static String COMPONENT_ID = "component.id"; @@ -123,4 +123,20 @@ public interface ComponentConstants { * @since 1.1 */ public static final int DEACTIVATION_REASON_BUNDLE_STOPPED = 6; + + /** + * Capability name for Service Component Runtime. + * + * <p> + * Used in {@code Provide-Capability} and {@code Require-Capability} + * manifest headers with the {@code osgi.extender} namespace. For example: + * + * <pre> + * Require-Capability: osgi.extender; + * filter:="(&(osgi.extender=osgi.component)(version>=1.3)(!(version>=2.0)))" + * </pre> + * + * @since 1.3 + */ + public static final String COMPONENT_CAPABILITY_NAME = "osgi.component"; } diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentContext.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentContext.java index 9855c2af8..549969184 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentContext.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2004, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2004, 2014). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,26 +28,8 @@ import org.osgi.framework.ServiceReference; * component instance has a unique Component Context. * * <p> - * A component instance may have an activate method. If a component instance has - * a suitable and accessible activate method, this method will be called when a - * component configuration is activated. If the activate method takes a - * {@code ComponentContext} argument, it will be passed the component instance's - * Component Context object. If the activate method takes a - * {@code BundleContext} argument, it will be passed the component instance's - * Bundle Context object. If the activate method takes a {@code Map} argument, - * it will be passed an unmodifiable Map containing the component properties. - * - * <p> - * A component instance may have a deactivate method. If a component instance - * has a suitable and accessible deactivate method, this method will be called - * when the component configuration is deactivated. If the deactivate method - * takes a {@code ComponentContext} argument, it will be passed the component - * instance's Component Context object. If the deactivate method takes a - * {@code BundleContext} argument, it will be passed the component instance's - * Bundle Context object. If the deactivate method takes a {@code Map} argument, - * it will be passed an unmodifiable Map containing the component properties. If - * the deactivate method takes an {@code int} or {@code Integer} argument, it - * will be passed the reason code for the component instance's deactivation. + * A component instance may obtain its Component Context object through its + * activate, modified, and deactivate methods. * * @ThreadSafe * @author $Id$ @@ -70,7 +52,7 @@ public interface ComponentContext { * multiple services are bound to the reference, the service with the * highest ranking (as specified in its {@code Constants.SERVICE_RANKING} * property) is returned. If there is a tie in ranking, the service with the - * lowest service ID (as specified in its {@code Constants.SERVICE_ID} + * lowest service id (as specified in its {@code Constants.SERVICE_ID} * property); that is, the service that was registered first is returned. * * @param name The name of a reference as specified in a {@code reference} @@ -78,7 +60,7 @@ public interface ComponentContext { * @return A service object for the referenced service or {@code null} if * the reference cardinality is {@code 0..1} or {@code 0..n} and no * bound service is available. - * @throws ComponentException If the Service Component Runtime catches an + * @throws ComponentException If Service Component Runtime catches an * exception while activating the bound service. */ public Object locateService(String name); @@ -87,6 +69,7 @@ public interface ComponentContext { * Returns the service object for the specified reference name and * {@code ServiceReference}. * + * @param <S> Type of Service. * @param name The name of a reference as specified in a {@code reference} * element in this component's description. * @param reference The {@code ServiceReference} to a bound service. This @@ -95,10 +78,10 @@ public interface ComponentContext { * @return A service object for the referenced service or {@code null} if * the specified {@code ServiceReference} is not a bound service for * the specified reference name. - * @throws ComponentException If the Service Component Runtime catches an + * @throws ComponentException If Service Component Runtime catches an * exception while activating the bound service. */ - public Object locateService(String name, ServiceReference<?> reference); + public <S> S locateService(String name, ServiceReference<S> reference); /** * Returns the service objects for the specified reference name. @@ -110,7 +93,7 @@ public interface ComponentContext { * {@code 0..n} and no bound service is available. If the reference * cardinality is {@code 0..1} or {@code 1..1} and a bound service * is available, the array will have exactly one element. - * @throws ComponentException If the Service Component Runtime catches an + * @throws ComponentException If Service Component Runtime catches an * exception while activating a bound service. */ public Object[] locateServices(String name); @@ -126,17 +109,18 @@ public interface ComponentContext { /** * If the component instance is registered as a service using the - * {@code servicefactory="true"} attribute, then this method returns the - * bundle using the service provided by the component instance. + * {@code servicescope="bundle"} or {@code servicescope="prototype"} + * attribute, then this method returns the bundle using the service provided + * by the component instance. * <p> * This method will return {@code null} if: * <ul> * <li>The component instance is not a service, then no bundle can be using * it as a service.</li> * <li>The component instance is a service but did not specify the - * {@code servicefactory="true"} attribute, then all bundles using the - * service provided by the component instance will share the same component - * instance.</li> + * {@code servicescope="bundle"} or {@code servicescope="prototype"} + * attribute, then all bundles using the service provided by the component + * instance will share the same component instance.</li> * <li>The service provided by the component instance is not currently being * used by any bundle.</li> * </ul> @@ -158,6 +142,12 @@ public interface ComponentContext { * Enables the specified component name. The specified component name must * be in the same bundle as this component. * + * <p> + * This method must return after changing the enabled state of the specified + * component name. Any actions that result from this, such as activating or + * deactivating a component configuration, must occur asynchronously to this + * method call. + * * @param name The name of a component or {@code null} to indicate all * components in the bundle. */ @@ -167,6 +157,12 @@ public interface ComponentContext { * Disables the specified component name. The specified component name must * be in the same bundle as this component. * + * <p> + * This method must return after changing the enabled state of the specified + * component name. Any actions that result from this, such as activating or + * deactivating a component configuration, must occur asynchronously to this + * method call. + * * @param name The name of a component. */ public void disableComponent(String name); diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentException.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentException.java index 9ec6a42f2..6b4777460 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentException.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2004, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2004, 2014). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package org.osgi.service.component; /** - * Unchecked exception which may be thrown by the Service Component Runtime. + * Unchecked exception which may be thrown by Service Component Runtime. * * @author $Id$ */ diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentFactory.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentFactory.java index efb1e1695..a6422fc51 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentFactory.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2004, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2004, 2014). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import org.osgi.annotation.versioning.ProviderType; /** * When a component is declared with the {@code factory} attribute on its - * {@code component} element, the Service Component Runtime will register a + * {@code component} element, Service Component Runtime will register a * Component Factory service to allow new component configurations to be created * and activated rather than automatically creating and activating component * configuration as necessary. @@ -42,7 +42,7 @@ public interface ComponentFactory { * configuration has been activated and, if the component specifies * a {@code service} element, the component instance has been * registered as a service. - * @throws ComponentException If the Service Component Runtime is unable to + * @throws ComponentException If Service Component Runtime is unable to * activate the component configuration. */ public ComponentInstance newInstance(Dictionary<String, ?> properties); diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentServiceObjects.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentServiceObjects.java new file mode 100644 index 000000000..49fc5e863 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/ComponentServiceObjects.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) OSGi Alliance (2012, 2014). All Rights Reserved. + * + * Licensed 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.osgi.service.component; + +import org.osgi.annotation.versioning.ProviderType; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceObjects; +import org.osgi.framework.ServiceReference; + +/** + * Allows multiple service objects for a service to be obtained. + * + * <p> + * A component instance can receive a {@code ComponentServiceObjects} object via + * a reference that is typed {@code ComponentServiceObjects}. + * + * <p> + * For services with {@link Constants#SCOPE_PROTOTYPE prototype} scope, multiple + * service objects for the service can be obtained. For services with + * {@link Constants#SCOPE_SINGLETON singleton} or {@link Constants#SCOPE_BUNDLE + * bundle} scope, only one, use-counted service object is available. + * + * <p> + * Any unreleased service objects obtained from this + * {@code ComponentServiceObjects} object are automatically released by Service + * Component Runtime when the service becomes unbound. + * + * @param <S> Type of Service + * @ThreadSafe + * @since 1.3 + * @see ServiceObjects + * @author $Id$ + */ +@ProviderType +public interface ComponentServiceObjects<S> { + /** + * Returns a service object for the {@link #getServiceReference() + * associated} service. + * + * <p> + * This method will always return {@code null} when the associated service + * has been become unbound. + * + * @return A service object for the associated service or {@code null} if + * the service is unbound, the customized service object returned by + * a {@code ServiceFactory} does not implement the classes under + * which it was registered or the {@code ServiceFactory} threw an + * exception. + * @throws IllegalStateException If the associated service has been become + * unbound. + * @see #ungetService(Object) + */ + public S getService(); + + /** + * Releases a service object for the {@link #getServiceReference() + * associated} service. + * + * <p> + * The specified service object must no longer be used and all references to + * it should be destroyed after calling this method. + * + * @param service A service object previously provided by this + * {@code ComponentServiceObjects} object. + * @throws IllegalStateException If the associated service has been become + * unbound. + * @throws IllegalArgumentException If the specified service object was not + * provided by this {@code ComponentServiceObjects} object. + * @see #getService() + */ + public void ungetService(S service); + + /** + * Returns the {@link ServiceReference} for the service associated with this + * {@code ComponentServiceObjects} object. + * + * @return The {@link ServiceReference} for the service associated with this + * {@code ComponentServiceObjects} object. + */ + public ServiceReference<S> getServiceReference(); +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Activate.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Activate.java index e24a899f7..6e320f371 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Activate.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Activate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2011, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2011, 2014). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,9 +29,9 @@ import java.lang.annotation.Target; * The annotated method is the activate method of the Component. * * <p> - * This annotation is not processed at runtime by a Service Component Runtime - * implementation. It must be processed by tools and used to add a Component - * Description to the bundle. + * This annotation is not processed at runtime by Service Component Runtime. It + * must be processed by tools and used to add a Component Description to the + * bundle. * * @see "The activate attribute of the component element of a Component Description." * @author $Id$ diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Component.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Component.java index e7f072364..d44af93dc 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Component.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Component.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2011, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2011, 2015). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,9 +28,9 @@ import java.lang.annotation.Target; * The annotated class is the implementation class of the Component. * * <p> - * This annotation is not processed at runtime by a Service Component Runtime - * implementation. It must be processed by tools and used to add a Component - * Description to the bundle. + * This annotation is not processed at runtime by Service Component Runtime. It + * must be processed by tools and used to add a Component Description to the + * bundle. * * @see "The component element of a Component Description." * @author $Id$ @@ -82,11 +82,16 @@ public @interface Component { * component instance. * * <p> - * If {@code true}, this Component uses the OSGi ServiceFactory concept. If - * {@code false} or not specified, this Component does not use the OSGi - * ServiceFactory concept. + * This element is ignored when the {@link #scope()} element does not have + * the default value. If {@code true}, this Component uses + * {@link ServiceScope#BUNDLE bundle} service scope. If {@code false} or not + * specified, this Component uses {@link ServiceScope#SINGLETON singleton} + * service scope. If the {@link #factory()} element is specified or the + * {@link #immediate()} element is specified with {@code true}, this element + * can only be specified with {@code false}. * - * @see "The servicefactory attribute of the service element of a Component Description." + * @see "The scope attribute of the service element of a Component Description." + * @deprecated Since 1.3. Replaced by {@link #scope()}. */ boolean servicefactory() default false; @@ -95,8 +100,8 @@ public @interface Component { * is started. * * <p> - * If {@code true}, this Component is enabled. If {@code false} or not - * specified, this Component is disabled. + * If {@code true} or not specified, this Component is enabled. If + * {@code false}, this Component is disabled. * * @see "The enabled attribute of the component element of a Component Description." */ @@ -110,13 +115,13 @@ public @interface Component { * If {@code true}, this Component must be immediately activated upon * becoming satisfied. If {@code false}, activation of this Component is * delayed. If this property is specified, its value must be {@code false} - * if the {@link #factory} property is also specified or must be - * {@code true} if the {@link #service} property is specified with an empty - * value. + * if the {@link #factory()} property is also specified or must be + * {@code true} if the {@link #service()} property is specified with an + * empty value. * * <p> - * If not specified, the default is {@code false} if the {@link #factory} - * property is specified or the {@link #service} property is not specified + * If not specified, the default is {@code false} if the {@link #factory()} + * property is specified or the {@link #service()} property is not specified * or specified with a non-empty value and {@code true} otherwise. * * @see "The immediate attribute of the component element of a Component Description." @@ -127,13 +132,14 @@ public @interface Component { * Properties for this Component. * * <p> - * Each property string is specified as {@code "key=value"}. The type of the - * property value can be specified in the key as {@code key:type=value}. The - * type must be one of the property types supported by the type attribute of - * the property element of a Component Description. + * Each property string is specified as {@code "name=value"}. The type of + * the property value can be specified in the name as + * {@code name:type=value}. The type must be one of the property types + * supported by the type attribute of the property element of a Component + * Description. * * <p> - * To specify a property with multiple values, use multiple key, value + * To specify a property with multiple values, use multiple name, value * pairs. For example, {@code "foo=bar", "foo=baz"}. * * @see "The property element of a Component Description." @@ -174,8 +180,18 @@ public @interface Component { * Configuration object where the PID equals the name of the component. * * <p> - * If not specified, the {@link ConfigurationPolicy#OPTIONAL OPTIONAL} - * configuration policy is used. + * If not specified, the configuration policy is based upon whether the + * component is also annotated with the Meta Type + * {@link org.osgi.service.metatype.annotations.Designate Designate} + * annotation. + * <ul> + * <li>Not annotated with {@code Designate} - The configuration policy is + * {@link ConfigurationPolicy#OPTIONAL OPTIONAL}.</li> + * <li>Annotated with {@code Designate(factory=false)} - The configuration + * policy is {@link ConfigurationPolicy#OPTIONAL OPTIONAL}.</li> + * <li>Annotated with {@code Designate(factory=true)} - The configuration + * policy is {@link ConfigurationPolicy#REQUIRE REQUIRE}.</li> + * </ul> * * @see "The configuration-policy attribute of the component element of a Component Description." * @since 1.1 @@ -183,18 +199,82 @@ public @interface Component { ConfigurationPolicy configurationPolicy() default ConfigurationPolicy.OPTIONAL; /** - * The configuration PID for the configuration of this Component. + * The configuration PIDs for the configuration of this Component. * * <p> - * Allows the configuration PID for this Component to be different than the - * name of this Component. + * Each value specifies a configuration PID for this Component. * * <p> - * If not specified, the name of this Component is used as the configuration - * PID of this Component. + * If no value is specified, the name of this Component is used as the + * configuration PID of this Component. + * + * <p> + * A special string (<code>{@value #NAME}</code>) can be used to specify the + * name of the component as a configuration PID. The {@code NAME} constant + * holds this special string. For example: + * + * <pre> + * @Component(configurationPid={"com.acme.system", Component.NAME}) + * </pre> + * + * Tools creating a Component Description from this annotation must replace + * the special string with the actual name of this Component. * * @see "The configuration-pid attribute of the component element of a Component Description." * @since 1.2 */ - String configurationPid() default ""; + String[] configurationPid() default NAME; + + /** + * Special string representing the name of this Component. + * + * <p> + * This string can be used in {@link #configurationPid()} to specify the + * name of the component as a configuration PID. For example: + * + * <pre> + * @Component(configurationPid={"com.acme.system", Component.NAME}) + * </pre> + * + * Tools creating a Component Description from this annotation must replace + * the special string with the actual name of this Component. + * + * @since 1.3 + */ + String NAME = "$"; + + /** + * The service scope for the service of this Component. + * + * <p> + * If not specified (and the deprecated {@link #servicefactory()} element is + * not specified), the {@link ServiceScope#SINGLETON singleton} service + * scope is used. If the {@link #factory()} element is specified or the + * {@link #immediate()} element is specified with {@code true}, this element + * can only be specified with the {@link ServiceScope#SINGLETON singleton} + * service scope. + * + * @see "The scope attribute of the service element of a Component Description." + * @since 1.3 + */ + ServiceScope scope() default ServiceScope.DEFAULT; + + /** + * The lookup strategy references of this Component. + * + * <p> + * To access references using the lookup strategy, {@link Reference} + * annotations are specified naming the reference and declaring the type of + * the referenced service. The referenced service can be accessed using one + * of the {@code locateService} methods of {@code ComponentContext}. + * + * <p> + * To access references using the event strategy, bind methods are annotated + * with {@link Reference}. To access references using the field strategy, + * fields are annotated with {@link Reference}. + * + * @see "The reference element of a Component Description." + * @since 1.3 + */ + Reference[] reference() default {}; } diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Deactivate.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Deactivate.java index 1809c70eb..78bb6b4fe 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Deactivate.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Deactivate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2011, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2011, 2014). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,9 +29,9 @@ import java.lang.annotation.Target; * The annotated method is the deactivate method of the Component. * * <p> - * This annotation is not processed at runtime by a Service Component Runtime - * implementation. It must be processed by tools and used to add a Component - * Description to the bundle. + * This annotation is not processed at runtime by Service Component Runtime. It + * must be processed by tools and used to add a Component Description to the + * bundle. * * @see "The deactivate attribute of the component element of a Component Description." * @author $Id$ diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/FieldOption.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/FieldOption.java new file mode 100644 index 000000000..35bfd6c67 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/FieldOption.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) OSGi Alliance (2014, 2015). All Rights Reserved. + * + * Licensed 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.osgi.service.component.annotations; + +/** + * Field options for the {@link Reference} annotation. + * + * @since 1.3 + * @author $Id$ + */ +public enum FieldOption { + + /** + * The update field option is used to update the collection referenced by + * the field when there are changes to the bound services. + * + * <p> + * This field option can only be used when the field reference has dynamic + * policy and multiple cardinality. + */ + UPDATE("update"), + + /** + * The replace field option is used to replace the field value with a new + * value when there are changes to the bound services. + */ + REPLACE("replace"); + + private final String value; + + FieldOption(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Modified.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Modified.java index fd9f97d2f..c4d0c68f2 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Modified.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Modified.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2011, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2011, 2014). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,9 +29,9 @@ import java.lang.annotation.Target; * The annotated method is the modified method of the Component. * * <p> - * This annotation is not processed at runtime by a Service Component Runtime - * implementation. It must be processed by tools and used to add a Component - * Description to the bundle. + * This annotation is not processed at runtime by Service Component Runtime. It + * must be processed by tools and used to add a Component Description to the + * bundle. * * @see "The modified attribute of the component element of a Component Description." * @author $Id$ diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Reference.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Reference.java index 9887c51c7..b13bbef27 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Reference.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/Reference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) OSGi Alliance (2011, 2013). All Rights Reserved. + * Copyright (c) OSGi Alliance (2011, 2014). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,16 +22,17 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Identify the annotated method as a {@code bind} method of a Service - * Component. + * Identify the annotated member as a reference of a Service Component. * * <p> - * The annotated method is a bind method of the Component. + * When the annotation is applied to a method, the method is the bind method of + * the reference. When the annotation is applied to a field, the field will + * contain the bound service(s) of the reference. * * <p> - * This annotation is not processed at runtime by a Service Component Runtime - * implementation. It must be processed by tools and used to add a Component - * Description to the bundle. + * This annotation is not processed at runtime by Service Component Runtime. It + * must be processed by tools and used to add a Component Description to the + * bundle. * * <p> * In the generated Component Description for a component, the references must @@ -42,111 +43,273 @@ import java.lang.annotation.Target; * @author $Id$ */ @Retention(RetentionPolicy.CLASS) -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.FIELD}) public @interface Reference { /** * The name of this reference. * * <p> - * If not specified, the name of this reference is based upon the name of - * the method being annotated. If the method name begins with {@code bind}, - * {@code set} or {@code add}, that is removed. + * The name of this reference must be specified when using this annotation + * in the {@link Component#reference()} element since there is no annotated + * member from which the name can be determined. + * + * If not specified, the name of this reference is based upon how this + * annotation is used: + * <ul> + * <li>Annotated method - If the method name begins with {@code bind}, + * {@code set} or {@code add}, that prefix is removed to create the name of + * the reference. Otherwise, the name of the reference is the method name.</li> + * <li>Annotated field - The name of the reference is the field name.</li> + * </ul> * * @see "The name attribute of the reference element of a Component Description." */ String name() default ""; /** - * The type of the service to bind to this reference. + * The type of the service for this reference. + * + * <p> + * The type of the service for this reference must be specified when using + * this annotation in the {@link Component#reference()} element since there + * is no annotated member from which the type of the service can be + * determined. * * <p> - * If not specified, the type of the service to bind is based upon the type - * of the first argument of the method being annotated. + * If not specified, the type of the service for this reference is based + * upon how this annotation is used: + * <ul> + * <li>Annotated method - The type of the service is the type of the first + * argument of the method.</li> + * <li>Annotated field - The type of the service is based upon the type of + * the field being annotated and the cardinality of the reference. If the + * cardinality is either {@link ReferenceCardinality#MULTIPLE 0..n}, or + * {@link ReferenceCardinality#AT_LEAST_ONE 1..n}, the type of the field + * must be one of {@code java.util.Collection}, {@code java.util.List}, or a + * subtype of {@code java.util.Collection} so the type of the service is the + * generic type of the collection. Otherwise, the type of the service is the + * type of the field.</li> + * </ul> * * @see "The interface attribute of the reference element of a Component Description." */ Class<?> service() default Object.class; /** - * The cardinality of the reference. + * The cardinality of this reference. * * <p> - * If not specified, the reference has a - * {@link ReferenceCardinality#MANDATORY 1..1} cardinality. + * If not specified, the cardinality of this reference is based upon how + * this annotation is used: + * <ul> + * <li>Annotated method - The cardinality is + * {@link ReferenceCardinality#MANDATORY 1..1}.</li> + * <li>Annotated field - The cardinality is based on the type of the field. + * If the type is either {@code java.util.Collection}, + * {@code java.util.List}, or a subtype of {@code java.util.Collection}, the + * cardinality is {@link ReferenceCardinality#MULTIPLE 0..n}. Otherwise the + * cardinality is {@link ReferenceCardinality#MANDATORY 1..1}.</li> + * <li>{@link Component#reference()} element - The cardinality is + * {@link ReferenceCardinality#MANDATORY 1..1}.</li> + * </ul> * * @see "The cardinality attribute of the reference element of a Component Description." */ ReferenceCardinality cardinality() default ReferenceCardinality.MANDATORY; /** - * The policy for the reference. + * The policy for this reference. * * <p> - * If not specified, the {@link ReferencePolicy#STATIC STATIC} reference - * policy is used. + * If not specified, the policy of this reference is based upon how this + * annotation is used: + * <ul> + * <li>Annotated method - The policy is {@link ReferencePolicy#STATIC + * STATIC}.</li> + * <li>Annotated field - The policy is based on the modifiers of the field. + * If the field is declared {@code volatile}, the policy is + * {@link ReferencePolicy#DYNAMIC}. Otherwise the policy is + * {@link ReferencePolicy#STATIC STATIC}.</li> + * <li>{@link Component#reference()} element - The policy is + * {@link ReferencePolicy#STATIC STATIC}.</li> + * </ul> * * @see "The policy attribute of the reference element of a Component Description." */ ReferencePolicy policy() default ReferencePolicy.STATIC; /** - * The target filter for the reference. + * The target property for this reference. + * + * <p> + * If not specified, no target property is set. * * @see "The target attribute of the reference element of a Component Description." */ String target() default ""; /** - * The name of the unbind method which is associated with the annotated bind - * method. + * The policy option for this reference. * * <p> - * To declare no unbind method, the value {@code "-"} must be used. + * If not specified, the {@link ReferencePolicyOption#RELUCTANT RELUCTANT} + * reference policy option is used. + * + * @see "The policy-option attribute of the reference element of a Component Description." + * @since 1.2 + */ + ReferencePolicyOption policyOption() default ReferencePolicyOption.RELUCTANT; + + /** + * The reference scope for this reference. * * <p> - * If not specified, the name of the unbind method is derived from the name - * of the annotated bind method. If the annotated method name begins with - * {@code bind}, {@code set} or {@code add}, that is replaced with - * {@code unbind}, {@code unset} or {@code remove}, respectively, to derive - * the unbind method name. Otherwise, {@code un} is prefixed to the - * annotated method name to derive the unbind method name. The unbind method - * is only set if the component type contains a method with the derived - * name. + * If not specified, the {@link ReferenceScope#BUNDLE bundle} reference + * scope is used. * - * @see "The unbind attribute of the reference element of a Component Description." + * @see "The scope attribute of the reference element of a Component Description." + * @since 1.3 */ - String unbind() default ""; + ReferenceScope scope() default ReferenceScope.BUNDLE; + + /* Method injection elements */ /** - * The policy option for the reference. + * The name of the bind method for this reference. * * <p> - * If not specified, the {@link ReferencePolicyOption#RELUCTANT RELUCTANT} - * reference policy option is used. + * If specified and this reference annotates a method, the specified name + * must match the name of the annotated method. * - * @see "The policy-option attribute of the reference element of a Component Description." - * @since 1.2 + * <p> + * If not specified, the name of the bind method is based upon how this + * annotation is used: + * <ul> + * <li>Annotated method - The name of the annotated method is the name of + * the bind method.</li> + * <li>Annotated field - There is no bind method name.</li> + * <li>{@link Component#reference()} element - There is no bind method name. + * </li> + * </ul> + * + * <p> + * If there is a bind method name, the component must contain a method with + * that name. + * + * @see "The bind attribute of the reference element of a Component Description." + * @since 1.3 */ - ReferencePolicyOption policyOption() default ReferencePolicyOption.RELUCTANT; + String bind() default ""; /** - * The name of the updated method which is associated with the annotated - * bind method. + * The name of the updated method for this reference. * * <p> - * To declare no updated method, the value {@code "-"} must be used. + * If not specified, the name of the updated method is based upon how this + * annotation is used: + * <ul> + * <li>Annotated method - The name of the updated method is created from the + * name of the annotated method. If the name of the annotated method begins + * with {@code bind}, {@code set} or {@code add}, that prefix is replaced + * with {@code updated} to create the name candidate for the updated method. + * Otherwise, {@code updated} is prefixed to the name of the annotated + * method to create the name candidate for the updated method. If the + * component type contains a method with the candidate name, the candidate + * name is used as the name of the updated method. To declare no updated + * method when the component type contains a method with the candidate name, + * the value {@code "-"} must be used.</li> + * <li>Annotated field - There is no updated method name.</li> + * <li>{@link Component#reference()} element - There is no updated method + * name.</li> + * </ul> * * <p> - * If not specified, the name of the updated method is derived from the name - * of the annotated bind method. If the annotated method name begins with - * {@code bind}, {@code set} or {@code add}, that is replaced with - * {@code updated} to derive the updated method name. Otherwise, - * {@code updated} is prefixed to the annotated method name to derive the - * updated method name. The updated method is only set if the component type - * contains a method with the derived name. + * If there is an updated method name, the component must contain a method + * with that name. * * @see "The updated attribute of the reference element of a Component Description." * @since 1.2 */ String updated() default ""; + + /** + * The name of the unbind method for this reference. + * + * <p> + * If not specified, the name of the unbind method is based upon how this + * annotation is used: + * <ul> + * <li>Annotated method - The name of the unbind method is created from the + * name of the annotated method. If the name of the annotated method begins + * with {@code bind}, {@code set} or {@code add}, that prefix is replaced + * with {@code unbind}, {@code unset} or {@code remove}, respectively, to + * create the name candidate for the unbind method. Otherwise, {@code un} is + * prefixed to the name of the annotated method to create the name candidate + * for the unbind method. If the component type contains a method with the + * candidate name, the candidate name is used as the name of the unbind + * method. To declare no unbind method when the component type contains a + * method with the candidate name, the value {@code "-"} must be used.</li> + * <li>Annotated field - There is no unbind method name.</li> + * <li>{@link Component#reference()} element - There is no unbind method + * name.</li> + * </ul> + * + * <p> + * If there is an unbind method name, the component must contain a method + * with that name. + * + * @see "The unbind attribute of the reference element of a Component Description." + */ + String unbind() default ""; + + /* Field injection elements */ + + /** + * The name of the field for this reference. + * + * <p> + * If specified and this reference annotates a field, the specified name + * must match the name of the annotated field. + * + * <p> + * If not specified, the name of the field is based upon how this annotation + * is used: + * <ul> + * <li>Annotated method - There is no field name.</li> + * <li>Annotated field - The name of the annotated field is the name of the + * field.</li> + * <li>{@link Component#reference()} element - There is no field name.</li> + * </ul> + * + * <p> + * If there is a field name, the component must contain a field with that + * name. + * + * @see "The field attribute of the reference element of a Component Description." + * @since 1.3 + */ + String field() default ""; + + /** + * The field option for this reference. + * + * <p> + * If not specified, the field option is based upon how this annotation is + * used: + * <ul> + * <li>Annotated method - There is no field option.</li> + * <li>Annotated field - The field option is based upon the policy and + * cardinality of the reference and the modifiers of the field. If the + * policy is {@link ReferencePolicy#DYNAMIC}, the cardinality is + * {@link ReferenceCardinality#MULTIPLE 0..n} or + * {@link ReferenceCardinality#AT_LEAST_ONE 1..n}, and the field is declared + * {@code final}, the field option is {@link FieldOption#UPDATE}. Otherwise, + * the field option is {@link FieldOption#REPLACE}</li> + * <li>{@link Component#reference()} element - There is no field option.</li> + * </ul> + * + * @see "The field-option attribute of the reference element of a Component Description." + * @since 1.3 + */ + FieldOption fieldOption() default FieldOption.REPLACE; } diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/ReferenceScope.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/ReferenceScope.java new file mode 100644 index 000000000..2a61ef265 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/ReferenceScope.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) OSGi Alliance (2013, 2014). All Rights Reserved. + * + * Licensed 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.osgi.service.component.annotations; + +/** + * Reference scope for the {@link Reference} annotation. + * + * @author $Id$ + * @since 1.3 + */ +public enum ReferenceScope { + /** + * A single service object is used for all references to the service in this + * bundle. + */ + BUNDLE("bundle"), + + /** + * If the bound service has prototype service scope, then each instance of + * the component with this reference can receive a unique instance of the + * service. If the bound service does not have prototype service scope, then + * this reference scope behaves the same as {@link #BUNDLE}. + */ + PROTOTYPE("prototype"), + + /** + * Bound services must have prototype service scope. Each instance of the + * component with this reference can receive a unique instance of the + * service. + */ + PROTOTYPE_REQUIRED("prototype_required"); + + private final String value; + + ReferenceScope(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/ServiceScope.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/ServiceScope.java new file mode 100644 index 000000000..3c43d5fea --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/ServiceScope.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) OSGi Alliance (2011, 2014). All Rights Reserved. + * + * Licensed 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.osgi.service.component.annotations; + +/** + * Service scope for the {@link Component} annotation. + * + * @author $Id$ + * @since 1.3 + */ +public enum ServiceScope { + /** + * When the component is registered as a service, it must be registered as a + * bundle scope service but only a single instance of the component must be + * used for all bundles using the service. + */ + SINGLETON("singleton"), + + /** + * When the component is registered as a service, it must be registered as a + * bundle scope service and an instance of the component must be created for + * each bundle using the service. + */ + BUNDLE("bundle"), + + /** + * When the component is registered as a service, it must be registered as a + * prototype scope service and an instance of the component must be created + * for each distinct request for the service. + */ + PROTOTYPE("prototype"), + + /** + * Default element value for annotation. This is used to distinguish the + * default value for an element and should not otherwise be used. + */ + DEFAULT("<<default>>"); + + private final String value; + + ServiceScope(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/package-info.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/package-info.java index dbfa48a1b..bcb93c6d8 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/package-info.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/package-info.java @@ -15,7 +15,7 @@ */ /** - * Service Component Annotations Package Version 1.2. + * Service Component Annotations Package Version 1.3. * * <p> * This package is not used at runtime. Annotated classes are processed by @@ -24,7 +24,7 @@ * @author $Id$ */ -@Version("1.2") +@Version("1.3") package org.osgi.service.component.annotations; import org.osgi.annotation.versioning.Version; diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/packageinfo b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/packageinfo index ef7df68cb..0117a56c1 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/packageinfo +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/annotations/packageinfo @@ -1 +1 @@ -version 1.2 +version 1.3 diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/package-info.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/package-info.java index 4b472f45b..9c158e8fb 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/package-info.java +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/package-info.java @@ -15,7 +15,7 @@ */ /** - * Service Component Package Version 1.2. + * Service Component Package Version 1.3. * * <p> * Bundles wishing to use this package must list the package in the @@ -26,16 +26,16 @@ * <p> * Example import for consumers using the API in this package: * <p> - * {@code Import-Package: org.osgi.service.component; version="[1.2,2.0)"} + * {@code Import-Package: org.osgi.service.component; version="[1.3,2.0)"} * <p> * Example import for providers implementing the API in this package: * <p> - * {@code Import-Package: org.osgi.service.component; version="[1.2,1.3)"} + * {@code Import-Package: org.osgi.service.component; version="[1.3,1.4)"} * * @author $Id$ */ -@Version("1.2.2") +@Version("1.3") package org.osgi.service.component; import org.osgi.annotation.versioning.Version; diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/packageinfo b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/packageinfo index 19727573b..0117a56c1 100644 --- a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/packageinfo +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/packageinfo @@ -1 +1 @@ -version 1.2.2 +version 1.3 diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/ServiceComponentRuntime.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/ServiceComponentRuntime.java new file mode 100644 index 000000000..59760dcd5 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/ServiceComponentRuntime.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) OSGi Alliance (2013, 2014). All Rights Reserved. + * + * Licensed 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.osgi.service.component.runtime; + +import java.util.Collection; +import org.osgi.annotation.versioning.ProviderType; +import org.osgi.framework.Bundle; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO; +import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO; +import org.osgi.util.promise.Promise; + +/** + * The {@code ServiceComponentRuntime} service represents the Declarative + * Services actor, known as Service Component Runtime (SCR), that manages the + * service components and their life cycle. The {@code ServiceComponentRuntime} + * service allows introspection of the components managed by Service Component + * Runtime. + * + * <p> + * This service differentiates between a {@link ComponentDescriptionDTO} and a + * {@link ComponentConfigurationDTO}. A {@link ComponentDescriptionDTO} is a + * representation of a declared component description. A + * {@link ComponentConfigurationDTO} is a representation of an actual instance + * of a declared component description parameterized by component properties. + * <p> + * + * Access to this service requires the + * {@code ServicePermission[ServiceComponentRuntime, GET]} permission. It is + * intended that only administrative bundles should be granted this permission + * to limit access to the potentially intrusive methods provided by this + * service. + * + * @ThreadSafe + * @since 1.3 + * @author $Id$ + */ +@ProviderType +public interface ServiceComponentRuntime { + + /** + * Returns the component descriptions declared by the specified active + * bundles. + * + * <p> + * Only component descriptions from active bundles are returned. If the + * specified bundles have no declared components or are not active, an empty + * collection is returned. + * + * @param bundles The bundles whose declared component descriptions are to + * be returned. Specifying no bundles, or the equivalent of an empty + * {@code Bundle} array, will return the declared component + * descriptions from all active bundles. + * @return The declared component descriptions of the specified active + * {@code bundles}. An empty collection is returned if there are no + * component descriptions for the specified active bundles. + */ + Collection<ComponentDescriptionDTO> getComponentDescriptionDTOs(Bundle... bundles); + + /** + * Returns the {@link ComponentDescriptionDTO} declared with the specified name + * by the specified bundle. + * + * <p> + * Only component descriptions from active bundles are returned. + * {@code null} if no such component is declared by the given {@code bundle} + * or the bundle is not active. + * + * @param bundle The bundle declaring the component description. Must not be + * {@code null}. + * @param name The name of the component description. Must not be + * {@code null}. + * @return The declared component description or {@code null} if the + * specified bundle is not active or does not declare a component + * description with the specified name. + */ + ComponentDescriptionDTO getComponentDescriptionDTO(Bundle bundle, String name); + + /** + * Returns the component configurations for the specified component + * description. + * + * @param description The component description. Must not be {@code null}. + * @return A collection containing a snapshot of the current component + * configurations for the specified component description. An empty + * collection is returned if there are none. + */ + Collection<ComponentConfigurationDTO> getComponentConfigurationDTOs(ComponentDescriptionDTO description); + + /** + * Returns whether the specified component description is currently enabled. + * + * <p> + * The enabled state of a component description is initially set by the + * {@link ComponentDescriptionDTO#defaultEnabled enabled} attribute of the + * component description. + * + * @param description The component description. Must not be {@code null}. + * @return {@code true} if the specified component description is currently + * enabled. Otherwise, {@code false}. + * @see #enableComponent(ComponentDescriptionDTO) + * @see #disableComponent(ComponentDescriptionDTO) + * @see ComponentContext#disableComponent(String) + * @see ComponentContext#enableComponent(String) + */ + boolean isComponentEnabled(ComponentDescriptionDTO description); + + /** + * Enables the specified component description. + * + * <p> + * If the specified component description is currently enabled, this method + * has no effect. + * + * <p> + * This method must return after changing the enabled state of the specified + * component description. Any actions that result from this, such as + * activating or deactivating a component configuration, must occur + * asynchronously to this method call. + * + * @param description The component description to enable. Must not be + * {@code null}. + * @return A promise that will be resolved when the actions that result from + * changing the enabled state of the specified component have + * completed. + * @see #isComponentEnabled(ComponentDescriptionDTO) + */ + Promise<Void> enableComponent(ComponentDescriptionDTO description); + + /** + * Disables the specified component description. + * + * <p> + * If the specified component description is currently disabled, this method + * has no effect. + * + * <p> + * This method must return after changing the enabled state of the specified + * component description. Any actions that result from this, such as + * activating or deactivating a component configuration, must occur + * asynchronously to this method call. + * + * @param description The component description to disable. Must not be + * {@code null}. + * @return A promise that will be resolved when the actions that result from + * changing the enabled state of the specified component have + * completed. + * @see #isComponentEnabled(ComponentDescriptionDTO) + */ + Promise<Void> disableComponent(ComponentDescriptionDTO description); +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ComponentConfigurationDTO.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ComponentConfigurationDTO.java new file mode 100644 index 000000000..a3e98bbac --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ComponentConfigurationDTO.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) OSGi Alliance (2013, 2014). All Rights Reserved. + * + * Licensed 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.osgi.service.component.runtime.dto; + +import java.util.Map; +import org.osgi.dto.DTO; +import org.osgi.service.component.ComponentContext; + +/** + * A representation of an actual instance of a declared component description + * parameterized by component properties. + * + * @since 1.3 + * @NotThreadSafe + * @author $Id$ + */ +public class ComponentConfigurationDTO extends DTO { + /** + * The component configuration is unsatisfied due to a missing required + * configuration. + */ + public static final int UNSATISFIED_CONFIGURATION = 1; + + /** + * The component configuration is unsatisfied due to an unsatisfied + * reference. + */ + public static final int UNSATISFIED_REFERENCE = 2; + + /** + * The component configuration is satisfied. + * + * <p> + * Any {@link ComponentDescriptionDTO#serviceInterfaces services} declared + * by the component description are registered. + */ + public static final int SATISFIED = 4; + + /** + * The component configuration is active. + * + * <p> + * This is the normal operational state of a component configuration. + */ + public static final int ACTIVE = 8; + + /** + * The representation of the component configuration's component + * description. + */ + public ComponentDescriptionDTO description; + + /** + * The current state of the component configuration. + * + * <p> + * This is one of {@link #UNSATISFIED_CONFIGURATION}, + * {@link #UNSATISFIED_REFERENCE}, {@link #SATISFIED} or {@link #ACTIVE}. + */ + public int state; + + /** + * The id of the component configuration. + * + * <p> + * The id is a non-persistent, unique value assigned at runtime. The id is + * also available as the {@code component.id} component property. The value + * of this field is unspecified if the state of this component configuration + * is unsatisfied. + */ + public long id; + + /** + * The component properties for the component configuration. + * + * @see ComponentContext#getProperties() + */ + public Map<String, Object> properties; + + /** + * The satisfied references. + * + * <p> + * Each {@link SatisfiedReferenceDTO} in the array represents a satisfied + * reference of the component configuration. The array must be empty if the + * component configuration has no satisfied references. + */ + public SatisfiedReferenceDTO[] satisfiedReferences; + + /** + * The unsatisfied references. + * + * <p> + * Each {@link UnsatisfiedReferenceDTO} in the array represents an + * unsatisfied reference of the component configuration. The array must be + * empty if the component configuration has no unsatisfied references. + */ + public UnsatisfiedReferenceDTO[] unsatisfiedReferences; +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ComponentDescriptionDTO.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ComponentDescriptionDTO.java new file mode 100644 index 000000000..9f098a6ed --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ComponentDescriptionDTO.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) OSGi Alliance (2013, 2014). All Rights Reserved. + * + * Licensed 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.osgi.service.component.runtime.dto; + +import java.util.Map; +import org.osgi.dto.DTO; +import org.osgi.framework.dto.BundleDTO; + +/** + * A representation of a declared component description. + * + * @since 1.3 + * @NotThreadSafe + * @author $Id$ + */ +public class ComponentDescriptionDTO extends DTO { + /** + * The name of the component. + * + * <p> + * This is declared in the {@code name} attribute of the {@code component} + * element. This must be the default name if the component description does + * not declare a name. + */ + public String name; + + /** + * The bundle declaring the component description. + */ + public BundleDTO bundle; + + /** + * The component factory name. + * + * <p> + * This is declared in the {@code factory} attribute of the + * {@code component} element. This must be {@code null} if the component + * description is not declared as a component factory. + */ + public String factory; + + /** + * The service scope. + * + * <p> + * This is declared in the {@code scope} attribute of the {@code service} + * element. This must be {@code null} if the component description does not + * declare any service interfaces. + */ + public String scope; + + /** + * The fully qualified name of the implementation class. + * + * <p> + * This is declared in the {@code class} attribute of the + * {@code implementation} element. + */ + public String implementationClass; + + /** + * The initial enabled state. + * + * <p> + * This is declared in the {@code enabled} attribute of the + * {@code component} element. + */ + public boolean defaultEnabled; + + /** + * The immediate state. + * + * <p> + * This is declared in the {@code immediate} attribute of the + * {@code component} element. + */ + public boolean immediate; + + /** + * The fully qualified names of the service interfaces. + * + * <p> + * These are declared in the {@code interface} attribute of the + * {@code provide} elements. The array must be empty if the component + * description does not declare any service interfaces. + */ + public String[] serviceInterfaces; + + /** + * The declared component properties. + * + * <p> + * These are declared in the {@code property} and {@code properties} + * elements. + */ + public Map<String, Object> properties; + + /** + * The referenced services. + * + * <p> + * These are declared in the {@code reference} elements. The array must be + * empty if the component description does not declare references to any + * services. + */ + public ReferenceDTO[] references; + + /** + * The name of the activate method. + * + * <p> + * This is declared in the {@code activate} attribute of the + * {@code component} element. This must be {@code null} if the component + * description does not declare an activate method name. + */ + public String activate; + + /** + * The name of the deactivate method. + * + * <p> + * This is declared in the {@code deactivate} attribute of the + * {@code component} element. This must be {@code null} if the component + * description does not declare a deactivate method name. + */ + public String deactivate; + + /** + * The name of the modified method. + * + * <p> + * This is declared in the {@code modified} attribute of the + * {@code component} element. This must be {@code null} if the component + * description does not declare a modified method name. + */ + public String modified; + + /** + * The configuration policy. + * + * <p> + * This is declared in the {@code configuration-policy} attribute of the + * {@code component} element. This must be the default configuration policy + * if the component description does not declare a configuration policy. + */ + public String configurationPolicy; + + /** + * The configuration pids. + * + * <p> + * These are declared in the {@code configuration-pid} attribute of the + * {@code component} element. This must contain the default configuration + * pid if the component description does not declare a configuration pid. + */ + public String[] configurationPid; +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ReferenceDTO.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ReferenceDTO.java new file mode 100644 index 000000000..fca241327 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/ReferenceDTO.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) OSGi Alliance (2013, 2015). All Rights Reserved. + * + * Licensed 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.osgi.service.component.runtime.dto; + +import org.osgi.dto.DTO; + +/** + * A representation of a declared reference to a service. + * + * @since 1.3 + * @NotThreadSafe + * @author $Id$ + */ +public class ReferenceDTO extends DTO { + + /** + * The name of the reference. + * + * <p> + * This is declared in the {@code name} attribute of the {@code reference} + * element. This must be the default name if the component description does + * not declare a name for the reference. + */ + public String name; + + /** + * The service interface of the reference. + * + * <p> + * This is declared in the {@code interface} attribute of the + * {@code reference} element. + */ + public String interfaceName; + + /** + * The cardinality of the reference. + * + * <p> + * This is declared in the {@code cardinality} attribute of the + * {@code reference} element. This must be the default cardinality if the + * component description does not declare a cardinality for the reference. + */ + public String cardinality; + + /** + * The policy of the reference. + * + * <p> + * This is declared in the {@code policy} attribute of the {@code reference} + * element. This must be the default policy if the component description + * does not declare a policy for the reference. + */ + public String policy; + + /** + * The policy option of the reference. + * + * <p> + * This is declared in the {@code policy-option} attribute of the + * {@code reference} element. This must be the default policy option if the + * component description does not declare a policy option for the reference. + */ + public String policyOption; + + /** + * The target of the reference. + * + * <p> + * This is declared in the {@code target} attribute of the {@code reference} + * element. This must be {@code null} if the component description does not + * declare a target for the reference. + */ + public String target; + + /** + * The name of the bind method of the reference. + * + * <p> + * This is declared in the {@code bind} attribute of the {@code reference} + * element. This must be {@code null} if the component description does not + * declare a bind method for the reference. + */ + public String bind; + + /** + * The name of the unbind method of the reference. + * + * <p> + * This is declared in the {@code unbind} attribute of the {@code reference} + * element. This must be {@code null} if the component description does not + * declare an unbind method for the reference. + */ + public String unbind; + + /** + * The name of the updated method of the reference. + * + * <p> + * This is declared in the {@code updated} attribute of the + * {@code reference} element. This must be {@code null} if the component + * description does not declare an updated method for the reference. + */ + public String updated; + + /** + * The name of the field of the reference. + * + * <p> + * This is declared in the {@code field} attribute of the {@code reference} + * element. This must be {@code null} if the component description does not + * declare a field for the reference. + */ + public String field; + + /** + * The field option of the reference. + * + * <p> + * This is declared in the {@code field-option} attribute of the + * {@code reference} element. This must be {@code null} if the component + * description does not declare a field for the reference. + */ + public String fieldOption; + + /** + * The scope of the reference. + * + * <p> + * This is declared in the {@code scope} attribute of the {@code reference} + * element. This must be the default scope if the component description does + * not declare a scope for the reference. + */ + public String scope; +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/SatisfiedReferenceDTO.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/SatisfiedReferenceDTO.java new file mode 100644 index 000000000..20de229ef --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/SatisfiedReferenceDTO.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) OSGi Alliance (2013, 2014). All Rights Reserved. + * + * Licensed 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.osgi.service.component.runtime.dto; + +import org.osgi.dto.DTO; +import org.osgi.framework.dto.ServiceReferenceDTO; + +/** + * A representation of a satisfied reference. + * + * @since 1.3 + * @NotThreadSafe + * @author $Id$ + */ +public class SatisfiedReferenceDTO extends DTO { + /** + * The name of the declared reference. + * + * <p> + * This is declared in the {@code name} attribute of the {@code reference} + * element of the component description. + * + * @see ReferenceDTO#name + */ + public String name; + + /** + * The target property of the satisfied reference. + * + * <p> + * This is the value of the {@link ComponentConfigurationDTO#properties + * component property} whose name is the concatenation of the + * {@link ReferenceDTO#name declared reference name} and + * ".target". This must be {@code null} if no target property is + * set for the reference. + */ + public String target; + + /** + * The bound services. + * + * <p> + * Each {@link ServiceReferenceDTO} in the array represents a service bound + * to the satisfied reference. The array must be empty if there are no bound + * services. + */ + public ServiceReferenceDTO[] boundServices; +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/UnsatisfiedReferenceDTO.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/UnsatisfiedReferenceDTO.java new file mode 100644 index 000000000..20ce77a3d --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/UnsatisfiedReferenceDTO.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) OSGi Alliance (2013, 2014). All Rights Reserved. + * + * Licensed 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.osgi.service.component.runtime.dto; + +import org.osgi.dto.DTO; +import org.osgi.framework.dto.ServiceReferenceDTO; + +/** + * A representation of an unsatisfied reference. + * + * @since 1.3 + * @NotThreadSafe + * @author $Id$ + */ +public class UnsatisfiedReferenceDTO extends DTO { + /** + * The name of the declared reference. + * + * <p> + * This is declared in the {@code name} attribute of the {@code reference} + * element of the component description. + * + * @see ReferenceDTO#name + */ + public String name; + + /** + * The target property of the unsatisfied reference. + * + * <p> + * This is the value of the {@link ComponentConfigurationDTO#properties + * component property} whose name is the concatenation of the + * {@link ReferenceDTO#name declared reference name} and + * ".target". This must be {@code null} if no target property is + * set for the reference. + */ + public String target; + + /** + * The target services. + * + * <p> + * Each {@link ServiceReferenceDTO} in the array represents a target service + * for the reference. The array must be empty if there are no target + * services. The upper bound on the number of target services in the array + * is the upper bound on the {@link ReferenceDTO#cardinality cardinality} of + * the reference. + */ + public ServiceReferenceDTO[] targetServices; +} diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/package-info.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/package-info.java new file mode 100644 index 000000000..d7d82da09 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/package-info.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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. + */ + +/** + * Service Component Runtime Data Transfer Objects Package Version 1.3. + * + * <p> + * Bundles wishing to use this package must list the package in the + * Import-Package header of the bundle's manifest. This package has two types of + * users: the consumers that use the API in this package and the providers that + * implement the API in this package. + * + * <p> + * Example import for consumers using the API in this package: + * <p> + * {@code Import-Package: org.osgi.service.component.runtime.dto; version="[1.3,2.0)"} + * <p> + * Example import for providers implementing the API in this package: + * <p> + * {@code Import-Package: org.osgi.service.component.runtime.dto; version="[1.3,1.4)"} + * + * @author $Id$ + */ + +@Version("1.3") +package org.osgi.service.component.runtime.dto; + +import org.osgi.annotation.versioning.Version; + diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/packageinfo b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/packageinfo new file mode 100644 index 000000000..0117a56c1 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/dto/packageinfo @@ -0,0 +1 @@ +version 1.3 diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/package-info.java b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/package-info.java new file mode 100644 index 000000000..3d4fa42ce --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/package-info.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) OSGi Alliance (2013). All Rights Reserved. + * + * Licensed 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. + */ + +/** + * Service Component Runtime Package Version 1.3. + * + * <p> + * Bundles wishing to use this package must list the package in the + * Import-Package header of the bundle's manifest. This package has two types of + * users: the consumers that use the API in this package and the providers that + * implement the API in this package. + * + * <p> + * Example import for consumers using the API in this package: + * <p> + * {@code Import-Package: org.osgi.service.component.runtime; version="[1.3,2.0)"} + * <p> + * Example import for providers implementing the API in this package: + * <p> + * {@code Import-Package: org.osgi.service.component.runtime; version="[1.3,1.4)"} + * + * @author $Id$ + */ + +@Version("1.3") +package org.osgi.service.component.runtime; + +import org.osgi.annotation.versioning.Version; + diff --git a/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/packageinfo b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/packageinfo new file mode 100644 index 000000000..0117a56c1 --- /dev/null +++ b/bundles/org.eclipse.osgi.services/src/org/osgi/service/component/runtime/packageinfo @@ -0,0 +1 @@ +version 1.3 diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/ApplicationAdminTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/ApplicationAdminTest.java index d5f681142..10e46111e 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/ApplicationAdminTest.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/appadmin/ApplicationAdminTest.java @@ -31,9 +31,10 @@ public class ApplicationAdminTest extends OSGiTest { public static final String MODIFIED = "modified"; //$NON-NLS-1$ public static final String REMOVED = "removed"; //$NON-NLS-1$ public static final String simpleResults = "test.simpleResults"; //$NON-NLS-1$ - public static final String[] tests = new String[] { - "testSimpleApp", "testInvalidArgs", "testAsyncValue01", "testAsyncValue02", "testAsyncValue03", "testAsyncValue04", "testAsyncValue05", "testAsyncValue06", "testExitValue01", "testExitValue02", "testExitValue03", "testExitValue04", "testExitValue05", "testExitValue06", "testExitValue07", "testExitValue08", "testExitValue09", "testExitValue10", "testGlobalSingleton", "testCardinality01", "testCardinality02", "testMainThreaded01", "testMainThreaded02", "testHandleEvents01", "testDescriptorEvents01", "testPersistentLock01", "testPersistentLock02", "testPersistentLock03", "testPersistentSchedule01", "testPersistentSchedule02", "testPersistentSchedule03", "testPersistentSchedule04", "testPersistentSchedule05", "testPersistentSchedule06", "testPersistentSchedule07", "testPersistentSchedule08", "testFailedApplication01", "testDestroyBeforeStart01", "testDestroyBeforeStart02"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ //$NON-NLS-13$ //$NON-NLS-14$ //$NON-NLS-15$ //$NON-NLS-16$ //$NON-NLS-17$ //$NON-NLS-18$ //$NON-NLS-19$ //$NON-NLS-20$ //$NON-NLS-21$ //$NON-NLS-22$ //$NON-NLS-23$ //$NON-NLS-24$ //$NON-NLS-25$ //$NON-NLS-26$ //$NON-NLS-27$ //$NON-NLS-28$ //$NON-NLS-29$ + public static final String[] tests = new String[] {"testSimpleApp", "testInvalidArgs", "testAsyncValue01", "testAsyncValue02", "testAsyncValue03", "testAsyncValue04", "testAsyncValue05", "testAsyncValue06", "testExitValue01", "testExitValue02", "testExitValue03", "testExitValue04", "testExitValue05", "testExitValue06", "testExitValue07", "testExitValue08", "testExitValue09", "testExitValue10", "testGlobalSingleton", "testCardinality01", "testCardinality02", "testMainThreaded01", "testMainThreaded02", "testHandleEvents01", "testDescriptorEvents01", "testPersistentLock01", "testPersistentLock02", "testPersistentLock03", "testPersistentSchedule01", "testPersistentSchedule02", "testPersistentSchedule03", "testPersistentSchedule04", "testPersistentSchedule05", "testPersistentSchedule06", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$//$NON-NLS-5$//$NON-NLS-6$//$NON-NLS-7$//$NON-NLS-8$//$NON-NLS-9$//$NON-NLS-10$//$NON-NLS-11$//$NON-NLS-12$//$NON-NLS-13$//$NON-NLS-14$//$NON-NLS-15$//$NON-NLS-16$//$NON-NLS-17$//$NON-NLS-18$//$NON-NLS-19$//$NON-NLS-20$//$NON-NLS-21$//$NON-NLS-22$//$NON-NLS-23$//$NON-NLS-24$//$NON-NLS-25$//$NON-NLS-26$//$NON-NLS-27$//$NON-NLS-28$//$NON-NLS-29$ + "testPersistentSchedule07", "testPersistentSchedule08", "testFailedApplication01", "testDestroyBeforeStart01", "testDestroyBeforeStart02"}; private static final String PI_OSGI_SERVICES = "org.eclipse.osgi.services"; //$NON-NLS-1$ + private static final String PI_OSGI_UTIL = "org.eclipse.osgi.util"; public static Test suite() { TestSuite suite = new TestSuite(ApplicationAdminTest.class.getName()); @@ -42,6 +43,7 @@ public class ApplicationAdminTest extends OSGiTest { String[] ids = ConfigurationSessionTestSuite.MINIMAL_BUNDLE_SET; for (int i = 0; i < ids.length; i++) appAdminSessionTest.addBundle(ids[i]); + appAdminSessionTest.addBundle(PI_OSGI_UTIL); appAdminSessionTest.addBundle(PI_OSGI_SERVICES); appAdminSessionTest.addBundle(PI_OSGI_TESTS); appAdminSessionTest.setApplicationId(testRunnerApp); diff --git a/bundles/org.eclipse.osgi.util/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.util/META-INF/MANIFEST.MF index 96aac648f..7e7bf9de7 100644 --- a/bundles/org.eclipse.osgi.util/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.osgi.util/META-INF/MANIFEST.MF @@ -2,18 +2,22 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %osgiUtil Bundle-SymbolicName: org.eclipse.osgi.util -Bundle-Version: 3.3.100.qualifier +Bundle-Version: 3.4.0.qualifier Bundle-Description: %osgiUtilDes Bundle-Vendor: %eclipse.org Bundle-Localization: plugin Bundle-DocUrl: http://www.eclipse.org Bundle-ContactAddress: www.eclipse.org -Export-Package: org.osgi.util.measurement; version="1.0.1", - org.osgi.util.position; version="1.0.1", - org.osgi.util.xml; version="1.0.1" +Export-Package: org.osgi.util.function;version="1.0", + org.osgi.util.measurement;version="1.0.1", + org.osgi.util.position;version="1.0.1";uses:="org.osgi.util.measurement", + org.osgi.util.promise;version="1.0";uses:="org.osgi.util.function", + org.osgi.util.xml;version="1.0.1";uses:="org.osgi.framework,javax.xml.parsers" Import-Package: org.osgi.framework; version=1.1, javax.xml.parsers, - org.osgi.util.measurement; version="[1.0.1, 1.2)", - org.osgi.util.position; version="[1.0.1, 1.2)", - org.osgi.util.xml; version="[1.0.1, 1.2)" + org.osgi.util.function; version="[1.0, 1.1)", + org.osgi.util.measurement; version="[1.0.1, 1.1)", + org.osgi.util.position; version="[1.0.1, 1.1)", + org.osgi.util.promise; version="[1.0, 1.1)", + org.osgi.util.xml; version="[1.0.1, 1.1)" Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/bundles/org.eclipse.osgi.util/build.properties b/bundles/org.eclipse.osgi.util/build.properties index 3ead86b05..95558127f 100644 --- a/bundles/org.eclipse.osgi.util/build.properties +++ b/bundles/org.eclipse.osgi.util/build.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2003, 2013 IBM Corporation and others. +# Copyright (c) 2003, 2016 IBM Corporation and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at @@ -17,3 +17,4 @@ src.includes = about.html,\ about_files/ source.. = src/ output.. = bin/ +jars.extra.classpath = lib/osgi.annotation.jar diff --git a/bundles/org.eclipse.osgi.util/lib/osgi.annotation.jar b/bundles/org.eclipse.osgi.util/lib/osgi.annotation.jar Binary files differnew file mode 100644 index 000000000..dda27d2fe --- /dev/null +++ b/bundles/org.eclipse.osgi.util/lib/osgi.annotation.jar diff --git a/bundles/org.eclipse.osgi.util/pom.xml b/bundles/org.eclipse.osgi.util/pom.xml index 3be91f5c1..c1b4a55fc 100644 --- a/bundles/org.eclipse.osgi.util/pom.xml +++ b/bundles/org.eclipse.osgi.util/pom.xml @@ -19,7 +19,7 @@ </parent> <groupId>org.eclipse.osgi</groupId> <artifactId>org.eclipse.osgi.util</artifactId> - <version>3.3.100-SNAPSHOT</version> + <version>3.4.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> <build> <plugins> diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Function.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Function.java new file mode 100644 index 000000000..5d812f75c --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Function.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.function; + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * A function that accepts a single argument and produces a result. + * + * <p> + * This is a functional interface and can be used as the assignment target for a + * lambda expression or method reference. + * + * @param <T> The type of the function input. + * @param <R> The type of the function output. + * + * @ThreadSafe + * @author $Id$ + */ +@ConsumerType +public interface Function<T, R> { + /** + * Applies this function to the specified argument. + * + * @param t The input to this function. + * @return The output of this function. + */ + R apply(T t); +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Predicate.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Predicate.java new file mode 100644 index 000000000..0c2c61f78 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/Predicate.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.function; + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * A predicate that accepts a single argument and produces a boolean result. + * + * <p> + * This is a functional interface and can be used as the assignment target for a + * lambda expression or method reference. + * + * @param <T> The type of the predicate input. + * + * @ThreadSafe + * @author $Id$ + */ +@ConsumerType +public interface Predicate<T> { + /** + * Evaluates this predicate on the specified argument. + * + * @param t The input to this predicate. + * @return {@code true} if the specified argument is accepted by this + * predicate; {@code false} otherwise. + */ + boolean test(T t); +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/package-info.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/package-info.java new file mode 100644 index 000000000..899d786b2 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/package-info.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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. + */ + +/** + * Function Package Version 1.0. + * + * <p> + * Bundles wishing to use this package must list the package in the + * Import-Package header of the bundle's manifest. + * + * <p> + * Example import for consumers using the API in this package: + * <p> + * {@code Import-Package: org.osgi.util.function; version="[1.0,2.0)"} + * <p> + * Example import for providers implementing the API in this package: + * <p> + * {@code Import-Package: org.osgi.util.function; version="[1.0,1.1)"} + * + * @author $Id$ + */ + +@Version("1.0") +package org.osgi.util.function; + +import org.osgi.annotation.versioning.Version; + diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/packageinfo b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/packageinfo new file mode 100644 index 000000000..7c8de0324 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/function/packageinfo @@ -0,0 +1 @@ +version 1.0 diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Deferred.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Deferred.java new file mode 100644 index 000000000..e9ff0a6fa --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Deferred.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.promise; + +import static org.osgi.util.promise.PromiseImpl.requireNonNull; + +/** + * A Deferred Promise resolution. + * + * <p> + * Instances of this class can be used to create a {@link Promise} that can be + * resolved in the future. The {@link #getPromise() associated} Promise can be + * successfully resolved with {@link #resolve(Object)} or resolved with a + * failure with {@link #fail(Throwable)}. It can also be resolved with the + * resolution of another promise using {@link #resolveWith(Promise)}. + * + * <p> + * The associated Promise can be provided to any one, but the Deferred object + * should be made available only to the party that will responsible for + * resolving the Promise. + * + * @param <T> The value type associated with the created Promise. + * + * @Immutable + * @author $Id$ + */ +public class Deferred<T> { + private final PromiseImpl<T> promise; + + /** + * Create a new Deferred with an associated Promise. + */ + public Deferred() { + promise = new PromiseImpl<T>(); + } + + /** + * Returns the Promise associated with this Deferred. + * + * @return The Promise associated with this Deferred. + */ + public Promise<T> getPromise() { + return promise; + } + + /** + * Successfully resolve the Promise associated with this Deferred. + * + * <p> + * After the associated Promise is resolved with the specified value, all + * registered {@link Promise#onResolve(Runnable) callbacks} are called and + * any {@link Promise#then(Success, Failure) chained} Promises are resolved. + * + * <p> + * Resolving the associated Promise <i>happens-before</i> any registered + * callback is called. That is, in a registered callback, + * {@link Promise#isDone()} must return {@code true} and + * {@link Promise#getValue()} and {@link Promise#getFailure()} must not + * block. + * + * @param value The value of the resolved Promise. + * @throws IllegalStateException If the associated Promise was already + * resolved. + */ + public void resolve(T value) { + promise.resolve(value, null); + } + + /** + * Fail the Promise associated with this Deferred. + * + * <p> + * After the associated Promise is resolved with the specified failure, all + * registered {@link Promise#onResolve(Runnable) callbacks} are called and + * any {@link Promise#then(Success, Failure) chained} Promises are resolved. + * + * <p> + * Resolving the associated Promise <i>happens-before</i> any registered + * callback is called. That is, in a registered callback, + * {@link Promise#isDone()} must return {@code true} and + * {@link Promise#getValue()} and {@link Promise#getFailure()} must not + * block. + * + * @param failure The failure of the resolved Promise. Must not be + * {@code null}. + * @throws IllegalStateException If the associated Promise was already + * resolved. + */ + public void fail(Throwable failure) { + promise.resolve(null, requireNonNull(failure)); + } + + /** + * Resolve the Promise associated with this Deferred with the specified + * Promise. + * + * <p> + * If the specified Promise is successfully resolved, the associated Promise + * is resolved with the value of the specified Promise. If the specified + * Promise is resolved with a failure, the associated Promise is resolved + * with the failure of the specified Promise. + * + * <p> + * After the associated Promise is resolved with the specified Promise, all + * registered {@link Promise#onResolve(Runnable) callbacks} are called and + * any {@link Promise#then(Success, Failure) chained} Promises are resolved. + * + * <p> + * Resolving the associated Promise <i>happens-before</i> any registered + * callback is called. That is, in a registered callback, + * {@link Promise#isDone()} must return {@code true} and + * {@link Promise#getValue()} and {@link Promise#getFailure()} must not + * block. + * + * @param with A Promise whose value or failure must be used to resolve the + * associated Promise. Must not be {@code null}. + * @return A Promise that is resolved only when the associated Promise is + * resolved by the specified Promise. The returned Promise must be + * successfully resolved with the value {@code null}, if the + * associated Promise was resolved by the specified Promise. The + * returned Promise must be resolved with a failure of + * {@link IllegalStateException}, if the associated Promise was + * already resolved when the specified Promise was resolved. + */ + public Promise<Void> resolveWith(Promise<? extends T> with) { + return promise.resolveWith(with); + } +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/FailedPromisesException.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/FailedPromisesException.java new file mode 100644 index 000000000..95546abe5 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/FailedPromisesException.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.promise; + +import java.util.Collection; +import java.util.Collections; + +/** + * Promise failure exception for a collection of failed Promises. + * + * @author $Id$ + */ +public class FailedPromisesException extends RuntimeException { + private static final long serialVersionUID = 1L; + private final Collection<Promise<?>> failed; + + /** + * Create a new FailedPromisesException with the specified Promises. + * + * @param failed A collection of Promises that have been resolved with a + * failure. Must not be {@code null}, must not be empty and all of + * the elements in the collection must not be {@code null}. + * @param cause The cause of this exception. This is typically the failure + * of the first Promise in the specified collection. + */ + public FailedPromisesException(Collection<Promise<?>> failed, Throwable cause) { + super(cause); + this.failed = Collections.unmodifiableCollection(failed); + } + + /** + * Returns the collection of Promises that have been resolved with a + * failure. + * + * @return The collection of Promises that have been resolved with a + * failure. The returned collection is unmodifiable. + */ + public Collection<Promise<?>> getFailedPromises() { + return failed; + } +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Failure.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Failure.java new file mode 100644 index 000000000..4e6a17884 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Failure.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.promise; + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * Failure callback for a Promise. + * + * <p> + * A Failure callback is registered with a {@link Promise} using the + * {@link Promise#then(Success, Failure)} method and is called if the Promise is + * resolved with a failure. + * + * <p> + * This is a functional interface and can be used as the assignment target for a + * lambda expression or method reference. + * + * @ThreadSafe + * @author $Id$ + */ +@ConsumerType +public interface Failure { + /** + * Failure callback for a Promise. + * + * <p> + * This method is called if the Promise with which it is registered resolves + * with a failure. + * + * <p> + * In the remainder of this description we will refer to the Promise + * returned by {@link Promise#then(Success, Failure)} when this Failure + * callback was registered as the chained Promise. + * + * <p> + * If this methods completes normally, the chained Promise must be failed + * with the same exception which failed the resolved Promise. If this method + * throws an exception, the chained Promise must be failed with the thrown + * exception. + * + * @param resolved The failed resolved {@link Promise}. + * @throws Exception The chained Promise must be failed with the thrown + * exception. + */ + void fail(Promise<?> resolved) throws Exception; +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promise.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promise.java new file mode 100644 index 000000000..ffa193393 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promise.java @@ -0,0 +1,403 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.promise; + +import java.lang.reflect.InvocationTargetException; +import org.osgi.annotation.versioning.ProviderType; +import org.osgi.util.function.Function; +import org.osgi.util.function.Predicate; + +/** + * A Promise of a value. + * + * <p> + * A Promise represents a future value. It handles the interactions for + * asynchronous processing. A {@link Deferred} object can be used to create a + * Promise and later resolve the Promise. A Promise is used by the caller of an + * asynchronous function to get the result or handle the error. The caller can + * either get a callback when the Promise is resolved with a value or an error, + * or the Promise can be used in chaining. In chaining, callbacks are provided + * that receive the resolved Promise, and a new Promise is generated that + * resolves based upon the result of a callback. + * + * <p> + * Both {@link #onResolve(Runnable) callbacks} and + * {@link #then(Success, Failure) chaining} can be repeated any number of times, + * even after the Promise has been resolved. + * <p> + * Example callback usage: + * + * <pre> + * final Promise<String> foo = foo(); + * foo.onResolve(new Runnable() { + * public void run() { + * System.out.println(foo.getValue()); + * } + * }); + * </pre> + * + * Example chaining usage; + * + * <pre> + * Success<String,String> doubler = new Success<String,String>() { + * public Promise<String> call(Promise<String> p) throws Exception { + * return Promises.resolved(p.getValue()+p.getValue()); + * } + * }; + * final Promise<String> foo = foo().then(doubler).then(doubler); + * foo.onResolve(new Runnable() { + * public void run() { + * System.out.println(foo.getValue()); + * } + * }); + * </pre> + * + * @param <T> The value type associated with this Promise. + * + * @ThreadSafe + * @author $Id$ + */ +@ProviderType +public interface Promise<T> { + + /** + * Returns whether this Promise has been resolved. + * + * <p> + * This Promise may be successfully resolved or resolved with a failure. + * + * @return {@code true} if this Promise was resolved either successfully or + * with a failure; {@code false} if this Promise is unresolved. + */ + boolean isDone(); + + /** + * Returns the value of this Promise. + * + * <p> + * If this Promise is not {@link #isDone() resolved}, this method must block + * and wait for this Promise to be resolved before completing. + * + * <p> + * If this Promise was successfully resolved, this method returns with the + * value of this Promise. If this Promise was resolved with a failure, this + * method must throw an {@code InvocationTargetException} with the + * {@link #getFailure() failure exception} as the cause. + * + * @return The value of this resolved Promise. + * @throws InvocationTargetException If this Promise was resolved with a + * failure. The cause of the {@code InvocationTargetException} is + * the failure exception. + * @throws InterruptedException If the current thread was interrupted while + * waiting. + */ + T getValue() throws InvocationTargetException, InterruptedException; + + /** + * Returns the failure of this Promise. + * + * <p> + * If this Promise is not {@link #isDone() resolved}, this method must block + * and wait for this Promise to be resolved before completing. + * + * <p> + * If this Promise was resolved with a failure, this method returns with the + * failure of this Promise. If this Promise was successfully resolved, this + * method must return {@code null}. + * + * @return The failure of this resolved Promise or {@code null} if this + * Promise was successfully resolved. + * @throws InterruptedException If the current thread was interrupted while + * waiting. + */ + Throwable getFailure() throws InterruptedException; + + /** + * Register a callback to be called when this Promise is resolved. + * + * <p> + * The specified callback is called when this Promise is resolved either + * successfully or with a failure. + * + * <p> + * This method may be called at any time including before and after this + * Promise has been resolved. + * + * <p> + * Resolving this Promise <i>happens-before</i> any registered callback is + * called. That is, in a registered callback, {@link #isDone()} must return + * {@code true} and {@link #getValue()} and {@link #getFailure()} must not + * block. + * + * <p> + * A callback may be called on a different thread than the thread which + * registered the callback. So the callback must be thread safe but can rely + * upon that the registration of the callback <i>happens-before</i> the + * registered callback is called. + * + * @param callback A callback to be called when this Promise is resolved. + * Must not be {@code null}. + * @return This Promise. + */ + Promise<T> onResolve(Runnable callback); + + /** + * Chain a new Promise to this Promise with Success and Failure callbacks. + * + * <p> + * The specified {@link Success} callback is called when this Promise is + * successfully resolved and the specified {@link Failure} callback is + * called when this Promise is resolved with a failure. + * + * <p> + * This method returns a new Promise which is chained to this Promise. The + * returned Promise must be resolved when this Promise is resolved after the + * specified Success or Failure callback is executed. The result of the + * executed callback must be used to resolve the returned Promise. Multiple + * calls to this method can be used to create a chain of promises which are + * resolved in sequence. + * + * <p> + * If this Promise is successfully resolved, the Success callback is + * executed and the result Promise, if any, or thrown exception is used to + * resolve the returned Promise from this method. If this Promise is + * resolved with a failure, the Failure callback is executed and the + * returned Promise from this method is failed. + * + * <p> + * This method may be called at any time including before and after this + * Promise has been resolved. + * + * <p> + * Resolving this Promise <i>happens-before</i> any registered callback is + * called. That is, in a registered callback, {@link #isDone()} must return + * {@code true} and {@link #getValue()} and {@link #getFailure()} must not + * block. + * + * <p> + * A callback may be called on a different thread than the thread which + * registered the callback. So the callback must be thread safe but can rely + * upon that the registration of the callback <i>happens-before</i> the + * registered callback is called. + * + * @param <R> The value type associated with the returned Promise. + * @param success A Success callback to be called when this Promise is + * successfully resolved. May be {@code null} if no Success callback + * is required. In this case, the returned Promise must be resolved + * with the value {@code null} when this Promise is successfully + * resolved. + * @param failure A Failure callback to be called when this Promise is + * resolved with a failure. May be {@code null} if no Failure + * callback is required. + * @return A new Promise which is chained to this Promise. The returned + * Promise must be resolved when this Promise is resolved after the + * specified Success or Failure callback, if any, is executed. + */ + <R> Promise<R> then(Success<? super T, ? extends R> success, Failure failure); + + /** + * Chain a new Promise to this Promise with a Success callback. + * + * <p> + * This method performs the same function as calling + * {@link #then(Success, Failure)} with the specified Success callback and + * {@code null} for the Failure callback. + * + * @param <R> The value type associated with the returned Promise. + * @param success A Success callback to be called when this Promise is + * successfully resolved. May be {@code null} if no Success callback + * is required. In this case, the returned Promise must be resolved + * with the value {@code null} when this Promise is successfully + * resolved. + * @return A new Promise which is chained to this Promise. The returned + * Promise must be resolved when this Promise is resolved after the + * specified Success, if any, is executed. + * @see #then(Success, Failure) + */ + <R> Promise<R> then(Success<? super T, ? extends R> success); + + /** + * Filter the value of this Promise. + * + * <p> + * If this Promise is successfully resolved, the returned Promise must + * either be resolved with the value of this Promise, if the specified + * Predicate accepts that value, or failed with a + * {@code NoSuchElementException}, if the specified Predicate does not + * accept that value. If the specified Predicate throws an exception, the + * returned Promise must be failed with the exception. + * + * <p> + * If this Promise is resolved with a failure, the returned Promise must be + * failed with that failure. + * + * <p> + * This method may be called at any time including before and after this + * Promise has been resolved. + * + * @param predicate The Predicate to evaluate the value of this Promise. + * Must not be {@code null}. + * @return A Promise that filters the value of this Promise. + */ + Promise<T> filter(Predicate<? super T> predicate); + + /** + * Map the value of this Promise. + * + * <p> + * If this Promise is successfully resolved, the returned Promise must be + * resolved with the value of specified Function as applied to the value of + * this Promise. If the specified Function throws an exception, the returned + * Promise must be failed with the exception. + * + * <p> + * If this Promise is resolved with a failure, the returned Promise must be + * failed with that failure. + * + * <p> + * This method may be called at any time including before and after this + * Promise has been resolved. + * + * @param <R> The value type associated with the returned Promise. + * @param mapper The Function that must map the value of this Promise to the + * value that must be used to resolve the returned Promise. Must not + * be {@code null}. + * @return A Promise that returns the value of this Promise as mapped by the + * specified Function. + */ + <R> Promise<R> map(Function<? super T, ? extends R> mapper); + + /** + * FlatMap the value of this Promise. + * + * <p> + * If this Promise is successfully resolved, the returned Promise must be + * resolved with the Promise from the specified Function as applied to the + * value of this Promise. If the specified Function throws an exception, the + * returned Promise must be failed with the exception. + * + * <p> + * If this Promise is resolved with a failure, the returned Promise must be + * failed with that failure. + * + * <p> + * This method may be called at any time including before and after this + * Promise has been resolved. + * + * @param <R> The value type associated with the returned Promise. + * @param mapper The Function that must flatMap the value of this Promise to + * a Promise that must be used to resolve the returned Promise. Must + * not be {@code null}. + * @return A Promise that returns the value of this Promise as mapped by the + * specified Function. + */ + <R> Promise<R> flatMap(Function<? super T, Promise<? extends R>> mapper); + + /** + * Recover from a failure of this Promise with a recovery value. + * + * <p> + * If this Promise is successfully resolved, the returned Promise must be + * resolved with the value of this Promise. + * + * <p> + * If this Promise is resolved with a failure, the specified Function is + * applied to this Promise to produce a recovery value. + * <ul> + * <li>If the recovery value is not {@code null}, the returned Promise must + * be resolved with the recovery value.</li> + * <li>If the recovery value is {@code null}, the returned Promise must be + * failed with the failure of this Promise.</li> + * <li>If the specified Function throws an exception, the returned Promise + * must be failed with that exception.</li> + * </ul> + * + * <p> + * To recover from a failure of this Promise with a recovery value of + * {@code null}, the {@link #recoverWith(Function)} method must be used. The + * specified Function for {@link #recoverWith(Function)} can return + * {@code Promises.resolved(null)} to supply the desired {@code null} value. + * + * <p> + * This method may be called at any time including before and after this + * Promise has been resolved. + * + * @param recovery If this Promise resolves with a failure, the specified + * Function is called to produce a recovery value to be used to + * resolve the returned Promise. Must not be {@code null}. + * @return A Promise that resolves with the value of this Promise or + * recovers from the failure of this Promise. + */ + Promise<T> recover(Function<Promise<?>, ? extends T> recovery); + + /** + * Recover from a failure of this Promise with a recovery Promise. + * + * <p> + * If this Promise is successfully resolved, the returned Promise must be + * resolved with the value of this Promise. + * + * <p> + * If this Promise is resolved with a failure, the specified Function is + * applied to this Promise to produce a recovery Promise. + * <ul> + * <li>If the recovery Promise is not {@code null}, the returned Promise + * must be resolved with the recovery Promise.</li> + * <li>If the recovery Promise is {@code null}, the returned Promise must be + * failed with the failure of this Promise.</li> + * <li>If the specified Function throws an exception, the returned Promise + * must be failed with that exception.</li> + * </ul> + * + * <p> + * This method may be called at any time including before and after this + * Promise has been resolved. + * + * @param recovery If this Promise resolves with a failure, the specified + * Function is called to produce a recovery Promise to be used to + * resolve the returned Promise. Must not be {@code null}. + * @return A Promise that resolves with the value of this Promise or + * recovers from the failure of this Promise. + */ + Promise<T> recoverWith(Function<Promise<?>, Promise<? extends T>> recovery); + + /** + * Fall back to the value of the specified Promise if this Promise fails. + * + * <p> + * If this Promise is successfully resolved, the returned Promise must be + * resolved with the value of this Promise. + * + * <p> + * If this Promise is resolved with a failure, the successful result of the + * specified Promise is used to resolve the returned Promise. If the + * specified Promise is resolved with a failure, the returned Promise must + * be failed with the failure of this Promise rather than the failure of the + * specified Promise. + * + * <p> + * This method may be called at any time including before and after this + * Promise has been resolved. + * + * @param fallback The Promise whose value must be used to resolve the + * returned Promise if this Promise resolves with a failure. Must not + * be {@code null}. + * @return A Promise that returns the value of this Promise or falls back to + * the value of the specified Promise. + */ + Promise<T> fallbackTo(Promise<? extends T> fallback); +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/PromiseImpl.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/PromiseImpl.java new file mode 100644 index 000000000..ca2be5e80 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/PromiseImpl.java @@ -0,0 +1,615 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.promise; + +import java.lang.reflect.InvocationTargetException; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import org.osgi.util.function.Function; +import org.osgi.util.function.Predicate; + +/** + * Promise implementation. + * + * <p> + * This class is not used directly by clients. Clients should use + * {@link Deferred} to create a resolvable {@link Promise}. + * + * @param <T> The result type associated with the Promise. + * + * @ThreadSafe + * @author $Id$ + */ +final class PromiseImpl<T> implements Promise<T> { + /** + * A ConcurrentLinkedQueue to hold the callbacks for this Promise, so no + * additional synchronization is required to write to or read from the + * queue. + */ + private final ConcurrentLinkedQueue<Runnable> callbacks; + /** + * A CountDownLatch to manage the resolved state of this Promise. + * + * <p> + * This object is used as the synchronizing object to provide a critical + * section in {@link #resolve(Object, Throwable)} so that only a single + * thread can write the resolved state variables and open the latch. + * + * <p> + * The resolved state variables, {@link #value} and {@link #fail}, must only + * be written when the latch is closed (getCount() != 0) and must only be + * read when the latch is open (getCount() == 0). The latch state must + * always be checked before writing or reading since the resolved state + * variables' memory consistency is guarded by the latch. + */ + private final CountDownLatch resolved; + /** + * The value of this Promise if successfully resolved. + * + * @GuardedBy("resolved") + * @see #resolved + */ + private T value; + /** + * The failure of this Promise if resolved with a failure or {@code null} if + * successfully resolved. + * + * @GuardedBy("resolved") + * @see #resolved + */ + private Throwable fail; + + /** + * Initialize this Promise. + */ + PromiseImpl() { + callbacks = new ConcurrentLinkedQueue<Runnable>(); + resolved = new CountDownLatch(1); + } + + /** + * Initialize and resolve this Promise. + * + * @param v The value of this resolved Promise. + * @param f The failure of this resolved Promise. + */ + PromiseImpl(T v, Throwable f) { + value = v; + fail = f; + callbacks = new ConcurrentLinkedQueue<Runnable>(); + resolved = new CountDownLatch(0); + } + + /** + * Resolve this Promise. + * + * @param v The value of this Promise. + * @param f The failure of this Promise. + */ + void resolve(T v, Throwable f) { + // critical section: only one resolver at a time + synchronized (resolved) { + if (resolved.getCount() == 0) { + throw new IllegalStateException("Already resolved"); + } + /* + * The resolved state variables must be set before opening the + * latch. This safely publishes them to be read by other threads + * that must verify the latch is open before reading. + */ + value = v; + fail = f; + resolved.countDown(); + } + notifyCallbacks(); // call any registered callbacks + } + + /** + * Call any registered callbacks if this Promise is resolved. + */ + private void notifyCallbacks() { + if (resolved.getCount() != 0) { + return; // return if not resolved + } + + /* + * Note: multiple threads can be in this method removing callbacks from + * the queue and calling them, so the order in which callbacks are + * called cannot be specified. + */ + for (Runnable callback = callbacks.poll(); callback != null; callback = callbacks.poll()) { + try { + callback.run(); + } catch (Throwable t) { + Logger.logCallbackException(t); + } + } + } + + /** + * {@inheritDoc} + */ + public boolean isDone() { + return resolved.getCount() == 0; + } + + /** + * {@inheritDoc} + */ + public T getValue() throws InvocationTargetException, InterruptedException { + resolved.await(); + if (fail == null) { + return value; + } + throw new InvocationTargetException(fail); + } + + /** + * {@inheritDoc} + */ + public Throwable getFailure() throws InterruptedException { + resolved.await(); + return fail; + } + + /** + * {@inheritDoc} + */ + public Promise<T> onResolve(Runnable callback) { + callbacks.offer(callback); + notifyCallbacks(); // call any registered callbacks + return this; + } + + /** + * {@inheritDoc} + */ + public <R> Promise<R> then(Success<? super T, ? extends R> success, Failure failure) { + PromiseImpl<R> chained = new PromiseImpl<R>(); + onResolve(new Then<R>(chained, success, failure)); + return chained; + } + + /** + * {@inheritDoc} + */ + public <R> Promise<R> then(Success<? super T, ? extends R> success) { + return then(success, null); + } + + /** + * A callback used to chain promises for the {@link #then(Success, Failure)} + * method. + * + * @Immutable + */ + private final class Then<R> implements Runnable { + private final PromiseImpl<R> chained; + private final Success<T, ? extends R> success; + private final Failure failure; + + @SuppressWarnings("unchecked") + Then(PromiseImpl<R> chained, Success<? super T, ? extends R> success, Failure failure) { + this.chained = chained; + this.success = (Success<T, ? extends R>) success; + this.failure = failure; + } + + public void run() { + Throwable f; + final boolean interrupted = Thread.interrupted(); + try { + f = getFailure(); + } catch (Throwable e) { + f = e; // propagate new exception + } finally { + if (interrupted) { // restore interrupt status + Thread.currentThread().interrupt(); + } + } + if (f != null) { + if (failure != null) { + try { + failure.fail(PromiseImpl.this); + } catch (Throwable e) { + f = e; // propagate new exception + } + } + // fail chained + chained.resolve(null, f); + return; + } + Promise<? extends R> returned = null; + if (success != null) { + try { + returned = success.call(PromiseImpl.this); + } catch (Throwable e) { + chained.resolve(null, e); + return; + } + } + if (returned == null) { + // resolve chained with null value + chained.resolve(null, null); + } else { + // resolve chained when returned promise is resolved + returned.onResolve(new Chain<R>(chained, returned)); + } + } + } + + /** + * A callback used to resolve the chained Promise when the Promise promise + * is resolved. + * + * @Immutable + */ + private final static class Chain<R> implements Runnable { + private final PromiseImpl<R> chained; + private final Promise<? extends R> promise; + private final Throwable failure; + + Chain(PromiseImpl<R> chained, Promise<? extends R> promise) { + this.chained = chained; + this.promise = promise; + this.failure = null; + } + + Chain(PromiseImpl<R> chained, Promise<? extends R> promise, Throwable failure) { + this.chained = chained; + this.promise = promise; + this.failure = failure; + } + + public void run() { + R value = null; + Throwable f; + final boolean interrupted = Thread.interrupted(); + try { + f = promise.getFailure(); + if (f == null) { + value = promise.getValue(); + } else if (failure != null) { + f = failure; + } + } catch (Throwable e) { + f = e; // propagate new exception + } finally { + if (interrupted) { // restore interrupt status + Thread.currentThread().interrupt(); + } + } + chained.resolve(value, f); + } + } + + /** + * Resolve this Promise with the specified Promise. + * + * <p> + * If the specified Promise is successfully resolved, this Promise is + * resolved with the value of the specified Promise. If the specified + * Promise is resolved with a failure, this Promise is resolved with the + * failure of the specified Promise. + * + * @param with A Promise whose value or failure must be used to resolve this + * Promise. Must not be {@code null}. + * @return A Promise that is resolved only when this Promise is resolved by + * the specified Promise. The returned Promise must be successfully + * resolved with the value {@code null}, if this Promise was + * resolved by the specified Promise. The returned Promise must be + * resolved with a failure of {@link IllegalStateException}, if this + * Promise was already resolved when the specified Promise was + * resolved. + */ + Promise<Void> resolveWith(Promise<? extends T> with) { + PromiseImpl<Void> chained = new PromiseImpl<Void>(); + ResolveWith resolveWith = new ResolveWith(chained); + with.then(resolveWith, resolveWith); + return chained; + } + + /** + * A callback used to resolve this Promise with another Promise for the + * {@link PromiseImpl#resolveWith(Promise)} method. + * + * @Immutable + */ + private final class ResolveWith implements Success<T, Void>, Failure { + private final PromiseImpl<Void> chained; + + ResolveWith(PromiseImpl<Void> chained) { + this.chained = chained; + } + + public Promise<Void> call(Promise<T> with) throws Exception { + try { + resolve(with.getValue(), null); + } catch (Throwable e) { + chained.resolve(null, e); + return null; + } + chained.resolve(null, null); + return null; + } + + public void fail(Promise<?> with) throws Exception { + try { + resolve(null, with.getFailure()); + } catch (Throwable e) { + chained.resolve(null, e); + return; + } + chained.resolve(null, null); + } + } + + /** + * {@inheritDoc} + */ + public Promise<T> filter(Predicate<? super T> predicate) { + return then(new Filter<T>(predicate)); + } + + /** + * A callback used by the {@link PromiseImpl#filter(Predicate)} method. + * + * @Immutable + */ + private static final class Filter<T> implements Success<T, T> { + private final Predicate<? super T> predicate; + + Filter(Predicate<? super T> predicate) { + this.predicate = requireNonNull(predicate); + } + + public Promise<T> call(Promise<T> resolved) throws Exception { + if (predicate.test(resolved.getValue())) { + return resolved; + } + throw new NoSuchElementException(); + } + } + + /** + * {@inheritDoc} + */ + public <R> Promise<R> map(Function<? super T, ? extends R> mapper) { + return then(new Map<T, R>(mapper)); + } + + /** + * A callback used by the {@link PromiseImpl#map(Function)} method. + * + * @Immutable + */ + private static final class Map<T, R> implements Success<T, R> { + private final Function<? super T, ? extends R> mapper; + + Map(Function<? super T, ? extends R> mapper) { + this.mapper = requireNonNull(mapper); + } + + public Promise<R> call(Promise<T> resolved) throws Exception { + return new PromiseImpl<R>(mapper.apply(resolved.getValue()), null); + } + } + + /** + * {@inheritDoc} + */ + public <R> Promise<R> flatMap(Function<? super T, Promise<? extends R>> mapper) { + return then(new FlatMap<T, R>(mapper)); + } + + /** + * A callback used by the {@link PromiseImpl#flatMap(Function)} method. + * + * @Immutable + */ + private static final class FlatMap<T, R> implements Success<T, R> { + private final Function<? super T, Promise<? extends R>> mapper; + + FlatMap(Function<? super T, Promise<? extends R>> mapper) { + this.mapper = requireNonNull(mapper); + } + + @SuppressWarnings("unchecked") + public Promise<R> call(Promise<T> resolved) throws Exception { + return (Promise<R>) mapper.apply(resolved.getValue()); + } + } + + /** + * {@inheritDoc} + */ + public Promise<T> recover(Function<Promise<?>, ? extends T> recovery) { + PromiseImpl<T> chained = new PromiseImpl<T>(); + Recover<T> recover = new Recover<T>(chained, recovery); + then(recover, recover); + return chained; + } + + /** + * A callback used by the {@link PromiseImpl#recover(Function)} method. + * + * @Immutable + */ + private static final class Recover<T> implements Success<T, Void>, Failure { + private final PromiseImpl<T> chained; + private final Function<Promise<?>, ? extends T> recovery; + + Recover(PromiseImpl<T> chained, Function<Promise<?>, ? extends T> recovery) { + this.chained = chained; + this.recovery = requireNonNull(recovery); + } + + public Promise<Void> call(Promise<T> resolved) throws Exception { + T value; + try { + value = resolved.getValue(); + } catch (Throwable e) { + chained.resolve(null, e); + return null; + } + chained.resolve(value, null); + return null; + } + + public void fail(Promise<?> resolved) throws Exception { + T recovered; + Throwable failure; + try { + recovered = recovery.apply(resolved); + failure = resolved.getFailure(); + } catch (Throwable e) { + chained.resolve(null, e); + return; + } + if (recovered == null) { + chained.resolve(null, failure); + } else { + chained.resolve(recovered, null); + } + } + } + + /** + * {@inheritDoc} + */ + public Promise<T> recoverWith(Function<Promise<?>, Promise<? extends T>> recovery) { + PromiseImpl<T> chained = new PromiseImpl<T>(); + RecoverWith<T> recoverWith = new RecoverWith<T>(chained, recovery); + then(recoverWith, recoverWith); + return chained; + } + + /** + * A callback used by the {@link PromiseImpl#recoverWith(Function)} method. + * + * @Immutable + */ + private static final class RecoverWith<T> implements Success<T, Void>, Failure { + private final PromiseImpl<T> chained; + private final Function<Promise<?>, Promise<? extends T>> recovery; + + RecoverWith(PromiseImpl<T> chained, Function<Promise<?>, Promise<? extends T>> recovery) { + this.chained = chained; + this.recovery = requireNonNull(recovery); + } + + public Promise<Void> call(Promise<T> resolved) throws Exception { + T value; + try { + value = resolved.getValue(); + } catch (Throwable e) { + chained.resolve(null, e); + return null; + } + chained.resolve(value, null); + return null; + } + + public void fail(Promise<?> resolved) throws Exception { + Promise<? extends T> recovered; + Throwable failure; + try { + recovered = recovery.apply(resolved); + failure = resolved.getFailure(); + } catch (Throwable e) { + chained.resolve(null, e); + return; + } + if (recovered == null) { + chained.resolve(null, failure); + } else { + recovered.onResolve(new Chain<T>(chained, recovered)); + } + } + } + + /** + * {@inheritDoc} + */ + public Promise<T> fallbackTo(Promise<? extends T> fallback) { + PromiseImpl<T> chained = new PromiseImpl<T>(); + FallbackTo<T> fallbackTo = new FallbackTo<T>(chained, fallback); + then(fallbackTo, fallbackTo); + return chained; + } + + /** + * A callback used by the {@link PromiseImpl#fallbackTo(Promise)} method. + * + * @Immutable + */ + private static final class FallbackTo<T> implements Success<T, Void>, Failure { + private final PromiseImpl<T> chained; + private final Promise<? extends T> fallback; + + FallbackTo(PromiseImpl<T> chained, Promise<? extends T> fallback) { + this.chained = chained; + this.fallback = requireNonNull(fallback); + } + + public Promise<Void> call(Promise<T> resolved) throws Exception { + T value; + try { + value = resolved.getValue(); + } catch (Throwable e) { + chained.resolve(null, e); + return null; + } + chained.resolve(value, null); + return null; + } + + public void fail(Promise<?> resolved) throws Exception { + Throwable failure; + try { + failure = resolved.getFailure(); + } catch (Throwable e) { + chained.resolve(null, e); + return; + } + fallback.onResolve(new Chain<T>(chained, fallback, failure)); + } + } + + static <V> V requireNonNull(V value) { + if (value != null) { + return value; + } + throw new NullPointerException(); + } + + /** + * Use the lazy initialization holder class idiom to delay creating a Logger + * until we actually need it. + */ + private static final class Logger { + private final static java.util.logging.Logger LOGGER; + static { + LOGGER = java.util.logging.Logger.getLogger(PromiseImpl.class.getName()); + } + + static void logCallbackException(Throwable t) { + LOGGER.log(java.util.logging.Level.WARNING, "Exception from Promise callback", t); + } + } +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promises.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promises.java new file mode 100644 index 000000000..06d506675 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Promises.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.promise; + +import static org.osgi.util.promise.PromiseImpl.requireNonNull; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Static helper methods for {@link Promise}s. + * + * @ThreadSafe + * @author $Id$ + */ +public class Promises { + private Promises() { + // disallow object creation + } + + /** + * Create a new Promise that has been resolved with the specified value. + * + * @param <T> The value type associated with the returned Promise. + * @param value The value of the resolved Promise. + * @return A new Promise that has been resolved with the specified value. + */ + public static <T> Promise<T> resolved(T value) { + return new PromiseImpl<T>(value, null); + } + + /** + * Create a new Promise that has been resolved with the specified failure. + * + * @param <T> The value type associated with the returned Promise. + * @param failure The failure of the resolved Promise. Must not be + * {@code null}. + * @return A new Promise that has been resolved with the specified failure. + */ + public static <T> Promise<T> failed(Throwable failure) { + return new PromiseImpl<T>(null, requireNonNull(failure)); + } + + /** + * Create a new Promise that is a latch on the resolution of the specified + * Promises. + * + * <p> + * The new Promise acts as a gate and must be resolved after all of the + * specified Promises are resolved. + * + * @param <T> The value type of the List value associated with the returned + * Promise. + * @param <S> A subtype of the value type of the List value associated with + * the returned Promise. + * @param promises The Promises which must be resolved before the returned + * Promise must be resolved. Must not be {@code null} and all of the + * elements in the collection must not be {@code null}. + * @return A Promise that is resolved only when all the specified Promises + * are resolved. The returned Promise must be successfully resolved + * with a List of the values in the order of the specified Promises + * if all the specified Promises are successfully resolved. The List + * in the returned Promise is the property of the caller and is + * modifiable. The returned Promise must be resolved with a failure + * of {@link FailedPromisesException} if any of the specified + * Promises are resolved with a failure. The failure + * {@link FailedPromisesException} must contain all of the specified + * Promises which resolved with a failure. + */ + public static <T, S extends T> Promise<List<T>> all(Collection<Promise<S>> promises) { + if (promises.isEmpty()) { + List<T> result = new ArrayList<T>(); + return resolved(result); + } + /* make a copy and capture the ordering */ + List<Promise<? extends T>> list = new ArrayList<Promise<? extends T>>(promises); + PromiseImpl<List<T>> chained = new PromiseImpl<List<T>>(); + All<T> all = new All<T>(chained, list); + for (Promise<? extends T> promise : list) { + promise.onResolve(all); + } + return chained; + } + + /** + * Create a new Promise that is a latch on the resolution of the specified + * Promises. + * + * <p> + * The new Promise acts as a gate and must be resolved after all of the + * specified Promises are resolved. + * + * @param <T> The value type associated with the specified Promises. + * @param promises The Promises which must be resolved before the returned + * Promise must be resolved. Must not be {@code null} and all of the + * arguments must not be {@code null}. + * @return A Promise that is resolved only when all the specified Promises + * are resolved. The returned Promise must be successfully resolved + * with a List of the values in the order of the specified Promises + * if all the specified Promises are successfully resolved. The List + * in the returned Promise is the property of the caller and is + * modifiable. The returned Promise must be resolved with a failure + * of {@link FailedPromisesException} if any of the specified + * Promises are resolved with a failure. The failure + * {@link FailedPromisesException} must contain all of the specified + * Promises which resolved with a failure. + */ + public static <T> Promise<List<T>> all(Promise<? extends T>... promises) { + @SuppressWarnings("unchecked") + List<Promise<T>> list = Arrays.asList((Promise<T>[]) promises); + return all(list); + } + + /** + * A callback used to resolve a Promise when the specified list of Promises + * are resolved for the {@link Promises#all(Collection)} method. + * + * @ThreadSafe + */ + private static final class All<T> implements Runnable { + private final PromiseImpl<List<T>> chained; + private final List<Promise<? extends T>> promises; + private final AtomicInteger promiseCount; + + All(PromiseImpl<List<T>> chained, List<Promise<? extends T>> promises) { + this.chained = chained; + this.promises = promises; + this.promiseCount = new AtomicInteger(promises.size()); + } + + public void run() { + if (promiseCount.decrementAndGet() != 0) { + return; + } + List<T> result = new ArrayList<T>(promises.size()); + List<Promise<?>> failed = new ArrayList<Promise<?>>(promises.size()); + Throwable cause = null; + for (Promise<? extends T> promise : promises) { + Throwable failure; + T value; + try { + failure = promise.getFailure(); + value = (failure != null) ? null : promise.getValue(); + } catch (Throwable e) { + chained.resolve(null, e); + return; + } + if (failure != null) { + failed.add(promise); + if (cause == null) { + cause = failure; + } + } else { + result.add(value); + } + } + if (failed.isEmpty()) { + chained.resolve(result, null); + } else { + chained.resolve(null, new FailedPromisesException(failed, cause)); + } + } + } +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Success.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Success.java new file mode 100644 index 000000000..c29fc4fb6 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/Success.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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.osgi.util.promise; + +import org.osgi.annotation.versioning.ConsumerType; + +/** + * Success callback for a Promise. + * + * <p> + * A Success callback is registered with a {@link Promise} using the + * {@link Promise#then(Success)} method and is called if the Promise is resolved + * successfully. + * + * <p> + * This is a functional interface and can be used as the assignment target for a + * lambda expression or method reference. + * + * @param <T> The value type of the resolved Promise passed as input to this + * callback. + * @param <R> The value type of the returned Promise from this callback. + * + * @ThreadSafe + * @author $Id$ + */ +@ConsumerType +public interface Success<T, R> { + /** + * Success callback for a Promise. + * + * <p> + * This method is called if the Promise with which it is registered resolves + * successfully. + * + * <p> + * In the remainder of this description we will refer to the Promise + * returned by this method as the returned Promise and the Promise returned + * by {@link Promise#then(Success)} when this Success callback was + * registered as the chained Promise. + * + * <p> + * If the returned Promise is {@code null} then the chained Promise must + * resolve immediately with a successful value of {@code null}. If the + * returned Promise is not {@code null} then the chained Promise must be + * resolved when the returned Promise is resolved. + * + * @param resolved The successfully resolved {@link Promise}. + * @return The Promise to use to resolve the chained Promise, or + * {@code null} if the chained Promise is to be resolved immediately + * with the value {@code null}. + * @throws Exception The chained Promise must be failed with the thrown + * exception. + */ + Promise<R> call(Promise<T> resolved) throws Exception; +} diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/package-info.java b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/package-info.java new file mode 100644 index 000000000..5a3ec65d3 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/package-info.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) OSGi Alliance (2014). All Rights Reserved. + * + * Licensed 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. + */ + +/** + * Promise Package Version 1.0. + * + * <p> + * Bundles wishing to use this package must list the package in the + * Import-Package header of the bundle's manifest. + * + * <p> + * Example import for consumers using the API in this package: + * <p> + * {@code Import-Package: org.osgi.util.promise; version="[1.0,2.0)"} + * <p> + * Example import for providers implementing the API in this package: + * <p> + * {@code Import-Package: org.osgi.util.promise; version="[1.0,1.1)"} + * + * @author $Id$ + */ + +@Version("1.0") +package org.osgi.util.promise; + +import org.osgi.annotation.versioning.Version; + diff --git a/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/packageinfo b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/packageinfo new file mode 100644 index 000000000..7c8de0324 --- /dev/null +++ b/bundles/org.eclipse.osgi.util/src/org/osgi/util/promise/packageinfo @@ -0,0 +1 @@ +version 1.0 diff --git a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF index 00cb55822..0f7985ae0 100644 --- a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF @@ -2,14 +2,22 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Export-Package: org.eclipse.core.runtime.adaptor;x-friends:="org.eclipse.core.runtime", org.eclipse.core.runtime.internal.adaptor;x-internal:=true, - org.eclipse.equinox.log;version="1.0", - org.eclipse.osgi.container;version="1.2", - org.eclipse.osgi.container.builders;version="1.0", - org.eclipse.osgi.container.namespaces;version="1.0", - org.eclipse.osgi.framework.console;version="1.1", + org.eclipse.equinox.log;version="1.0";uses:="org.osgi.framework,org.osgi.service.log", + org.eclipse.osgi.container;version="1.2"; + uses:="org.eclipse.osgi.report.resolution, + org.osgi.framework.wiring, + org.osgi.framework.startlevel, + org.osgi.framework, + org.osgi.framework.hooks.resolver, + org.osgi.service.resolver, + org.osgi.resource, + org.eclipse.osgi.service.debug", + org.eclipse.osgi.container.builders;version="1.0";uses:="org.eclipse.osgi.util,org.eclipse.osgi.container", + org.eclipse.osgi.container.namespaces;version="1.0";uses:="org.osgi.resource", + org.eclipse.osgi.framework.console;version="1.1";uses:="org.osgi.framework", org.eclipse.osgi.framework.eventmgr;version="1.2", org.eclipse.osgi.framework.internal.reliablefile;x-internal:=true, - org.eclipse.osgi.framework.log;version="1.1", + org.eclipse.osgi.framework.log;version="1.1";uses:="org.osgi.framework", org.eclipse.osgi.framework.util;x-internal:=true, org.eclipse.osgi.internal.debug;x-internal:=true, org.eclipse.osgi.internal.framework;x-internal:=true, @@ -26,18 +34,18 @@ Export-Package: org.eclipse.core.runtime.adaptor;x-friends:="org.eclipse.core.ru org.eclipse.osgi.internal.serviceregistry;x-internal:=true, org.eclipse.osgi.internal.signedcontent;x-internal:=true, org.eclipse.osgi.internal.url;x-internal:=true, - org.eclipse.osgi.launch;version="1.0", - org.eclipse.osgi.report.resolution;version="1.0", + org.eclipse.osgi.launch;version="1.0";uses:="org.osgi.framework,org.osgi.framework.launch", + org.eclipse.osgi.report.resolution;version="1.0";uses:="org.osgi.service.resolver,org.osgi.resource", org.eclipse.osgi.service.datalocation;version="1.3", org.eclipse.osgi.service.debug;version="1.2", org.eclipse.osgi.service.environment;version="1.3", - org.eclipse.osgi.service.localization;version="1.1", + org.eclipse.osgi.service.localization;version="1.1";uses:="org.osgi.framework", org.eclipse.osgi.service.pluginconversion;version="1.0", - org.eclipse.osgi.service.resolver;version="1.6", + org.eclipse.osgi.service.resolver;version="1.6";uses:="org.osgi.framework,org.osgi.framework.hooks.resolver,org.osgi.framework.wiring", org.eclipse.osgi.service.runnable;version="1.1", org.eclipse.osgi.service.security;version="1.0", org.eclipse.osgi.service.urlconversion;version="1.0", - org.eclipse.osgi.signedcontent;version="1.0", + org.eclipse.osgi.signedcontent;version="1.0";uses:="org.osgi.framework", org.eclipse.osgi.storage;x-friends:="org.eclipse.osgi.tests", org.eclipse.osgi.storage.bundlefile;x-internal:=true, org.eclipse.osgi.storage.url.reference;x-internal:=true, @@ -45,27 +53,27 @@ Export-Package: org.eclipse.core.runtime.adaptor;x-friends:="org.eclipse.core.ru org.eclipse.osgi.util;version="1.1", org.osgi.dto;version="1.0", org.osgi.framework;version="1.8", - org.osgi.framework.dto;version="1.8", - org.osgi.framework.hooks.bundle;version="1.1", - org.osgi.framework.hooks.resolver;version="1.0", - org.osgi.framework.hooks.service;version="1.1", - org.osgi.framework.hooks.weaving;version="1.1", - org.osgi.framework.launch;version="1.2", - org.osgi.framework.namespace;version="1.1", - org.osgi.framework.startlevel;version="1.0", - org.osgi.framework.startlevel.dto;version="1.0", - org.osgi.framework.wiring;version="1.2", - org.osgi.framework.wiring.dto;version="1.2", + org.osgi.framework.dto;version="1.8";uses:="org.osgi.dto", + org.osgi.framework.hooks.bundle;version="1.1";uses:="org.osgi.framework", + org.osgi.framework.hooks.resolver;version="1.0";uses:="org.osgi.framework.wiring", + org.osgi.framework.hooks.service;version="1.1";uses:="org.osgi.framework", + org.osgi.framework.hooks.weaving;version="1.1";uses:="org.osgi.framework.wiring", + org.osgi.framework.launch;version="1.2";uses:="org.osgi.framework", + org.osgi.framework.namespace;version="1.1";uses:="org.osgi.resource", + org.osgi.framework.startlevel;version="1.0";uses:="org.osgi.framework", + org.osgi.framework.startlevel.dto;version="1.0";uses:="org.osgi.dto", + org.osgi.framework.wiring;version="1.2";uses:="org.osgi.framework,org.osgi.resource", + org.osgi.framework.wiring.dto;version="1.2";uses:="org.osgi.dto,org.osgi.resource.dto", org.osgi.resource;version="1.0", - org.osgi.resource.dto;version="1.0", - org.osgi.service.condpermadmin;version="1.1.1", - org.osgi.service.log;version="1.3", - org.osgi.service.packageadmin;version="1.2", + org.osgi.resource.dto;version="1.0";uses:="org.osgi.dto", + org.osgi.service.condpermadmin;version="1.1.1";uses:="org.osgi.framework", + org.osgi.service.log;version="1.3";uses:="org.osgi.framework", + org.osgi.service.packageadmin;version="1.2";uses:="org.osgi.framework", org.osgi.service.permissionadmin;version="1.2", - org.osgi.service.resolver;version="1.0.1", - org.osgi.service.startlevel;version="1.1", + org.osgi.service.resolver;version="1.0.1";uses:="org.osgi.resource", + org.osgi.service.startlevel;version="1.1";uses:="org.osgi.framework", org.osgi.service.url;version="1.0", - org.osgi.util.tracker;version="1.5.1" + org.osgi.util.tracker;version="1.5.1";uses:="org.osgi.framework" Export-Service: org.osgi.service.packageadmin.PackageAdmin, org.osgi.service.permissionadmin.PermissionAdmin, org.osgi.service.startlevel.StartLevel, |