blob: 73518b28e8d9a9dc1ca11203b9630ed4a8bca33a [file] [log] [blame]
Pierre De Ropb9dee1a2009-12-04 22:59:03 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
3 * agreements. See the NOTICE file distributed with this work for additional information
4 * regarding copyright ownership. The ASF licenses this file to you under the Apache License,
5 * Version 2.0 (the "License"); you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
7 * required by applicable law or agreed to in writing, software distributed under the License is
8 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
9 * or implied. See the License for the specific language governing permissions and limitations
10 * under the License.
11 */
Pierre De Ropc2429862009-12-04 23:00:03 +000012package org.apache.felix.dm.impl.dependencies;
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000013
14import java.lang.reflect.InvocationHandler;
15import java.lang.reflect.Method;
16import java.lang.reflect.Proxy;
17
Pierre De Ropc2429862009-12-04 23:00:03 +000018import org.apache.felix.dm.DependencyActivatorBase;
Marcel Offermans3d921212010-08-09 13:37:02 +000019import org.apache.felix.dm.DependencyService;
Pierre De Rop5fed9ef2011-01-26 16:48:33 +000020import org.apache.felix.dm.ServiceDependency;
Marcel Offermans8b93efa2010-07-02 18:27:21 +000021import org.apache.felix.dm.TemporalServiceDependency;
Pierre De Ropc2429862009-12-04 23:00:03 +000022import org.apache.felix.dm.impl.Logger;
Pierre De Rop5fed9ef2011-01-26 16:48:33 +000023import org.osgi.framework.Bundle;
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000024import org.osgi.framework.BundleContext;
25import org.osgi.framework.ServiceReference;
26
27/**
Marcel Offermans5be5f142011-04-26 10:47:12 +000028 * Temporal Service dependency implementation, used to hide temporary service dependency "outage".
29 * Only works with a required dependency.
30 *
31 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
32 */
Pierre De Ropc2429862009-12-04 23:00:03 +000033public class TemporalServiceDependencyImpl extends ServiceDependencyImpl implements TemporalServiceDependency, InvocationHandler {
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000034 // Max millis to wait for service availability.
35 private long m_timeout = 30000;
Pierre De Rop5fed9ef2011-01-26 16:48:33 +000036
37 // Dependency service currently used (with highest rank or highest service id).
38 private volatile Object m_cachedService;
39
40 // Framework bundle (we use it to detect if the framework is stopping)
41 private final Bundle m_frameworkBundle;
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000042
Pierre De Rop6aaa4042013-10-25 18:40:16 +000043 // The service proxy, which blocks when service is not available.
44 private Object m_serviceInstance;
45
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000046 /**
47 * Creates a new Temporal Service Dependency.
48 *
49 * @param context The bundle context of the bundle which is instantiating this dependency object
50 * @param logger the logger our Internal logger for logging events.
51 * @see DependencyActivatorBase#createTemporalServiceDependency()
52 */
Pierre De Ropc2429862009-12-04 23:00:03 +000053 public TemporalServiceDependencyImpl(BundleContext context, Logger logger) {
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000054 super(context, logger);
55 super.setRequired(true);
Pierre De Rop5fed9ef2011-01-26 16:48:33 +000056 m_frameworkBundle = context.getBundle(0);
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000057 }
58
59 /**
60 * Sets the timeout for this temporal dependency. Specifying a timeout value of zero means that there is no timeout period,
61 * and an invocation on a missing service will fail immediately.
62 *
63 * @param timeout the dependency timeout value greater or equals to 0
64 * @throws IllegalArgumentException if the timeout is negative
65 * @return this temporal dependency
66 */
Pierre De Rop5fed9ef2011-01-26 16:48:33 +000067 public synchronized TemporalServiceDependency setTimeout(long timeout) {
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000068 if (timeout < 0) {
69 throw new IllegalArgumentException("Invalid timeout value: " + timeout);
70 }
71 m_timeout = timeout;
72 return this;
73 }
74
75 /**
Pierre De Rop5fed9ef2011-01-26 16:48:33 +000076 * Sets the required flag which determines if this service is required or not. This method
77 * just override the superclass method in order to check if the required flag is true
78 * (optional dependency is not supported by this class).
79 *
80 * @param required the required flag, which must be set to true
81 * @return this service dependency
82 * @throws IllegalArgumentException if the "required" parameter is not true.
83 */
Pierre De Rop6aaa4042013-10-25 18:40:16 +000084 //@Override
Pierre De Rop5fed9ef2011-01-26 16:48:33 +000085 public ServiceDependency setRequired(boolean required) {
86 if (! required) {
87 throw new IllegalArgumentException("A Temporal Service dependency can't be optional");
88 }
89 super.setRequired(required);
90 return this;
91 }
92
93 /**
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000094 * The ServiceTracker calls us here in order to inform about a service arrival.
95 */
Pierre De Rop6aaa4042013-10-25 18:40:16 +000096 //@Override
Pierre De Ropb9dee1a2009-12-04 22:59:03 +000097 public synchronized void addedService(ServiceReference ref, Object service) {
Pierre De Rop5fed9ef2011-01-26 16:48:33 +000098 // Update our service cache, using the tracker. We do this because the
99 // just added service might not be the service with the highest rank ...
100 m_cachedService = m_tracker.getService();
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000101 boolean makeAvailable = makeAvailable();
102 if (makeAvailable) {
103 m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] { m_trackedServiceName }, this);
104 }
105 Object[] services = m_services.toArray();
106 for (int i = 0; i < services.length; i++) {
107 DependencyService ds = (DependencyService) services[i];
108 if (makeAvailable) {
109 ds.dependencyAvailable(this);
110 }
111 }
112 if (!makeAvailable) {
113 notifyAll();
114 }
115 }
116
117 /**
118 * The ServiceTracker calls us here when a tracked service properties are modified.
119 */
Pierre De Rop6aaa4042013-10-25 18:40:16 +0000120 //@Override
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000121 public void modifiedService(ServiceReference ref, Object service) {
122 // We don't care.
123 }
124
125 /**
126 * The ServiceTracker calls us here when a tracked service is lost.
127 */
Pierre De Rop6aaa4042013-10-25 18:40:16 +0000128 //@Override
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000129 public synchronized void removedService(ServiceReference ref, Object service) {
Pierre De Rop5fed9ef2011-01-26 16:48:33 +0000130 // If we detect that the fwk is stopping, we behave as our superclass. That is:
131 // the lost dependency has to trigger our service deactivation, since the fwk is stopping
132 // and the lost dependency won't come up anymore.
133 if (m_frameworkBundle.getState() == Bundle.STOPPING) {
134 // Important: Notice that calling "super.removedService() might invoke our service "stop"
135 // callback, which in turn might invoke the just removed service dependency. In this case,
136 // our "invoke" method won't use the tracker to get the service dependency (because at this point,
137 // the tracker has withdrawn its reference to the lost service). So, you will see that the "invoke"
138 // method will use the "m_cachedService" instead ...
139 super.removedService(ref, service);
140 } else {
141 // Unget what we got in addingService (see ServiceTracker 701.4.1)
142 m_context.ungetService(ref);
143 // Now, ask the service tracker if there is another available service (with a lower rank).
144 // If no more service dependencies are available, the tracker will then return null;
145 // and our invoke method will block the service method invocation, until another service
146 // becomes available.
147 m_cachedService = m_tracker.getService();
148 }
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000149 }
150
151 /**
152 * @returns our dependency instance. Unlike in ServiceDependency, we always returns our proxy.
153 */
Pierre De Rop6aaa4042013-10-25 18:40:16 +0000154 //@Override
155 protected synchronized Object getService() {
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000156 return m_serviceInstance;
157 }
158
Pierre De Rop6aaa4042013-10-25 18:40:16 +0000159 //@Override
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000160 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Pierre De Rop5fed9ef2011-01-26 16:48:33 +0000161 Object service = m_cachedService;
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000162 if (service == null) {
163 synchronized (this) {
164 long start = System.currentTimeMillis();
165 long waitTime = m_timeout;
166 while (service == null) {
167 if (waitTime <= 0) {
168 throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
169 }
170 try {
171 wait(waitTime);
172 }
173 catch (InterruptedException e) {
174 throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
175 }
176 waitTime = m_timeout - (System.currentTimeMillis() - start);
Pierre De Rop5fed9ef2011-01-26 16:48:33 +0000177 service = m_cachedService;
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000178 }
Pierre De Rop5fed9ef2011-01-26 16:48:33 +0000179
Pierre De Ropb9dee1a2009-12-04 22:59:03 +0000180 }
181 }
182 try {
183 return method.invoke(service, args);
184 }
185 catch (IllegalAccessException iae) {
186 method.setAccessible(true);
187 return method.invoke(service, args);
188 }
189 }
190}