/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.felix.dm.impl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.apache.felix.dm.DependencyActivatorBase; import org.apache.felix.dm.ServiceDependency; import org.apache.felix.dm.context.DependencyContext; import org.apache.felix.dm.context.EventType; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; /** * Temporal Service dependency implementation, used to hide temporary service dependency "outage". * Only works with a required dependency. * * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class TemporalServiceDependencyImpl extends ServiceDependencyImpl implements ServiceDependency, InvocationHandler { // Max millis to wait for service availability. private final long m_timeout; // 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 volatile 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, long timeout) { super.setRequired(true); if (timeout < 0) { throw new IllegalArgumentException("Invalid timeout value: " + timeout); } m_timeout = timeout; m_frameworkBundle = context.getBundle(0); } /** * Creates a clone of an existing temporal service dependency. */ public TemporalServiceDependencyImpl(TemporalServiceDependencyImpl prototype) { super(prototype); super.setRequired(true); m_timeout = prototype.m_timeout; m_frameworkBundle = prototype.m_frameworkBundle; } @Override public DependencyContext createCopy() { return new TemporalServiceDependencyImpl(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. */ @SuppressWarnings("rawtypes") @Override public void addedService(ServiceReference ref, Object event) { // Update our service cache, using the tracker. We do this because the // just added service might not be the service with the highest rank ... boolean makeAvailable = false; synchronized (this) { if (m_serviceInstance == null) { m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] { m_trackedServiceName }, this); makeAvailable = true; } } if (makeAvailable) { getComponentContext().handleEvent(this, EventType.ADDED, new ServiceEventImpl(m_component, ref, m_serviceInstance)); } else { // This added will possibly unblock our invoke() method (if it's blocked in m_tracker.waitForService method). } } /** * The ServiceTracker calls us here when a tracked service properties are modified. */ @SuppressWarnings("rawtypes") @Override public void modifiedService(ServiceReference ref, Object service) { // We don't care. } /** * The ServiceTracker calls us here when a tracked service is lost. */ @SuppressWarnings("rawtypes") @Override public void removedService(ServiceReference ref, Object event) { ServiceEventImpl eventImpl = (ServiceEventImpl) event; // 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 ... boolean makeUnavailable = false; synchronized (this) { if (m_tracker.getService() == null) { makeUnavailable = true; } } if (makeUnavailable) { // the event.close method will unget the service. m_component.handleEvent(this, EventType.REMOVED, new ServiceEventImpl(m_component, ref, m_serviceInstance)); } } else { eventImpl.close(); // will unget the service. // if there is no available services, the next call to invoke() method will block until another service // becomes available. Else the next call to invoke() will return that highest ranked available service. } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ServiceEventImpl event = null; try { event = (ServiceEventImpl) m_tracker.waitForService(m_timeout); } catch (InterruptedException e) { } if (event == null) { throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName()); } Object service = event.getEvent(); if (service == null) { throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName()); } try { try { return method.invoke(service, args); } catch (IllegalAccessException iae) { method.setAccessible(true); return method.invoke(service, args); } } catch (InvocationTargetException e) { throw e.getTargetException(); } } }