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