blob: 95dda61af26c789ed06e6d84ad42bbee548bc9eb [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
Marcel Offermans203bdad2009-12-04 09:23:04 +000021import java.util.ArrayList;
Marcel Offermans117aa2f2009-12-10 09:48:17 +000022import java.util.Dictionary;
Marcel Offermans203bdad2009-12-04 09:23:04 +000023import java.util.List;
Marcel Offermanse14b3422009-11-25 23:04:32 +000024import java.util.Properties;
25
Pierre De Rope8c339f2009-12-04 22:51:51 +000026import org.apache.felix.dm.dependencies.ResourceDependency;
27import org.apache.felix.dm.impl.Logger;
28import org.apache.felix.dm.resources.Resource;
29import org.apache.felix.dm.resources.ResourceHandler;
Marcel Offermansea89b862010-06-24 13:14:43 +000030import org.apache.felix.dm.service.Service;
Marcel Offermanse14b3422009-11-25 23:04:32 +000031import org.osgi.framework.BundleContext;
32import org.osgi.framework.ServiceRegistration;
33
Marcel Offermans61a81142010-04-02 15:16:50 +000034public class ResourceDependencyImpl extends DependencyBase implements ResourceDependency, ResourceHandler, DependencyActivation {
Marcel Offermanse14b3422009-11-25 23:04:32 +000035 private volatile BundleContext m_context;
36 private volatile ServiceRegistration m_registration;
Marcel Offermansb99e7e42009-12-10 09:11:32 +000037// private long m_resourceCounter;
Marcel Offermanse14b3422009-11-25 23:04:32 +000038
39 private Object m_callbackInstance;
40 private String m_callbackAdded;
41 private String m_callbackChanged;
42 private String m_callbackRemoved;
43 private boolean m_autoConfig;
Marcel Offermanse14b3422009-11-25 23:04:32 +000044 private String m_autoConfigInstance;
Marcel Offermans203bdad2009-12-04 09:23:04 +000045 protected List m_services = new ArrayList();
Marcel Offermanse14b3422009-11-25 23:04:32 +000046 private boolean m_isRequired;
47 private String m_resourceFilter;
Marcel Offermanse14b3422009-11-25 23:04:32 +000048 private Resource m_trackedResource;
Marcel Offermans203bdad2009-12-04 09:23:04 +000049 private boolean m_isStarted;
Marcel Offermansb99e7e42009-12-10 09:11:32 +000050 private List m_resources = new ArrayList();
51 private Resource m_resourceInstance;
Marcel Offermans117aa2f2009-12-10 09:48:17 +000052 private boolean m_propagate;
Marcel Offermanse14b3422009-11-25 23:04:32 +000053
Pierre De Rope8c339f2009-12-04 22:51:51 +000054 public ResourceDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermans61a81142010-04-02 15:16:50 +000055 super(logger);
Marcel Offermanse14b3422009-11-25 23:04:32 +000056 m_context = context;
Marcel Offermanse14b3422009-11-25 23:04:32 +000057 m_autoConfig = true;
58 }
59
60 public synchronized boolean isAvailable() {
Marcel Offermansb99e7e42009-12-10 09:11:32 +000061 return m_resources.size() > 0;
Marcel Offermanse14b3422009-11-25 23:04:32 +000062 }
63
Marcel Offermanse14b3422009-11-25 23:04:32 +000064 public void start(DependencyService service) {
Marcel Offermans203bdad2009-12-04 09:23:04 +000065 boolean needsStarting = false;
66 synchronized (this) {
67 m_services.add(service);
68 if (!m_isStarted) {
69 m_isStarted = true;
70 needsStarting = true;
71 }
72 }
73 if (needsStarting) {
Marcel Offermans266eee52009-12-08 10:56:09 +000074 Properties props = null;
75 if (m_resourceFilter != null) {
76 props = new Properties();
77 props.setProperty(Resource.FILTER, m_resourceFilter);
78 }
Marcel Offermans203bdad2009-12-04 09:23:04 +000079 m_registration = m_context.registerService(ResourceHandler.class.getName(), this, props);
80 }
Marcel Offermanse14b3422009-11-25 23:04:32 +000081 }
82
83 public void stop(DependencyService service) {
Marcel Offermans203bdad2009-12-04 09:23:04 +000084 boolean needsStopping = false;
85 synchronized (this) {
86 if (m_services.size() == 1 && m_services.contains(service)) {
87 m_isStarted = false;
88 needsStopping = true;
89 m_services.remove(service);
90 }
91 }
92 if (needsStopping) {
93 m_registration.unregister();
94 m_registration = null;
95 }
Marcel Offermanse14b3422009-11-25 23:04:32 +000096 }
97
98 public void added(Resource resource) {
Marcel Offermansb99e7e42009-12-10 09:11:32 +000099 if (m_trackedResource == null || m_trackedResource.equals(resource)) {
100 long counter;
101 Object[] services;
102 synchronized (this) {
103 m_resources.add(resource);
104 counter = m_resources.size();
105 services = m_services.toArray();
106 }
107 for (int i = 0; i < services.length; i++) {
108 DependencyService ds = (DependencyService) services[i];
109 if (counter == 1) {
110 ds.dependencyAvailable(this);
111 if (!isRequired()) {
112 invokeAdded(ds, resource);
113 }
114 }
115 else {
116 ds.dependencyChanged(this);
Marcel Offermans203bdad2009-12-04 09:23:04 +0000117 invokeAdded(ds, resource);
118 }
119 }
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000120 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000121 }
122
123 public void changed(Resource resource) {
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000124 if (m_trackedResource == null || m_trackedResource.equals(resource)) {
125 Object[] services;
126 synchronized (this) {
127 services = m_services.toArray();
128 }
129 for (int i = 0; i < services.length; i++) {
130 DependencyService ds = (DependencyService) services[i];
131 invokeChanged(ds, resource);
132 }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000133 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000134 }
135
136 public void removed(Resource resource) {
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000137 if (m_trackedResource == null || m_trackedResource.equals(resource)) {
138 long counter;
139 Object[] services;
140 synchronized (this) {
141 m_resources.remove(resource);
142 counter = m_resources.size();
143 services = m_services.toArray();
144 }
145 for (int i = 0; i < services.length; i++) {
146 DependencyService ds = (DependencyService) services[i];
147 if (counter == 0) {
148 ds.dependencyUnavailable(this);
149 if (!isRequired()) {
150 invokeRemoved(ds, resource);
151 }
152 }
153 else {
154 ds.dependencyChanged(this);
Marcel Offermans203bdad2009-12-04 09:23:04 +0000155 invokeRemoved(ds, resource);
156 }
157 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000158 }
159 }
160
Marcel Offermans203bdad2009-12-04 09:23:04 +0000161 public void invokeAdded(DependencyService ds, Resource serviceInstance) {
Marcel Offermansea89b862010-06-24 13:14:43 +0000162 invoke(ds, serviceInstance, m_callbackAdded);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000163 }
164
Marcel Offermans203bdad2009-12-04 09:23:04 +0000165 public void invokeChanged(DependencyService ds, Resource serviceInstance) {
Marcel Offermansea89b862010-06-24 13:14:43 +0000166 invoke(ds, serviceInstance, m_callbackChanged);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000167 }
168
Marcel Offermans203bdad2009-12-04 09:23:04 +0000169 public void invokeRemoved(DependencyService ds, Resource serviceInstance) {
Marcel Offermansea89b862010-06-24 13:14:43 +0000170 invoke(ds, serviceInstance, m_callbackRemoved);
171 }
172
173 private void invoke(DependencyService ds, Resource serviceInstance, String name) {
174 if (name != null) {
175 ds.invokeCallbackMethod(getCallbackInstances(ds), name,
176 new Class[][] {{ Service.class, Resource.class }, { Service.class, Object.class }, { Service.class }, { Resource.class }, { Object.class }, {}},
177 new Object[][] {{ ds.getServiceInterface(), serviceInstance }, { ds.getServiceInterface(), serviceInstance }, { ds.getServiceInterface() }, { serviceInstance }, { serviceInstance }, {}}
Marcel Offermans61a81142010-04-02 15:16:50 +0000178 );
Marcel Offermanse14b3422009-11-25 23:04:32 +0000179 }
180 }
Marcel Offermansea89b862010-06-24 13:14:43 +0000181
Marcel Offermanse14b3422009-11-25 23:04:32 +0000182 /**
183 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
184 * dependency is added or removed. When you specify callbacks, the auto configuration
185 * feature is automatically turned off, because we're assuming you don't need it in this
186 * case.
187 *
188 * @param added the method to call when a service was added
189 * @param removed the method to call when a service was removed
190 * @return this service dependency
191 */
192 public synchronized ResourceDependency setCallbacks(String added, String removed) {
193 return setCallbacks(null, added, null, removed);
194 }
195
196 /**
197 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
198 * dependency is added, changed or removed. When you specify callbacks, the auto
199 * configuration feature is automatically turned off, because we're assuming you don't
200 * need it in this case.
201 *
202 * @param added the method to call when a service was added
203 * @param changed the method to call when a service was changed
204 * @param removed the method to call when a service was removed
205 * @return this service dependency
206 */
207 public synchronized ResourceDependency setCallbacks(String added, String changed, String removed) {
208 return setCallbacks(null, added, changed, removed);
209 }
210
211 /**
212 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
213 * dependency is added or removed. They are called on the instance you provide. When you
214 * specify callbacks, the auto configuration feature is automatically turned off, because
215 * we're assuming you don't need it in this case.
216 *
217 * @param instance the instance to call the callbacks on
218 * @param added the method to call when a service was added
219 * @param removed the method to call when a service was removed
220 * @return this service dependency
221 */
222 public synchronized ResourceDependency setCallbacks(Object instance, String added, String removed) {
223 return setCallbacks(instance, added, null, removed);
224 }
225
226 /**
227 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
228 * dependency is added, changed or removed. They are called on the instance you provide. When you
229 * specify callbacks, the auto configuration feature is automatically turned off, because
230 * we're assuming you don't need it in this case.
231 *
232 * @param instance the instance to call the callbacks on
233 * @param added the method to call when a service was added
234 * @param changed the method to call when a service was changed
235 * @param removed the method to call when a service was removed
236 * @return this service dependency
237 */
238 public synchronized ResourceDependency setCallbacks(Object instance, String added, String changed, String removed) {
239 ensureNotActive();
240 // if at least one valid callback is specified, we turn off auto configuration
241 if (added != null || removed != null || changed != null) {
242 setAutoConfig(false);
243 }
244 m_callbackInstance = instance;
245 m_callbackAdded = added;
246 m_callbackChanged = changed;
247 m_callbackRemoved = removed;
248 return this;
249 }
250
251 private void ensureNotActive() {
252 if (m_registration != null) {
253 throw new IllegalStateException("Cannot modify state while active.");
254 }
255 }
256
257 /**
258 * Sets auto configuration for this service. Auto configuration allows the
259 * dependency to fill in any attributes in the service implementation that
260 * are of the same type as this dependency. Default is on.
261 *
262 * @param autoConfig the value of auto config
263 * @return this service dependency
264 */
265 public synchronized ResourceDependency setAutoConfig(boolean autoConfig) {
266 ensureNotActive();
267 m_autoConfig = autoConfig;
268 return this;
269 }
270
271 /**
272 * Sets auto configuration for this service. Auto configuration allows the
273 * dependency to fill in the attribute in the service implementation that
274 * has the same type and instance name.
275 *
276 * @param instanceName the name of attribute to auto config
277 * @return this service dependency
278 */
279 public synchronized ResourceDependency setAutoConfig(String instanceName) {
280 ensureNotActive();
281 m_autoConfig = (instanceName != null);
282 m_autoConfigInstance = instanceName;
283 return this;
284 }
285
Marcel Offermans61a81142010-04-02 15:16:50 +0000286// private void invokeCallbackMethod(Object[] instances, String methodName, Object service) {
287// for (int i = 0; i < instances.length; i++) {
288// try {
289// invokeCallbackMethod(instances[i], methodName, service);
290// }
291// catch (NoSuchMethodException e) {
292// m_logger.log(Logger.LOG_DEBUG, "Method '" + methodName + "' does not exist on " + instances[i] + ". Callback skipped.");
293// }
294// }
295// }
296//
297// private void invokeCallbackMethod(Object instance, String methodName, Object service) throws NoSuchMethodException {
298// Class currentClazz = instance.getClass();
299// boolean done = false;
300// while (!done && currentClazz != null) {
301// done = invokeMethod(instance, currentClazz, methodName,
302// new Class[][] {{Resource.class}, {Object.class}, {}},
303// new Object[][] {{service}, {service}, {}},
304// false);
305// if (!done) {
306// currentClazz = currentClazz.getSuperclass();
307// }
308// }
309// if (!done && currentClazz == null) {
310// throw new NoSuchMethodException(methodName);
311// }
312// }
313//
314// private boolean invokeMethod(Object object, Class clazz, String name, Class[][] signatures, Object[][] parameters, boolean isSuper) {
315// Method m = null;
316// for (int i = 0; i < signatures.length; i++) {
317// Class[] signature = signatures[i];
318// try {
319// m = clazz.getDeclaredMethod(name, signature);
320// if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
321// m.setAccessible(true);
322// try {
323// m.invoke(object, parameters[i]);
324// }
325// catch (InvocationTargetException e) {
326// m_logger.log(Logger.LOG_ERROR, "Exception while invoking method " + m + ".", e);
327// }
328// // we did find and invoke the method, so we return true
329// return true;
330// }
331// }
332// catch (NoSuchMethodException e) {
333// // ignore this and keep looking
334// }
335// catch (Exception e) {
336// // could not even try to invoke the method
337// m_logger.log(Logger.LOG_ERROR, "Exception while trying to invoke method " + m + ".", e);
338// }
339// }
340// return false;
341// }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000342
343 private synchronized Object[] getCallbackInstances(DependencyService ds) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000344 if (m_callbackInstance == null) {
Marcel Offermans203bdad2009-12-04 09:23:04 +0000345 return ds.getCompositionInstances();
Marcel Offermanse14b3422009-11-25 23:04:32 +0000346 }
Marcel Offermans203bdad2009-12-04 09:23:04 +0000347 else {
348 return new Object[] { m_callbackInstance };
349 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000350 }
351
352 public ResourceDependency setResource(Resource resource) {
353 m_trackedResource = resource;
354 return this;
355 }
356
357 public synchronized ResourceDependency setRequired(boolean required) {
358 ensureNotActive();
Marcel Offermans61a81142010-04-02 15:16:50 +0000359 setIsRequired(required);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000360 return this;
361 }
362
363 public ResourceDependency setFilter(String resourceFilter) {
Marcel Offermans001db052009-12-08 08:58:40 +0000364 ensureNotActive();
Marcel Offermanse14b3422009-11-25 23:04:32 +0000365 m_resourceFilter = resourceFilter;
366 return this;
367 }
368 public synchronized boolean isAutoConfig() {
369 return m_autoConfig;
370 }
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000371
Marcel Offermanse14b3422009-11-25 23:04:32 +0000372 public Resource getResource() {
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000373 return lookupResource();
Marcel Offermanse14b3422009-11-25 23:04:32 +0000374 }
Marcel Offermans001db052009-12-08 08:58:40 +0000375
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000376 private Resource lookupResource() {
377 try {
378 return (Resource) m_resources.get(0);
379 }
380 catch (IndexOutOfBoundsException e) {
381 return null;
382 }
383 }
384
Marcel Offermans001db052009-12-08 08:58:40 +0000385 public Object getAutoConfigInstance() {
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000386 return lookupResource();
Marcel Offermans001db052009-12-08 08:58:40 +0000387 }
388
389 public String getAutoConfigName() {
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000390 return m_autoConfigInstance;
Marcel Offermans001db052009-12-08 08:58:40 +0000391 }
392
393 public Class getAutoConfigType() {
394 return Resource.class;
395 }
396
397 public void invokeAdded(DependencyService service) {
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000398 // we remember these for future reference, needed for required callbacks
399 m_resourceInstance = lookupResource();
400 invokeAdded(service, m_resourceInstance);
Marcel Offermans001db052009-12-08 08:58:40 +0000401 }
402
403 public void invokeRemoved(DependencyService service) {
Marcel Offermansb99e7e42009-12-10 09:11:32 +0000404 invokeRemoved(service, m_resourceInstance);
405 m_resourceInstance = null;
Marcel Offermans001db052009-12-08 08:58:40 +0000406 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000407
408 public ResourceDependency setPropagate(boolean propagate) {
409 ensureNotActive();
410 m_propagate = propagate;
411 return this;
412 }
413
414 public Dictionary getProperties() {
415 Resource resource = lookupResource();
416 if (resource != null) {
417 Properties props = new Properties();
418 props.put(Resource.NAME, resource.getName());
419 props.put(Resource.PATH, resource.getPath());
420 props.put(Resource.REPOSITORY, resource.getRepository());
421 return props;
422 }
423 else {
424 throw new IllegalStateException("cannot find resource");
425 }
426 }
427
428 public boolean isPropagated() {
429 return m_propagate;
430 }
Marcel Offermans61a81142010-04-02 15:16:50 +0000431
432 public ResourceDependency setInstanceBound(boolean isInstanceBound) {
433 setIsInstanceBound(isInstanceBound);
434 return this;
435 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000436}