blob: 31768ff9458385936dd0c1b9b26c60cb80f4479f [file] [log] [blame]
Marcel Offermanse14b3422009-11-25 23:04:32 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
Pierre De Rope8c339f2009-12-04 22:51:51 +000019package org.apache.felix.dm.impl.dependencies;
Marcel Offermanse14b3422009-11-25 23:04:32 +000020
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.lang.reflect.Modifier;
Marcel Offermans203bdad2009-12-04 09:23:04 +000024import java.util.ArrayList;
25import java.util.List;
Marcel Offermanse14b3422009-11-25 23:04:32 +000026import java.util.Properties;
27
Pierre De Rope8c339f2009-12-04 22:51:51 +000028import org.apache.felix.dm.dependencies.ResourceDependency;
29import org.apache.felix.dm.impl.Logger;
30import org.apache.felix.dm.resources.Resource;
31import org.apache.felix.dm.resources.ResourceHandler;
Marcel Offermanse14b3422009-11-25 23:04:32 +000032import org.osgi.framework.BundleContext;
33import org.osgi.framework.ServiceRegistration;
34
Pierre De Rope8c339f2009-12-04 22:51:51 +000035public class ResourceDependencyImpl implements ResourceDependency, ResourceHandler, DependencyActivation {
Marcel Offermanse14b3422009-11-25 23:04:32 +000036 private volatile BundleContext m_context;
37 private volatile ServiceRegistration m_registration;
38 private long m_resourceCounter;
39
40 private Object m_callbackInstance;
41 private String m_callbackAdded;
42 private String m_callbackChanged;
43 private String m_callbackRemoved;
44 private boolean m_autoConfig;
45 private final Logger m_logger;
46 private String m_autoConfigInstance;
Marcel Offermans203bdad2009-12-04 09:23:04 +000047// private DependencyService m_service;
48 protected List m_services = new ArrayList();
Marcel Offermanse14b3422009-11-25 23:04:32 +000049 private boolean m_isRequired;
50 private String m_resourceFilter;
51 private Resource m_resource;
52 private Resource m_trackedResource;
Marcel Offermans203bdad2009-12-04 09:23:04 +000053 private boolean m_isStarted;
Marcel Offermanse14b3422009-11-25 23:04:32 +000054
55
Pierre De Rope8c339f2009-12-04 22:51:51 +000056 public ResourceDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermanse14b3422009-11-25 23:04:32 +000057 m_context = context;
58 m_logger = logger;
59 m_autoConfig = true;
60 }
61
62 public synchronized boolean isAvailable() {
63 return m_resourceCounter > 0;
64 }
65
66 public boolean isRequired() {
67 return m_isRequired;
68 }
69
70 public boolean isInstanceBound() {
71 return false; // TODO for now we are never bound to the service implementation instance
72 }
73
74 public void start(DependencyService service) {
Marcel Offermans203bdad2009-12-04 09:23:04 +000075 boolean needsStarting = false;
76 synchronized (this) {
77 m_services.add(service);
78 if (!m_isStarted) {
79 m_isStarted = true;
80 needsStarting = true;
81 }
82 }
83 if (needsStarting) {
84 Properties props = new Properties();
85 props.setProperty(Resource.FILTER, m_resourceFilter);
86 m_registration = m_context.registerService(ResourceHandler.class.getName(), this, props);
87 }
Marcel Offermanse14b3422009-11-25 23:04:32 +000088 }
89
90 public void stop(DependencyService service) {
Marcel Offermans203bdad2009-12-04 09:23:04 +000091 boolean needsStopping = false;
92 synchronized (this) {
93 if (m_services.size() == 1 && m_services.contains(service)) {
94 m_isStarted = false;
95 needsStopping = true;
96 m_services.remove(service);
97 }
98 }
99 if (needsStopping) {
100 m_registration.unregister();
101 m_registration = null;
102 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000103 }
104
105 public void added(Resource resource) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000106 long counter;
Marcel Offermans203bdad2009-12-04 09:23:04 +0000107 Object[] services;
Marcel Offermanse14b3422009-11-25 23:04:32 +0000108 synchronized (this) {
109 m_resourceCounter++;
110 counter = m_resourceCounter;
111 m_resource = resource; // TODO this really sucks as a way to track a single resource
Marcel Offermans203bdad2009-12-04 09:23:04 +0000112 services = m_services.toArray();
Marcel Offermanse14b3422009-11-25 23:04:32 +0000113 }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000114 for (int i = 0; i < services.length; i++) {
115 DependencyService ds = (DependencyService) services[i];
116 if (counter == 1) {
117 ds.dependencyAvailable(this);
118 if (!isRequired()) {
119 invokeAdded(ds, resource);
120 }
121 }
122 else {
123 ds.dependencyChanged(this);
124 invokeAdded(ds, resource);
125 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000126 }
127 }
128
129 public void changed(Resource resource) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000130 Object[] services;
131 synchronized (this) {
132 services = m_services.toArray();
133 }
134 for (int i = 0; i < services.length; i++) {
135 DependencyService ds = (DependencyService) services[i];
136 invokeChanged(ds, resource);
137 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000138 }
139
140 public void removed(Resource resource) {
141 long counter;
Marcel Offermans203bdad2009-12-04 09:23:04 +0000142 Object[] services;
Marcel Offermanse14b3422009-11-25 23:04:32 +0000143 synchronized (this) {
144 m_resourceCounter--;
145 counter = m_resourceCounter;
Marcel Offermans203bdad2009-12-04 09:23:04 +0000146 services = m_services.toArray();
Marcel Offermanse14b3422009-11-25 23:04:32 +0000147 }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000148 for (int i = 0; i < services.length; i++) {
149 DependencyService ds = (DependencyService) services[i];
150 if (counter == 0) {
151 ds.dependencyUnavailable(this);
152 if (!isRequired()) {
153 invokeRemoved(ds, resource);
154 }
155 }
156 else {
157 ds.dependencyChanged(this);
158 invokeRemoved(ds, resource);
159 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000160 }
161 }
162
Marcel Offermans203bdad2009-12-04 09:23:04 +0000163 public void invokeAdded(DependencyService ds, Resource serviceInstance) {
164 Object[] callbackInstances = getCallbackInstances(ds);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000165 if ((callbackInstances != null) && (m_callbackAdded != null)) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000166 invokeCallbackMethod(callbackInstances, m_callbackAdded, serviceInstance);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000167 }
168 }
169
Marcel Offermans203bdad2009-12-04 09:23:04 +0000170 public void invokeChanged(DependencyService ds, Resource serviceInstance) {
171 Object[] callbackInstances = getCallbackInstances(ds);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000172 if ((callbackInstances != null) && (m_callbackChanged != null)) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000173 invokeCallbackMethod(callbackInstances, m_callbackChanged, serviceInstance);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000174 }
175 }
176
Marcel Offermans203bdad2009-12-04 09:23:04 +0000177 public void invokeRemoved(DependencyService ds, Resource serviceInstance) {
178 Object[] callbackInstances = getCallbackInstances(ds);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000179 if ((callbackInstances != null) && (m_callbackRemoved != null)) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000180 invokeCallbackMethod(callbackInstances, m_callbackRemoved, serviceInstance);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000181 }
182 }
183
Marcel Offermanse14b3422009-11-25 23:04:32 +0000184 /**
185 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
186 * dependency is added or removed. When you specify callbacks, the auto configuration
187 * feature is automatically turned off, because we're assuming you don't need it in this
188 * case.
189 *
190 * @param added the method to call when a service was added
191 * @param removed the method to call when a service was removed
192 * @return this service dependency
193 */
194 public synchronized ResourceDependency setCallbacks(String added, String removed) {
195 return setCallbacks(null, added, null, removed);
196 }
197
198 /**
199 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
200 * dependency is added, changed or removed. When you specify callbacks, the auto
201 * configuration feature is automatically turned off, because we're assuming you don't
202 * need it in this case.
203 *
204 * @param added the method to call when a service was added
205 * @param changed the method to call when a service was changed
206 * @param removed the method to call when a service was removed
207 * @return this service dependency
208 */
209 public synchronized ResourceDependency setCallbacks(String added, String changed, String removed) {
210 return setCallbacks(null, added, changed, removed);
211 }
212
213 /**
214 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
215 * dependency is added or removed. They are called on the instance you provide. When you
216 * specify callbacks, the auto configuration feature is automatically turned off, because
217 * we're assuming you don't need it in this case.
218 *
219 * @param instance the instance to call the callbacks on
220 * @param added the method to call when a service was added
221 * @param removed the method to call when a service was removed
222 * @return this service dependency
223 */
224 public synchronized ResourceDependency setCallbacks(Object instance, String added, String removed) {
225 return setCallbacks(instance, added, null, removed);
226 }
227
228 /**
229 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
230 * dependency is added, changed or removed. They are called on the instance you provide. When you
231 * specify callbacks, the auto configuration feature is automatically turned off, because
232 * we're assuming you don't need it in this case.
233 *
234 * @param instance the instance to call the callbacks on
235 * @param added the method to call when a service was added
236 * @param changed the method to call when a service was changed
237 * @param removed the method to call when a service was removed
238 * @return this service dependency
239 */
240 public synchronized ResourceDependency setCallbacks(Object instance, String added, String changed, String removed) {
241 ensureNotActive();
242 // if at least one valid callback is specified, we turn off auto configuration
243 if (added != null || removed != null || changed != null) {
244 setAutoConfig(false);
245 }
246 m_callbackInstance = instance;
247 m_callbackAdded = added;
248 m_callbackChanged = changed;
249 m_callbackRemoved = removed;
250 return this;
251 }
252
253 private void ensureNotActive() {
254 if (m_registration != null) {
255 throw new IllegalStateException("Cannot modify state while active.");
256 }
257 }
258
259 /**
260 * Sets auto configuration for this service. Auto configuration allows the
261 * dependency to fill in any attributes in the service implementation that
262 * are of the same type as this dependency. Default is on.
263 *
264 * @param autoConfig the value of auto config
265 * @return this service dependency
266 */
267 public synchronized ResourceDependency setAutoConfig(boolean autoConfig) {
268 ensureNotActive();
269 m_autoConfig = autoConfig;
270 return this;
271 }
272
273 /**
274 * Sets auto configuration for this service. Auto configuration allows the
275 * dependency to fill in the attribute in the service implementation that
276 * has the same type and instance name.
277 *
278 * @param instanceName the name of attribute to auto config
279 * @return this service dependency
280 */
281 public synchronized ResourceDependency setAutoConfig(String instanceName) {
282 ensureNotActive();
283 m_autoConfig = (instanceName != null);
284 m_autoConfigInstance = instanceName;
285 return this;
286 }
287
288 private void invokeCallbackMethod(Object[] instances, String methodName, Object service) {
289 for (int i = 0; i < instances.length; i++) {
290 try {
291 invokeCallbackMethod(instances[i], methodName, service);
292 }
293 catch (NoSuchMethodException e) {
294 m_logger.log(Logger.LOG_DEBUG, "Method '" + methodName + "' does not exist on " + instances[i] + ". Callback skipped.");
295 }
296 }
297 }
298
299 private void invokeCallbackMethod(Object instance, String methodName, Object service) throws NoSuchMethodException {
300 Class currentClazz = instance.getClass();
301 boolean done = false;
302 while (!done && currentClazz != null) {
303 done = invokeMethod(instance, currentClazz, methodName,
304 new Class[][] {{Resource.class}, {Object.class}, {}},
305 new Object[][] {{service}, {service}, {}},
306 false);
307 if (!done) {
308 currentClazz = currentClazz.getSuperclass();
309 }
310 }
311 if (!done && currentClazz == null) {
312 throw new NoSuchMethodException(methodName);
313 }
314 }
315
316 private boolean invokeMethod(Object object, Class clazz, String name, Class[][] signatures, Object[][] parameters, boolean isSuper) {
317 Method m = null;
318 for (int i = 0; i < signatures.length; i++) {
319 Class[] signature = signatures[i];
320 try {
321 m = clazz.getDeclaredMethod(name, signature);
322 if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
323 m.setAccessible(true);
324 try {
325 m.invoke(object, parameters[i]);
326 }
327 catch (InvocationTargetException e) {
328 m_logger.log(Logger.LOG_ERROR, "Exception while invoking method " + m + ".", e);
329 }
330 // we did find and invoke the method, so we return true
331 return true;
332 }
333 }
334 catch (NoSuchMethodException e) {
335 // ignore this and keep looking
336 }
337 catch (Exception e) {
338 // could not even try to invoke the method
339 m_logger.log(Logger.LOG_ERROR, "Exception while trying to invoke method " + m + ".", e);
340 }
341 }
342 return false;
343 }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000344
345 private synchronized Object[] getCallbackInstances(DependencyService ds) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000346 if (m_callbackInstance == null) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000347 return ds.getCompositionInstances();
Marcel Offermanse14b3422009-11-25 23:04:32 +0000348 }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000349 else {
350 return new Object[] { m_callbackInstance };
351 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000352 }
353
354 public ResourceDependency setResource(Resource resource) {
355 m_trackedResource = resource;
356 return this;
357 }
358
359 public synchronized ResourceDependency setRequired(boolean required) {
360 ensureNotActive();
361 m_isRequired = required;
362 return this;
363 }
364
365 public ResourceDependency setFilter(String resourceFilter) {
366 m_resourceFilter = resourceFilter;
367 return this;
368 }
369 public synchronized boolean isAutoConfig() {
370 return m_autoConfig;
371 }
372
373 public Resource getResource() {
374 System.out.println("Fetching resource");
375 return m_resource;
376 }
377}