blob: 810409139dfbd6ada140d58c91171c3662bbc63d [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 Rope4d80b92009-12-04 22:48:32 +000019package org.apache.felix.dm.impl.dependencies;
Marcel Offermanse14b3422009-11-25 23:04:32 +000020
Marcel Offermansd665eaf2010-02-16 15:56:35 +000021import java.lang.reflect.Proxy;
Marcel Offermanscae61362009-12-01 08:37:10 +000022import java.util.ArrayList;
Marcel Offermanse14b3422009-11-25 23:04:32 +000023import java.util.Dictionary;
Marcel Offermanscae61362009-12-01 08:37:10 +000024import java.util.List;
Marcel Offermanse14b3422009-11-25 23:04:32 +000025
Pierre De Rope4d80b92009-12-04 22:48:32 +000026import org.apache.felix.dm.dependencies.BundleDependency;
Marcel Offermansd665eaf2010-02-16 15:56:35 +000027import org.apache.felix.dm.impl.DefaultNullObject;
Pierre De Rope4d80b92009-12-04 22:48:32 +000028import org.apache.felix.dm.impl.Logger;
29import org.apache.felix.dm.impl.tracker.BundleTracker;
30import org.apache.felix.dm.impl.tracker.BundleTrackerCustomizer;
31import org.apache.felix.dm.management.ServiceComponentDependency;
Marcel Offermanse14b3422009-11-25 23:04:32 +000032import org.osgi.framework.Bundle;
33import org.osgi.framework.BundleContext;
34import org.osgi.framework.BundleEvent;
35import org.osgi.framework.Filter;
36import org.osgi.framework.InvalidSyntaxException;
37
Marcel Offermans61a81142010-04-02 15:16:50 +000038public class BundleDependencyImpl extends DependencyBase implements BundleDependency, BundleTrackerCustomizer, ServiceComponentDependency {
Marcel Offermanse14b3422009-11-25 23:04:32 +000039 private final BundleContext m_context;
Marcel Offermanse14b3422009-11-25 23:04:32 +000040 private boolean m_isStarted;
41 private BundleTracker m_tracker;
Marcel Offermansd66c5ce2009-11-26 09:58:44 +000042 private int m_stateMask = Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE;
Marcel Offermanscae61362009-12-01 08:37:10 +000043 private List m_services = new ArrayList();
Marcel Offermansb196d722009-11-26 17:12:12 +000044 private boolean m_isAvailable;
Marcel Offermanse14b3422009-11-25 23:04:32 +000045
46 private Object m_callbackInstance;
47 private String m_callbackAdded;
48 private String m_callbackChanged;
49 private String m_callbackRemoved;
50 private boolean m_autoConfig;
51 private Bundle m_bundleInstance;
52 private Filter m_filter;
53 private long m_bundleId = -1;
Marcel Offermansd665eaf2010-02-16 15:56:35 +000054 private boolean m_propagate;
55 private String m_autoConfigInstance;
56 private Object m_nullObject;
57 private boolean m_autoConfigInvoked;
58
Marcel Offermansb196d722009-11-26 17:12:12 +000059
Pierre De Rope4d80b92009-12-04 22:48:32 +000060 public BundleDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermansb196d722009-11-26 17:12:12 +000061 super(logger);
Marcel Offermanse14b3422009-11-25 23:04:32 +000062 m_context = context;
Marcel Offermanse14b3422009-11-25 23:04:32 +000063 m_autoConfig = true;
64 }
65
Marcel Offermans61a81142010-04-02 15:16:50 +000066 public BundleDependency setInstanceBound(boolean isInstanceBound) {
67 setIsInstanceBound(isInstanceBound);
68 return this;
69 }
70
Marcel Offermansb196d722009-11-26 17:12:12 +000071 public synchronized boolean isAvailable() {
72 return m_isAvailable;
73 }
74
Marcel Offermansb196d722009-11-26 17:12:12 +000075 public void start(DependencyService service) {
Marcel Offermanscae61362009-12-01 08:37:10 +000076 boolean needsStarting = false;
Marcel Offermanse14b3422009-11-25 23:04:32 +000077 synchronized (this) {
Marcel Offermanscae61362009-12-01 08:37:10 +000078 m_services.add(service);
79 if (!m_isStarted) {
80 m_tracker = new BundleTracker(m_context, m_stateMask, this);
81 m_isStarted = true;
82 needsStarting = true;
83 }
Marcel Offermanse14b3422009-11-25 23:04:32 +000084 }
Marcel Offermanscae61362009-12-01 08:37:10 +000085 if (needsStarting) {
86 m_tracker.open();
87 }
Marcel Offermanse14b3422009-11-25 23:04:32 +000088 }
89
90 public void stop(DependencyService service) {
Marcel Offermanscae61362009-12-01 08:37:10 +000091 boolean needsStopping = false;
Marcel Offermanse14b3422009-11-25 23:04:32 +000092 synchronized (this) {
Marcel Offermanscae61362009-12-01 08:37:10 +000093 if (m_services.size() == 1 && m_services.contains(service)) {
94 m_isStarted = false;
95 needsStopping = true;
Marcel Offermanse14b3422009-11-25 23:04:32 +000096 }
Marcel Offermanse14b3422009-11-25 23:04:32 +000097 }
Marcel Offermanscae61362009-12-01 08:37:10 +000098 if (needsStopping) {
99 m_tracker.close();
100 m_tracker = null;
101 m_services.remove(service);
102 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000103 }
104
105 public String getName() {
Marcel Offermanscae61362009-12-01 08:37:10 +0000106 StringBuilder sb = new StringBuilder();
Marcel Offermans001db052009-12-08 08:58:40 +0000107 if (m_bundleInstance != null) {
108 sb.append(m_bundleInstance.getSymbolicName());
109 sb.append(' ');
110 sb.append(m_bundleInstance.getHeaders().get("Bundle-Version"));
111 sb.append(' ');
112 }
Marcel Offermanscae61362009-12-01 08:37:10 +0000113 sb.append(Integer.toString(m_stateMask, 2));
114 if (m_filter != null) {
115 sb.append(' ');
116 sb.append(m_filter.toString());
117 }
118 return sb.toString();
Marcel Offermanse14b3422009-11-25 23:04:32 +0000119 }
120
121 public int getState() {
122 // TODO Auto-generated method stub
123 return 0;
124 }
125
126 public String getType() {
Marcel Offermanscae61362009-12-01 08:37:10 +0000127 return "bundle";
Marcel Offermanse14b3422009-11-25 23:04:32 +0000128 }
129
130 public Object addingBundle(Bundle bundle, BundleEvent event) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000131 // if we don't like a bundle, we could reject it here by returning null
Marcel Offermans001db052009-12-08 08:58:40 +0000132 long bundleId = bundle.getBundleId();
133 if (m_bundleId >= 0 && m_bundleId != bundleId) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000134 return null;
135 }
136 Filter filter = m_filter;
137 if (filter != null) {
138 Dictionary headers = bundle.getHeaders();
139 if (!m_filter.match(headers)) {
140 return null;
141 }
142 }
143 return bundle;
144 }
145
146 public void addedBundle(Bundle bundle, BundleEvent event, Object object) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000147 boolean makeAvailable = makeAvailable();
148 Object[] services = m_services.toArray();
149 for (int i = 0; i < services.length; i++) {
150 DependencyService ds = (DependencyService) services[i];
151 if (makeAvailable) {
152 ds.dependencyAvailable(this);
153 if (!isRequired()) {
154 invokeAdded(ds, bundle);
155 }
156 }
157 else {
158 ds.dependencyChanged(this);
159 invokeAdded(ds, bundle);
160 }
161 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000162 }
163
164 public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000165 Object[] services = m_services.toArray();
166 for (int i = 0; i < services.length; i++) {
167 DependencyService ds = (DependencyService) services[i];
168 ds.dependencyChanged(this);
169 if (ds.isRegistered()) {
170 invokeChanged(ds, bundle);
171 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000172 }
173 }
174
175 public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000176 boolean makeUnavailable = makeUnavailable();
177 Object[] services = m_services.toArray();
178 for (int i = 0; i < services.length; i++) {
179 DependencyService ds = (DependencyService) services[i];
180 if (makeUnavailable) {
181 ds.dependencyUnavailable(this);
182 if (!isRequired()) {
183 invokeRemoved(ds, bundle);
184 }
Marcel Offermans9f3c5f42009-11-26 11:17:36 +0000185 }
Marcel Offermanscae61362009-12-01 08:37:10 +0000186 else {
187 ds.dependencyChanged(this);
188 invokeRemoved(ds, bundle);
189 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000190 }
191 }
192
193 private synchronized boolean makeAvailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000194 if (!isAvailable()) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000195 m_isAvailable = true;
196 return true;
197 }
198 return false;
199 }
200
201 private synchronized boolean makeUnavailable() {
Marcel Offermansb196d722009-11-26 17:12:12 +0000202 if ((isAvailable()) && (m_tracker.getTrackingCount() == 0)) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000203 m_isAvailable = false;
204 return true;
205 }
206 return false;
207 }
208
Marcel Offermanscae61362009-12-01 08:37:10 +0000209 public void invokeAdded(DependencyService dependencyService, Bundle service) {
Marcel Offermansea89b862010-06-24 13:14:43 +0000210 invoke(dependencyService, service, m_callbackAdded);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000211 }
212
Marcel Offermanscae61362009-12-01 08:37:10 +0000213 public void invokeChanged(DependencyService dependencyService, Bundle service) {
Marcel Offermansea89b862010-06-24 13:14:43 +0000214 invoke(dependencyService, service, m_callbackChanged);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000215 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000216
Marcel Offermanscae61362009-12-01 08:37:10 +0000217 public void invokeRemoved(DependencyService dependencyService, Bundle service) {
Marcel Offermansea89b862010-06-24 13:14:43 +0000218 invoke(dependencyService, service, m_callbackRemoved);
219 }
220
221 public void invoke(DependencyService dependencyService, Bundle service, String name) {
222 if (name != null) {
223 dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name,
Marcel Offermansb196d722009-11-26 17:12:12 +0000224 new Class[][] {{Bundle.class}, {Object.class}, {}},
225 new Object[][] {{service}, {service}, {}}
226 );
Marcel Offermanse14b3422009-11-25 23:04:32 +0000227 }
228 }
Marcel Offermanscae61362009-12-01 08:37:10 +0000229
230 private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000231 if (m_callbackInstance == null) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000232 return dependencyService.getCompositionInstances();
Marcel Offermanse14b3422009-11-25 23:04:32 +0000233 }
Marcel Offermanscae61362009-12-01 08:37:10 +0000234 else {
235 return new Object[] { m_callbackInstance };
236 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000237 }
Marcel Offermanscae61362009-12-01 08:37:10 +0000238
Marcel Offermanse14b3422009-11-25 23:04:32 +0000239 /**
240 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
241 * dependency is added or removed. When you specify callbacks, the auto configuration
242 * feature is automatically turned off, because we're assuming you don't need it in this
243 * case.
244 *
245 * @param added the method to call when a service was added
246 * @param removed the method to call when a service was removed
247 * @return this service dependency
248 */
249 public synchronized BundleDependency setCallbacks(String added, String removed) {
250 return setCallbacks(null, added, null, removed);
251 }
252
253 /**
254 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
255 * dependency is added, changed or removed. When you specify callbacks, the auto
256 * configuration feature is automatically turned off, because we're assuming you don't
257 * need it in this case.
258 *
259 * @param added the method to call when a service was added
260 * @param changed the method to call when a service was changed
261 * @param removed the method to call when a service was removed
262 * @return this service dependency
263 */
264 public synchronized BundleDependency setCallbacks(String added, String changed, String removed) {
265 return setCallbacks(null, added, changed, removed);
266 }
267
268 /**
269 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
270 * dependency is added or removed. They are called on the instance you provide. When you
271 * specify callbacks, the auto configuration feature is automatically turned off, because
272 * we're assuming you don't need it in this case.
273 *
274 * @param instance the instance to call the callbacks on
275 * @param added the method to call when a service was added
276 * @param removed the method to call when a service was removed
277 * @return this service dependency
278 */
279 public synchronized BundleDependency setCallbacks(Object instance, String added, String removed) {
280 return setCallbacks(instance, added, null, removed);
281 }
282
283 /**
284 * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
285 * dependency is added, changed or removed. They are called on the instance you provide. When you
286 * specify callbacks, the auto configuration feature is automatically turned off, because
287 * we're assuming you don't need it in this case.
288 *
289 * @param instance the instance to call the callbacks on
290 * @param added the method to call when a service was added
291 * @param changed the method to call when a service was changed
292 * @param removed the method to call when a service was removed
293 * @return this service dependency
294 */
295 public synchronized BundleDependency setCallbacks(Object instance, String added, String changed, String removed) {
296 ensureNotActive();
297 // if at least one valid callback is specified, we turn off auto configuration
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000298 if ((added != null || removed != null || changed != null) && ! m_autoConfigInvoked) {
Marcel Offermanse14b3422009-11-25 23:04:32 +0000299 setAutoConfig(false);
300 }
301 m_callbackInstance = instance;
302 m_callbackAdded = added;
303 m_callbackChanged = changed;
304 m_callbackRemoved = removed;
305 return this;
306 }
307
308 private void ensureNotActive() {
309 if (m_tracker != null) {
310 throw new IllegalStateException("Cannot modify state while active.");
311 }
312 }
313 public synchronized BundleDependency setAutoConfig(boolean autoConfig) {
314 ensureNotActive();
315 m_autoConfig = autoConfig;
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000316 m_autoConfigInvoked = true;
317 return this;
318 }
319
320 public synchronized BundleDependency setAutoConfig(String instanceName) {
321 ensureNotActive();
322 m_autoConfig = (instanceName != null);
323 m_autoConfigInstance = instanceName;
324 m_autoConfigInvoked = true;
Marcel Offermanse14b3422009-11-25 23:04:32 +0000325 return this;
326 }
327
328 public synchronized BundleDependency setRequired(boolean required) {
329 ensureNotActive();
Marcel Offermansb196d722009-11-26 17:12:12 +0000330 setIsRequired(required);
Marcel Offermanse14b3422009-11-25 23:04:32 +0000331 return this;
332 }
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000333
334 public BundleDependency setPropagate(boolean propagate) {
335 ensureNotActive();
336 m_propagate = propagate;
337 return this;
338 }
339
Marcel Offermanse14b3422009-11-25 23:04:32 +0000340 public BundleDependency setBundle(Bundle bundle) {
341 m_bundleId = bundle.getBundleId();
342 return this;
343 }
344
345 public BundleDependency setFilter(String filter) throws IllegalArgumentException {
346 if (filter != null) {
347 try {
348 m_filter = m_context.createFilter(filter);
349 }
350 catch (InvalidSyntaxException e) {
351 throw new IllegalArgumentException(e.getMessage());
352 }
353 }
354 return this;
355 }
356
357 public BundleDependency setStateMask(int mask) {
358 m_stateMask = mask;
359 return this;
360 }
361
362 public synchronized boolean isAutoConfig() {
363 return m_autoConfig;
364 }
365
366 public Bundle getBundle() {
367 Bundle[] bundles = m_tracker.getBundles();
368 if (bundles != null && bundles.length > 0) {
369 return bundles[0];
370 }
371 return null;
372 }
Marcel Offermans001db052009-12-08 08:58:40 +0000373
374 public Object getAutoConfigInstance() {
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000375 return lookupBundle();
Marcel Offermans001db052009-12-08 08:58:40 +0000376 }
377
Marcel Offermansad760672010-03-03 15:30:01 +0000378 public Bundle lookupBundle() {
379 Bundle service = null;
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000380 if (m_isStarted) {
381 service = getBundle();
382 }
383 else {
384 Bundle[] bundles = m_context.getBundles();
385 for (int i = 0; i < bundles.length; i++) {
386 if ((bundles[i].getState() & m_stateMask) > 0) {
Marcel Offermans9ace4f42010-02-16 16:25:38 +0000387 Filter filter = m_filter;
388 if (filter == null) {
389 service = bundles[i];
390 break;
391 }
392 else if (filter.match(bundles[i].getHeaders())) {
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000393 service = bundles[i];
394 break;
395 }
396 }
397 }
398 }
399 if (service == null && isAutoConfig()) {
400 // TODO does it make sense to add support for custom bundle impls?
401// service = getDefaultImplementation();
402 if (service == null) {
403 service = getNullObject();
404 }
405 }
406 return service;
407 }
408
Marcel Offermansad760672010-03-03 15:30:01 +0000409 private Bundle getNullObject() {
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000410 if (m_nullObject == null) {
411 try {
412 m_nullObject = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Bundle.class }, new DefaultNullObject());
413 }
414 catch (Exception e) {
415 m_logger.log(Logger.LOG_ERROR, "Could not create null object for Bundle.", e);
416 }
417 }
Marcel Offermansad760672010-03-03 15:30:01 +0000418 return (Bundle) m_nullObject;
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000419 }
420
Marcel Offermans001db052009-12-08 08:58:40 +0000421 public String getAutoConfigName() {
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000422 return m_autoConfigInstance;
Marcel Offermans001db052009-12-08 08:58:40 +0000423 }
424
425 public Class getAutoConfigType() {
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000426 return Bundle.class;
Marcel Offermans001db052009-12-08 08:58:40 +0000427 }
428
429 public void invokeAdded(DependencyService service) {
430 // we remember these for future reference, needed for required service callbacks
Marcel Offermansad760672010-03-03 15:30:01 +0000431 m_bundleInstance = lookupBundle();
Marcel Offermans001db052009-12-08 08:58:40 +0000432 invokeAdded(service, m_bundleInstance);
433 }
434
435 public void invokeRemoved(DependencyService service) {
436 invokeRemoved(service, m_bundleInstance);
437 m_bundleInstance = null;
438 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000439
440 public Dictionary getProperties() {
441 // TODO Auto-generated method stub
442 return null;
443 }
444
445 public boolean isPropagated() {
Marcel Offermansd665eaf2010-02-16 15:56:35 +0000446 return m_propagate;
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000447 }
Marcel Offermanse14b3422009-11-25 23:04:32 +0000448}