moved to org.apache.felix.dm.impl.dependencies package
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@887422 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
new file mode 100644
index 0000000..02bdfec
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
+ * required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dependencymanager.dependencies;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyService;
+import org.apache.felix.dependencymanager.impl.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+* A Temporal Service dependency that can block the caller thread between service updates. Only
+* useful for required stateless dependencies that can be replaced transparently.
+* A Dynamic Proxy is used to wrap the actual service dependency. When the dependency goes
+* away, an attempt is made to replace it with another one which satisfies the service dependency
+* criteria. If no service replacement is available, then any method invocation (through the
+* dynamic proxy) will block during a configurable timeout. On timeout, an unchecked ServiceUnavailable
+* exception is raised (but the service is not deactivated).<p>
+*
+* When an OSGi update takes place, we use the following locking strategy: A Read/Write lock is used
+* to synchronize the updating thread with respect to the other threads which invoke the backed service.
+* The Updating thread uses an exclusive Write lock and service invokers uses a Read lock. This model
+* allows multiple threads to invoke the backed service concurrently, but the updating thread won't
+* update the dependency if it's currently in use.<p>
+*
+* <b>This class only supports required dependencies, and temporal dependencies must be accessed outside
+* the Activator (OSGi) thread, because method invocations may block the caller thread when dependencies
+* are not satisfied.
+* </b>
+*
+* <p> Sample Code:<p>
+* <blockquote>
+*
+* <pre>
+* import org.apache.felix.dependencymanager.*;
+*
+* public class Activator extends DependencyActivatorBase {
+* public void init(BundleContext ctx, DependencyManager dm) throws Exception {
+* dm.add(createService()
+* .setImplementation(MyServer.class)
+* .add(createTemporalServiceDependency()
+* .setService(MyDependency.class)
+* .setTimeout(15000)));
+* }
+*
+* public void destroy(BundleContext ctx, DependencyManager dm) throws Exception {
+* }
+* }
+*
+* class MyServer implements Runnable {
+* MyDependency _dependency; // Auto-Injected by reflection.
+* void start() {
+* (new Thread(this)).start();
+* }
+*
+* public void run() {
+* try {
+* _dependency.doWork();
+* } catch (ServiceUnavailableException e) {
+* t.printStackTrace();
+* }
+* }
+* </pre>
+*
+* </blockquote>
+*/
+public class TemporalServiceDependency extends ServiceDependency implements InvocationHandler {
+ // Max millis to wait for service availability.
+ private long m_timeout = 30000;
+
+ /**
+ * Creates a new Temporal Service Dependency.
+ *
+ * @param context The bundle context of the bundle which is instantiating this dependency object
+ * @param logger the logger our Internal logger for logging events.
+ * @see DependencyActivatorBase#createTemporalServiceDependency()
+ */
+ public TemporalServiceDependency(BundleContext context, Logger logger) {
+ super(context, logger);
+ super.setRequired(true);
+ }
+
+ /**
+ * Sets the timeout for this temporal dependency. Specifying a timeout value of zero means that there is no timeout period,
+ * and an invocation on a missing service will fail immediately.
+ *
+ * @param timeout the dependency timeout value greater or equals to 0
+ * @throws IllegalArgumentException if the timeout is negative
+ * @return this temporal dependency
+ */
+ public TemporalServiceDependency setTimeout(long timeout) {
+ if (timeout < 0) {
+ throw new IllegalArgumentException("Invalid timeout value: " + timeout);
+ }
+ m_timeout = timeout;
+ return this;
+ }
+
+ /**
+ * The ServiceTracker calls us here in order to inform about a service arrival.
+ */
+ public synchronized void addedService(ServiceReference ref, Object service) {
+ boolean makeAvailable = makeAvailable();
+ if (makeAvailable) {
+ m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] { m_trackedServiceName }, this);
+ }
+ Object[] services = m_services.toArray();
+ for (int i = 0; i < services.length; i++) {
+ DependencyService ds = (DependencyService) services[i];
+ if (makeAvailable) {
+ ds.dependencyAvailable(this);
+ }
+ }
+ if (!makeAvailable) {
+ notifyAll();
+ }
+ }
+
+ /**
+ * The ServiceTracker calls us here when a tracked service properties are modified.
+ */
+ public void modifiedService(ServiceReference ref, Object service) {
+ // We don't care.
+ }
+
+ /**
+ * The ServiceTracker calls us here when a tracked service is lost.
+ */
+ public synchronized void removedService(ServiceReference ref, Object service) {
+ // Unget what we got in addingService (see ServiceTracker 701.4.1)
+ m_context.ungetService(ref);
+ }
+
+ /**
+ * @returns our dependency instance. Unlike in ServiceDependency, we always returns our proxy.
+ */
+ public synchronized Object getService() {
+ return m_serviceInstance;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ Object service = m_tracker.getService();
+ if (service == null) {
+ synchronized (this) {
+ long start = System.currentTimeMillis();
+ long waitTime = m_timeout;
+ while (service == null) {
+ if (waitTime <= 0) {
+ throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+ }
+ try {
+ wait(waitTime);
+ }
+ catch (InterruptedException e) {
+ throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+ }
+ waitTime = m_timeout - (System.currentTimeMillis() - start);
+ service = m_tracker.getService();
+ }
+ }
+ }
+ try {
+ return method.invoke(service, args);
+ }
+ catch (IllegalAccessException iae) {
+ method.setAccessible(true);
+ return method.invoke(service, args);
+ }
+ }
+}