Return-Path: X-Original-To: apmail-felix-commits-archive@www.apache.org Delivered-To: apmail-felix-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 5CFCE105F6 for ; Tue, 18 Mar 2014 21:46:16 +0000 (UTC) Received: (qmail 94651 invoked by uid 500); 18 Mar 2014 21:46:15 -0000 Delivered-To: apmail-felix-commits-archive@felix.apache.org Received: (qmail 94583 invoked by uid 500); 18 Mar 2014 21:46:15 -0000 Mailing-List: contact commits-help@felix.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@felix.apache.org Delivered-To: mailing list commits@felix.apache.org Received: (qmail 94573 invoked by uid 99); 18 Mar 2014 21:46:15 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 18 Mar 2014 21:46:15 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 18 Mar 2014 21:46:12 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id BD01323888D7; Tue, 18 Mar 2014 21:45:50 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1579066 - /felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java Date: Tue, 18 Mar 2014 21:45:50 -0000 To: commits@felix.apache.org From: pderop@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20140318214550.BD01323888D7@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: pderop Date: Tue Mar 18 21:45:50 2014 New Revision: 1579066 URL: http://svn.apache.org/r1579066 Log: added timed service dependency impl Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java?rev=1579066&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java (added) +++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/TemporalServiceDependencyImpl.java Tue Mar 18 21:45:50 2014 @@ -0,0 +1,176 @@ +package dm.impl; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +import dm.ServiceDependency; +import dm.TemporalServiceDependency; + +/** + * Temporal Service dependency implementation, used to hide temporary service dependency "outage". + * Only works with a required dependency. + * + * @author Felix Project Team + */ +public class TemporalServiceDependencyImpl extends ServiceDependencyImpl implements TemporalServiceDependency, InvocationHandler { + // Max millis to wait for service availability. + private long m_timeout = 30000; + + // Dependency service currently used (with highest rank or highest service id). + private volatile Object m_cachedService; + + // Framework bundle (we use it to detect if the framework is stopping) + private final Bundle m_frameworkBundle; + + // The service proxy, which blocks when service is not available. + private Object m_serviceInstance; + + /** + * 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 TemporalServiceDependencyImpl(BundleContext context, Logger logger) { + super(context, logger); + super.setRequired(true); + m_frameworkBundle = context.getBundle(0); + } + + /** + * 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 synchronized TemporalServiceDependency setTimeout(long timeout) { + if (timeout < 0) { + throw new IllegalArgumentException("Invalid timeout value: " + timeout); + } + m_timeout = timeout; + return this; + } + + /** + * Sets the required flag which determines if this service is required or not. This method + * just override the superclass method in order to check if the required flag is true + * (optional dependency is not supported by this class). + * + * @param required the required flag, which must be set to true + * @return this service dependency + * @throws IllegalArgumentException if the "required" parameter is not true. + */ + @Override + public ServiceDependency setRequired(boolean required) { + if (! required) { + throw new IllegalArgumentException("A Temporal Service dependency can't be optional"); + } + super.setRequired(required); + return this; + } + + /** + * The ServiceTracker calls us here in order to inform about a service arrival. + */ + @Override + public void addedService(ServiceReference ref, Object service) { + // Update our service cache, using the tracker. We do this because the + // just added service might not be the service with the highest rank ... + m_cachedService = m_tracker.getService(); + boolean makeAvailable = ! isAvailable(); + if (makeAvailable) { + m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] { m_trackedServiceName }, this); + } + add(new ServiceEventImpl(ref, service)); + if (!makeAvailable) { + synchronized (this) { + notifyAll(); + } + } + } + + /** + * The ServiceTracker calls us here when a tracked service properties are modified. + */ + @Override + public void modifiedService(ServiceReference ref, Object service) { + // We don't care. + } + + /** + * The ServiceTracker calls us here when a tracked service is lost. + */ + @Override + public void removedService(ServiceReference ref, Object service) { + // If we detect that the fwk is stopping, we behave as our superclass. That is: + // the lost dependency has to trigger our service deactivation, since the fwk is stopping + // and the lost dependency won't come up anymore. + if (m_frameworkBundle.getState() == Bundle.STOPPING) { + // Important: Notice that calling "super.removedService() might invoke our service "stop" + // callback, which in turn might invoke the just removed service dependency. In this case, + // our "invoke" method won't use the tracker to get the service dependency (because at this point, + // the tracker has withdrawn its reference to the lost service). So, you will see that the "invoke" + // method will use the "m_cachedService" instead ... + super.removedService(ref, service); + } else { + // Unget what we got in addingService (see ServiceTracker 701.4.1) + m_context.ungetService(ref); + // Now, ask the service tracker if there is another available service (with a lower rank). + // If no more service dependencies are available, the tracker will then return null; + // and our invoke method will block the service method invocation, until another service + // becomes available. + m_cachedService = m_tracker.getService(); + } + } + + /** + * @returns our dependency instance. Unlike in ServiceDependency, we always returns our proxy. + */ + @Override + protected synchronized Object getService() { + return m_serviceInstance; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object service = m_cachedService; + 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_cachedService; + } + + } + } + try { + return method.invoke(service, args); + } + catch (IllegalAccessException iae) { + method.setAccessible(true); + return method.invoke(service, args); + } + } +}