blob: 05dd2f07adddc7642222a43e22a880efcdcc8386 [file] [log] [blame]
Richard S. Hall930fecc2005-08-16 18:33:34 +00001/*
2 * Copyright 2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
Richard S. Hall5a031592005-08-19 19:53:58 +000017package org.apache.felix.framework;
Richard S. Hall930fecc2005-08-16 18:33:34 +000018
19import java.io.*;
20import java.net.*;
21import java.security.AccessController;
22import java.security.PrivilegedActionException;
23import java.util.*;
24
Richard S. Hall5a031592005-08-19 19:53:58 +000025import org.apache.felix.framework.cache.*;
26import org.apache.felix.framework.searchpolicy.*;
27import org.apache.felix.framework.util.*;
28import org.apache.felix.framework.util.Util;
29import org.apache.felix.moduleloader.*;
30import org.apache.felix.moduleloader.search.ResolveException;
31import org.apache.felix.moduleloader.search.ResolveListener;
Richard S. Hall930fecc2005-08-16 18:33:34 +000032import org.osgi.framework.*;
33import org.osgi.service.packageadmin.ExportedPackage;
34
35public class Felix
36{
37 // Logging related member variables.
38 private LogWrapper m_logger = new LogWrapper();
39 // Config properties.
40 private PropertyResolver m_config = new ConfigImpl();
41 // Configuration properties passed into constructor.
Richard S. Hallea415752005-12-05 19:30:28 +000042 private MutablePropertyResolver m_configMutable = null;
Richard S. Hall930fecc2005-08-16 18:33:34 +000043
44 // MODULE MANAGER.
45 private ModuleManager m_mgr = null;
46
47 // Object used as a lock when calculating which bundles
48 // when performing an operation on one or more bundles.
49 private Object[] m_bundleLock = new Object[0];
50
51 // Maps a bundle location to a bundle location;
52 // used to reserve a location when installing a bundle.
53 private Map m_installRequestMap = null;
54 // This lock must be acquired to modify m_installRequestMap;
55 // to help avoid deadlock this lock as priority 1 and should
56 // be acquired before locks with lower priority.
57 private Object[] m_installRequestLock_Priority1 = new Object[0];
58
59 // Maps a bundle location to a bundle.
60 private HashMap m_installedBundleMap = null;
61 // This lock must be acquired to modify m_installedBundleMap;
62 // to help avoid deadlock this lock as priority 2 and should
63 // be acquired before locks with lower priority.
64 private Object[] m_installedBundleLock_Priority2 = new Object[0];
65
66 // An array of uninstalled bundles before a refresh occurs.
67 private BundleImpl[] m_uninstalledBundles = null;
68 // This lock must be acquired to modify m_uninstalledBundles;
69 // to help avoid deadlock this lock as priority 3 and should
70 // be acquired before locks with lower priority.
71 private Object[] m_uninstalledBundlesLock_Priority3 = new Object[0];
72
73 // Status flag for framework.
74 public static final int INITIAL_STATUS = -1;
75 public static final int RUNNING_STATUS = 0;
76 public static final int STARTING_STATUS = 1;
77 public static final int STOPPING_STATUS = 2;
78 private int m_frameworkStatus = INITIAL_STATUS;
79
80 // Framework's active start level.
81 private int m_activeStartLevel =
82 FelixConstants.FRAMEWORK_INACTIVE_STARTLEVEL;
83
84 // Local file system cache.
85 private BundleCache m_cache = null;
86
87 // Next available bundle identifier.
88 private long m_nextId = 1L;
89
90 // List of event listeners.
91 private FelixDispatchQueue m_dispatchQueue = null;
92 // Re-usable event dispatchers.
93 private Dispatcher m_frameworkDispatcher = null;
94 private Dispatcher m_bundleDispatcher = null;
95 private Dispatcher m_serviceDispatcher = null;
96
97 // Service registry.
98 private ServiceRegistry m_registry = null;
99
100 // Reusable admin permission object for all instances
101 // of the BundleImpl.
102 private static AdminPermission m_adminPerm = new AdminPermission();
103
104 /**
105 * <p>
106 * This method starts the framework instance; instances of the framework
107 * are dormant until this method is called. The caller may also provide
108 * <tt>MutablePropertyResolver</tt> implementations that the instance will
109 * use to obtain configuration or framework properties. Configuration
110 * properties are used internally by the framework and its extensions to alter
111 * its default behavior. Framework properties are used by bundles
112 * and are accessible from <tt>BundleContext.getProperty()</tt>.
113 * </p>
114 * <p>
115 * Configuration properties are the sole means to configure the framework's
116 * default behavior; the framework does not refer to any system properties for
117 * configuration information. If a <tt>MutablePropertyResolver</tt> is
118 * supplied to this method for configuration properties, then the framework will
119 * consult the <tt>MutablePropertyResolver</tt> instance for any and all
120 * configuration properties. It is possible to specify a <tt>null</tt>
121 * configuration property resolver, in which case the framework will use its
122 * default behavior in all cases. However, if the
123 * <a href="cache/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
124 * is used, then at a minimum a profile name or profile directory must
125 * be specified.
126 * </p>
127 * <p>
128 * The following configuration properties can be specified:
129 * </p>
130 * <ul>
131 * <li><tt>felix.cache.class</tt> - The class name to be used when
132 * creating an instance for the bundle cache; this class must
133 * implement the <tt>BundleCache</tt> interface and have a default
134 * constructor. By default, the framework will create an instance of
135 * <tt>DefaultBundleCache</tt> for the bundle cache.
136 * </li>
137 * <li><tt>felix.auto.install.&lt;n&gt;</tt> - Space-delimited list of
138 * bundles to automatically install into start level <tt>n</tt> when
139 * the framework is started. Append a specific start level to this
140 * property name to assign the bundles' start level
141 * (e.g., <tt>felix.auto.install.2</tt>).
142 * </li>
143 * <li><tt>felix.auto.start.&lt;n&gt;</tt> - Space-delimited list of
144 * bundles to automatically install and start into start level
145 * <tt>n</tt> when the framework is started. Append a
146 * specific start level to this property name to assign the
147 * bundles' start level(e.g., <tt>felix.auto.start.2</tt>).
148 * </li>
149 * <li><tt>felix.startlevel.framework</tt> - The initial start level
150 * of the framework once it starts execution; the default
151 * value is 1.
152 * </li>
153 * <li><tt>felix.startlevel.bundle</tt> - The default start level for
154 * newly installed bundles; the default value is 1.
155 * </li>
Richard S. Hall5d226732005-11-08 09:09:05 +0000156 * <li><tt>framework.service.urlhandlers</tt> - Flag to indicate whether
157 * to activate the URL Handlers service for the framework instance;
158 * the default value is "<tt>true</tt>". Activating the URL Handlers
159 * service will result in the <tt>URL.setURLStreamHandlerFactory()</tt>
160 * and <tt>URLConnection.setContentHandlerFactory()</tt> being called.
161 * </li>
Richard S. Hall930fecc2005-08-16 18:33:34 +0000162 * <li><tt>felix.embedded.execution</tt> - Flag to indicate whether
163 * the framework is embedded into a host application; the default value is
164 * "<tt>false</tt>". If this flag is "<tt>true</tt>" then the framework
165 * will not called <tt>System.exit()</tt> upon termination.
166 * </li>
167 * <li><tt>felix.strict.osgi</tt> - Flag to indicate whether the framework is
168 * running in strict OSGi mode; the default value is "<tt>true</tt>".
169 * If this flag is "<tt>false</tt>" it enables a non-OSGi-compliant
170 * feature by persisting <tt>BundleActivator</tt>s that implement
171 * <tt>Serializable</tt>. This feature is not recommended since
172 * it is non-compliant.
173 * </li>
174 * </ul>
175 * <p>
176 * Besides the above framework configuration properties, it is also
177 * possible to specify properties for the bundle cache. The available
178 * bundle cache properties depend on the cache implementation
179 * being used. For the properties of the default bundle cache, refer to the
180 * <a href="cache/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
181 * API documentation.
182 * </p>
183 * <p>
184 * Framework properties are somewhat misnamed, since they are not used by
185 * the framework, but by bundles via <tt>BundleContext.getProperty()</tt>.
186 * Please refer to bundle documentation of your specific bundle for any
187 * available properties.
188 * </p>
189 * <p>
190 * The <a href="Main.html"><tt>Main</tt></a> class implements some
191 * functionality for default property file handling, which makes it
192 * possible to specify configuration properties and framework properties
193 * in files that are automatically loaded when starting the framework. If you
194 * plan to create your own framework instance, you may be
195 * able to take advantage of the features it provides; refer to its
196 * class documentation for more information.
197 * </p>
198 *
Richard S. Hallea415752005-12-05 19:30:28 +0000199 * @param configMutable An object for obtaining configuration properties,
Richard S. Hall930fecc2005-08-16 18:33:34 +0000200 * may be <tt>null</tt>.
201 * @param frameworkProps An object for obtaining framework properties,
202 * may be <tt>null</tt>.
203 * @param activatorList A list of System Bundle activators.
204 **/
205 public synchronized void start(
Richard S. Hallea415752005-12-05 19:30:28 +0000206 MutablePropertyResolver configMutable,
Richard S. Hall930fecc2005-08-16 18:33:34 +0000207 List activatorList)
208 {
209 if (m_frameworkStatus != INITIAL_STATUS)
210 {
211 throw new IllegalStateException("Invalid framework status: " + m_frameworkStatus);
212 }
213
214 // The framework is now in its startup sequence.
215 m_frameworkStatus = STARTING_STATUS;
216
217 // Initialize member variables.
218 m_mgr = null;
Richard S. Hallea415752005-12-05 19:30:28 +0000219 m_configMutable = (configMutable == null)
220 ? new MutablePropertyResolverImpl(new StringMap(false)) : configMutable;
Richard S. Hall930fecc2005-08-16 18:33:34 +0000221 m_activeStartLevel = FelixConstants.FRAMEWORK_INACTIVE_STARTLEVEL;
222 m_installRequestMap = new HashMap();
223 m_installedBundleMap = new HashMap();
224 m_uninstalledBundles = null;
225 m_cache = null;
226 m_nextId = 1L;
227 m_dispatchQueue = null;
228 m_registry = new ServiceRegistry(m_logger);
229
230 // Add a listener to the service registry; this is
231 // used to distribute service registry events to
232 // service listeners.
233 m_registry.addServiceListener(new ServiceListener() {
234 public void serviceChanged(ServiceEvent event)
235 {
236 fireServiceEvent(event);
237 }
238 });
239
240 // Create default storage system from the specified cache class
241 // or use the default cache if no custom cache was specified.
242 String className = m_config.get(FelixConstants.CACHE_CLASS_PROP);
243 if (className == null)
244 {
245 className = DefaultBundleCache.class.getName();
246 }
247
248 try
249 {
250 Class clazz = Class.forName(className);
251 m_cache = (BundleCache) clazz.newInstance();
252 m_cache.initialize(m_config, m_logger);
253 }
254 catch (Exception ex)
255 {
256 System.err.println("Error creating bundle cache:");
257 ex.printStackTrace();
258
259 // Only shutdown the JVM if the framework is running stand-alone.
260 String embedded = m_config.get(
261 FelixConstants.EMBEDDED_EXECUTION_PROP);
262 boolean isEmbedded = (embedded == null)
263 ? false : embedded.equals("true");
264 if (!isEmbedded)
265 {
266 System.exit(-1);
267 }
268 else
269 {
270 throw new RuntimeException(ex.toString());
271 }
272 }
273
274 // Create search policy for module loader.
275 R4SearchPolicy searchPolicy = new R4SearchPolicy(m_logger);
276
277 // Add a resolver listener to the search policy
278 // so that we will be notified when modules are resolved
279 // in order to update the bundle state.
280 searchPolicy.addResolverListener(new ResolveListener() {
281 public void moduleResolved(ModuleEvent event)
282 {
283 BundleImpl bundle = null;
284 try
285 {
286 long id = BundleInfo.getBundleIdFromModuleId(
287 event.getModule().getId());
288 if (id >= 0)
289 {
290 // Update the bundle's state to resolved when the
291 // current module is resolved; just ignore resolve
292 // events for older revisions since this only occurs
293 // when an update is done on an unresolved bundle
294 // and there was no refresh performed.
295 bundle = (BundleImpl) getBundle(id);
296
297 // Lock the bundle first.
298 try
299 {
300 acquireBundleLock(bundle);
301 if (bundle.getInfo().getCurrentModule() == event.getModule())
302 {
303 bundle.getInfo().setState(Bundle.RESOLVED);
304 }
305 }
306 catch (BundleException ex)
307 {
308 // This should not happen, but if it does
309 // there isn't much we can do.
310 }
311 finally
312 {
313 releaseBundleLock(bundle);
314 }
315 }
316 }
317 catch (NumberFormatException ex)
318 {
319 // Ignore.
320 }
321 }
322
323 public void moduleUnresolved(ModuleEvent event)
324 {
325 // We can ignore this, because the only time it
326 // should happen is when a refresh occurs. The
327 // refresh operation resets the bundle's state
328 // by calling BundleInfo.reset(), thus it is not
329 // necessary for us to reset the bundle's state
330 // here.
331 }
332 });
333
334 m_mgr = new ModuleManager(searchPolicy, new OSGiURLPolicy(this));
335
336 // Initialize dispatch queue.
337 m_dispatchQueue = new FelixDispatchQueue(m_logger);
338
339 // Initialize framework properties.
340 initializeFrameworkProperties();
341
342 // Before we reload any cached bundles, let's create a system
343 // bundle that is responsible for providing specific container
344 // related services.
345 SystemBundle systembundle = null;
346 try
347 {
348 // Create a simple bundle info for the system bundle.
349 BundleInfo info = new BundleInfo(
350 m_logger, new SystemBundleArchive(), null);
351 systembundle = new SystemBundle(this, info, activatorList);
352 systembundle.getInfo().addModule(
353 m_mgr.addModule(
354 "0", systembundle.getAttributes(),
355 systembundle.getResourceSources(),
356 systembundle.getLibrarySources(),
357 true)); // HACK ALERT! This flag indicates that we will
358 // use the parent class loader as a resource source.
359 m_installedBundleMap.put(
360 systembundle.getInfo().getLocation(), systembundle);
361
362 // Manually resolve the System Bundle, which will cause its
363 // state to be set to RESOLVED.
364 try
365 {
366 searchPolicy.resolve(systembundle.getInfo().getCurrentModule());
367 }
368 catch (ResolveException ex)
369 {
370 // This should never happen.
371 throw new BundleException(
372 "Unresolved package in System Bundle:"
373 + ex.getPackage());
374 }
375
376 // Start the system bundle; this will set its state
377 // to STARTING, we must set its state to ACTIVE after
378 // all bundles are restarted below according to the spec.
379 systembundle.start();
380 }
381 catch (Exception ex)
382 {
383 m_mgr = null;
384 DispatchQueue.shutdown();
385 m_logger.log(LogWrapper.LOG_ERROR, "Unable to start system bundle.", ex);
386 throw new RuntimeException("Unable to start system bundle.");
387 }
388
389 // Reload and cached bundles.
390 BundleArchive[] archives = null;
391
392 // First get cached bundle identifiers.
393 try
394 {
395 archives = m_cache.getArchives();
396 }
397 catch (Exception ex)
398 {
399 m_logger.log(
400 LogWrapper.LOG_ERROR,
401 "Unable to list saved bundles: " + ex, ex);
402 archives = null;
403 }
404
405 BundleImpl bundle = null;
406
407 // Now install all cached bundles.
408 for (int i = 0; (archives != null) && (i < archives.length); i++)
409 {
410 // Make sure our id generator is not going to overlap.
411 // TODO: This is not correct since it may lead to re-used
412 // ids, which is not okay according to OSGi.
413 m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
414
415 try
416 {
417 // It is possible that a bundle in the cache was previously
418 // uninstalled, but not completely deleted (perhaps because
419 // of a crash or a locked file), so if we see an archive
420 // with an UNINSTALLED persistent state, then try to remove
421 // it now.
422 if (archives[i].getPersistentState() == Bundle.UNINSTALLED)
423 {
424 m_cache.remove(archives[i]);
425 }
426 // Otherwise re-install the cached bundle.
427 else
428 {
429 // Install the cached bundle.
430 bundle = (BundleImpl) installBundle(
431 archives[i].getId(), archives[i].getLocation(), null);
432 }
433 }
434 catch (Exception ex)
435 {
436 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
437 try
438 {
439 m_logger.log(
440 LogWrapper.LOG_ERROR,
441 "Unable to re-install " + archives[i].getLocation(),
442 ex);
443 }
444 catch (Exception ex2)
445 {
446 m_logger.log(
447 LogWrapper.LOG_ERROR,
448 "Unable to re-install bundle " + archives[i].getId(),
449 ex);
450 }
451 // TODO: Perhaps we should remove the cached bundle?
452 }
453 }
454
455 // Get the framework's default start level.
456 int startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
457 String s = m_config.get(FelixConstants.FRAMEWORK_STARTLEVEL_PROP);
458 if (s != null)
459 {
460 try
461 {
462 startLevel = Integer.parseInt(s);
463 }
464 catch (NumberFormatException ex)
465 {
466 startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
467 }
468 }
469
470 // Load bundles from auto-install and auto-start properties;
471 processAutoProperties();
472
473 // This will restart bundles if necessary.
474 setFrameworkStartLevel(startLevel);
475
476 // The framework is now running.
477 m_frameworkStatus = RUNNING_STATUS;
478
479 // Set the system bundle state to ACTIVE.
480 systembundle.getInfo().setState(Bundle.ACTIVE);
481
482 // Fire started event for system bundle.
483 fireBundleEvent(BundleEvent.STARTED, systembundle);
484
485 // Send a framework event to indicate the framework has started.
486 fireFrameworkEvent(FrameworkEvent.STARTED, getBundle(0), null);
487 }
488
489 /**
490 * This method cleanly shuts down the framework, it must be called at the
491 * end of a session in order to shutdown all active bundles.
492 **/
493 public synchronized void shutdown()
494 {
495 if (System.getSecurityManager() != null)
496 {
497 AccessController.checkPermission(m_adminPerm);
498 }
499
500 // Change framework status from running to stopping.
501 // If framework is not running, then just return.
502 if (m_frameworkStatus != RUNNING_STATUS)
503 {
504 return;
505 }
506
507 // The framework is now in its shutdown sequence.
508 m_frameworkStatus = STOPPING_STATUS;
509
510 // Set the start level to zero in order to stop
511 // all bundles in the framework.
512 setFrameworkStartLevel(0);
513
514 // Just like initialize() called the system bundle's start()
515 // method, we must call its stop() method here so that it
516 // can perform any necessary clean up.
517 try
518 {
519 getBundle(0).stop();
520 }
521 catch (Exception ex)
522 {
523 fireFrameworkEvent(FrameworkEvent.ERROR, getBundle(0), ex);
524 m_logger.log(LogWrapper.LOG_ERROR, "Error stopping system bundle.", ex);
525 }
526
527 // Loop through all bundles and update any updated bundles.
528 Bundle[] bundles = getBundles();
529 for (int i = 0; i < bundles.length; i++)
530 {
531 BundleImpl bundle = (BundleImpl) bundles[i];
532 if (bundle.getInfo().isRemovalPending())
533 {
534 try
535 {
536 purgeBundle(bundle);
537 }
538 catch (Exception ex)
539 {
540 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
541 m_logger.log(LogWrapper.LOG_ERROR, "Unable to purge bundle "
542 + bundle.getInfo().getLocation(), ex);
543 }
544 }
545 }
546
547 // Remove any uninstalled bundles.
548 for (int i = 0;
549 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
550 i++)
551 {
552 try
553 {
554 garbageCollectBundle(m_uninstalledBundles[i]);
555 }
556 catch (Exception ex)
557 {
558 m_logger.log(
559 LogWrapper.LOG_ERROR,
560 "Unable to remove "
561 + m_uninstalledBundles[i].getInfo().getLocation(), ex);
562 }
563 }
564
565 // Shutdown event dispatching queue.
566 DispatchQueue.shutdown();
567
568 // The framework is no longer in a usable state.
569 m_frameworkStatus = INITIAL_STATUS;
570 }
571
572 public int getStatus()
573 {
574 return m_frameworkStatus;
575 }
576
577 /**
578 * Returns the active start level of the framework; this method
579 * implements functionality for the Start Level service.
580 * @return The active start level of the framework.
581 **/
582 protected int getStartLevel()
583 {
584 return m_activeStartLevel;
585 }
586
587 /**
588 * Implements the functionality of the <tt>setStartLevel()</tt>
589 * method for the StartLevel service, but does not do the security or
590 * parameter check. The security and parameter check are done in the
591 * StartLevel service implementation because this method is called on
592 * a separate thread and the caller's thread would already be gone if
593 * we did the checks in this method.
594 * @param requestedLevel The new start level of the framework.
595 **/
596 protected synchronized void setFrameworkStartLevel(int requestedLevel)
597 {
598 // Determine if we are lowering or raising the
599 // active start level.
600 boolean lowering = (requestedLevel < m_activeStartLevel);
601
602 // Record new start level.
603 m_activeStartLevel = requestedLevel;
604
605 // Get array of all installed bundles.
606 Bundle[] bundles = getBundles();
607
608 // Sort bundle array by start level either ascending or
609 // descending depending on whether the start level is being
610 // lowered or raised.
611 Comparator comparator = null;
612 if (lowering)
613 {
614 // Sort descending to stop highest start level first.
615 comparator = new Comparator() {
616 public int compare(Object o1, Object o2)
617 {
618 BundleImpl b1 = (BundleImpl) o1;
619 BundleImpl b2 = (BundleImpl) o2;
620 if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
621 < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
622 {
623 return 1;
624 }
625 else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
626 > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
627 {
628 return -1;
629 }
630 return 0;
631 }
632 };
633 }
634 else
635 {
636 // Sort ascending to start lowest start level first.
637 comparator = new Comparator() {
638 public int compare(Object o1, Object o2)
639 {
640 BundleImpl b1 = (BundleImpl) o1;
641 BundleImpl b2 = (BundleImpl) o2;
642 if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
643 > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
644 {
645 return 1;
646 }
647 else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
648 < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
649 {
650 return -1;
651 }
652 return 0;
653 }
654 };
655 }
656
657 Arrays.sort(bundles, comparator);
658
659 // Stop or start the bundles according to the start level.
660 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
661 {
662 BundleImpl impl = (BundleImpl) bundles[i];
663
664 // Ignore the system bundle, since its start() and
665 // stop() methods get called explicitly in initialize()
666 // and shutdown(), respectively.
667 if (impl.getInfo().getBundleId() == 0)
668 {
669 continue;
670 }
671
672 // Start the bundle if necessary.
673 if ((impl.getInfo().getPersistentState() == Bundle.ACTIVE) &&
674 (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
675 <= m_activeStartLevel))
676 {
677 try
678 {
679 startBundle(impl, false);
680 }
681 catch (Throwable th)
682 {
683 fireFrameworkEvent(FrameworkEvent.ERROR, impl, th);
684 m_logger.log(
685 LogWrapper.LOG_ERROR,
686 "Error starting " + impl.getInfo().getLocation(), th);
687 }
688 }
689 // Stop the bundle if necessary.
690 else if (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
691 > m_activeStartLevel)
692 {
693 try
694 {
695 stopBundle(impl, false);
696 }
697 catch (Throwable th)
698 {
699 fireFrameworkEvent(FrameworkEvent.ERROR, impl, th);
700 m_logger.log(
701 LogWrapper.LOG_ERROR,
702 "Error stopping " + impl.getInfo().getLocation(), th);
703 }
704 }
705 }
706
707 fireFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, getBundle(0), null);
708 }
709
710 /**
711 * Returns the start level into which newly installed bundles will
712 * be placed by default; this method implements functionality for
713 * the Start Level service.
714 * @return The default start level for newly installed bundles.
715 **/
716 protected int getInitialBundleStartLevel()
717 {
718 String s = m_config.get(FelixConstants.BUNDLE_STARTLEVEL_PROP);
719
720 if (s != null)
721 {
722 try
723 {
724 int i = Integer.parseInt(s);
725 return (i > 0) ? i : FelixConstants.BUNDLE_DEFAULT_STARTLEVEL;
726 }
727 catch (NumberFormatException ex)
728 {
729 // Ignore and return the default value.
730 }
731 }
732 return FelixConstants.BUNDLE_DEFAULT_STARTLEVEL;
733 }
734
735 /**
736 * Sets the default start level into which newly installed bundles
737 * will be placed; this method implements functionality for the Start
738 * Level service.
739 * @param startLevel The new default start level for newly installed
740 * bundles.
741 * @throws java.lang.IllegalArgumentException If the specified start
742 * level is not greater than zero.
743 * @throws java.security.SecurityException If the caller does not
744 * have <tt>AdminPermission</tt>.
745 **/
746 protected void setInitialBundleStartLevel(int startLevel)
747 {
748 if (System.getSecurityManager() != null)
749 {
750 AccessController.checkPermission(m_adminPerm);
751 }
752
753 if (startLevel <= 0)
754 {
755 throw new IllegalArgumentException(
756 "Initial start level must be greater than zero.");
757 }
758
Richard S. Hallea415752005-12-05 19:30:28 +0000759 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +0000760 FelixConstants.BUNDLE_STARTLEVEL_PROP, Integer.toString(startLevel));
761 }
762
763 /**
764 * Returns the start level for the specified bundle; this method
765 * implements functionality for the Start Level service.
766 * @param bundle The bundle to examine.
767 * @return The start level of the specified bundle.
768 * @throws java.lang.IllegalArgumentException If the specified
769 * bundle has been uninstalled.
770 **/
771 protected int getBundleStartLevel(Bundle bundle)
772 {
773 if (bundle.getState() == Bundle.UNINSTALLED)
774 {
775 throw new IllegalArgumentException("Bundle is uninstalled.");
776 }
777
778 return ((BundleImpl) bundle).getInfo().getStartLevel(getInitialBundleStartLevel());
779 }
780
781 /**
782 * Sets the start level of the specified bundle; this method
783 * implements functionality for the Start Level service.
784 * @param bundle The bundle whose start level is to be modified.
785 * @param startLevel The new start level of the specified bundle.
786 * @throws java.lang.IllegalArgumentException If the specified
787 * bundle is the system bundle or if the bundle has been
788 * uninstalled.
789 * @throws java.security.SecurityException If the caller does not
790 * have <tt>AdminPermission</tt>.
791 **/
792 protected void setBundleStartLevel(Bundle bundle, int startLevel)
793 {
794 if (System.getSecurityManager() != null)
795 {
796 AccessController.checkPermission(m_adminPerm);
797 }
798
799 // Cannot change the system bundle.
800 if (bundle.getBundleId() == 0)
801 {
802 throw new IllegalArgumentException(
803 "Cannot change system bundle start level.");
804 }
805
806 // Acquire bundle lock.
807 try
808 {
809 acquireBundleLock((BundleImpl) bundle);
810 }
811 catch (BundleException ex)
812 {
813 m_logger.log(LogWrapper.LOG_ERROR, "Unable to acquire lock to set start level.", ex);
814 return;
815 }
816
817 Throwable rethrow = null;
818
819 try
820 {
821 if (bundle.getState() == Bundle.UNINSTALLED)
822 {
823 throw new IllegalArgumentException("Bundle is uninstalled.");
824 }
825
826 if (startLevel >= 1)
827 {
828 BundleImpl impl = (BundleImpl) bundle;
829 impl.getInfo().setStartLevel(startLevel);
830
831 try
832 {
833 // Start the bundle if necessary.
834 if ((impl.getInfo().getPersistentState() == Bundle.ACTIVE) &&
835 (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
836 <= m_activeStartLevel))
837 {
838 startBundle(impl, false);
839 }
840 // Stop the bundle if necessary.
841 else if (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
842 > m_activeStartLevel)
843 {
844 stopBundle(impl, false);
845 }
846 }
847 catch (Throwable th)
848 {
849 rethrow = th;
850 m_logger.log(LogWrapper.LOG_ERROR, "Error starting/stopping bundle.", th);
851 }
852 }
853 else
854 {
855 m_logger.log(LogWrapper.LOG_WARNING, "Bundle start level must be greater than zero.");
856 }
857 }
858 finally
859 {
860 // Always release bundle lock.
861 releaseBundleLock((BundleImpl) bundle);
862 }
863
864 if (rethrow != null)
865 {
866 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, rethrow);
867 }
868 }
869
870 /**
871 * Returns whether a bundle is persistently started; this is an
872 * method implementation for the Start Level service.
873 * @param bundle The bundle to examine.
874 * @return <tt>true</tt> if the bundle is marked as persistently
875 * started, <tt>false</tt> otherwise.
876 * @throws java.lang.IllegalArgumentException If the specified
877 * bundle has been uninstalled.
878 **/
879 protected boolean isBundlePersistentlyStarted(Bundle bundle)
880 {
881 if (bundle.getState() == Bundle.UNINSTALLED)
882 {
883 throw new IllegalArgumentException("Bundle is uninstalled.");
884 }
885
886 return (((BundleImpl) bundle).getInfo().getPersistentState() == Bundle.ACTIVE);
887 }
888
Richard S. Hall5d226732005-11-08 09:09:05 +0000889 /**
890 * <p>
891 * This method is used to determine if the specified class is from
892 * a bundle installed in the framework instance. This method is used
893 * by the URL Handlers service when determining the framework instance
894 * associated with call stack.
895 * </p>
896 * @param clazz The class to test for whether it comes from a bundle.
897 * @return <tt>true</tt> if the class comes from a bundle installed in
898 * the framework, <tt>false</tt> otherwise.
899 **/
900 protected boolean isBundleClass(Class clazz)
901 {
902 if (clazz.getClassLoader() instanceof ModuleClassLoader)
903 {
904 return ((ModuleClassLoader) clazz.getClassLoader()).isModuleManagerEqual(m_mgr);
905 }
906 return false;
907 }
908
909 /**
910 * <p>
911 * This method returns an input stream for the specified bundle resource
912 * URL.
913 * </p>
914 * @param url the URL representing the bundle resource for which an input
915 * stream is desired.
916 * @return an input stream to the bundle resource.
917 * @throws IOException if the input stream could not be created.
918 **/
919 protected InputStream getBundleResourceInputStream(URL url)
920 throws IOException
921 {
922 // The URL is constructed like this:
923 // bundle://<module-id>/<source-idx>/<resource-path>
924
925 Module module = m_mgr.getModule(url.getHost());
926 if (module == null)
927 {
928 throw new IOException("Unable to find bundle's module.");
929 }
930
931 String resource = url.getFile();
932 if (resource == null)
933 {
934 throw new IOException("Unable to find resource: " + url.toString());
935 }
936 if (resource.startsWith("/"))
937 {
938 resource = resource.substring(1);
939 }
940 int rsIdx = -1;
941 try
942 {
943 rsIdx = Integer.parseInt(resource.substring(0, resource.indexOf("/")));
944 }
945 catch (NumberFormatException ex)
946 {
947 new IOException("Error parsing resource index.");
948 }
949 resource = resource.substring(resource.indexOf("/") + 1);
950
951 // Get the resource bytes from the resource source.
952 byte[] bytes = null;
953 ResourceSource[] resSources = module.getResourceSources();
954 if ((resSources != null) && (rsIdx < resSources.length))
955 {
956 if (resSources[rsIdx].hasResource(resource))
957 {
958 bytes = resSources[rsIdx].getBytes(resource);
959 }
960 }
961 if (bytes == null)
962 {
963 throw new IOException("Unable to find resource: " + url.toString());
964 }
965 return new ByteArrayInputStream(bytes);
966 }
967
Richard S. Hall930fecc2005-08-16 18:33:34 +0000968 //
969 // Implementation of Bundle interface methods.
970 //
971
972 /**
973 * Implementation for Bundle.getHeaders().
974 **/
975 protected Dictionary getBundleHeaders(BundleImpl bundle)
976 {
977 if (System.getSecurityManager() != null)
978 {
979 AccessController.checkPermission(m_adminPerm);
980 }
981 return new MapToDictionary(bundle.getInfo().getCurrentHeader());
982 }
983
984 /**
985 * Implementation for Bundle.getLocation().
986 **/
987 protected String getBundleLocation(BundleImpl bundle)
988 {
989 if (System.getSecurityManager() != null)
990 {
991 AccessController.checkPermission(m_adminPerm);
992 }
993 return bundle.getInfo().getLocation();
994 }
995
996 /**
997 * Implementation for Bundle.getResource().
998 **/
999 protected URL getBundleResource(BundleImpl bundle, String name)
1000 {
1001 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1002 {
1003 throw new IllegalStateException("The bundle is uninstalled.");
1004 }
1005 else if (System.getSecurityManager() != null)
1006 {
1007 AccessController.checkPermission(m_adminPerm);
1008 }
1009 return bundle.getInfo().getCurrentModule().getClassLoader().getResource(name);
1010 }
1011
1012 protected ServiceReference[] getBundleRegisteredServices(BundleImpl bundle)
1013 {
1014 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1015 {
1016 throw new IllegalStateException("The bundle is uninstalled.");
1017 }
1018
1019 // Filter list of registered service references.
1020 ServiceReference[] refs = m_registry.getRegisteredServices(bundle);
1021 List list = new ArrayList();
1022 for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
1023 {
1024 // Check that the current security context has permission
1025 // to get at least one of the service interfaces; the
1026 // objectClass property of the service stores its service
1027 // interfaces.
1028 boolean hasPermission = false;
1029 if (System.getSecurityManager() != null)
1030 {
1031 String[] objectClass = (String[])
1032 refs[refIdx].getProperty(Constants.OBJECTCLASS);
1033 if (objectClass == null)
1034 {
1035 return null;
1036 }
1037 for (int ifcIdx = 0;
1038 !hasPermission && (ifcIdx < objectClass.length);
1039 ifcIdx++)
1040 {
1041 try
1042 {
1043 ServicePermission perm =
1044 new ServicePermission(
1045 objectClass[ifcIdx], ServicePermission.GET);
1046 AccessController.checkPermission(perm);
1047 hasPermission = true;
1048 }
1049 catch (Exception ex)
1050 {
1051 }
1052 }
1053 }
1054 else
1055 {
1056 hasPermission = true;
1057 }
1058
1059 if (hasPermission)
1060 {
1061 list.add(refs[refIdx]);
1062 }
1063 }
1064
1065 if (list.size() > 0)
1066 {
1067 return (ServiceReference[])
1068 list.toArray(new ServiceReference[list.size()]);
1069 }
1070
1071 return null;
1072 }
1073
1074 protected ServiceReference[] getBundleServicesInUse(Bundle bundle)
1075 {
1076 // Filter list of "in use" service references.
1077 ServiceReference[] refs = m_registry.getServicesInUse(bundle);
1078 List list = new ArrayList();
1079 for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
1080 {
1081 // Check that the current security context has permission
1082 // to get at least one of the service interfaces; the
1083 // objectClass property of the service stores its service
1084 // interfaces.
1085 boolean hasPermission = false;
1086 if (System.getSecurityManager() != null)
1087 {
1088 String[] objectClass = (String[])
1089 refs[refIdx].getProperty(Constants.OBJECTCLASS);
1090 if (objectClass == null)
1091 {
1092 return null;
1093 }
1094 for (int ifcIdx = 0;
1095 !hasPermission && (ifcIdx < objectClass.length);
1096 ifcIdx++)
1097 {
1098 try
1099 {
1100 ServicePermission perm =
1101 new ServicePermission(
1102 objectClass[ifcIdx], ServicePermission.GET);
1103 AccessController.checkPermission(perm);
1104 hasPermission = true;
1105 }
1106 catch (Exception ex)
1107 {
1108 }
1109 }
1110 }
1111 else
1112 {
1113 hasPermission = true;
1114 }
1115
1116 if (hasPermission)
1117 {
1118 list.add(refs[refIdx]);
1119 }
1120 }
1121
1122 if (list.size() > 0)
1123 {
1124 return (ServiceReference[])
1125 list.toArray(new ServiceReference[list.size()]);
1126 }
1127
1128 return null;
1129 }
1130
1131 protected boolean bundleHasPermission(BundleImpl bundle, Object obj)
1132 {
1133 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1134 {
1135 throw new IllegalStateException("The bundle is uninstalled.");
1136 }
1137
Richard S. Hall65730962006-01-23 19:23:13 +00001138 if (null != System.getSecurityManager())
1139 {
1140 try
1141 {
1142 return (obj instanceof java.security.Permission)
1143 ? java.security.Policy.getPolicy().getPermissions(
1144 new java.security.CodeSource(
1145 new java.net.URL(bundle.getInfo().getLocation()), null))
1146 .implies((java.security.Permission) obj)
1147 : false;
1148 }
1149 catch (Exception ex)
1150 {
1151 m_logger.log(
1152 LogWrapper.LOG_WARNING,
1153 "Exception while evaluating the permission.",
1154 ex);
1155 return false;
1156 }
1157 }
1158
1159 return true;
Richard S. Hall930fecc2005-08-16 18:33:34 +00001160 }
1161
1162 /**
Richard S. Hall74b97972005-11-30 15:51:41 +00001163 * Implementation for Bundle.loadClass().
1164 **/
1165 protected Class loadBundleClass(BundleImpl bundle, String name) throws ClassNotFoundException
1166 {
1167 try
1168 {
1169 return bundle.getInfo().getCurrentModule().getClassLoader().loadClass(name);
1170 }
1171 catch (ClassNotFoundException ex)
1172 {
Richard S. Hall624f8012005-11-30 15:53:43 +00001173 // The spec says we must fire a framework error.
Richard S. Hall74b97972005-11-30 15:51:41 +00001174 fireFrameworkEvent(
1175 FrameworkEvent.ERROR, bundle,
1176 new BundleException(ex.getMessage()));
1177 throw ex;
1178 }
1179 }
1180
1181 /**
Richard S. Hall930fecc2005-08-16 18:33:34 +00001182 * Implementation for Bundle.start().
1183 **/
1184 protected void startBundle(BundleImpl bundle, boolean record)
1185 throws BundleException
1186 {
1187 if (System.getSecurityManager() != null)
1188 {
1189 AccessController.checkPermission(m_adminPerm);
1190 }
1191
1192 // CONCURRENCY NOTE:
1193 // Starting a bundle may actually impact many bundles, since
1194 // the bundle being started my need to be resolved, which in
1195 // turn may need to resolve other bundles. Despite this fact,
1196 // we only acquire the lock for the bundle being started, because
1197 // when resolve is called on this bundle, it will eventually
1198 // call resolve on the module loader search policy, which does
1199 // its own locking on the module manager instance. Since the
1200 // resolve algorithm is locking the module manager instance, it
1201 // is not possible for other bundles to be installed or removed,
1202 // so we don't have to worry about these possibilities.
1203 //
1204 // Further, if other bundles are started during this operation,
1205 // then either they will resolve first because they got the lock
1206 // on the module manager or we will resolve first since we got
1207 // the lock on the module manager, so there should be no interference.
1208 // If other bundles are stopped or uninstalled, this should pose
1209 // no problems, since this does not impact their resolved state.
1210 // If a refresh occurs, then the refresh algorithm ulimately has
1211 // to acquire the module manager instance lock too before it can
1212 // completely purge old modules, so it should also complete either
1213 // before or after this bundle is started. At least that's the
1214 // theory.
1215
1216 // Acquire bundle lock.
1217 acquireBundleLock(bundle);
1218
1219 try
1220 {
1221 _startBundle(bundle, record);
1222 }
1223 finally
1224 {
1225 // Release bundle lock.
1226 releaseBundleLock(bundle);
1227 }
1228 }
1229
1230 private void _startBundle(BundleImpl bundle, boolean record)
1231 throws BundleException
1232 {
1233 // Set and save the bundle's persistent state to active
1234 // if we are supposed to record state change.
1235 if (record)
1236 {
1237 bundle.getInfo().setPersistentStateActive();
1238 }
1239
1240 // Try to start the bundle.
1241 BundleInfo info = bundle.getInfo();
1242
1243 // Ignore bundles whose persistent state is not active
1244 // or whose start level is greater than the framework's.
1245 if ((info.getPersistentState() != Bundle.ACTIVE)
1246 || (info.getStartLevel(getInitialBundleStartLevel()) > getStartLevel()))
1247 {
1248 return;
1249 }
1250
1251 switch (info.getState())
1252 {
1253 case Bundle.UNINSTALLED:
1254 throw new IllegalStateException("Cannot start an uninstalled bundle.");
1255 case Bundle.STARTING:
1256 case Bundle.STOPPING:
1257 throw new BundleException("Starting a bundle that is starting or stopping is currently not supported.");
1258 case Bundle.ACTIVE:
1259 return;
1260 case Bundle.INSTALLED:
1261 _resolveBundle(bundle);
1262 case Bundle.RESOLVED:
1263 info.setState(Bundle.STARTING);
1264 }
1265
1266 try
1267 {
1268 // Set the bundle's activator.
1269 bundle.getInfo().setActivator(createBundleActivator(bundle.getInfo()));
1270
1271 // Activate the bundle if it has an activator.
1272 if (bundle.getInfo().getActivator() != null)
1273 {
1274 if (info.getContext() == null)
1275 {
1276 info.setContext(new BundleContextImpl(this, bundle));
1277 }
1278
1279 if (System.getSecurityManager() != null)
1280 {
1281// m_startStopPrivileged.setAction(StartStopPrivileged.START_ACTION);
1282// m_startStopPrivileged.setBundle(bundle);
1283// AccessController.doPrivileged(m_startStopPrivileged);
1284 }
1285 else
1286 {
1287 info.getActivator().start(info.getContext());
1288 }
1289 }
1290
1291 info.setState(Bundle.ACTIVE);
1292
1293 fireBundleEvent(BundleEvent.STARTED, bundle);
1294 }
1295 catch (Throwable th)
1296 {
1297 // If there was an error starting the bundle,
1298 // then reset its state to RESOLVED.
1299 info.setState(Bundle.RESOLVED);
1300
1301 // Unregister any services offered by this bundle.
1302 m_registry.unregisterServices(bundle);
1303
1304 // Release any services being used by this bundle.
1305 m_registry.ungetServices(bundle);
1306
1307 // Remove any listeners registered by this bundle.
1308 removeListeners(bundle);
1309
1310 // The spec says to expect BundleException or
1311 // SecurityException, so rethrow these exceptions.
1312 if (th instanceof BundleException)
1313 {
1314 throw (BundleException) th;
1315 }
1316 else if (th instanceof SecurityException)
1317 {
1318 throw (SecurityException) th;
1319 }
1320 // Convert a privileged action exception to the
1321 // nested exception.
1322 else if (th instanceof PrivilegedActionException)
1323 {
1324 th = ((PrivilegedActionException) th).getException();
1325 }
1326
1327 // Rethrow all other exceptions as a BundleException.
1328 throw new BundleException("Activator start error.", th);
1329 }
1330 }
1331
1332 protected void _resolveBundle(BundleImpl bundle)
1333 throws BundleException
1334 {
1335 // If a security manager is installed, then check for permission
1336 // to import the necessary packages.
1337 if (System.getSecurityManager() != null)
1338 {
1339 URL url = null;
1340 try
1341 {
1342 url = new URL(bundle.getInfo().getLocation());
1343 }
1344 catch (MalformedURLException ex)
1345 {
1346 throw new BundleException("Cannot resolve, bad URL "
1347 + bundle.getInfo().getLocation());
1348 }
1349
1350// try
1351// {
1352// AccessController.doPrivileged(new CheckImportsPrivileged(url, bundle));
1353// }
1354// catch (PrivilegedActionException ex)
1355// {
1356// Exception thrown = ((PrivilegedActionException) ex).getException();
1357// if (thrown instanceof AccessControlException)
1358// {
1359// throw (AccessControlException) thrown;
1360// }
1361// else
1362// {
1363// throw new BundleException("Problem resolving: " + ex);
1364// }
1365// }
1366 }
1367
1368 // Get the import search policy.
1369 R4SearchPolicy search = (R4SearchPolicy) m_mgr.getSearchPolicy();
1370
1371 Module module = bundle.getInfo().getCurrentModule();
1372 try
1373 {
1374 search.resolve(module);
1375 }
1376 catch (ResolveException ex)
1377 {
1378 if (ex.getModule() != null)
1379 {
1380 throw new BundleException(
1381 "Unresolved package in bundle "
1382 + BundleInfo.getBundleIdFromModuleId(ex.getModule().getId())
1383 + ": " + ex.getPackage());
1384 }
1385 else
1386 {
1387 throw new BundleException(ex.getMessage());
1388 }
1389 }
1390
1391 bundle.getInfo().setState(Bundle.RESOLVED);
1392 }
1393
1394 protected void updateBundle(BundleImpl bundle, InputStream is)
1395 throws BundleException
1396 {
1397 if (System.getSecurityManager() != null)
1398 {
1399 AccessController.checkPermission(m_adminPerm);
1400 }
1401
1402 // Acquire bundle lock.
1403 acquireBundleLock(bundle);
1404
1405 try
1406 {
1407 _updateBundle(bundle, is);
1408 }
1409 finally
1410 {
1411 // Release bundle lock.
1412 releaseBundleLock(bundle);
1413 }
1414 }
1415
1416 protected void _updateBundle(BundleImpl bundle, InputStream is)
1417 throws BundleException
1418 {
1419 // We guarantee to close the input stream, so put it in a
1420 // finally clause.
1421
1422 try
1423 {
1424 // Variable to indicate whether bundle is active or not.
1425 Exception rethrow = null;
1426
1427 // Cannot update an uninstalled bundle.
1428 BundleInfo info = bundle.getInfo();
1429 if (info.getState() == Bundle.UNINSTALLED)
1430 {
1431 throw new IllegalStateException("The bundle is uninstalled.");
1432 }
1433
1434 // First get the update-URL from our header.
1435 String updateLocation = (String)
1436 info.getCurrentHeader().get(Constants.BUNDLE_UPDATELOCATION);
1437
1438 // If no update location specified, use original location.
1439 if (updateLocation == null)
1440 {
1441 updateLocation = info.getLocation();
1442 }
1443
1444 // Stop the bundle, but do not change the persistent state.
1445 stopBundle(bundle, false);
1446
1447 try
1448 {
1449 // Get the URL input stream if necessary.
1450 if (is == null)
1451 {
1452 // Do it the manual way to have a chance to
1453 // set request properties such as proxy auth.
1454 URL url = new URL(updateLocation);
1455 URLConnection conn = url.openConnection();
1456
1457 // Support for http proxy authentication.
1458 String auth = System.getProperty("http.proxyAuth");
1459 if ((auth != null) && (auth.length() > 0))
1460 {
1461 if ("http".equals(url.getProtocol()) ||
1462 "https".equals(url.getProtocol()))
1463 {
1464 String base64 = Util.base64Encode(auth);
1465 conn.setRequestProperty(
1466 "Proxy-Authorization", "Basic " + base64);
1467 }
1468 }
1469 is = conn.getInputStream();
1470 }
1471
1472 // Get the bundle's archive.
1473 BundleArchive archive = m_cache.getArchive(info.getBundleId());
1474 // Update the bundle; this operation will increase
1475 // the revision count for the bundle.
1476 m_cache.update(archive, is);
1477 // Create a module for the new revision; the revision is
1478 // base zero, so subtract one from the revision count to
1479 // get the revision of the new update.
1480 Module module = createModule(
1481 info.getBundleId(),
1482 archive.getRevisionCount() - 1,
1483 info.getCurrentHeader());
1484 // Add module to bundle info.
1485 info.addModule(module);
1486 }
1487 catch (Exception ex)
1488 {
1489 m_logger.log(LogWrapper.LOG_ERROR, "Unable to update the bundle.", ex);
1490 rethrow = ex;
1491 }
1492
1493 info.setState(Bundle.INSTALLED);
Richard S. Hall69d84792006-01-13 13:55:13 +00001494 info.setLastModified(System.currentTimeMillis());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001495
1496 // Mark as needing a refresh.
1497 info.setRemovalPending();
1498
1499 // Fire updated event if successful.
1500 if (rethrow == null)
1501 {
1502 fireBundleEvent(BundleEvent.UPDATED, bundle);
1503 }
1504
1505 // Restart bundle, but do not change the persistent state.
1506 // This will not start the bundle if it was not previously
1507 // active.
1508 startBundle(bundle, false);
1509
1510 // If update failed, rethrow exception.
1511 if (rethrow != null)
1512 {
1513 throw new BundleException("Update failed.", rethrow);
1514 }
1515 }
1516 finally
1517 {
1518 try
1519 {
1520 if (is != null) is.close();
1521 }
1522 catch (IOException ex)
1523 {
1524 m_logger.log(LogWrapper.LOG_ERROR, "Unable to close input stream.", ex);
1525 }
1526 }
1527 }
1528
1529 protected void stopBundle(BundleImpl bundle, boolean record)
1530 throws BundleException
1531 {
1532 if (System.getSecurityManager() != null)
1533 {
1534 AccessController.checkPermission(m_adminPerm);
1535 }
1536
1537 // Acquire bundle lock.
1538 acquireBundleLock(bundle);
1539
1540 try
1541 {
1542 _stopBundle(bundle, record);
1543 }
1544 finally
1545 {
1546 // Always release bundle lock.
1547 releaseBundleLock(bundle);
1548 }
1549 }
1550
1551 private void _stopBundle(BundleImpl bundle, boolean record)
1552 throws BundleException
1553 {
1554 Throwable rethrow = null;
1555
1556 // Set the bundle's persistent state to inactive if necessary.
1557 if (record)
1558 {
1559 bundle.getInfo().setPersistentStateInactive();
1560 }
1561
1562 BundleInfo info = bundle.getInfo();
1563
1564 switch (info.getState())
1565 {
1566 case Bundle.UNINSTALLED:
1567 throw new IllegalStateException("Cannot stop an uninstalled bundle.");
1568 case Bundle.STARTING:
1569 case Bundle.STOPPING:
1570 throw new BundleException("Stopping a bundle that is starting or stopping is currently not supported.");
1571 case Bundle.INSTALLED:
1572 case Bundle.RESOLVED:
1573 return;
1574 case Bundle.ACTIVE:
1575 // Set bundle state..
1576 info.setState(Bundle.STOPPING);
1577 }
1578
1579 try
1580 {
1581 if (bundle.getInfo().getActivator() != null)
1582 {
1583 if (System.getSecurityManager() != null)
1584 {
1585// m_startStopPrivileged.setAction(StartStopPrivileged.STOP_ACTION);
1586// m_startStopPrivileged.setBundle(bundle);
1587// AccessController.doPrivileged(m_startStopPrivileged);
1588 }
1589 else
1590 {
1591 info.getActivator().stop(info.getContext());
1592 }
1593 }
1594
1595 // Try to save the activator in the cache.
1596 // NOTE: This is non-standard OSGi behavior and only
1597 // occurs if strictness is disabled.
1598 String strict = m_config.get(FelixConstants.STRICT_OSGI_PROP);
1599 boolean isStrict = (strict == null) ? true : strict.equals("true");
1600 if (!isStrict)
1601 {
1602 try
1603 {
1604 m_cache.getArchive(info.getBundleId())
1605 .setActivator(info.getActivator());
1606 }
1607 catch (Exception ex)
1608 {
1609 // Problem saving activator, so ignore it.
1610 // TODO: Perhaps we should handle this some other way?
1611 }
1612 }
1613 }
1614 catch (Throwable th)
1615 {
1616 m_logger.log(LogWrapper.LOG_ERROR, "Error stopping bundle.", th);
1617 rethrow = th;
1618 }
1619
1620 // Unregister any services offered by this bundle.
1621 m_registry.unregisterServices(bundle);
1622
1623 // Release any services being used by this bundle.
1624 m_registry.ungetServices(bundle);
1625
1626 // The spec says that we must remove all event
1627 // listeners for a bundle when it is stopped.
1628 removeListeners(bundle);
1629
1630 info.setState(Bundle.RESOLVED);
1631 fireBundleEvent(BundleEvent.STOPPED, bundle);
1632
1633 // Throw activator error if there was one.
1634 if (rethrow != null)
1635 {
1636 // The spec says to expect BundleException or
1637 // SecurityException, so rethrow these exceptions.
1638 if (rethrow instanceof BundleException)
1639 {
1640 throw (BundleException) rethrow;
1641 }
1642 else if (rethrow instanceof SecurityException)
1643 {
1644 throw (SecurityException) rethrow;
1645 }
1646 else if (rethrow instanceof PrivilegedActionException)
1647 {
1648 rethrow = ((PrivilegedActionException) rethrow).getException();
1649 }
1650
1651 // Rethrow all other exceptions as a BundleException.
1652 throw new BundleException("Activator stop error.", rethrow);
1653 }
1654 }
1655
1656 protected void uninstallBundle(BundleImpl bundle) throws BundleException
1657 {
1658 if (System.getSecurityManager() != null)
1659 {
1660 AccessController.checkPermission(m_adminPerm);
1661 }
1662
1663 // Acquire bundle lock.
1664 acquireBundleLock(bundle);
1665
1666 try
1667 {
1668 _uninstallBundle(bundle);
1669 }
1670 finally
1671 {
1672 // Always release bundle lock.
1673 releaseBundleLock(bundle);
1674 }
1675 }
1676
1677 private void _uninstallBundle(BundleImpl bundle) throws BundleException
1678 {
1679 if (System.getSecurityManager() != null)
1680 {
1681 AccessController.checkPermission(m_adminPerm);
1682 }
1683
1684 BundleException rethrow = null;
1685
1686 BundleInfo info = bundle.getInfo();
1687 if (info.getState() == Bundle.UNINSTALLED)
1688 {
1689 throw new IllegalStateException("The bundle is uninstalled.");
1690 }
1691
1692 // The spec says that uninstall should always succeed, so
1693 // catch an exception here if stop() doesn't succeed and
1694 // rethrow it at the end.
1695 try
1696 {
1697 stopBundle(bundle, true);
1698 }
1699 catch (BundleException ex)
1700 {
1701 rethrow = ex;
1702 }
1703
1704 // Remove the bundle from the installed map.
1705 BundleImpl target = null;
1706 synchronized (m_installedBundleLock_Priority2)
1707 {
1708 target = (BundleImpl) m_installedBundleMap.remove(info.getLocation());
1709 }
1710
1711 // Finally, put the uninstalled bundle into the
1712 // uninstalled list for subsequent refreshing.
1713 if (target != null)
1714 {
1715 // Set the bundle's persistent state to uninstalled.
1716 target.getInfo().setPersistentStateUninstalled();
1717
1718 // Mark bundle for removal.
1719 target.getInfo().setRemovalPending();
1720
1721 // Put bundle in uninstalled bundle array.
1722 rememberUninstalledBundle(bundle);
1723 }
1724 else
1725 {
1726 m_logger.log(
1727 LogWrapper.LOG_ERROR, "Unable to remove bundle from installed map!");
1728 }
1729
1730 // Set state to uninstalled.
1731 info.setState(Bundle.UNINSTALLED);
Richard S. Hall69d84792006-01-13 13:55:13 +00001732 info.setLastModified(System.currentTimeMillis());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001733
1734 // Fire bundle event.
1735 fireBundleEvent(BundleEvent.UNINSTALLED, bundle);
1736
1737 if (rethrow != null)
1738 {
1739 throw rethrow;
1740 }
1741 }
1742
1743 //
1744 // Implementation of BundleContext interface methods.
1745 //
1746
1747 /**
1748 * Implementation for BundleContext.getProperty(). Returns
1749 * environment property associated with the framework.
1750 *
1751 * @param key The name of the property to retrieve.
1752 * @return The value of the specified property or null.
1753 **/
1754 protected String getProperty(String key)
1755 {
1756 // First, check the config properties.
Richard S. Hallea415752005-12-05 19:30:28 +00001757 String val = (String) m_config.get(key);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001758 // If not found, then try the system properties.
1759 return (val == null) ? System.getProperty(key) : val;
1760 }
1761
1762 protected Bundle installBundle(String location, InputStream is)
1763 throws BundleException
1764 {
1765 return installBundle(-1, location, is);
1766 }
1767
1768 private Bundle installBundle(long id, String location, InputStream is)
1769 throws BundleException
1770 {
1771 if (System.getSecurityManager() != null)
1772 {
1773 AccessController.checkPermission(m_adminPerm);
1774 }
1775
1776 BundleImpl bundle = null;
1777
1778 // Acquire an install lock.
1779 acquireInstallLock(location);
1780
1781 try
1782 {
1783 // Check to see if the framework is still running;
1784 if ((getStatus() == Felix.STOPPING_STATUS) ||
1785 (getStatus() == Felix.INITIAL_STATUS))
1786 {
1787 throw new BundleException("The framework has been shutdown.");
1788 }
1789
1790 // If bundle location is already installed, then
1791 // return it as required by the OSGi specification.
1792 bundle = (BundleImpl) getBundle(location);
1793 if (bundle != null)
1794 {
1795 return bundle;
1796 }
1797
1798 // Determine if this is a new or existing bundle.
1799 boolean isNew = (id < 0);
1800
1801 // If the bundle is new we must cache its JAR file.
1802 if (isNew)
1803 {
1804 // First generate an identifier for it.
1805 id = getNextId();
1806
1807 try
1808 {
1809 // Get the URL input stream if necessary.
1810 if (is == null)
1811 {
1812 // Do it the manual way to have a chance to
1813 // set request properties such as proxy auth.
1814 URL url = new URL(location);
1815 URLConnection conn = url.openConnection();
1816
1817 // Support for http proxy authentication.
1818 String auth = System.getProperty("http.proxyAuth");
1819 if ((auth != null) && (auth.length() > 0))
1820 {
1821 if ("http".equals(url.getProtocol()) ||
1822 "https".equals(url.getProtocol()))
1823 {
1824 String base64 = Util.base64Encode(auth);
1825 conn.setRequestProperty(
1826 "Proxy-Authorization", "Basic " + base64);
1827 }
1828 }
1829 is = conn.getInputStream();
1830 }
1831 // Add the bundle to the cache.
1832 m_cache.create(id, location, is);
1833 }
1834 catch (Exception ex)
1835 {
1836 throw new BundleException(
1837 "Unable to cache bundle: " + location, ex);
1838 }
1839 finally
1840 {
1841 try
1842 {
1843 if (is != null) is.close();
1844 }
1845 catch (IOException ex)
1846 {
1847 m_logger.log(
1848 LogWrapper.LOG_ERROR,
1849 "Unable to close input stream.", ex);
1850 }
1851 }
1852 }
1853 else
1854 {
1855 // If the bundle we are installing is not new,
1856 // then try to purge old revisions before installing
1857 // it; this is done just in case a "refresh"
1858 // didn't occur last session...this would only be
1859 // due to an error or system crash.
1860 try
1861 {
1862 if (m_cache.getArchive(id).getRevisionCount() > 1)
1863 {
1864 m_cache.purge(m_cache.getArchive(id));
1865 }
1866 }
1867 catch (Exception ex)
1868 {
1869 ex.printStackTrace();
1870 m_logger.log(
1871 LogWrapper.LOG_ERROR,
1872 "Could not purge bundle.", ex);
1873 }
1874 }
1875
1876 try
1877 {
1878 BundleArchive archive = m_cache.getArchive(id);
1879 bundle = new BundleImpl(this, createBundleInfo(archive));
1880 }
1881 catch (Exception ex)
1882 {
1883 // If the bundle is new, then remove it from the cache.
1884 // TODO: Perhaps it should be removed if it is not new too.
1885 if (isNew)
1886 {
1887 try
1888 {
1889 m_cache.remove(m_cache.getArchive(id));
1890 }
1891 catch (Exception ex1)
1892 {
1893 m_logger.log(
1894 LogWrapper.LOG_ERROR,
1895 "Could not remove from cache.", ex1);
1896 }
1897 }
1898 throw new BundleException("Could not create bundle object.", ex);
1899 }
1900
1901 // If the bundle is new, then set its start level; existing
1902 // bundles already have their start level set.
1903 if (isNew)
1904 {
1905 // This will persistently set the bundle's start level.
1906 bundle.getInfo().setStartLevel(getInitialBundleStartLevel());
Richard S. Hall69d84792006-01-13 13:55:13 +00001907 bundle.getInfo().setLastModified(System.currentTimeMillis());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001908 }
1909
1910 synchronized (m_installedBundleLock_Priority2)
1911 {
1912 m_installedBundleMap.put(location, bundle);
1913 }
1914 }
1915 finally
1916 {
1917 // Always release install lock.
1918 releaseInstallLock(location);
1919
1920 // Always try to close the input stream.
1921 try
1922 {
1923 if (is != null) is.close();
1924 }
1925 catch (IOException ex)
1926 {
1927 m_logger.log(
1928 LogWrapper.LOG_ERROR,
1929 "Unable to close input stream.", ex);
1930 // Not much else we can do.
1931 }
1932 }
1933
1934 // Fire bundle event.
1935 fireBundleEvent(BundleEvent.INSTALLED, bundle);
1936
1937 // Return new bundle.
1938 return bundle;
1939 }
1940
1941 /**
1942 * Retrieves a bundle from its location.
1943 *
1944 * @param location The location of the bundle to retrieve.
1945 * @return The bundle associated with the location or null if there
1946 * is no bundle associated with the location.
1947 **/
1948 protected Bundle getBundle(String location)
1949 {
1950 synchronized (m_installedBundleLock_Priority2)
1951 {
1952 return (Bundle) m_installedBundleMap.get(location);
1953 }
1954 }
1955
1956 /**
1957 * Implementation for BundleContext.getBundle(). Retrieves a
1958 * bundle from its identifier.
1959 *
1960 * @param id The identifier of the bundle to retrieve.
1961 * @return The bundle associated with the identifier or null if there
1962 * is no bundle associated with the identifier.
1963 **/
1964 protected Bundle getBundle(long id)
1965 {
1966 synchronized (m_installedBundleLock_Priority2)
1967 {
1968 BundleImpl bundle = null;
1969
1970 for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
1971 {
1972 bundle = (BundleImpl) i.next();
1973 if (bundle.getInfo().getBundleId() == id)
1974 {
1975 return bundle;
1976 }
1977 }
1978 }
1979
1980 return null;
1981 }
1982
1983 // Private member for method below.
1984 private Comparator m_comparator = null;
1985
1986 /**
1987 * Implementation for BundleContext.getBundles(). Retrieves
1988 * all installed bundles.
1989 *
1990 * @return An array containing all installed bundles or null if
1991 * there are no installed bundles.
1992 **/
1993 protected Bundle[] getBundles()
1994 {
1995 if (m_comparator == null)
1996 {
1997 m_comparator = new Comparator() {
1998 public int compare(Object o1, Object o2)
1999 {
2000 Bundle b1 = (Bundle) o1;
2001 Bundle b2 = (Bundle) o2;
2002 if (b1.getBundleId() > b2.getBundleId())
2003 return 1;
2004 else if (b1.getBundleId() < b2.getBundleId())
2005 return -1;
2006 return 0;
2007 }
2008 };
2009 }
2010
2011 Bundle[] bundles = null;
2012
2013 synchronized (m_installedBundleLock_Priority2)
2014 {
2015 if (m_installedBundleMap.size() == 0)
2016 {
2017 return null;
2018 }
2019
2020 bundles = new Bundle[m_installedBundleMap.size()];
2021 int counter = 0;
2022 for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
2023 {
2024 bundles[counter++] = (Bundle) i.next();
2025 }
2026 }
2027
2028 Arrays.sort(bundles, m_comparator);
2029
2030 return bundles;
2031 }
2032
2033 protected void addBundleListener(Bundle bundle, BundleListener l)
2034 {
2035 // The spec says do nothing if the listener is
2036 // already registered.
2037 BundleListenerWrapper old = (BundleListenerWrapper)
2038 m_dispatchQueue.getListener(BundleListener.class, l);
2039 if (old == null)
2040 {
2041 l = new BundleListenerWrapper(bundle, l);
2042 m_dispatchQueue.addListener(BundleListener.class, l);
2043 }
2044 }
2045
2046 protected void removeBundleListener(BundleListener l)
2047 {
2048 m_dispatchQueue.removeListener(BundleListener.class, l);
2049 }
2050
2051 /**
2052 * Implementation for BundleContext.addServiceListener().
2053 * Adds service listener to the listener list so that is
2054 * can listen for <code>ServiceEvent</code>s.
2055 *
2056 * @param bundle The bundle that registered the listener.
2057 * @param l The service listener to add to the listener list.
2058 * @param f The filter for the listener; may be null.
2059 **/
2060 protected void addServiceListener(Bundle bundle, ServiceListener l, String f)
2061 throws InvalidSyntaxException
2062 {
2063 // The spec says if the listener is already registered,
2064 // then replace filter.
2065 ServiceListenerWrapper old = (ServiceListenerWrapper)
2066 m_dispatchQueue.getListener(ServiceListener.class, l);
2067 if (old != null)
2068 {
2069 old.setFilter((f == null) ? null : new FilterImpl(m_logger, f));
2070 }
2071 else
2072 {
2073 l = new ServiceListenerWrapper(
2074 bundle, l, (f == null) ? null : new FilterImpl(m_logger, f));
2075 m_dispatchQueue.addListener(ServiceListener.class, l);
2076 }
2077 }
2078
2079 /**
2080 * Implementation for BundleContext.removeServiceListener().
2081 * Removes service listeners from the listener list.
2082 *
2083 * @param l The service listener to remove from the listener list.
2084 **/
2085 protected void removeServiceListener(ServiceListener l)
2086 {
2087 m_dispatchQueue.removeListener(ServiceListener.class, l);
2088 }
2089
2090 protected void addFrameworkListener(Bundle bundle, FrameworkListener l)
2091 {
2092 // The spec says do nothing if the listener is
2093 // already registered.
2094 FrameworkListenerWrapper old = (FrameworkListenerWrapper)
2095 m_dispatchQueue.getListener(FrameworkListener.class, l);
2096 if (old == null)
2097 {
2098 l = new FrameworkListenerWrapper(bundle, l);
2099 m_dispatchQueue.addListener(FrameworkListener.class, l);
2100 }
2101 }
2102
2103 protected void removeFrameworkListener(FrameworkListener l)
2104 {
2105 m_dispatchQueue.removeListener(FrameworkListener.class, l);
2106 }
2107
2108 /**
2109 * Remove all of the specified bundle's event listeners from
2110 * the framework.
2111 * @param bundle The bundle whose listeners are to be removed.
2112 **/
2113 private void removeListeners(Bundle bundle)
2114 {
2115 if (bundle == null)
2116 {
2117 return;
2118 }
2119
2120 // Remove all listeners associated with the supplied bundle;
2121 // it is only possible to know the bundle associated with a
2122 // listener if the listener was wrapper by a ListenerWrapper,
2123 // so look for those.
2124 Object[] listeners = m_dispatchQueue.getListeners();
2125 for (int i = listeners.length - 2; i >= 0; i -= 2)
2126 {
2127 // Check for listener wrappers and then compare the bundle.
2128 if (listeners[i + 1] instanceof ListenerWrapper)
2129 {
2130 ListenerWrapper lw = (ListenerWrapper) listeners[i + 1];
2131 if ((lw.getBundle() != null) && (lw.getBundle().equals(bundle)))
2132 {
2133 m_dispatchQueue.removeListener(
2134 (Class) listeners[i], (EventListener) listeners[i+1]);
2135 }
2136 }
2137 }
2138 }
2139
2140 /**
2141 * Implementation for BundleContext.registerService(). Registers
2142 * a service for the specified bundle bundle.
2143 *
2144 * @param classNames A string array containing the names of the classes
2145 * under which the new service is available.
2146 * @param svcObj The service object or <code>ServiceFactory</code>.
2147 * @param dict A dictionary of properties that further describe the
2148 * service or null.
2149 * @return A <code>ServiceRegistration</code> object or null.
2150 **/
2151 protected ServiceRegistration registerService(
2152 BundleImpl bundle, String[] classNames, Object svcObj, Dictionary dict)
2153 {
2154 if (classNames == null)
2155 {
2156 throw new NullPointerException("Service class names cannot be null.");
2157 }
2158 else if (svcObj == null)
2159 {
2160 throw new IllegalArgumentException("Service object cannot be null.");
2161 }
2162
2163 // Check for permission to register all passed in interface names.
2164 if (System.getSecurityManager() != null)
2165 {
2166 for (int i = 0; i < classNames.length; i++)
2167 {
2168 ServicePermission perm = new ServicePermission(
2169 classNames[i], ServicePermission.REGISTER);
2170 AccessController.checkPermission(perm);
2171 }
2172 }
2173
2174 // Acquire bundle lock.
2175 try
2176 {
2177 acquireBundleLock(bundle);
2178 }
2179 catch (BundleException ex)
2180 {
2181 // This would probably only happen when the bundle is uninstalled.
2182 throw new IllegalStateException(
2183 "Can only register services while bundle is active or activating.");
2184 }
2185
2186 ServiceRegistration reg = null;
2187
2188 try
2189 {
2190 BundleInfo info = bundle.getInfo();
2191
2192 // Can only register services if starting or active.
2193 if ((info.getState() & (Bundle.STARTING | Bundle.ACTIVE)) == 0)
2194 {
2195 throw new IllegalStateException(
2196 "Can only register services while bundle is active or activating.");
2197 }
2198
2199 // Check to make sure that the service object is
2200 // an instance of all service classes; ignore if
2201 // service object is a service factory.
2202 if (!(svcObj instanceof ServiceFactory))
2203 {
2204 for (int i = 0; i < classNames.length; i++)
2205 {
2206 Class clazz = loadClassUsingClass(svcObj.getClass(), classNames[i]);
2207 if (clazz == null)
2208 {
2209 throw new IllegalArgumentException(
2210 "Cannot cast service: " + classNames[i]);
2211 }
2212 else if (!clazz.isAssignableFrom(svcObj.getClass()))
2213 {
2214 throw new IllegalArgumentException(
2215 "Service object is not an instance of \""
2216 + classNames[i] + "\".");
2217 }
2218 }
2219 }
2220
2221 reg = m_registry.registerService(bundle, classNames, svcObj, dict);
2222 }
2223 finally
2224 {
2225 // Always release bundle lock.
2226 releaseBundleLock(bundle);
2227 }
2228
2229 // NOTE: The service registered event is fired from the service
2230 // registry to the framework, where it is then redistributed to
2231 // interested service event listeners.
2232
2233 return reg;
2234 }
2235
2236 /**
2237 * <p>
2238 * This is a simple utility class that attempts to load the named
2239 * class using the class loader of the supplied class or
2240 * the class loader of one of its super classes or their implemented
2241 * interfaces. This is necessary during service registration to test
2242 * whether a given service object implements its declared service
2243 * interfaces.
2244 * </p>
2245 * <p>
2246 * To perform this test, the framework must try to load
2247 * the classes associated with the declared service interfaces, so
2248 * it must choose a class loader. The class loader of the registering
2249 * bundle cannot be used, since this disallows third parties to
2250 * register service on behalf of another bundle. Consequently, the
2251 * class loader of the service object must be used. However, this is
2252 * also not sufficient since the class loader of the service object
2253 * may not have direct access to the class in question.
2254 * </p>
2255 * <p>
2256 * The service object's class loader may not have direct access to
2257 * its service interface if it extends a super class from another
2258 * bundle which implements the service interface from an imported
2259 * bundle or if it implements an extension of the service interface
2260 * from another bundle which imports the base interface from another
2261 * bundle. In these cases, the service object's class loader only has
2262 * access to the super class's class or the extended service interface,
2263 * respectively, but not to the actual service interface.
2264 * </p>
2265 * <p>
2266 * Thus, it is necessary to not only try to load the service interface
2267 * class from the service object's class loader, but from the class
2268 * loaders of any interfaces it implements and the class loaders of
2269 * all super classes.
2270 * </p>
2271 * @param svcObj the class that is the root of the search.
2272 * @param name the name of the class to load.
2273 * @return the loaded class or <tt>null</tt> if it could not be
2274 * loaded.
2275 **/
2276 private static Class loadClassUsingClass(Class clazz, String name)
2277 {
2278 while (clazz != null)
2279 {
2280 // Get the class loader of the current class object.
2281 ClassLoader loader = clazz.getClassLoader();
2282 // A null class loader represents the system class loader.
2283 loader = (loader == null) ? ClassLoader.getSystemClassLoader() : loader;
2284 try
2285 {
2286 return loader.loadClass(name);
2287 }
2288 catch (ClassNotFoundException ex)
2289 {
2290 // Ignore and try interface class loaders.
2291 }
2292
2293 // Try to see if we can load the class from
2294 // one of the class's implemented interface
2295 // class loaders.
2296 Class[] ifcs = clazz.getInterfaces();
2297 for (int i = 0; i < ifcs.length; i++)
2298 {
2299 clazz = loadClassUsingClass(ifcs[i], name);
2300 if (clazz != null)
2301 {
2302 return clazz;
2303 }
2304 }
2305
2306 // Try to see if we can load the class from
2307 // the super class class loader.
2308 clazz = clazz.getSuperclass();
2309 }
2310
2311 return null;
2312 }
2313
2314 protected ServiceReference[] getServiceReferences(
2315 BundleImpl bundle, String className, String expr)
2316 throws InvalidSyntaxException
2317 {
2318 // Define filter if expression is not null.
2319 Filter filter = null;
2320 if (expr != null)
2321 {
2322 filter = new FilterImpl(m_logger, expr);
2323 }
2324
2325 // Ask the service registry for all matching service references.
2326 List refList = m_registry.getServiceReferences(className, filter);
2327
2328 // The returned reference list must be filtered for two cases:
2329 // 1) The requesting bundle may not be wired to the same class
2330 // as the providing bundle (i.e, different versions), so filter
2331 // any services for which the requesting bundle might get a
2332 // class cast exception.
2333 // 2) Security is enabled and the requesting bundle does not have
2334 // permission access the service.
2335 for (int refIdx = 0; (refList != null) && (refIdx < refList.size()); refIdx++)
2336 {
2337 // Get the current service reference.
2338 ServiceReference ref = (ServiceReference) refList.get(refIdx);
2339
2340 // Get the service's objectClass property.
2341 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
2342
2343 // Boolean flag.
2344 boolean allow = false;
2345
2346 // Filter the service reference if the requesting bundle
2347 // does not have permission.
2348 if (System.getSecurityManager() != null)
2349 {
2350 for (int classIdx = 0;
2351 !allow && (classIdx < objectClass.length);
2352 classIdx++)
2353 {
2354 try
2355 {
2356 ServicePermission perm = new ServicePermission(
2357 objectClass[classIdx], ServicePermission.GET);
2358 AccessController.checkPermission(perm);
2359 // The bundle only needs permission for one
2360 // of the service interfaces, so break out
2361 // of the loop when permission is granted.
2362 allow = true;
2363 }
2364 catch (Exception ex)
2365 {
2366 // We do not throw this exception since the bundle
2367 // is not supposed to know about the service at all
2368 // if it does not have permission.
2369 m_logger.log(LogWrapper.LOG_ERROR, ex.getMessage());
2370 }
2371 }
2372
2373 if (!allow)
2374 {
2375 refList.remove(refIdx);
2376 refIdx--;
2377 continue;
2378 }
2379 }
2380
2381 // Now check for castability.
2382 if (!isServiceAssignable(bundle, ref))
2383 {
2384 refList.remove(refIdx);
2385 refIdx--;
2386 }
2387 }
2388
2389 if (refList.size() > 0)
2390 {
2391 return (ServiceReference[]) refList.toArray(new ServiceReference[refList.size()]);
2392 }
2393
2394 return null;
2395 }
2396
2397 /**
2398 * This method determines if the requesting bundle is able to cast
2399 * the specified service reference based on class visibility rules
2400 * of the underlying modules.
2401 * @param requester The bundle requesting the service.
2402 * @param ref The service in question.
2403 * @return <tt>true</tt> if the requesting bundle is able to case
2404 * the service object to a known type.
2405 **/
2406 protected boolean isServiceAssignable(BundleImpl requester, ServiceReference ref)
2407 {
2408 // Boolean flag.
2409 boolean allow = true;
2410 // Get the service's objectClass property.
2411 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
2412
2413 // The the service reference is not assignable when the requesting
2414 // bundle is wired to a different version of the service object.
2415 // NOTE: We are pessimistic here, if any class in the service's
2416 // objectClass is not usable by the requesting bundle, then we
2417 // disallow the service reference.
2418 for (int classIdx = 0; (allow) && (classIdx < objectClass.length); classIdx++)
2419 {
2420 if (!ref.isAssignableTo(requester, objectClass[classIdx]))
2421 {
2422 allow = false;
2423 }
2424 }
2425 return allow;
2426 }
2427
2428 protected Object getService(Bundle bundle, ServiceReference ref)
2429 {
2430 // Check that the bundle has permission to get at least
2431 // one of the service interfaces; the objectClass property
2432 // of the service stores its service interfaces.
2433 String[] objectClass = (String[])
2434 ref.getProperty(Constants.OBJECTCLASS);
2435 if (objectClass == null)
2436 {
2437 return null;
2438 }
2439
2440 boolean hasPermission = false;
2441 if (System.getSecurityManager() != null)
2442 {
2443 for (int i = 0;
2444 !hasPermission && (i < objectClass.length);
2445 i++)
2446 {
2447 try
2448 {
2449 ServicePermission perm =
2450 new ServicePermission(
2451 objectClass[i], ServicePermission.GET);
2452 AccessController.checkPermission(perm);
2453 hasPermission = true;
2454 }
2455 catch (Exception ex)
2456 {
2457 }
2458 }
2459 }
2460 else
2461 {
2462 hasPermission = true;
2463 }
2464
2465 // If the bundle does not permission to access the service,
2466 // then return null.
2467 if (!hasPermission)
2468 {
2469 return null;
2470 }
2471
2472 return m_registry.getService(bundle, ref);
2473 }
2474
2475 protected boolean ungetService(Bundle bundle, ServiceReference ref)
2476 {
2477 return m_registry.ungetService(bundle, ref);
2478 }
2479
2480 protected File getDataFile(BundleImpl bundle, String s)
2481 {
2482 // The spec says to throw an error if the bundle
2483 // is stopped, which I assume means not active,
2484 // starting, or stopping.
2485 if ((bundle.getInfo().getState() != Bundle.ACTIVE) &&
2486 (bundle.getInfo().getState() != Bundle.STARTING) &&
2487 (bundle.getInfo().getState() != Bundle.STOPPING))
2488 {
2489 throw new IllegalStateException("Only active bundles can create files.");
2490 }
2491 try
2492 {
2493 return m_cache.getArchive(
2494 bundle.getInfo().getBundleId()).getDataFile(s);
2495 }
2496 catch (Exception ex)
2497 {
2498 m_logger.log(LogWrapper.LOG_ERROR, ex.getMessage());
2499 return null;
2500 }
2501 }
2502
2503 //
2504 // PackageAdmin related methods.
2505 //
2506
2507 /**
2508 * Returns the exported package associated with the specified
2509 * package name. This is used by the PackageAdmin service
2510 * implementation.
2511 *
2512 * @param name The name of the exported package to find.
2513 * @return The exported package or null if no matching package was found.
2514 **/
2515 protected ExportedPackage getExportedPackage(String pkgName)
2516 {
2517 // First, find the bundle exporting the package.
2518 BundleImpl bundle = null;
2519 R4SearchPolicy search = (R4SearchPolicy) m_mgr.getSearchPolicy();
2520 Module[] exporters = search.getInUseExporters(new R4Package(pkgName, null, null));
2521 if (exporters != null)
2522 {
2523 // Since OSGi R4 there may be more than one exporting, so just
2524 // take the first one.
2525 bundle = (BundleImpl) getBundle(
2526 BundleInfo.getBundleIdFromModuleId(exporters[0].getId()));
2527 }
2528
2529 // If we have found the exporting bundle, then return the
2530 // exported package interface instance.
2531 if (bundle != null)
2532 {
2533 // We need to find the version of the exported package, but this
2534 // is tricky since there may be multiple versions of the package
2535 // offered by a given bundle, since multiple revisions of the
2536 // bundle JAR file may exist if the bundle was updated without
2537 // refreshing the framework. In this case, each revision of the
2538 // bundle JAR file is represented as a module in the BundleInfo
2539 // module array, which is ordered from oldest to newest. We assume
2540 // that the first module found to be exporting the package is the
2541 // provider of the package, which makes sense since it must have
2542 // been resolved first.
2543 Module[] modules = bundle.getInfo().getModules();
2544 for (int modIdx = 0; modIdx < modules.length; modIdx++)
2545 {
2546 R4Package pkg = R4SearchPolicy.getExportPackage(modules[modIdx], pkgName);
2547 if (pkg != null)
2548 {
2549 return new ExportedPackageImpl(this, bundle, pkgName, pkg.getVersionLow());
2550 }
2551 }
2552 }
2553
2554 return null;
2555 }
2556
2557 /**
2558 * Returns an array of all actively exported packages from the specified
2559 * bundle or if the specified bundle is <tt>null</tt> an array
2560 * containing all actively exported packages by all bundles.
2561 *
2562 * @param b The bundle whose exported packages are to be retrieved
2563 * or <tt>null</tt> if the exported packages of all bundles are
2564 * to be retrieved.
2565 * @return An array of exported packages.
2566 **/
2567 protected ExportedPackage[] getExportedPackages(Bundle b)
2568 {
2569 List list = new ArrayList();
2570
2571 // If a bundle is specified, then return its
2572 // exported packages.
2573 if (b != null)
2574 {
2575 BundleImpl bundle = (BundleImpl) b;
2576 getExportedPackages(bundle, list);
2577 }
2578 // Otherwise return all exported packages.
2579 else
2580 {
2581 // To create a list of all exported packages, we must look
2582 // in the installed and uninstalled sets of bundles. To
2583 // ensure a somewhat consistent view, we will gather all
2584 // of this information from within the installed bundle
2585 // lock.
2586 synchronized (m_installedBundleLock_Priority2)
2587 {
2588 // First get exported packages from uninstalled bundles.
2589 synchronized (m_uninstalledBundlesLock_Priority3)
2590 {
2591 for (int bundleIdx = 0;
2592 (m_uninstalledBundles != null) && (bundleIdx < m_uninstalledBundles.length);
2593 bundleIdx++)
2594 {
2595 BundleImpl bundle = m_uninstalledBundles[bundleIdx];
2596 getExportedPackages(bundle, list);
2597 }
2598 }
2599
2600 // Now get exported packages from installed bundles.
2601 Bundle[] bundles = getBundles();
2602 for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
2603 {
2604 BundleImpl bundle = (BundleImpl) bundles[bundleIdx];
2605 getExportedPackages(bundle, list);
2606 }
2607 }
2608 }
2609
2610 return (ExportedPackage[]) list.toArray(new ExportedPackage[list.size()]);
2611 }
2612
2613 /**
2614 * Adds any current active exported packages from the specified bundle
2615 * to the passed in list.
2616 * @param bundle The bundle from which to retrieve exported packages.
2617 * @param list The list to which the exported packages are added
2618 **/
2619 private void getExportedPackages(BundleImpl bundle, List list)
2620 {
2621 R4SearchPolicy policy = (R4SearchPolicy) m_mgr.getSearchPolicy();
2622
2623 // Since a bundle may have many modules associated with it,
2624 // one for each revision in the cache, search each module
2625 // for each revision to get all exports.
2626 Module[] modules = bundle.getInfo().getModules();
2627 for (int modIdx = 0; modIdx < modules.length; modIdx++)
2628 {
2629 R4Package[] exports = R4SearchPolicy.getExportsAttr(modules[modIdx]);
2630 if (exports.length > 0)
2631 {
2632 for (int expIdx = 0; expIdx < exports.length; expIdx++)
2633 {
2634 // See if the target bundle's module is one of the
2635 // "in use" exporters of the package.
2636 Module[] inUseModules = policy.getInUseExporters(exports[expIdx]);
2637 if (R4SearchPolicy.isModuleInArray(inUseModules, modules[modIdx]))
2638 {
2639 list.add(new ExportedPackageImpl(
2640 this, bundle, exports[expIdx].getId(), exports[expIdx].getVersionLow()));
2641 }
2642 }
2643 }
2644 }
2645 }
2646
2647 protected Bundle[] getImportingBundles(ExportedPackage ep)
2648 {
2649 // Get exporting bundle; we need to use this internal
2650 // method because the spec says ep.getExportingBundle()
2651 // should return null if the package is stale.
2652 BundleImpl exporter = (BundleImpl)
2653 ((ExportedPackageImpl) ep).getExportingBundleInternal();
2654 BundleInfo exporterInfo = exporter.getInfo();
2655
2656 // Create list for storing importing bundles.
2657 List list = new ArrayList();
2658 Bundle[] bundles = getBundles();
2659
2660 // Check all bundles to see who imports the package.
2661 for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
2662 {
2663 BundleImpl importer = (BundleImpl) bundles[bundleIdx];
2664
2665 // Ignore the bundle if it imports from itself.
2666 if (exporter != importer)
2667 {
2668 // Check the import wires of all modules for all bundles.
2669 Module[] modules = importer.getInfo().getModules();
2670 for (int modIdx = 0; modIdx < modules.length; modIdx++)
2671 {
2672 R4Wire wire = R4SearchPolicy.getWire(modules[modIdx], ep.getName());
2673
2674 // If the resolving module is associated with the
2675 // exporting bundle, then add current bundle to
2676 // import list.
2677 if ((wire != null) && exporterInfo.hasModule(wire.m_module))
2678 {
2679 // Add the bundle to the list of importers.
2680 list.add(bundles[bundleIdx]);
2681 break;
2682 }
2683 }
2684 }
2685 }
2686
2687 // Return the results.
2688 if (list.size() > 0)
2689 {
2690 return (Bundle[]) list.toArray(new Bundle[list.size()]);
2691 }
2692
2693 return null;
2694 }
2695
2696 protected void refreshPackages(Bundle[] targets)
2697 {
2698 if (System.getSecurityManager() != null)
2699 {
2700 AccessController.checkPermission(m_adminPerm);
2701 }
2702
2703 // Acquire locks for all impacted bundles.
2704 BundleImpl[] bundles = acquireBundleRefreshLocks(targets);
2705
2706 // Remove any targeted bundles from the uninstalled bundles
2707 // array, since they will be removed from the system after
2708 // the refresh.
2709 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
2710 {
2711 forgetUninstalledBundle(bundles[i]);
2712 }
2713
2714 try
2715 {
2716 // If there are targets, then refresh each one.
2717 if (bundles != null)
2718 {
2719 // At this point the map contains every bundle that has been
2720 // updated and/or removed as well as all bundles that import
2721 // packages from these bundles.
2722
2723 // Create refresh helpers for each bundle.
2724 RefreshHelper[] helpers = new RefreshHelper[bundles.length];
2725 for (int i = 0; i < bundles.length; i++)
2726 {
2727 helpers[i] = new RefreshHelper(bundles[i]);
2728 }
2729
2730 // Stop, purge or remove, and reinitialize all bundles first.
2731 for (int i = 0; i < helpers.length; i++)
2732 {
2733 helpers[i].stop();
2734 helpers[i].purgeOrRemove();
2735 helpers[i].reinitialize();
2736 }
2737
2738 // Then restart all bundles that were previously running.
2739 for (int i = 0; i < helpers.length; i++)
2740 {
2741 helpers[i].restart();
2742 }
2743 }
2744 }
2745 finally
2746 {
2747 // Always release all bundle locks.
2748 releaseBundleLocks(bundles);
2749 }
2750
2751 fireFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, getBundle(0), null);
2752 }
2753
2754 private void populateImportGraph(BundleImpl target, Map map)
2755 {
2756 // Get the exported packages for the specified bundle.
2757 ExportedPackage[] pkgs = getExportedPackages(target);
2758
2759 for (int pkgIdx = 0; (pkgs != null) && (pkgIdx < pkgs.length); pkgIdx++)
2760 {
2761 // Get all imports of this package.
2762 Bundle[] importers = getImportingBundles(pkgs[pkgIdx]);
2763
2764 for (int impIdx = 0;
2765 (importers != null) && (impIdx < importers.length);
2766 impIdx++)
2767 {
Richard S. Halle34df092005-10-06 17:03:05 +00002768 // Avoid cycles if the bundle is already in map.
2769 if (!map.containsKey(importers[impIdx]))
2770 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00002771 // Add each importing bundle to map.
2772 map.put(importers[impIdx], importers[impIdx]);
2773 // Now recurse into each bundle to get its importers.
2774 populateImportGraph(
2775 (BundleImpl) importers[impIdx], map);
Richard S. Halle34df092005-10-06 17:03:05 +00002776 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002777 }
2778 }
2779 }
2780
2781 //
2782 // Miscellaneous private methods.
2783 //
2784
2785 private BundleInfo createBundleInfo(BundleArchive archive)
2786 throws Exception
2787 {
2788 // Get the bundle manifest.
2789 Map headerMap = null;
2790 try
2791 {
2792 // Although there should only ever be one revision at this
2793 // point, get the header for the current revision to be safe.
2794 headerMap = archive.getManifestHeader(archive.getRevisionCount() - 1);
2795 }
2796 catch (Exception ex)
2797 {
2798 throw new BundleException("Unable to read JAR manifest.", ex);
2799 }
2800
2801 // We can't do anything without the manifest header.
2802 if (headerMap == null)
2803 {
2804 throw new BundleException("Unable to read JAR manifest header.");
2805 }
2806
2807 // Create the module for the bundle; although there should only
2808 // ever be one revision at this point, create the module for
2809 // the current revision to be safe.
2810 Module module = createModule(
2811 archive.getId(), archive.getRevisionCount() - 1, headerMap);
2812
2813 // Finally, create an return the bundle info.
2814 return new BundleInfo(m_logger, archive, module);
2815 }
2816
2817 /**
2818 * Creates a module for a given bundle by reading the bundle's
2819 * manifest meta-data and converting it to work with the underlying
2820 * import/export search policy of the module loader.
2821 * @param id The identifier of the bundle for which the module should
2822 * be created.
2823 * @param headers The headers map associated with the bundle.
2824 * @return The initialized and/or newly created module.
2825 **/
2826 private Module createModule(long id, int revision, Map headerMap)
2827 throws Exception
2828 {
2829 // Get the manifest version.
2830 String version = (String) headerMap.get(FelixConstants.BUNDLE_MANIFESTVERSION);
2831 version = (version == null) ? "1" : version;
2832 if (!version.equals("1") && !version.equals("2"))
2833 {
2834 throw new BundleException("Unknown 'Bundle-ManifestVersion' value: " + version);
2835 }
2836
2837 // Create the resource sources for the bundle. The resource sources
2838 // are comprised of the bundle's class path values (as JarResourceSources).
2839 ResourceSource[] resSources = null;
2840 try
2841 {
2842 // Get bundle class path for the specified revision from cache.
2843 String[] classPath = m_cache.getArchive(id).getClassPath(revision);
2844
2845 // Create resource sources for everything.
2846 resSources = new ResourceSource[classPath.length];
2847 for (int i = 0; i < classPath.length; i++)
2848 {
2849 resSources[i] = new JarResourceSource(classPath[i]);
2850 }
2851 }
2852 catch (Exception ex)
2853 {
2854 ex.printStackTrace();
2855 throw new BundleException("Error in class path: " + ex);
2856 }
2857
2858 // Get import packages from bundle manifest.
2859 R4Package[] imports = R4Package.parseImportOrExportHeader(
2860 (String) headerMap.get(Constants.IMPORT_PACKAGE));
2861
2862
2863 // Check to make sure that R3 bundles have only specified
2864 // the 'specification-version' attribute and no directives.
2865 if (version.equals("1"))
2866 {
2867 for (int i = 0; (imports != null) && (i < imports.length); i++)
2868 {
2869 if (imports[i].getDirectives().length != 0)
2870 {
2871 throw new BundleException("R3 imports cannot contain directives.");
2872 }
2873 // NOTE: This is checking for "version" rather than "specification-version"
2874 // because the package class normalizes to "version" to avoid having
2875 // future special cases. This could be changed if more strict behavior
2876 // is required.
2877 if ((imports[i].getVersionHigh() != null) ||
2878 (imports[i].getAttributes().length > 1) ||
2879 ((imports[i].getAttributes().length == 1) &&
2880 (!imports[i].getAttributes()[0].getName().equals(FelixConstants.VERSION_ATTRIBUTE))))
2881 {
2882 throw new BundleException(
2883 "Import does not conform to R3 syntax: " + imports[i]);
2884 }
2885 }
2886 }
2887
2888 // Get export packages from bundle manifest.
2889 R4Package[] exports = R4Package.parseImportOrExportHeader(
2890 (String) headerMap.get(Constants.EXPORT_PACKAGE));
2891
2892 // Check to make sure that R3 bundles have only specified
2893 // the 'specification-version' attribute and no directives.
2894 // In addition, all R3 exports imply imports, so add a
2895 // corresponding import for each export.
2896 if (version.equals("1"))
2897 {
2898 for (int i = 0; (exports != null) && (i < exports.length); i++)
2899 {
2900 if (exports[i].getDirectives().length != 0)
2901 {
2902 throw new BundleException("R3 exports cannot contain directives.");
2903 }
2904 // NOTE: This is checking for "version" rather than "specification-version"
2905 // because the package class normalizes to "version" to avoid having
2906 // future special cases. This could be changed if more strict behavior
2907 // is required.
2908 if ((exports[i].getAttributes().length > 1) ||
2909 ((exports[i].getAttributes().length == 1) &&
2910 (!exports[i].getAttributes()[0].getName().equals(FelixConstants.VERSION_ATTRIBUTE))))
2911 {
2912 throw new BundleException(
2913 "Export does not conform to R3 syntax: " + imports[i]);
2914 }
2915 }
2916
2917 R4Package[] newImports = new R4Package[imports.length + exports.length];
2918 System.arraycopy(imports, 0, newImports, 0, imports.length);
2919 System.arraycopy(exports, 0, newImports, imports.length, exports.length);
2920 imports = newImports;
2921 }
2922
2923 // For R3 bundles, add a "uses" directive onto each export
2924 // that references every other import (which will include
2925 // exports, since export implies import); this is
2926 // necessary since R3 bundles assumed a single class space,
2927 // but R4 allows for multiple class spaces.
2928 if (version.equals("1"))
2929 {
2930 String usesValue = "";
2931 for (int i = 0; (imports != null) && (i < imports.length); i++)
2932 {
2933 usesValue = usesValue
2934 + ((usesValue.length() > 0) ? "," : "")
2935 + imports[i].getId();
2936 }
2937 R4Directive uses = new R4Directive(
2938 FelixConstants.USES_DIRECTIVE, usesValue);
2939 for (int i = 0; (exports != null) && (i < exports.length); i++)
2940 {
2941 exports[i] = new R4Package(
2942 exports[i].getId(),
2943 new R4Directive[] { uses },
2944 exports[i].getAttributes());
2945 }
2946 }
2947
2948// TODO: CHECK FOR DUPLICATE IMPORTS/EXPORTS HERE.
2949
2950 // Get dynamic import packages from bundle manifest.
2951 R4Package[] dynamics = R4Package.parseImportOrExportHeader(
2952 (String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
2953
2954 // Check to make sure that R3 bundles have no attributes or
2955 // directives.
2956 if (version.equals("1"))
2957 {
2958 for (int i = 0; (dynamics != null) && (i < dynamics.length); i++)
2959 {
2960 if (dynamics[i].getDirectives().length != 0)
2961 {
2962 throw new BundleException("R3 dynamic imports cannot contain directives.");
2963 }
2964 if (dynamics[i].getAttributes().length != 0)
2965 {
2966 throw new BundleException("R3 dynamic imports cannot contain attributes.");
2967 }
2968 }
2969 }
2970
2971 Object[][] attributes = {
2972 new Object[] { R4SearchPolicy.EXPORTS_ATTR, exports },
2973 new Object[] { R4SearchPolicy.IMPORTS_ATTR, imports },
2974 new Object[] { R4SearchPolicy.DYNAMICIMPORTS_ATTR, dynamics }
2975 };
2976
2977 // Get native library entry names for module library sources.
2978 LibraryInfo[] libraries =
2979 Util.parseLibraryStrings(
2980 Util.parseDelimitedString(
2981 (String) headerMap.get(Constants.BUNDLE_NATIVECODE), ","));
2982 LibrarySource[] libSources = {
2983 new OSGiLibrarySource(
2984 m_logger, m_cache, id, revision,
2985 getProperty(Constants.FRAMEWORK_OS_NAME),
2986 getProperty(Constants.FRAMEWORK_PROCESSOR),
2987 libraries)
2988 };
2989
2990 Module module =
2991 m_mgr.addModule(
2992 Long.toString(id) + "." + Integer.toString(revision),
2993 attributes, resSources, libSources);
2994
2995 return module;
2996 }
2997
2998 private BundleActivator createBundleActivator(BundleInfo info)
2999 throws Exception
3000 {
3001 // CONCURRENCY NOTE:
3002 // This method is called indirectly from startBundle() (via _startBundle()),
3003 // which has the exclusion lock, so there is no need to do any locking here.
3004
3005 BundleActivator activator = null;
3006
3007 String strict = m_config.get(FelixConstants.STRICT_OSGI_PROP);
3008 boolean isStrict = (strict == null) ? true : strict.equals("true");
3009 if (!isStrict)
3010 {
3011 try
3012 {
3013 activator =
3014 m_cache.getArchive(info.getBundleId())
3015 .getActivator(info.getCurrentModule().getClassLoader());
3016 }
3017 catch (Exception ex)
3018 {
3019 activator = null;
3020 }
3021 }
3022
3023 // If there was no cached activator, then get the activator
3024 // class from the bundle manifest.
3025 if (activator == null)
3026 {
3027 // Get the associated bundle archive.
3028 BundleArchive ba = m_cache.getArchive(info.getBundleId());
3029 // Get the manifest from the current revision; revision is
3030 // base zero so subtract one from the count to get the
3031 // current revision.
3032 Map headerMap = ba.getManifestHeader(ba.getRevisionCount() - 1);
3033 // Get the activator class attribute.
3034 String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
3035 // Try to instantiate activator class if present.
3036 if (className != null)
3037 {
3038 className = className.trim();
3039 Class clazz = info.getCurrentModule().getClassLoader().loadClass(className);
3040 if (clazz == null)
3041 {
3042 throw new BundleException("Not found: "
3043 + className);
3044 }
3045 activator = (BundleActivator) clazz.newInstance();
3046 }
3047 }
3048
3049 return activator;
3050 }
3051
3052 private void purgeBundle(BundleImpl bundle) throws Exception
3053 {
3054 // Acquire bundle lock.
3055 acquireBundleLock(bundle);
3056
3057 try
3058 {
3059 BundleInfo info = bundle.getInfo();
3060
3061 // In case of a refresh, then we want to physically
3062 // remove the bundle's modules from the module manager.
3063 // This is necessary for two reasons: 1) because
3064 // under Windows we won't be able to delete the bundle
3065 // because files might be left open in the resource
3066 // sources of its modules and 2) we want to make sure
3067 // that no references to old modules exist since they
3068 // will all be stale after the refresh. The only other
3069 // way to do this is to remove the bundle, but that
3070 // would be incorrect, because this is a refresh operation
3071 // and should not trigger bundle REMOVE events.
3072 Module[] modules = info.getModules();
3073 for (int i = 0; i < modules.length; i++)
3074 {
3075 m_mgr.removeModule(modules[i]);
3076 }
3077
3078 // Purge all bundle revisions, but the current one.
3079 m_cache.purge(m_cache.getArchive(info.getBundleId()));
3080 }
3081 finally
3082 {
3083 // Always release the bundle lock.
3084 releaseBundleLock(bundle);
3085 }
3086 }
3087
3088 private void garbageCollectBundle(BundleImpl bundle) throws Exception
3089 {
3090 // CONCURRENCY NOTE: There is no reason to lock this bundle,
3091 // because this method is only called during shutdown or a
3092 // refresh operation and these are already guarded by locks.
3093
3094 // Remove the bundle's associated modules from
3095 // the module manager.
3096 Module[] modules = bundle.getInfo().getModules();
3097 for (int i = 0; i < modules.length; i++)
3098 {
3099 m_mgr.removeModule(modules[i]);
3100 }
3101
3102 // Remove the bundle from the cache.
3103 m_cache.remove(m_cache.getArchive(bundle.getInfo().getBundleId()));
3104 }
3105
3106 //
3107 // Event-related methods.
3108 //
3109
3110 /**
3111 * Fires bundle events.
3112 **/
3113 private void fireFrameworkEvent(
3114 int type, Bundle bundle, Throwable throwable)
3115 {
3116 if (m_frameworkDispatcher == null)
3117 {
3118 m_frameworkDispatcher = new Dispatcher() {
3119 public void dispatch(EventListener l, EventObject eventObj)
3120 {
3121 ((FrameworkListener) l)
3122 .frameworkEvent((FrameworkEvent) eventObj);
3123 }
3124 };
3125 }
3126 FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
3127 m_dispatchQueue.dispatch(
3128 m_frameworkDispatcher, FrameworkListener.class, event);
3129 }
3130
3131 /**
3132 * Fires bundle events.
3133 *
3134 * @param type The type of bundle event to fire.
3135 * @param bundle The bundle associated with the event.
3136 **/
3137 private void fireBundleEvent(int type, Bundle bundle)
3138 {
3139 if (m_bundleDispatcher == null)
3140 {
3141 m_bundleDispatcher = new Dispatcher() {
3142 public void dispatch(EventListener l, EventObject eventObj)
3143 {
3144 ((BundleListener) l)
3145 .bundleChanged((BundleEvent) eventObj);
3146 }
3147 };
3148 }
3149 BundleEvent event = null;
3150 event = new BundleEvent(type, bundle);
3151 m_dispatchQueue.dispatch(m_bundleDispatcher,
3152 BundleListener.class, event);
3153 }
3154
3155 /**
3156 * Fires service events.
3157 *
3158 * @param type The type of service event to fire.
3159 * @param ref The service reference associated with the event.
3160 **/
3161 private void fireServiceEvent(ServiceEvent event)
3162 {
3163 if (m_serviceDispatcher == null)
3164 {
3165 m_serviceDispatcher = new Dispatcher() {
3166 public void dispatch(EventListener l, EventObject eventObj)
3167 {
3168// TODO: Filter service events based on service permissions.
3169 if (l instanceof ListenerWrapper)
3170 {
3171 BundleImpl bundle = (BundleImpl) ((ServiceListenerWrapper) l).getBundle();
3172 if (isServiceAssignable(bundle, ((ServiceEvent) eventObj).getServiceReference()))
3173 {
3174 ((ServiceListener) l)
3175 .serviceChanged((ServiceEvent) eventObj);
3176 }
3177 }
3178 else
3179 {
3180 ((ServiceListener) l)
3181 .serviceChanged((ServiceEvent) eventObj);
3182 }
3183 }
3184 };
3185 }
3186 m_dispatchQueue.dispatch(m_serviceDispatcher,
3187 ServiceListener.class, event);
3188 }
3189
3190 //
3191 // Property related methods.
3192 //
3193
3194 private void initializeFrameworkProperties()
3195 {
3196 // Standard OSGi properties.
Richard S. Hallea415752005-12-05 19:30:28 +00003197 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003198 FelixConstants.FRAMEWORK_VERSION,
3199 FelixConstants.FRAMEWORK_VERSION_VALUE);
Richard S. Hallea415752005-12-05 19:30:28 +00003200 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003201 FelixConstants.FRAMEWORK_VENDOR,
3202 FelixConstants.FRAMEWORK_VENDOR_VALUE);
Richard S. Hallea415752005-12-05 19:30:28 +00003203 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003204 FelixConstants.FRAMEWORK_LANGUAGE,
3205 System.getProperty("user.language"));
Richard S. Hallea415752005-12-05 19:30:28 +00003206 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003207 FelixConstants.FRAMEWORK_OS_VERSION,
3208 System.getProperty("os.version"));
3209
3210 String s = null;
3211 s = OSGiLibrarySource.normalizePropertyValue(
3212 FelixConstants.FRAMEWORK_OS_NAME,
3213 System.getProperty("os.name"));
Richard S. Hallea415752005-12-05 19:30:28 +00003214 m_configMutable.put(FelixConstants.FRAMEWORK_OS_NAME, s);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003215 s = OSGiLibrarySource.normalizePropertyValue(
3216 FelixConstants.FRAMEWORK_PROCESSOR,
3217 System.getProperty("os.arch"));
Richard S. Hallea415752005-12-05 19:30:28 +00003218 m_configMutable.put(FelixConstants.FRAMEWORK_PROCESSOR, s);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003219
3220 // The framework version property.
Richard S. Hallea415752005-12-05 19:30:28 +00003221 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003222 FelixConstants.FELIX_VERSION_PROPERTY,
3223 FelixConstants.FELIX_VERSION_VALUE);
3224 }
3225
3226 private void processAutoProperties()
3227 {
3228 // The auto-install property specifies a space-delimited list of
3229 // bundle URLs to be automatically installed into each new profile;
3230 // the start level to which the bundles are assigned is specified by
3231 // appending a ".n" to the auto-install property name, where "n" is
3232 // the desired start level for the list of bundles.
3233 String[] keys = m_config.getKeys();
3234 for (int i = 0; (keys != null) && (i < keys.length); i++)
3235 {
3236 if (keys[i].startsWith(FelixConstants.AUTO_INSTALL_PROP))
3237 {
3238 int startLevel = 1;
3239 try
3240 {
3241 startLevel = Integer.parseInt(keys[i].substring(keys[i].lastIndexOf('.') + 1));
3242 }
3243 catch (NumberFormatException ex)
3244 {
3245 m_logger.log(LogWrapper.LOG_ERROR, "Invalid property: " + keys[i]);
3246 }
3247 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3248 if (st.countTokens() > 0)
3249 {
3250 String location = null;
3251 do
3252 {
3253 location = nextLocation(st);
3254 if (location != null)
3255 {
3256 try
3257 {
3258 BundleImpl b = (BundleImpl) installBundle(location, null);
3259 b.getInfo().setStartLevel(startLevel);
3260 }
3261 catch (Exception ex)
3262 {
3263 m_logger.log(
3264 LogWrapper.LOG_ERROR, "Auto-properties install.", ex);
3265 }
3266 }
3267 }
3268 while (location != null);
3269 }
3270 }
3271 }
3272
3273 // The auto-start property specifies a space-delimited list of
3274 // bundle URLs to be automatically installed and started into each
3275 // new profile; the start level to which the bundles are assigned
3276 // is specified by appending a ".n" to the auto-start property name,
3277 // where "n" is the desired start level for the list of bundles.
3278 // The following code starts bundles in two passes, first it installs
3279 // them, then it starts them.
3280 for (int i = 0; (keys != null) && (i < keys.length); i++)
3281 {
3282 if (keys[i].startsWith(FelixConstants.AUTO_START_PROP))
3283 {
3284 int startLevel = 1;
3285 try
3286 {
3287 startLevel = Integer.parseInt(keys[i].substring(keys[i].lastIndexOf('.') + 1));
3288 }
3289 catch (NumberFormatException ex)
3290 {
3291 m_logger.log(LogWrapper.LOG_ERROR, "Invalid property: " + keys[i]);
3292 }
3293 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3294 if (st.countTokens() > 0)
3295 {
3296 String location = null;
3297 do
3298 {
3299 location = nextLocation(st);
3300 if (location != null)
3301 {
3302 try
3303 {
3304 BundleImpl b = (BundleImpl) installBundle(location, null);
3305 b.getInfo().setStartLevel(startLevel);
3306 }
3307 catch (Exception ex)
3308 {
3309 m_logger.log(LogWrapper.LOG_ERROR, "Auto-properties install.", ex);
3310 }
3311 }
3312 }
3313 while (location != null);
3314 }
3315 }
3316 }
3317
3318 // Now loop through and start the installed bundles.
3319 for (int i = 0; (keys != null) && (i < keys.length); i++)
3320 {
3321 if (keys[i].startsWith(FelixConstants.AUTO_START_PROP))
3322 {
3323 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3324 if (st.countTokens() > 0)
3325 {
3326 String location = null;
3327 do
3328 {
3329 location = nextLocation(st);
3330 if (location != null)
3331 {
3332 // Installing twice just returns the same bundle.
3333 try
3334 {
3335 BundleImpl bundle = (BundleImpl) installBundle(location, null);
3336 if (bundle != null)
3337 {
3338 startBundle(bundle, true);
3339 }
3340 }
3341 catch (Exception ex)
3342 {
3343 m_logger.log(
3344 LogWrapper.LOG_ERROR, "Auto-properties start.", ex);
3345 }
3346 }
3347 }
3348 while (location != null);
3349 }
3350 }
3351 }
3352 }
3353
3354 private String nextLocation(StringTokenizer st)
3355 {
3356 String retVal = null;
3357
3358 if (st.countTokens() > 0)
3359 {
3360 String tokenList = "\" ";
3361 StringBuffer tokBuf = new StringBuffer(10);
3362 String tok = null;
3363 boolean inQuote = false;
3364 boolean tokStarted = false;
3365 boolean exit = false;
3366 while ((st.hasMoreTokens()) && (!exit))
3367 {
3368 tok = st.nextToken(tokenList);
3369 if (tok.equals("\""))
3370 {
3371 inQuote = ! inQuote;
3372 if (inQuote)
3373 {
3374 tokenList = "\"";
3375 }
3376 else
3377 {
3378 tokenList = "\" ";
3379 }
3380
3381 }
3382 else if (tok.equals(" "))
3383 {
3384 if (tokStarted)
3385 {
3386 retVal = tokBuf.toString();
3387 tokStarted=false;
3388 tokBuf = new StringBuffer(10);
3389 exit = true;
3390 }
3391 }
3392 else
3393 {
3394 tokStarted = true;
3395 tokBuf.append(tok.trim());
3396 }
3397 }
3398
3399 // Handle case where end of token stream and
3400 // still got data
3401 if ((!exit) && (tokStarted))
3402 {
3403 retVal = tokBuf.toString();
3404 }
3405 }
3406
3407 return retVal;
3408 }
3409
3410 //
3411 // Private utility methods.
3412 //
3413
3414 /**
3415 * Generated the next valid bundle identifier.
3416 **/
3417 private synchronized long getNextId()
3418 {
3419 return m_nextId++;
3420 }
3421
3422 //
3423 // Configuration methods and inner classes.
3424 //
3425
3426 public PropertyResolver getConfig()
3427 {
3428 return m_config;
3429 }
3430
3431 private class ConfigImpl implements PropertyResolver
3432 {
3433 public String get(String key)
3434 {
Richard S. Hallea415752005-12-05 19:30:28 +00003435 return (m_configMutable == null) ? null : m_configMutable.get(key);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003436 }
3437
3438 public String[] getKeys()
3439 {
Richard S. Hallea415752005-12-05 19:30:28 +00003440 return m_configMutable.getKeys();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003441 }
3442 }
3443
3444 //
3445 // Logging methods and inner classes.
3446 //
3447
3448 public LogWrapper getLogger()
3449 {
3450 return m_logger;
3451 }
3452
3453 /**
3454 * Simple class that is used in <tt>refreshPackages()</tt> to embody
3455 * the refresh logic in order to keep the code clean. This class is
3456 * not static because it needs access to framework event firing methods.
3457 **/
3458 private class RefreshHelper
3459 {
3460 private BundleImpl m_bundle = null;
3461
3462 public RefreshHelper(Bundle bundle)
3463 {
3464 m_bundle = (BundleImpl) bundle;
3465 }
3466
3467 public void stop()
3468 {
3469 if (m_bundle.getInfo().getState() == Bundle.ACTIVE)
3470 {
3471 try
3472 {
3473 stopBundle(m_bundle, false);
3474 }
3475 catch (BundleException ex)
3476 {
3477 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3478 }
3479 }
3480 }
3481
3482 public void purgeOrRemove()
3483 {
3484 try
3485 {
3486 BundleInfo info = m_bundle.getInfo();
3487
3488 // Remove or purge the bundle depending on its
3489 // current state.
3490 if (info.getState() == Bundle.UNINSTALLED)
3491 {
3492 // This physically removes the bundle from memory
3493 // as well as the bundle cache.
3494 garbageCollectBundle(m_bundle);
3495 m_bundle = null;
3496 }
3497 else
3498 {
3499 // This physically removes all old revisions of the
3500 // bundle from memory and only maintains the newest
3501 // version in the bundle cache.
3502 purgeBundle(m_bundle);
3503 }
3504 }
3505 catch (Exception ex)
3506 {
3507 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3508 }
3509 }
3510
3511 public void reinitialize()
3512 {
3513 if (m_bundle != null)
3514 {
3515 try
3516 {
3517 BundleInfo info = m_bundle.getInfo();
3518 BundleInfo newInfo = createBundleInfo(info.getArchive());
3519 newInfo.syncLock(info);
3520 m_bundle.setInfo(newInfo);
3521 }
3522 catch (Exception ex)
3523 {
3524 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3525 }
3526 }
3527 }
3528
3529 public void restart()
3530 {
3531 if (m_bundle != null)
3532 {
3533 try
3534 {
3535 startBundle(m_bundle, false);
3536 }
3537 catch (BundleException ex)
3538 {
3539 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3540 }
3541 }
3542 }
3543 }
3544
3545 //
3546 // Locking related methods.
3547 //
3548
3549 private void rememberUninstalledBundle(BundleImpl bundle)
3550 {
3551 synchronized (m_uninstalledBundlesLock_Priority3)
3552 {
3553 // Verify that the bundle is not already in the array.
3554 for (int i = 0;
3555 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
3556 i++)
3557 {
3558 if (m_uninstalledBundles[i] == bundle)
3559 {
3560 return;
3561 }
3562 }
3563
3564 if (m_uninstalledBundles != null)
3565 {
3566 BundleImpl[] newBundles =
3567 new BundleImpl[m_uninstalledBundles.length + 1];
3568 System.arraycopy(m_uninstalledBundles, 0,
3569 newBundles, 0, m_uninstalledBundles.length);
3570 newBundles[m_uninstalledBundles.length] = bundle;
3571 m_uninstalledBundles = newBundles;
3572 }
3573 else
3574 {
3575 m_uninstalledBundles = new BundleImpl[] { bundle };
3576 }
3577 }
3578 }
3579
3580 private void forgetUninstalledBundle(BundleImpl bundle)
3581 {
3582 synchronized (m_uninstalledBundlesLock_Priority3)
3583 {
3584 if (m_uninstalledBundles == null)
3585 {
3586 return;
3587 }
3588
3589 int idx = -1;
3590 for (int i = 0; i < m_uninstalledBundles.length; i++)
3591 {
3592 if (m_uninstalledBundles[i] == bundle)
3593 {
3594 idx = i;
3595 break;
3596 }
3597 }
3598
3599 if (idx >= 0)
3600 {
3601 // If this is the only bundle, then point to empty list.
3602 if ((m_uninstalledBundles.length - 1) == 0)
3603 {
3604 m_uninstalledBundles = new BundleImpl[0];
3605 }
3606 // Otherwise, we need to do some array copying.
3607 else
3608 {
3609 BundleImpl[] newBundles =
3610 new BundleImpl[m_uninstalledBundles.length - 1];
3611 System.arraycopy(m_uninstalledBundles, 0, newBundles, 0, idx);
3612 if (idx < newBundles.length)
3613 {
3614 System.arraycopy(
3615 m_uninstalledBundles, idx + 1,
3616 newBundles, idx, newBundles.length - idx);
3617 }
3618 m_uninstalledBundles = newBundles;
3619 }
3620 }
3621 }
3622 }
3623
3624 protected void acquireInstallLock(String location)
3625 throws BundleException
3626 {
3627 synchronized (m_installRequestLock_Priority1)
3628 {
3629 while (m_installRequestMap.get(location) != null)
3630 {
3631 try
3632 {
3633 m_installRequestLock_Priority1.wait();
3634 }
3635 catch (InterruptedException ex)
3636 {
3637 throw new BundleException("Unable to install, thread interrupted.");
3638 }
3639 }
3640
3641 m_installRequestMap.put(location, location);
3642 }
3643 }
3644
3645 protected void releaseInstallLock(String location)
3646 {
3647 synchronized (m_installRequestLock_Priority1)
3648 {
3649 m_installRequestMap.remove(location);
3650 m_installRequestLock_Priority1.notifyAll();
3651 }
3652 }
3653
3654 protected void acquireBundleLock(BundleImpl bundle)
3655 throws BundleException
3656 {
3657 synchronized (m_bundleLock)
3658 {
3659 while (!bundle.getInfo().isLockable())
3660 {
3661 try
3662 {
3663 m_bundleLock.wait();
3664 }
3665 catch (InterruptedException ex)
3666 {
3667 // Ignore and just keep waiting.
3668 }
3669 }
3670 bundle.getInfo().lock();
3671 }
3672 }
3673
3674 protected void releaseBundleLock(BundleImpl bundle)
3675 {
3676 synchronized (m_bundleLock)
3677 {
3678 bundle.getInfo().unlock();
3679 m_bundleLock.notifyAll();
3680 }
3681 }
3682
3683 protected BundleImpl[] acquireBundleRefreshLocks(Bundle[] targets)
3684 {
3685 // Hold bundles to be locked.
3686 BundleImpl[] bundles = null;
3687
3688 synchronized (m_bundleLock)
3689 {
3690 boolean success = false;
3691 while (!success)
3692 {
3693 // If targets is null, then refresh all pending bundles.
3694 Bundle[] newTargets = targets;
3695 if (newTargets == null)
3696 {
3697 List list = new ArrayList();
3698
3699 // First add all uninstalled bundles.
3700 synchronized (m_uninstalledBundlesLock_Priority3)
3701 {
3702 for (int i = 0;
3703 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
3704 i++)
3705 {
3706 list.add(m_uninstalledBundles[i]);
3707 }
3708 }
3709
3710 // Then add all updated bundles.
3711 synchronized (m_installedBundleLock_Priority2)
3712 {
3713 Iterator iter = m_installedBundleMap.values().iterator();
3714 while (iter.hasNext())
3715 {
3716 BundleImpl bundle = (BundleImpl) iter.next();
3717 if (bundle.getInfo().isRemovalPending())
3718 {
3719 list.add(bundle);
3720 }
3721 }
3722 }
3723
3724 // Create an array.
3725 if (list.size() > 0)
3726 {
3727 newTargets = (Bundle[]) list.toArray(new Bundle[list.size()]);
3728 }
3729 }
3730
3731 // If there are targets, then find all dependencies
3732 // for each one.
3733 if (newTargets != null)
3734 {
3735 // Create map of bundles that import the packages
3736 // from the target bundles.
3737 Map map = new HashMap();
3738 for (int targetIdx = 0; targetIdx < newTargets.length; targetIdx++)
3739 {
3740 // Add the current target bundle to the map of
3741 // bundles to be refreshed.
3742 BundleImpl target = (BundleImpl) newTargets[targetIdx];
3743 map.put(target, target);
3744 // Add all importing bundles to map.
3745 populateImportGraph(target, map);
3746 }
3747
3748 bundles = (BundleImpl[]) map.values().toArray(new BundleImpl[map.size()]);
3749 }
3750
3751 // Check if all corresponding bundles can be locked
3752 boolean lockable = true;
3753 if (bundles != null)
3754 {
3755 for (int i = 0; lockable && (i < bundles.length); i++)
3756 {
3757 lockable = bundles[i].getInfo().isLockable();
3758 }
3759
3760 // If we can lock all bundles, then lock them.
3761 if (lockable)
3762 {
3763 for (int i = 0; i < bundles.length; i++)
3764 {
3765 bundles[i].getInfo().lock();
3766 }
3767 success = true;
3768 }
3769 // Otherwise, wait and try again.
3770 else
3771 {
3772 try
3773 {
3774 m_bundleLock.wait();
3775 }
3776 catch (InterruptedException ex)
3777 {
3778 // Ignore and just keep waiting.
3779 }
3780 }
3781 }
3782 else
3783 {
3784 // If there were no bundles to lock, then we can just
3785 // exit the lock loop.
3786 success = true;
3787 }
3788 }
3789 }
3790
3791 return bundles;
3792 }
3793
3794 protected void releaseBundleLocks(BundleImpl[] bundles)
3795 {
3796 // Always unlock any locked bundles.
3797 synchronized (m_bundleLock)
3798 {
3799 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
3800 {
3801 bundles[i].getInfo().unlock();
3802 }
3803 m_bundleLock.notifyAll();
3804 }
3805 }
Richard S. Hall5d226732005-11-08 09:09:05 +00003806}