blob: f946fbf964c9431bb7606c18f223f61f2a653cea [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. Hall930fecc2005-08-16 18:33:34 +0000987 protected ServiceReference[] getBundleRegisteredServices(BundleImpl bundle)
988 {
989 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
990 {
991 throw new IllegalStateException("The bundle is uninstalled.");
992 }
993
994 // Filter list of registered service references.
995 ServiceReference[] refs = m_registry.getRegisteredServices(bundle);
996 List list = new ArrayList();
997 for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
998 {
999 // Check that the current security context has permission
1000 // to get at least one of the service interfaces; the
1001 // objectClass property of the service stores its service
1002 // interfaces.
1003 boolean hasPermission = false;
1004 if (System.getSecurityManager() != null)
1005 {
1006 String[] objectClass = (String[])
1007 refs[refIdx].getProperty(Constants.OBJECTCLASS);
1008 if (objectClass == null)
1009 {
1010 return null;
1011 }
1012 for (int ifcIdx = 0;
1013 !hasPermission && (ifcIdx < objectClass.length);
1014 ifcIdx++)
1015 {
1016 try
1017 {
1018 ServicePermission perm =
1019 new ServicePermission(
1020 objectClass[ifcIdx], ServicePermission.GET);
1021 AccessController.checkPermission(perm);
1022 hasPermission = true;
1023 }
1024 catch (Exception ex)
1025 {
1026 }
1027 }
1028 }
1029 else
1030 {
1031 hasPermission = true;
1032 }
1033
1034 if (hasPermission)
1035 {
1036 list.add(refs[refIdx]);
1037 }
1038 }
1039
1040 if (list.size() > 0)
1041 {
1042 return (ServiceReference[])
1043 list.toArray(new ServiceReference[list.size()]);
1044 }
1045
1046 return null;
1047 }
1048
1049 protected ServiceReference[] getBundleServicesInUse(Bundle bundle)
1050 {
1051 // Filter list of "in use" service references.
1052 ServiceReference[] refs = m_registry.getServicesInUse(bundle);
1053 List list = new ArrayList();
1054 for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
1055 {
1056 // Check that the current security context has permission
1057 // to get at least one of the service interfaces; the
1058 // objectClass property of the service stores its service
1059 // interfaces.
1060 boolean hasPermission = false;
1061 if (System.getSecurityManager() != null)
1062 {
1063 String[] objectClass = (String[])
1064 refs[refIdx].getProperty(Constants.OBJECTCLASS);
1065 if (objectClass == null)
1066 {
1067 return null;
1068 }
1069 for (int ifcIdx = 0;
1070 !hasPermission && (ifcIdx < objectClass.length);
1071 ifcIdx++)
1072 {
1073 try
1074 {
1075 ServicePermission perm =
1076 new ServicePermission(
1077 objectClass[ifcIdx], ServicePermission.GET);
1078 AccessController.checkPermission(perm);
1079 hasPermission = true;
1080 }
1081 catch (Exception ex)
1082 {
1083 }
1084 }
1085 }
1086 else
1087 {
1088 hasPermission = true;
1089 }
1090
1091 if (hasPermission)
1092 {
1093 list.add(refs[refIdx]);
1094 }
1095 }
1096
1097 if (list.size() > 0)
1098 {
1099 return (ServiceReference[])
1100 list.toArray(new ServiceReference[list.size()]);
1101 }
1102
1103 return null;
1104 }
1105
1106 protected boolean bundleHasPermission(BundleImpl bundle, Object obj)
1107 {
1108 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1109 {
1110 throw new IllegalStateException("The bundle is uninstalled.");
1111 }
1112
Richard S. Hall2cf44c92006-01-23 19:23:56 +00001113 if (System.getSecurityManager() != null)
Richard S. Hall65730962006-01-23 19:23:13 +00001114 {
1115 try
1116 {
Richard S. Halld1e3cbd2006-01-27 15:23:01 +00001117 return (obj instanceof java.security.Permission)
1118 ? java.security.Policy.getPolicy().getPermissions(
Richard S. Hall65730962006-01-23 19:23:13 +00001119 new java.security.CodeSource(
Richard S. Hallc762b692006-01-27 15:18:57 +00001120 new java.net.URL(bundle.getInfo().getLocation()),
1121 (java.security.cert.Certificate[]) null))
Richard S. Hall65730962006-01-23 19:23:13 +00001122 .implies((java.security.Permission) obj)
Richard S. Halld1e3cbd2006-01-27 15:23:01 +00001123 : false;
Richard S. Hall65730962006-01-23 19:23:13 +00001124 }
1125 catch (Exception ex)
1126 {
1127 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001128 Logger.LOG_WARNING,
Richard S. Hall65730962006-01-23 19:23:13 +00001129 "Exception while evaluating the permission.",
1130 ex);
1131 return false;
1132 }
Richard S. Halld1e3cbd2006-01-27 15:23:01 +00001133 }
Richard S. Hall65730962006-01-23 19:23:13 +00001134
Richard S. Halld1e3cbd2006-01-27 15:23:01 +00001135 return true;
Richard S. Hall930fecc2005-08-16 18:33:34 +00001136 }
1137
1138 /**
Richard S. Hall74b97972005-11-30 15:51:41 +00001139 * Implementation for Bundle.loadClass().
1140 **/
1141 protected Class loadBundleClass(BundleImpl bundle, String name) throws ClassNotFoundException
1142 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001143 Class clazz = bundle.getInfo().getCurrentModule().getContentLoader().getClass(name);
1144 if (clazz == null)
Richard S. Hall74b97972005-11-30 15:51:41 +00001145 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001146 // Throw exception.
1147 ClassNotFoundException ex = new ClassNotFoundException(name);
1148
Richard S. Hall624f8012005-11-30 15:53:43 +00001149 // The spec says we must fire a framework error.
Richard S. Hall74b97972005-11-30 15:51:41 +00001150 fireFrameworkEvent(
1151 FrameworkEvent.ERROR, bundle,
1152 new BundleException(ex.getMessage()));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001153
Richard S. Hall74b97972005-11-30 15:51:41 +00001154 throw ex;
1155 }
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001156 return clazz;
Richard S. Hall74b97972005-11-30 15:51:41 +00001157 }
1158
1159 /**
Richard S. Hall930fecc2005-08-16 18:33:34 +00001160 * Implementation for Bundle.start().
1161 **/
1162 protected void startBundle(BundleImpl bundle, boolean record)
1163 throws BundleException
1164 {
1165 if (System.getSecurityManager() != null)
1166 {
1167 AccessController.checkPermission(m_adminPerm);
1168 }
1169
1170 // CONCURRENCY NOTE:
1171 // Starting a bundle may actually impact many bundles, since
1172 // the bundle being started my need to be resolved, which in
1173 // turn may need to resolve other bundles. Despite this fact,
1174 // we only acquire the lock for the bundle being started, because
1175 // when resolve is called on this bundle, it will eventually
1176 // call resolve on the module loader search policy, which does
1177 // its own locking on the module manager instance. Since the
1178 // resolve algorithm is locking the module manager instance, it
1179 // is not possible for other bundles to be installed or removed,
1180 // so we don't have to worry about these possibilities.
1181 //
1182 // Further, if other bundles are started during this operation,
1183 // then either they will resolve first because they got the lock
1184 // on the module manager or we will resolve first since we got
1185 // the lock on the module manager, so there should be no interference.
1186 // If other bundles are stopped or uninstalled, this should pose
1187 // no problems, since this does not impact their resolved state.
1188 // If a refresh occurs, then the refresh algorithm ulimately has
1189 // to acquire the module manager instance lock too before it can
1190 // completely purge old modules, so it should also complete either
1191 // before or after this bundle is started. At least that's the
1192 // theory.
1193
1194 // Acquire bundle lock.
1195 acquireBundleLock(bundle);
1196
1197 try
1198 {
1199 _startBundle(bundle, record);
1200 }
1201 finally
1202 {
1203 // Release bundle lock.
1204 releaseBundleLock(bundle);
1205 }
1206 }
1207
1208 private void _startBundle(BundleImpl bundle, boolean record)
1209 throws BundleException
1210 {
1211 // Set and save the bundle's persistent state to active
1212 // if we are supposed to record state change.
1213 if (record)
1214 {
1215 bundle.getInfo().setPersistentStateActive();
1216 }
1217
1218 // Try to start the bundle.
1219 BundleInfo info = bundle.getInfo();
1220
1221 // Ignore bundles whose persistent state is not active
1222 // or whose start level is greater than the framework's.
1223 if ((info.getPersistentState() != Bundle.ACTIVE)
1224 || (info.getStartLevel(getInitialBundleStartLevel()) > getStartLevel()))
1225 {
1226 return;
1227 }
1228
1229 switch (info.getState())
1230 {
1231 case Bundle.UNINSTALLED:
1232 throw new IllegalStateException("Cannot start an uninstalled bundle.");
1233 case Bundle.STARTING:
1234 case Bundle.STOPPING:
1235 throw new BundleException("Starting a bundle that is starting or stopping is currently not supported.");
1236 case Bundle.ACTIVE:
1237 return;
1238 case Bundle.INSTALLED:
1239 _resolveBundle(bundle);
1240 case Bundle.RESOLVED:
1241 info.setState(Bundle.STARTING);
1242 }
1243
1244 try
1245 {
1246 // Set the bundle's activator.
1247 bundle.getInfo().setActivator(createBundleActivator(bundle.getInfo()));
1248
1249 // Activate the bundle if it has an activator.
1250 if (bundle.getInfo().getActivator() != null)
1251 {
1252 if (info.getContext() == null)
1253 {
1254 info.setContext(new BundleContextImpl(this, bundle));
1255 }
1256
1257 if (System.getSecurityManager() != null)
1258 {
1259// m_startStopPrivileged.setAction(StartStopPrivileged.START_ACTION);
1260// m_startStopPrivileged.setBundle(bundle);
1261// AccessController.doPrivileged(m_startStopPrivileged);
1262 }
1263 else
1264 {
1265 info.getActivator().start(info.getContext());
1266 }
1267 }
1268
1269 info.setState(Bundle.ACTIVE);
1270
1271 fireBundleEvent(BundleEvent.STARTED, bundle);
1272 }
1273 catch (Throwable th)
1274 {
1275 // If there was an error starting the bundle,
1276 // then reset its state to RESOLVED.
1277 info.setState(Bundle.RESOLVED);
1278
1279 // Unregister any services offered by this bundle.
1280 m_registry.unregisterServices(bundle);
1281
1282 // Release any services being used by this bundle.
1283 m_registry.ungetServices(bundle);
1284
1285 // Remove any listeners registered by this bundle.
1286 removeListeners(bundle);
1287
1288 // The spec says to expect BundleException or
1289 // SecurityException, so rethrow these exceptions.
1290 if (th instanceof BundleException)
1291 {
1292 throw (BundleException) th;
1293 }
1294 else if (th instanceof SecurityException)
1295 {
1296 throw (SecurityException) th;
1297 }
1298 // Convert a privileged action exception to the
1299 // nested exception.
1300 else if (th instanceof PrivilegedActionException)
1301 {
1302 th = ((PrivilegedActionException) th).getException();
1303 }
1304
1305 // Rethrow all other exceptions as a BundleException.
1306 throw new BundleException("Activator start error.", th);
1307 }
1308 }
1309
1310 protected void _resolveBundle(BundleImpl bundle)
1311 throws BundleException
1312 {
1313 // If a security manager is installed, then check for permission
1314 // to import the necessary packages.
1315 if (System.getSecurityManager() != null)
1316 {
1317 URL url = null;
1318 try
1319 {
1320 url = new URL(bundle.getInfo().getLocation());
1321 }
1322 catch (MalformedURLException ex)
1323 {
1324 throw new BundleException("Cannot resolve, bad URL "
1325 + bundle.getInfo().getLocation());
1326 }
1327
1328// try
1329// {
1330// AccessController.doPrivileged(new CheckImportsPrivileged(url, bundle));
1331// }
1332// catch (PrivilegedActionException ex)
1333// {
1334// Exception thrown = ((PrivilegedActionException) ex).getException();
1335// if (thrown instanceof AccessControlException)
1336// {
1337// throw (AccessControlException) thrown;
1338// }
1339// else
1340// {
1341// throw new BundleException("Problem resolving: " + ex);
1342// }
1343// }
1344 }
1345
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001346 IModule module = bundle.getInfo().getCurrentModule();
Richard S. Hall930fecc2005-08-16 18:33:34 +00001347 try
1348 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001349 m_policyCore.resolve(module);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001350 }
1351 catch (ResolveException ex)
1352 {
1353 if (ex.getModule() != null)
1354 {
1355 throw new BundleException(
1356 "Unresolved package in bundle "
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001357 + Util.getBundleIdFromModuleId(ex.getModule().getId())
Richard S. Hall930fecc2005-08-16 18:33:34 +00001358 + ": " + ex.getPackage());
1359 }
1360 else
1361 {
1362 throw new BundleException(ex.getMessage());
1363 }
1364 }
1365
1366 bundle.getInfo().setState(Bundle.RESOLVED);
1367 }
1368
1369 protected void updateBundle(BundleImpl bundle, InputStream is)
1370 throws BundleException
1371 {
1372 if (System.getSecurityManager() != null)
1373 {
1374 AccessController.checkPermission(m_adminPerm);
1375 }
1376
1377 // Acquire bundle lock.
1378 acquireBundleLock(bundle);
1379
1380 try
1381 {
1382 _updateBundle(bundle, is);
1383 }
1384 finally
1385 {
1386 // Release bundle lock.
1387 releaseBundleLock(bundle);
1388 }
1389 }
1390
1391 protected void _updateBundle(BundleImpl bundle, InputStream is)
1392 throws BundleException
1393 {
1394 // We guarantee to close the input stream, so put it in a
1395 // finally clause.
1396
1397 try
1398 {
1399 // Variable to indicate whether bundle is active or not.
1400 Exception rethrow = null;
1401
1402 // Cannot update an uninstalled bundle.
1403 BundleInfo info = bundle.getInfo();
1404 if (info.getState() == Bundle.UNINSTALLED)
1405 {
1406 throw new IllegalStateException("The bundle is uninstalled.");
1407 }
1408
1409 // First get the update-URL from our header.
1410 String updateLocation = (String)
1411 info.getCurrentHeader().get(Constants.BUNDLE_UPDATELOCATION);
1412
1413 // If no update location specified, use original location.
1414 if (updateLocation == null)
1415 {
1416 updateLocation = info.getLocation();
1417 }
1418
1419 // Stop the bundle, but do not change the persistent state.
1420 stopBundle(bundle, false);
1421
1422 try
1423 {
1424 // Get the URL input stream if necessary.
1425 if (is == null)
1426 {
1427 // Do it the manual way to have a chance to
1428 // set request properties such as proxy auth.
1429 URL url = new URL(updateLocation);
1430 URLConnection conn = url.openConnection();
1431
1432 // Support for http proxy authentication.
1433 String auth = System.getProperty("http.proxyAuth");
1434 if ((auth != null) && (auth.length() > 0))
1435 {
1436 if ("http".equals(url.getProtocol()) ||
1437 "https".equals(url.getProtocol()))
1438 {
1439 String base64 = Util.base64Encode(auth);
1440 conn.setRequestProperty(
1441 "Proxy-Authorization", "Basic " + base64);
1442 }
1443 }
1444 is = conn.getInputStream();
1445 }
1446
1447 // Get the bundle's archive.
1448 BundleArchive archive = m_cache.getArchive(info.getBundleId());
1449 // Update the bundle; this operation will increase
1450 // the revision count for the bundle.
1451 m_cache.update(archive, is);
1452 // Create a module for the new revision; the revision is
1453 // base zero, so subtract one from the revision count to
1454 // get the revision of the new update.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001455 IModule module = createModule(
Richard S. Hall930fecc2005-08-16 18:33:34 +00001456 info.getBundleId(),
1457 archive.getRevisionCount() - 1,
1458 info.getCurrentHeader());
1459 // Add module to bundle info.
1460 info.addModule(module);
1461 }
1462 catch (Exception ex)
1463 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001464 m_logger.log(Logger.LOG_ERROR, "Unable to update the bundle.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001465 rethrow = ex;
1466 }
1467
1468 info.setState(Bundle.INSTALLED);
Richard S. Hall69d84792006-01-13 13:55:13 +00001469 info.setLastModified(System.currentTimeMillis());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001470
1471 // Mark as needing a refresh.
1472 info.setRemovalPending();
1473
1474 // Fire updated event if successful.
1475 if (rethrow == null)
1476 {
1477 fireBundleEvent(BundleEvent.UPDATED, bundle);
1478 }
1479
1480 // Restart bundle, but do not change the persistent state.
1481 // This will not start the bundle if it was not previously
1482 // active.
1483 startBundle(bundle, false);
1484
1485 // If update failed, rethrow exception.
1486 if (rethrow != null)
1487 {
1488 throw new BundleException("Update failed.", rethrow);
1489 }
1490 }
1491 finally
1492 {
1493 try
1494 {
1495 if (is != null) is.close();
1496 }
1497 catch (IOException ex)
1498 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001499 m_logger.log(Logger.LOG_ERROR, "Unable to close input stream.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001500 }
1501 }
1502 }
1503
1504 protected void stopBundle(BundleImpl bundle, boolean record)
1505 throws BundleException
1506 {
1507 if (System.getSecurityManager() != null)
1508 {
1509 AccessController.checkPermission(m_adminPerm);
1510 }
1511
1512 // Acquire bundle lock.
1513 acquireBundleLock(bundle);
1514
1515 try
1516 {
1517 _stopBundle(bundle, record);
1518 }
1519 finally
1520 {
1521 // Always release bundle lock.
1522 releaseBundleLock(bundle);
1523 }
1524 }
1525
1526 private void _stopBundle(BundleImpl bundle, boolean record)
1527 throws BundleException
1528 {
1529 Throwable rethrow = null;
1530
1531 // Set the bundle's persistent state to inactive if necessary.
1532 if (record)
1533 {
1534 bundle.getInfo().setPersistentStateInactive();
1535 }
1536
1537 BundleInfo info = bundle.getInfo();
1538
1539 switch (info.getState())
1540 {
1541 case Bundle.UNINSTALLED:
1542 throw new IllegalStateException("Cannot stop an uninstalled bundle.");
1543 case Bundle.STARTING:
1544 case Bundle.STOPPING:
1545 throw new BundleException("Stopping a bundle that is starting or stopping is currently not supported.");
1546 case Bundle.INSTALLED:
1547 case Bundle.RESOLVED:
1548 return;
1549 case Bundle.ACTIVE:
1550 // Set bundle state..
1551 info.setState(Bundle.STOPPING);
1552 }
1553
1554 try
1555 {
1556 if (bundle.getInfo().getActivator() != null)
1557 {
1558 if (System.getSecurityManager() != null)
1559 {
1560// m_startStopPrivileged.setAction(StartStopPrivileged.STOP_ACTION);
1561// m_startStopPrivileged.setBundle(bundle);
1562// AccessController.doPrivileged(m_startStopPrivileged);
1563 }
1564 else
1565 {
1566 info.getActivator().stop(info.getContext());
1567 }
1568 }
1569
1570 // Try to save the activator in the cache.
1571 // NOTE: This is non-standard OSGi behavior and only
1572 // occurs if strictness is disabled.
1573 String strict = m_config.get(FelixConstants.STRICT_OSGI_PROP);
1574 boolean isStrict = (strict == null) ? true : strict.equals("true");
1575 if (!isStrict)
1576 {
1577 try
1578 {
1579 m_cache.getArchive(info.getBundleId())
1580 .setActivator(info.getActivator());
1581 }
1582 catch (Exception ex)
1583 {
1584 // Problem saving activator, so ignore it.
1585 // TODO: Perhaps we should handle this some other way?
1586 }
1587 }
1588 }
1589 catch (Throwable th)
1590 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001591 m_logger.log(Logger.LOG_ERROR, "Error stopping bundle.", th);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001592 rethrow = th;
1593 }
1594
1595 // Unregister any services offered by this bundle.
1596 m_registry.unregisterServices(bundle);
1597
1598 // Release any services being used by this bundle.
1599 m_registry.ungetServices(bundle);
1600
1601 // The spec says that we must remove all event
1602 // listeners for a bundle when it is stopped.
1603 removeListeners(bundle);
1604
1605 info.setState(Bundle.RESOLVED);
1606 fireBundleEvent(BundleEvent.STOPPED, bundle);
1607
1608 // Throw activator error if there was one.
1609 if (rethrow != null)
1610 {
1611 // The spec says to expect BundleException or
1612 // SecurityException, so rethrow these exceptions.
1613 if (rethrow instanceof BundleException)
1614 {
1615 throw (BundleException) rethrow;
1616 }
1617 else if (rethrow instanceof SecurityException)
1618 {
1619 throw (SecurityException) rethrow;
1620 }
1621 else if (rethrow instanceof PrivilegedActionException)
1622 {
1623 rethrow = ((PrivilegedActionException) rethrow).getException();
1624 }
1625
1626 // Rethrow all other exceptions as a BundleException.
1627 throw new BundleException("Activator stop error.", rethrow);
1628 }
1629 }
1630
1631 protected void uninstallBundle(BundleImpl bundle) throws BundleException
1632 {
1633 if (System.getSecurityManager() != null)
1634 {
1635 AccessController.checkPermission(m_adminPerm);
1636 }
1637
1638 // Acquire bundle lock.
1639 acquireBundleLock(bundle);
1640
1641 try
1642 {
1643 _uninstallBundle(bundle);
1644 }
1645 finally
1646 {
1647 // Always release bundle lock.
1648 releaseBundleLock(bundle);
1649 }
1650 }
1651
1652 private void _uninstallBundle(BundleImpl bundle) throws BundleException
1653 {
1654 if (System.getSecurityManager() != null)
1655 {
1656 AccessController.checkPermission(m_adminPerm);
1657 }
1658
1659 BundleException rethrow = null;
1660
1661 BundleInfo info = bundle.getInfo();
1662 if (info.getState() == Bundle.UNINSTALLED)
1663 {
1664 throw new IllegalStateException("The bundle is uninstalled.");
1665 }
1666
1667 // The spec says that uninstall should always succeed, so
1668 // catch an exception here if stop() doesn't succeed and
1669 // rethrow it at the end.
1670 try
1671 {
1672 stopBundle(bundle, true);
1673 }
1674 catch (BundleException ex)
1675 {
1676 rethrow = ex;
1677 }
1678
1679 // Remove the bundle from the installed map.
1680 BundleImpl target = null;
1681 synchronized (m_installedBundleLock_Priority2)
1682 {
1683 target = (BundleImpl) m_installedBundleMap.remove(info.getLocation());
1684 }
1685
1686 // Finally, put the uninstalled bundle into the
1687 // uninstalled list for subsequent refreshing.
1688 if (target != null)
1689 {
1690 // Set the bundle's persistent state to uninstalled.
1691 target.getInfo().setPersistentStateUninstalled();
1692
1693 // Mark bundle for removal.
1694 target.getInfo().setRemovalPending();
1695
1696 // Put bundle in uninstalled bundle array.
1697 rememberUninstalledBundle(bundle);
1698 }
1699 else
1700 {
1701 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001702 Logger.LOG_ERROR, "Unable to remove bundle from installed map!");
Richard S. Hall930fecc2005-08-16 18:33:34 +00001703 }
1704
1705 // Set state to uninstalled.
1706 info.setState(Bundle.UNINSTALLED);
Richard S. Hall69d84792006-01-13 13:55:13 +00001707 info.setLastModified(System.currentTimeMillis());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001708
1709 // Fire bundle event.
1710 fireBundleEvent(BundleEvent.UNINSTALLED, bundle);
1711
1712 if (rethrow != null)
1713 {
1714 throw rethrow;
1715 }
1716 }
1717
1718 //
1719 // Implementation of BundleContext interface methods.
1720 //
1721
1722 /**
1723 * Implementation for BundleContext.getProperty(). Returns
1724 * environment property associated with the framework.
1725 *
1726 * @param key The name of the property to retrieve.
1727 * @return The value of the specified property or null.
1728 **/
1729 protected String getProperty(String key)
1730 {
1731 // First, check the config properties.
Richard S. Hallea415752005-12-05 19:30:28 +00001732 String val = (String) m_config.get(key);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001733 // If not found, then try the system properties.
1734 return (val == null) ? System.getProperty(key) : val;
1735 }
1736
1737 protected Bundle installBundle(String location, InputStream is)
1738 throws BundleException
1739 {
1740 return installBundle(-1, location, is);
1741 }
1742
1743 private Bundle installBundle(long id, String location, InputStream is)
1744 throws BundleException
1745 {
1746 if (System.getSecurityManager() != null)
1747 {
1748 AccessController.checkPermission(m_adminPerm);
1749 }
1750
1751 BundleImpl bundle = null;
1752
1753 // Acquire an install lock.
1754 acquireInstallLock(location);
1755
1756 try
1757 {
1758 // Check to see if the framework is still running;
1759 if ((getStatus() == Felix.STOPPING_STATUS) ||
1760 (getStatus() == Felix.INITIAL_STATUS))
1761 {
1762 throw new BundleException("The framework has been shutdown.");
1763 }
1764
1765 // If bundle location is already installed, then
1766 // return it as required by the OSGi specification.
1767 bundle = (BundleImpl) getBundle(location);
1768 if (bundle != null)
1769 {
1770 return bundle;
1771 }
1772
1773 // Determine if this is a new or existing bundle.
1774 boolean isNew = (id < 0);
1775
1776 // If the bundle is new we must cache its JAR file.
1777 if (isNew)
1778 {
1779 // First generate an identifier for it.
1780 id = getNextId();
1781
1782 try
1783 {
1784 // Get the URL input stream if necessary.
1785 if (is == null)
1786 {
1787 // Do it the manual way to have a chance to
1788 // set request properties such as proxy auth.
1789 URL url = new URL(location);
1790 URLConnection conn = url.openConnection();
1791
1792 // Support for http proxy authentication.
1793 String auth = System.getProperty("http.proxyAuth");
1794 if ((auth != null) && (auth.length() > 0))
1795 {
1796 if ("http".equals(url.getProtocol()) ||
1797 "https".equals(url.getProtocol()))
1798 {
1799 String base64 = Util.base64Encode(auth);
1800 conn.setRequestProperty(
1801 "Proxy-Authorization", "Basic " + base64);
1802 }
1803 }
1804 is = conn.getInputStream();
1805 }
1806 // Add the bundle to the cache.
1807 m_cache.create(id, location, is);
1808 }
1809 catch (Exception ex)
1810 {
1811 throw new BundleException(
1812 "Unable to cache bundle: " + location, ex);
1813 }
1814 finally
1815 {
1816 try
1817 {
1818 if (is != null) is.close();
1819 }
1820 catch (IOException ex)
1821 {
1822 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001823 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +00001824 "Unable to close input stream.", ex);
1825 }
1826 }
1827 }
1828 else
1829 {
1830 // If the bundle we are installing is not new,
1831 // then try to purge old revisions before installing
1832 // it; this is done just in case a "refresh"
1833 // didn't occur last session...this would only be
1834 // due to an error or system crash.
1835 try
1836 {
1837 if (m_cache.getArchive(id).getRevisionCount() > 1)
1838 {
1839 m_cache.purge(m_cache.getArchive(id));
1840 }
1841 }
1842 catch (Exception ex)
1843 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00001844 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001845 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +00001846 "Could not purge bundle.", ex);
1847 }
1848 }
1849
1850 try
1851 {
1852 BundleArchive archive = m_cache.getArchive(id);
1853 bundle = new BundleImpl(this, createBundleInfo(archive));
1854 }
1855 catch (Exception ex)
1856 {
1857 // If the bundle is new, then remove it from the cache.
1858 // TODO: Perhaps it should be removed if it is not new too.
1859 if (isNew)
1860 {
1861 try
1862 {
1863 m_cache.remove(m_cache.getArchive(id));
1864 }
1865 catch (Exception ex1)
1866 {
1867 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 remove from cache.", ex1);
1870 }
1871 }
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001872ex.printStackTrace();
Richard S. Hall930fecc2005-08-16 18:33:34 +00001873 throw new BundleException("Could not create bundle object.", ex);
1874 }
1875
1876 // If the bundle is new, then set its start level; existing
1877 // bundles already have their start level set.
1878 if (isNew)
1879 {
1880 // This will persistently set the bundle's start level.
1881 bundle.getInfo().setStartLevel(getInitialBundleStartLevel());
Richard S. Hall69d84792006-01-13 13:55:13 +00001882 bundle.getInfo().setLastModified(System.currentTimeMillis());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001883 }
1884
1885 synchronized (m_installedBundleLock_Priority2)
1886 {
1887 m_installedBundleMap.put(location, bundle);
1888 }
1889 }
1890 finally
1891 {
1892 // Always release install lock.
1893 releaseInstallLock(location);
1894
1895 // Always try to close the input stream.
1896 try
1897 {
1898 if (is != null) is.close();
1899 }
1900 catch (IOException ex)
1901 {
1902 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001903 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +00001904 "Unable to close input stream.", ex);
1905 // Not much else we can do.
1906 }
1907 }
1908
1909 // Fire bundle event.
1910 fireBundleEvent(BundleEvent.INSTALLED, bundle);
1911
1912 // Return new bundle.
1913 return bundle;
1914 }
1915
1916 /**
1917 * Retrieves a bundle from its location.
1918 *
1919 * @param location The location of the bundle to retrieve.
1920 * @return The bundle associated with the location or null if there
1921 * is no bundle associated with the location.
1922 **/
1923 protected Bundle getBundle(String location)
1924 {
1925 synchronized (m_installedBundleLock_Priority2)
1926 {
1927 return (Bundle) m_installedBundleMap.get(location);
1928 }
1929 }
1930
1931 /**
1932 * Implementation for BundleContext.getBundle(). Retrieves a
1933 * bundle from its identifier.
1934 *
1935 * @param id The identifier of the bundle to retrieve.
1936 * @return The bundle associated with the identifier or null if there
1937 * is no bundle associated with the identifier.
1938 **/
1939 protected Bundle getBundle(long id)
1940 {
1941 synchronized (m_installedBundleLock_Priority2)
1942 {
1943 BundleImpl bundle = null;
1944
1945 for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
1946 {
1947 bundle = (BundleImpl) i.next();
1948 if (bundle.getInfo().getBundleId() == id)
1949 {
1950 return bundle;
1951 }
1952 }
1953 }
1954
1955 return null;
1956 }
1957
1958 // Private member for method below.
1959 private Comparator m_comparator = null;
1960
1961 /**
1962 * Implementation for BundleContext.getBundles(). Retrieves
1963 * all installed bundles.
1964 *
1965 * @return An array containing all installed bundles or null if
1966 * there are no installed bundles.
1967 **/
1968 protected Bundle[] getBundles()
1969 {
1970 if (m_comparator == null)
1971 {
1972 m_comparator = new Comparator() {
1973 public int compare(Object o1, Object o2)
1974 {
1975 Bundle b1 = (Bundle) o1;
1976 Bundle b2 = (Bundle) o2;
1977 if (b1.getBundleId() > b2.getBundleId())
1978 return 1;
1979 else if (b1.getBundleId() < b2.getBundleId())
1980 return -1;
1981 return 0;
1982 }
1983 };
1984 }
1985
1986 Bundle[] bundles = null;
1987
1988 synchronized (m_installedBundleLock_Priority2)
1989 {
1990 if (m_installedBundleMap.size() == 0)
1991 {
1992 return null;
1993 }
1994
1995 bundles = new Bundle[m_installedBundleMap.size()];
1996 int counter = 0;
1997 for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
1998 {
1999 bundles[counter++] = (Bundle) i.next();
2000 }
2001 }
2002
2003 Arrays.sort(bundles, m_comparator);
2004
2005 return bundles;
2006 }
2007
2008 protected void addBundleListener(Bundle bundle, BundleListener l)
2009 {
2010 // The spec says do nothing if the listener is
2011 // already registered.
2012 BundleListenerWrapper old = (BundleListenerWrapper)
2013 m_dispatchQueue.getListener(BundleListener.class, l);
2014 if (old == null)
2015 {
2016 l = new BundleListenerWrapper(bundle, l);
2017 m_dispatchQueue.addListener(BundleListener.class, l);
2018 }
2019 }
2020
2021 protected void removeBundleListener(BundleListener l)
2022 {
2023 m_dispatchQueue.removeListener(BundleListener.class, l);
2024 }
2025
2026 /**
2027 * Implementation for BundleContext.addServiceListener().
2028 * Adds service listener to the listener list so that is
2029 * can listen for <code>ServiceEvent</code>s.
2030 *
2031 * @param bundle The bundle that registered the listener.
2032 * @param l The service listener to add to the listener list.
2033 * @param f The filter for the listener; may be null.
2034 **/
2035 protected void addServiceListener(Bundle bundle, ServiceListener l, String f)
2036 throws InvalidSyntaxException
2037 {
2038 // The spec says if the listener is already registered,
2039 // then replace filter.
2040 ServiceListenerWrapper old = (ServiceListenerWrapper)
2041 m_dispatchQueue.getListener(ServiceListener.class, l);
2042 if (old != null)
2043 {
2044 old.setFilter((f == null) ? null : new FilterImpl(m_logger, f));
2045 }
2046 else
2047 {
2048 l = new ServiceListenerWrapper(
2049 bundle, l, (f == null) ? null : new FilterImpl(m_logger, f));
2050 m_dispatchQueue.addListener(ServiceListener.class, l);
2051 }
2052 }
2053
2054 /**
2055 * Implementation for BundleContext.removeServiceListener().
2056 * Removes service listeners from the listener list.
2057 *
2058 * @param l The service listener to remove from the listener list.
2059 **/
2060 protected void removeServiceListener(ServiceListener l)
2061 {
2062 m_dispatchQueue.removeListener(ServiceListener.class, l);
2063 }
2064
2065 protected void addFrameworkListener(Bundle bundle, FrameworkListener l)
2066 {
2067 // The spec says do nothing if the listener is
2068 // already registered.
2069 FrameworkListenerWrapper old = (FrameworkListenerWrapper)
2070 m_dispatchQueue.getListener(FrameworkListener.class, l);
2071 if (old == null)
2072 {
2073 l = new FrameworkListenerWrapper(bundle, l);
2074 m_dispatchQueue.addListener(FrameworkListener.class, l);
2075 }
2076 }
2077
2078 protected void removeFrameworkListener(FrameworkListener l)
2079 {
2080 m_dispatchQueue.removeListener(FrameworkListener.class, l);
2081 }
2082
2083 /**
2084 * Remove all of the specified bundle's event listeners from
2085 * the framework.
2086 * @param bundle The bundle whose listeners are to be removed.
2087 **/
2088 private void removeListeners(Bundle bundle)
2089 {
2090 if (bundle == null)
2091 {
2092 return;
2093 }
2094
2095 // Remove all listeners associated with the supplied bundle;
2096 // it is only possible to know the bundle associated with a
2097 // listener if the listener was wrapper by a ListenerWrapper,
2098 // so look for those.
2099 Object[] listeners = m_dispatchQueue.getListeners();
2100 for (int i = listeners.length - 2; i >= 0; i -= 2)
2101 {
2102 // Check for listener wrappers and then compare the bundle.
2103 if (listeners[i + 1] instanceof ListenerWrapper)
2104 {
2105 ListenerWrapper lw = (ListenerWrapper) listeners[i + 1];
2106 if ((lw.getBundle() != null) && (lw.getBundle().equals(bundle)))
2107 {
2108 m_dispatchQueue.removeListener(
2109 (Class) listeners[i], (EventListener) listeners[i+1]);
2110 }
2111 }
2112 }
2113 }
2114
2115 /**
2116 * Implementation for BundleContext.registerService(). Registers
2117 * a service for the specified bundle bundle.
2118 *
2119 * @param classNames A string array containing the names of the classes
2120 * under which the new service is available.
2121 * @param svcObj The service object or <code>ServiceFactory</code>.
2122 * @param dict A dictionary of properties that further describe the
2123 * service or null.
2124 * @return A <code>ServiceRegistration</code> object or null.
2125 **/
2126 protected ServiceRegistration registerService(
2127 BundleImpl bundle, String[] classNames, Object svcObj, Dictionary dict)
2128 {
2129 if (classNames == null)
2130 {
2131 throw new NullPointerException("Service class names cannot be null.");
2132 }
2133 else if (svcObj == null)
2134 {
2135 throw new IllegalArgumentException("Service object cannot be null.");
2136 }
2137
2138 // Check for permission to register all passed in interface names.
2139 if (System.getSecurityManager() != null)
2140 {
2141 for (int i = 0; i < classNames.length; i++)
2142 {
2143 ServicePermission perm = new ServicePermission(
2144 classNames[i], ServicePermission.REGISTER);
2145 AccessController.checkPermission(perm);
2146 }
2147 }
2148
2149 // Acquire bundle lock.
2150 try
2151 {
2152 acquireBundleLock(bundle);
2153 }
2154 catch (BundleException ex)
2155 {
2156 // This would probably only happen when the bundle is uninstalled.
2157 throw new IllegalStateException(
2158 "Can only register services while bundle is active or activating.");
2159 }
2160
2161 ServiceRegistration reg = null;
2162
2163 try
2164 {
2165 BundleInfo info = bundle.getInfo();
2166
2167 // Can only register services if starting or active.
2168 if ((info.getState() & (Bundle.STARTING | Bundle.ACTIVE)) == 0)
2169 {
2170 throw new IllegalStateException(
2171 "Can only register services while bundle is active or activating.");
2172 }
2173
2174 // Check to make sure that the service object is
2175 // an instance of all service classes; ignore if
2176 // service object is a service factory.
2177 if (!(svcObj instanceof ServiceFactory))
2178 {
2179 for (int i = 0; i < classNames.length; i++)
2180 {
2181 Class clazz = loadClassUsingClass(svcObj.getClass(), classNames[i]);
2182 if (clazz == null)
2183 {
2184 throw new IllegalArgumentException(
2185 "Cannot cast service: " + classNames[i]);
2186 }
2187 else if (!clazz.isAssignableFrom(svcObj.getClass()))
2188 {
2189 throw new IllegalArgumentException(
2190 "Service object is not an instance of \""
2191 + classNames[i] + "\".");
2192 }
2193 }
2194 }
2195
2196 reg = m_registry.registerService(bundle, classNames, svcObj, dict);
2197 }
2198 finally
2199 {
2200 // Always release bundle lock.
2201 releaseBundleLock(bundle);
2202 }
2203
2204 // NOTE: The service registered event is fired from the service
2205 // registry to the framework, where it is then redistributed to
2206 // interested service event listeners.
2207
2208 return reg;
2209 }
2210
2211 /**
2212 * <p>
2213 * This is a simple utility class that attempts to load the named
2214 * class using the class loader of the supplied class or
2215 * the class loader of one of its super classes or their implemented
2216 * interfaces. This is necessary during service registration to test
2217 * whether a given service object implements its declared service
2218 * interfaces.
2219 * </p>
2220 * <p>
2221 * To perform this test, the framework must try to load
2222 * the classes associated with the declared service interfaces, so
2223 * it must choose a class loader. The class loader of the registering
2224 * bundle cannot be used, since this disallows third parties to
2225 * register service on behalf of another bundle. Consequently, the
2226 * class loader of the service object must be used. However, this is
2227 * also not sufficient since the class loader of the service object
2228 * may not have direct access to the class in question.
2229 * </p>
2230 * <p>
2231 * The service object's class loader may not have direct access to
2232 * its service interface if it extends a super class from another
2233 * bundle which implements the service interface from an imported
2234 * bundle or if it implements an extension of the service interface
2235 * from another bundle which imports the base interface from another
2236 * bundle. In these cases, the service object's class loader only has
2237 * access to the super class's class or the extended service interface,
2238 * respectively, but not to the actual service interface.
2239 * </p>
2240 * <p>
2241 * Thus, it is necessary to not only try to load the service interface
2242 * class from the service object's class loader, but from the class
2243 * loaders of any interfaces it implements and the class loaders of
2244 * all super classes.
2245 * </p>
2246 * @param svcObj the class that is the root of the search.
2247 * @param name the name of the class to load.
2248 * @return the loaded class or <tt>null</tt> if it could not be
2249 * loaded.
2250 **/
2251 private static Class loadClassUsingClass(Class clazz, String name)
2252 {
2253 while (clazz != null)
2254 {
2255 // Get the class loader of the current class object.
2256 ClassLoader loader = clazz.getClassLoader();
2257 // A null class loader represents the system class loader.
2258 loader = (loader == null) ? ClassLoader.getSystemClassLoader() : loader;
2259 try
2260 {
2261 return loader.loadClass(name);
2262 }
2263 catch (ClassNotFoundException ex)
2264 {
2265 // Ignore and try interface class loaders.
2266 }
2267
2268 // Try to see if we can load the class from
2269 // one of the class's implemented interface
2270 // class loaders.
2271 Class[] ifcs = clazz.getInterfaces();
2272 for (int i = 0; i < ifcs.length; i++)
2273 {
2274 clazz = loadClassUsingClass(ifcs[i], name);
2275 if (clazz != null)
2276 {
2277 return clazz;
2278 }
2279 }
2280
2281 // Try to see if we can load the class from
2282 // the super class class loader.
2283 clazz = clazz.getSuperclass();
2284 }
2285
2286 return null;
2287 }
2288
2289 protected ServiceReference[] getServiceReferences(
2290 BundleImpl bundle, String className, String expr)
2291 throws InvalidSyntaxException
2292 {
2293 // Define filter if expression is not null.
2294 Filter filter = null;
2295 if (expr != null)
2296 {
2297 filter = new FilterImpl(m_logger, expr);
2298 }
2299
2300 // Ask the service registry for all matching service references.
2301 List refList = m_registry.getServiceReferences(className, filter);
2302
2303 // The returned reference list must be filtered for two cases:
2304 // 1) The requesting bundle may not be wired to the same class
2305 // as the providing bundle (i.e, different versions), so filter
2306 // any services for which the requesting bundle might get a
2307 // class cast exception.
2308 // 2) Security is enabled and the requesting bundle does not have
2309 // permission access the service.
2310 for (int refIdx = 0; (refList != null) && (refIdx < refList.size()); refIdx++)
2311 {
2312 // Get the current service reference.
2313 ServiceReference ref = (ServiceReference) refList.get(refIdx);
2314
2315 // Get the service's objectClass property.
2316 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
2317
2318 // Boolean flag.
2319 boolean allow = false;
2320
2321 // Filter the service reference if the requesting bundle
2322 // does not have permission.
2323 if (System.getSecurityManager() != null)
2324 {
2325 for (int classIdx = 0;
2326 !allow && (classIdx < objectClass.length);
2327 classIdx++)
2328 {
2329 try
2330 {
2331 ServicePermission perm = new ServicePermission(
2332 objectClass[classIdx], ServicePermission.GET);
2333 AccessController.checkPermission(perm);
2334 // The bundle only needs permission for one
2335 // of the service interfaces, so break out
2336 // of the loop when permission is granted.
2337 allow = true;
2338 }
2339 catch (Exception ex)
2340 {
2341 // We do not throw this exception since the bundle
2342 // is not supposed to know about the service at all
2343 // if it does not have permission.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002344 m_logger.log(Logger.LOG_ERROR, ex.getMessage());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002345 }
2346 }
2347
2348 if (!allow)
2349 {
2350 refList.remove(refIdx);
2351 refIdx--;
2352 continue;
2353 }
2354 }
2355
2356 // Now check for castability.
2357 if (!isServiceAssignable(bundle, ref))
2358 {
2359 refList.remove(refIdx);
2360 refIdx--;
2361 }
2362 }
2363
2364 if (refList.size() > 0)
2365 {
2366 return (ServiceReference[]) refList.toArray(new ServiceReference[refList.size()]);
2367 }
2368
2369 return null;
2370 }
2371
2372 /**
2373 * This method determines if the requesting bundle is able to cast
2374 * the specified service reference based on class visibility rules
2375 * of the underlying modules.
2376 * @param requester The bundle requesting the service.
2377 * @param ref The service in question.
2378 * @return <tt>true</tt> if the requesting bundle is able to case
2379 * the service object to a known type.
2380 **/
2381 protected boolean isServiceAssignable(BundleImpl requester, ServiceReference ref)
2382 {
2383 // Boolean flag.
2384 boolean allow = true;
2385 // Get the service's objectClass property.
2386 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
2387
2388 // The the service reference is not assignable when the requesting
2389 // bundle is wired to a different version of the service object.
2390 // NOTE: We are pessimistic here, if any class in the service's
2391 // objectClass is not usable by the requesting bundle, then we
2392 // disallow the service reference.
2393 for (int classIdx = 0; (allow) && (classIdx < objectClass.length); classIdx++)
2394 {
2395 if (!ref.isAssignableTo(requester, objectClass[classIdx]))
2396 {
2397 allow = false;
2398 }
2399 }
2400 return allow;
2401 }
2402
2403 protected Object getService(Bundle bundle, ServiceReference ref)
2404 {
2405 // Check that the bundle has permission to get at least
2406 // one of the service interfaces; the objectClass property
2407 // of the service stores its service interfaces.
2408 String[] objectClass = (String[])
2409 ref.getProperty(Constants.OBJECTCLASS);
2410 if (objectClass == null)
2411 {
2412 return null;
2413 }
2414
2415 boolean hasPermission = false;
2416 if (System.getSecurityManager() != null)
2417 {
2418 for (int i = 0;
2419 !hasPermission && (i < objectClass.length);
2420 i++)
2421 {
2422 try
2423 {
2424 ServicePermission perm =
2425 new ServicePermission(
2426 objectClass[i], ServicePermission.GET);
2427 AccessController.checkPermission(perm);
2428 hasPermission = true;
2429 }
2430 catch (Exception ex)
2431 {
2432 }
2433 }
2434 }
2435 else
2436 {
2437 hasPermission = true;
2438 }
2439
2440 // If the bundle does not permission to access the service,
2441 // then return null.
2442 if (!hasPermission)
2443 {
2444 return null;
2445 }
2446
2447 return m_registry.getService(bundle, ref);
2448 }
2449
2450 protected boolean ungetService(Bundle bundle, ServiceReference ref)
2451 {
2452 return m_registry.ungetService(bundle, ref);
2453 }
2454
2455 protected File getDataFile(BundleImpl bundle, String s)
2456 {
2457 // The spec says to throw an error if the bundle
2458 // is stopped, which I assume means not active,
2459 // starting, or stopping.
2460 if ((bundle.getInfo().getState() != Bundle.ACTIVE) &&
2461 (bundle.getInfo().getState() != Bundle.STARTING) &&
2462 (bundle.getInfo().getState() != Bundle.STOPPING))
2463 {
2464 throw new IllegalStateException("Only active bundles can create files.");
2465 }
2466 try
2467 {
2468 return m_cache.getArchive(
2469 bundle.getInfo().getBundleId()).getDataFile(s);
2470 }
2471 catch (Exception ex)
2472 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002473 m_logger.log(Logger.LOG_ERROR, ex.getMessage());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002474 return null;
2475 }
2476 }
2477
2478 //
2479 // PackageAdmin related methods.
2480 //
2481
2482 /**
2483 * Returns the exported package associated with the specified
2484 * package name. This is used by the PackageAdmin service
2485 * implementation.
2486 *
2487 * @param name The name of the exported package to find.
2488 * @return The exported package or null if no matching package was found.
2489 **/
2490 protected ExportedPackage getExportedPackage(String pkgName)
2491 {
2492 // First, find the bundle exporting the package.
2493 BundleImpl bundle = null;
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002494 IModule[] exporters = m_policyCore.getInUseExporters(new R4Import(pkgName, null, null));
Richard S. Hall930fecc2005-08-16 18:33:34 +00002495 if (exporters != null)
2496 {
2497 // Since OSGi R4 there may be more than one exporting, so just
2498 // take the first one.
2499 bundle = (BundleImpl) getBundle(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002500 Util.getBundleIdFromModuleId(exporters[0].getId()));
Richard S. Hall930fecc2005-08-16 18:33:34 +00002501 }
2502
2503 // If we have found the exporting bundle, then return the
2504 // exported package interface instance.
2505 if (bundle != null)
2506 {
2507 // We need to find the version of the exported package, but this
2508 // is tricky since there may be multiple versions of the package
2509 // offered by a given bundle, since multiple revisions of the
2510 // bundle JAR file may exist if the bundle was updated without
2511 // refreshing the framework. In this case, each revision of the
2512 // bundle JAR file is represented as a module in the BundleInfo
2513 // module array, which is ordered from oldest to newest. We assume
2514 // that the first module found to be exporting the package is the
2515 // provider of the package, which makes sense since it must have
2516 // been resolved first.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002517 IModule[] modules = bundle.getInfo().getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002518 for (int modIdx = 0; modIdx < modules.length; modIdx++)
2519 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002520 R4Export export = Util.getExportPackage(modules[modIdx], pkgName);
2521 if (export != null)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002522 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002523 return new ExportedPackageImpl(this, bundle, pkgName, export.getVersion());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002524 }
2525 }
2526 }
2527
2528 return null;
2529 }
2530
2531 /**
2532 * Returns an array of all actively exported packages from the specified
2533 * bundle or if the specified bundle is <tt>null</tt> an array
2534 * containing all actively exported packages by all bundles.
2535 *
2536 * @param b The bundle whose exported packages are to be retrieved
2537 * or <tt>null</tt> if the exported packages of all bundles are
2538 * to be retrieved.
2539 * @return An array of exported packages.
2540 **/
2541 protected ExportedPackage[] getExportedPackages(Bundle b)
2542 {
2543 List list = new ArrayList();
2544
2545 // If a bundle is specified, then return its
2546 // exported packages.
2547 if (b != null)
2548 {
2549 BundleImpl bundle = (BundleImpl) b;
2550 getExportedPackages(bundle, list);
2551 }
2552 // Otherwise return all exported packages.
2553 else
2554 {
2555 // To create a list of all exported packages, we must look
2556 // in the installed and uninstalled sets of bundles. To
2557 // ensure a somewhat consistent view, we will gather all
2558 // of this information from within the installed bundle
2559 // lock.
2560 synchronized (m_installedBundleLock_Priority2)
2561 {
2562 // First get exported packages from uninstalled bundles.
2563 synchronized (m_uninstalledBundlesLock_Priority3)
2564 {
2565 for (int bundleIdx = 0;
2566 (m_uninstalledBundles != null) && (bundleIdx < m_uninstalledBundles.length);
2567 bundleIdx++)
2568 {
2569 BundleImpl bundle = m_uninstalledBundles[bundleIdx];
2570 getExportedPackages(bundle, list);
2571 }
2572 }
2573
2574 // Now get exported packages from installed bundles.
2575 Bundle[] bundles = getBundles();
2576 for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
2577 {
2578 BundleImpl bundle = (BundleImpl) bundles[bundleIdx];
2579 getExportedPackages(bundle, list);
2580 }
2581 }
2582 }
2583
2584 return (ExportedPackage[]) list.toArray(new ExportedPackage[list.size()]);
2585 }
2586
2587 /**
2588 * Adds any current active exported packages from the specified bundle
2589 * to the passed in list.
2590 * @param bundle The bundle from which to retrieve exported packages.
2591 * @param list The list to which the exported packages are added
2592 **/
2593 private void getExportedPackages(BundleImpl bundle, List list)
2594 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00002595 // Since a bundle may have many modules associated with it,
2596 // one for each revision in the cache, search each module
2597 // for each revision to get all exports.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002598 IModule[] modules = bundle.getInfo().getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002599 for (int modIdx = 0; modIdx < modules.length; modIdx++)
2600 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002601 R4Export[] exports = m_policyCore.getExports(modules[modIdx]);
2602 if ((exports != null) && (exports.length > 0))
Richard S. Hall930fecc2005-08-16 18:33:34 +00002603 {
2604 for (int expIdx = 0; expIdx < exports.length; expIdx++)
2605 {
2606 // See if the target bundle's module is one of the
2607 // "in use" exporters of the package.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002608 IModule[] inUseModules =
2609 m_policyCore.getInUseExporters(
2610 new R4Import(exports[expIdx].getName(), null, null));
2611 // Search through the current providers to find the target
2612 // module.
2613 for (int i = 0; (inUseModules != null) && (i < inUseModules.length); i++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002614 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002615 if (inUseModules[i] == modules[modIdx])
2616 {
2617 list.add(new ExportedPackageImpl(
2618 this, bundle, exports[expIdx].getName(),
2619 exports[expIdx].getVersion()));
2620 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002621 }
2622 }
2623 }
2624 }
2625 }
2626
2627 protected Bundle[] getImportingBundles(ExportedPackage ep)
2628 {
2629 // Get exporting bundle; we need to use this internal
2630 // method because the spec says ep.getExportingBundle()
2631 // should return null if the package is stale.
2632 BundleImpl exporter = (BundleImpl)
2633 ((ExportedPackageImpl) ep).getExportingBundleInternal();
2634 BundleInfo exporterInfo = exporter.getInfo();
2635
2636 // Create list for storing importing bundles.
2637 List list = new ArrayList();
2638 Bundle[] bundles = getBundles();
2639
2640 // Check all bundles to see who imports the package.
2641 for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
2642 {
2643 BundleImpl importer = (BundleImpl) bundles[bundleIdx];
2644
2645 // Ignore the bundle if it imports from itself.
2646 if (exporter != importer)
2647 {
2648 // Check the import wires of all modules for all bundles.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002649 IModule[] modules = importer.getInfo().getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002650 for (int modIdx = 0; modIdx < modules.length; modIdx++)
2651 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002652 R4Wire wire = Util.getWire(modules[modIdx], ep.getName());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002653
2654 // If the resolving module is associated with the
2655 // exporting bundle, then add current bundle to
2656 // import list.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002657 if ((wire != null) && exporterInfo.hasModule(wire.getExportingModule()))
Richard S. Hall930fecc2005-08-16 18:33:34 +00002658 {
2659 // Add the bundle to the list of importers.
2660 list.add(bundles[bundleIdx]);
2661 break;
2662 }
2663 }
2664 }
2665 }
2666
2667 // Return the results.
2668 if (list.size() > 0)
2669 {
2670 return (Bundle[]) list.toArray(new Bundle[list.size()]);
2671 }
2672
2673 return null;
2674 }
2675
2676 protected void refreshPackages(Bundle[] targets)
2677 {
2678 if (System.getSecurityManager() != null)
2679 {
2680 AccessController.checkPermission(m_adminPerm);
2681 }
2682
2683 // Acquire locks for all impacted bundles.
2684 BundleImpl[] bundles = acquireBundleRefreshLocks(targets);
2685
2686 // Remove any targeted bundles from the uninstalled bundles
2687 // array, since they will be removed from the system after
2688 // the refresh.
2689 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
2690 {
2691 forgetUninstalledBundle(bundles[i]);
2692 }
2693
2694 try
2695 {
2696 // If there are targets, then refresh each one.
2697 if (bundles != null)
2698 {
2699 // At this point the map contains every bundle that has been
2700 // updated and/or removed as well as all bundles that import
2701 // packages from these bundles.
2702
2703 // Create refresh helpers for each bundle.
2704 RefreshHelper[] helpers = new RefreshHelper[bundles.length];
2705 for (int i = 0; i < bundles.length; i++)
2706 {
2707 helpers[i] = new RefreshHelper(bundles[i]);
2708 }
2709
2710 // Stop, purge or remove, and reinitialize all bundles first.
2711 for (int i = 0; i < helpers.length; i++)
2712 {
2713 helpers[i].stop();
2714 helpers[i].purgeOrRemove();
2715 helpers[i].reinitialize();
2716 }
2717
2718 // Then restart all bundles that were previously running.
2719 for (int i = 0; i < helpers.length; i++)
2720 {
2721 helpers[i].restart();
2722 }
2723 }
2724 }
2725 finally
2726 {
2727 // Always release all bundle locks.
2728 releaseBundleLocks(bundles);
2729 }
2730
2731 fireFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, getBundle(0), null);
2732 }
2733
2734 private void populateImportGraph(BundleImpl target, Map map)
2735 {
2736 // Get the exported packages for the specified bundle.
2737 ExportedPackage[] pkgs = getExportedPackages(target);
2738
2739 for (int pkgIdx = 0; (pkgs != null) && (pkgIdx < pkgs.length); pkgIdx++)
2740 {
2741 // Get all imports of this package.
2742 Bundle[] importers = getImportingBundles(pkgs[pkgIdx]);
2743
2744 for (int impIdx = 0;
2745 (importers != null) && (impIdx < importers.length);
2746 impIdx++)
2747 {
Richard S. Halle34df092005-10-06 17:03:05 +00002748 // Avoid cycles if the bundle is already in map.
2749 if (!map.containsKey(importers[impIdx]))
2750 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00002751 // Add each importing bundle to map.
2752 map.put(importers[impIdx], importers[impIdx]);
2753 // Now recurse into each bundle to get its importers.
2754 populateImportGraph(
2755 (BundleImpl) importers[impIdx], map);
Richard S. Halle34df092005-10-06 17:03:05 +00002756 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002757 }
2758 }
2759 }
2760
2761 //
2762 // Miscellaneous private methods.
2763 //
2764
2765 private BundleInfo createBundleInfo(BundleArchive archive)
2766 throws Exception
2767 {
2768 // Get the bundle manifest.
2769 Map headerMap = null;
2770 try
2771 {
2772 // Although there should only ever be one revision at this
2773 // point, get the header for the current revision to be safe.
2774 headerMap = archive.getManifestHeader(archive.getRevisionCount() - 1);
2775 }
2776 catch (Exception ex)
2777 {
2778 throw new BundleException("Unable to read JAR manifest.", ex);
2779 }
2780
2781 // We can't do anything without the manifest header.
2782 if (headerMap == null)
2783 {
2784 throw new BundleException("Unable to read JAR manifest header.");
2785 }
2786
2787 // Create the module for the bundle; although there should only
2788 // ever be one revision at this point, create the module for
2789 // the current revision to be safe.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002790 IModule module = createModule(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002791 archive.getId(), archive.getRevisionCount() - 1, headerMap);
2792
2793 // Finally, create an return the bundle info.
2794 return new BundleInfo(m_logger, archive, module);
2795 }
2796
2797 /**
2798 * Creates a module for a given bundle by reading the bundle's
2799 * manifest meta-data and converting it to work with the underlying
2800 * import/export search policy of the module loader.
2801 * @param id The identifier of the bundle for which the module should
2802 * be created.
2803 * @param headers The headers map associated with the bundle.
2804 * @return The initialized and/or newly created module.
2805 **/
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002806 private IModule createModule(long id, int revision, Map headerMap)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002807 throws Exception
2808 {
2809 // Get the manifest version.
2810 String version = (String) headerMap.get(FelixConstants.BUNDLE_MANIFESTVERSION);
2811 version = (version == null) ? "1" : version;
2812 if (!version.equals("1") && !version.equals("2"))
2813 {
2814 throw new BundleException("Unknown 'Bundle-ManifestVersion' value: " + version);
2815 }
2816
Richard S. Hall930fecc2005-08-16 18:33:34 +00002817 // Get import packages from bundle manifest.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002818 R4Package[] pkgs = R4Package.parseImportOrExportHeader(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002819 (String) headerMap.get(Constants.IMPORT_PACKAGE));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002820 R4Import[] imports = new R4Import[pkgs.length];
2821 for (int i = 0; i < pkgs.length; i++)
2822 {
2823 imports[i] = new R4Import(pkgs[i]);
2824 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002825
2826 // Check to make sure that R3 bundles have only specified
2827 // the 'specification-version' attribute and no directives.
2828 if (version.equals("1"))
2829 {
2830 for (int i = 0; (imports != null) && (i < imports.length); i++)
2831 {
2832 if (imports[i].getDirectives().length != 0)
2833 {
2834 throw new BundleException("R3 imports cannot contain directives.");
2835 }
2836 // NOTE: This is checking for "version" rather than "specification-version"
2837 // because the package class normalizes to "version" to avoid having
2838 // future special cases. This could be changed if more strict behavior
2839 // is required.
2840 if ((imports[i].getVersionHigh() != null) ||
2841 (imports[i].getAttributes().length > 1) ||
2842 ((imports[i].getAttributes().length == 1) &&
2843 (!imports[i].getAttributes()[0].getName().equals(FelixConstants.VERSION_ATTRIBUTE))))
2844 {
2845 throw new BundleException(
2846 "Import does not conform to R3 syntax: " + imports[i]);
2847 }
2848 }
2849 }
2850
2851 // Get export packages from bundle manifest.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002852 pkgs = R4Package.parseImportOrExportHeader(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002853 (String) headerMap.get(Constants.EXPORT_PACKAGE));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002854 R4Export[] exports = new R4Export[pkgs.length];
2855 for (int i = 0; i < pkgs.length; i++)
2856 {
2857 exports[i] = new R4Export(pkgs[i]);
2858 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002859
2860 // Check to make sure that R3 bundles have only specified
2861 // the 'specification-version' attribute and no directives.
2862 // In addition, all R3 exports imply imports, so add a
2863 // corresponding import for each export.
2864 if (version.equals("1"))
2865 {
2866 for (int i = 0; (exports != null) && (i < exports.length); i++)
2867 {
2868 if (exports[i].getDirectives().length != 0)
2869 {
2870 throw new BundleException("R3 exports cannot contain directives.");
2871 }
2872 // NOTE: This is checking for "version" rather than "specification-version"
2873 // because the package class normalizes to "version" to avoid having
2874 // future special cases. This could be changed if more strict behavior
2875 // is required.
2876 if ((exports[i].getAttributes().length > 1) ||
2877 ((exports[i].getAttributes().length == 1) &&
2878 (!exports[i].getAttributes()[0].getName().equals(FelixConstants.VERSION_ATTRIBUTE))))
2879 {
2880 throw new BundleException(
2881 "Export does not conform to R3 syntax: " + imports[i]);
2882 }
2883 }
2884
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002885 R4Import[] newImports = new R4Import[imports.length + exports.length];
Richard S. Hall930fecc2005-08-16 18:33:34 +00002886 System.arraycopy(imports, 0, newImports, 0, imports.length);
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002887 for (int i = 0; i < exports.length; i++)
2888 {
2889 newImports[i + imports.length] = new R4Import(exports[i]);
2890 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002891 imports = newImports;
2892 }
2893
2894 // For R3 bundles, add a "uses" directive onto each export
2895 // that references every other import (which will include
2896 // exports, since export implies import); this is
2897 // necessary since R3 bundles assumed a single class space,
2898 // but R4 allows for multiple class spaces.
2899 if (version.equals("1"))
2900 {
2901 String usesValue = "";
2902 for (int i = 0; (imports != null) && (i < imports.length); i++)
2903 {
2904 usesValue = usesValue
2905 + ((usesValue.length() > 0) ? "," : "")
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002906 + imports[i].getName();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002907 }
2908 R4Directive uses = new R4Directive(
2909 FelixConstants.USES_DIRECTIVE, usesValue);
2910 for (int i = 0; (exports != null) && (i < exports.length); i++)
2911 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002912 exports[i] = new R4Export(
2913 exports[i].getName(),
Richard S. Hall930fecc2005-08-16 18:33:34 +00002914 new R4Directive[] { uses },
2915 exports[i].getAttributes());
2916 }
2917 }
2918
2919// TODO: CHECK FOR DUPLICATE IMPORTS/EXPORTS HERE.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002920 // Get dynamic import packages from bundle manifest.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002921 pkgs = R4Package.parseImportOrExportHeader(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002922 (String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002923 R4Import[] dynamics = new R4Import[pkgs.length];
2924 for (int i = 0; i < pkgs.length; i++)
2925 {
2926 dynamics[i] = new R4Import(pkgs[i]);
2927 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002928
2929 // Check to make sure that R3 bundles have no attributes or
2930 // directives.
2931 if (version.equals("1"))
2932 {
2933 for (int i = 0; (dynamics != null) && (i < dynamics.length); i++)
2934 {
2935 if (dynamics[i].getDirectives().length != 0)
2936 {
2937 throw new BundleException("R3 dynamic imports cannot contain directives.");
2938 }
2939 if (dynamics[i].getAttributes().length != 0)
2940 {
2941 throw new BundleException("R3 dynamic imports cannot contain attributes.");
2942 }
2943 }
2944 }
2945
Richard S. Hall930fecc2005-08-16 18:33:34 +00002946 // Get native library entry names for module library sources.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002947 R4LibraryHeader[] libraryHeaders =
Richard S. Hall930fecc2005-08-16 18:33:34 +00002948 Util.parseLibraryStrings(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002949 m_logger,
Richard S. Hall930fecc2005-08-16 18:33:34 +00002950 Util.parseDelimitedString(
2951 (String) headerMap.get(Constants.BUNDLE_NATIVECODE), ","));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002952 R4Library[] libraries = new R4Library[libraryHeaders.length];
2953 for (int i = 0; i < libraries.length; i++)
2954 {
2955 libraries[i] = new R4Library(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002956 m_logger, m_cache, id, revision,
2957 getProperty(Constants.FRAMEWORK_OS_NAME),
2958 getProperty(Constants.FRAMEWORK_PROCESSOR),
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002959 libraryHeaders[i]);
2960 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002961
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002962 // Now that we have all of the metadata associated with the
2963 // module, we need to create the module itself. This is somewhat
2964 // complicated because a module is constructed out of several
2965 // interrelated pieces (e.g., content loader, search policy,
2966 // url policy). We need to create all of these pieces and bind
2967 // them together.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002968
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002969 // First, create the module.
2970 IModule module = m_factory.createModule(
2971 Long.toString(id) + "." + Integer.toString(revision));
2972 // Attach the R4 search policy metadata to the module.
2973 m_policyCore.setExports(module, exports);
2974 m_policyCore.setImports(module, imports);
2975 m_policyCore.setDynamicImports(module, dynamics);
2976 m_policyCore.setLibraries(module, libraries);
2977
2978 // Create the content loader associated with the module archive.
2979 IContentLoader contentLoader = new ContentLoaderImpl(
2980 m_logger,
2981 m_cache.getArchive(id).getContent(revision),
2982 m_cache.getArchive(id).getContentPath(revision));
2983 // Set the content loader's search policy.
2984 contentLoader.setSearchPolicy(
2985 new R4SearchPolicy(m_policyCore, module));
2986 // Set the content loader's URL policy.
2987 contentLoader.setURLPolicy(
2988// TODO: FIX - NEED URL POLICY PER MODULE.
2989 new URLPolicyImpl(
2990 m_logger, m_bundleStreamHandler, module));
2991
2992 // Set the module's content loader to the created content loader.
2993 m_factory.setContentLoader(module, contentLoader);
2994
2995 // Done, so return the module.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002996 return module;
2997 }
2998
2999 private BundleActivator createBundleActivator(BundleInfo info)
3000 throws Exception
3001 {
3002 // CONCURRENCY NOTE:
3003 // This method is called indirectly from startBundle() (via _startBundle()),
3004 // which has the exclusion lock, so there is no need to do any locking here.
3005
3006 BundleActivator activator = null;
3007
3008 String strict = m_config.get(FelixConstants.STRICT_OSGI_PROP);
3009 boolean isStrict = (strict == null) ? true : strict.equals("true");
3010 if (!isStrict)
3011 {
3012 try
3013 {
3014 activator =
3015 m_cache.getArchive(info.getBundleId())
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003016 .getActivator(info.getCurrentModule().getContentLoader());
Richard S. Hall930fecc2005-08-16 18:33:34 +00003017 }
3018 catch (Exception ex)
3019 {
3020 activator = null;
3021 }
3022 }
3023
3024 // If there was no cached activator, then get the activator
3025 // class from the bundle manifest.
3026 if (activator == null)
3027 {
3028 // Get the associated bundle archive.
3029 BundleArchive ba = m_cache.getArchive(info.getBundleId());
3030 // Get the manifest from the current revision; revision is
3031 // base zero so subtract one from the count to get the
3032 // current revision.
3033 Map headerMap = ba.getManifestHeader(ba.getRevisionCount() - 1);
3034 // Get the activator class attribute.
3035 String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
3036 // Try to instantiate activator class if present.
3037 if (className != null)
3038 {
3039 className = className.trim();
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003040 Class clazz = info.getCurrentModule().getContentLoader().getClass(className);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003041 if (clazz == null)
3042 {
3043 throw new BundleException("Not found: "
3044 + className);
3045 }
3046 activator = (BundleActivator) clazz.newInstance();
3047 }
3048 }
3049
3050 return activator;
3051 }
3052
3053 private void purgeBundle(BundleImpl bundle) throws Exception
3054 {
3055 // Acquire bundle lock.
3056 acquireBundleLock(bundle);
3057
3058 try
3059 {
3060 BundleInfo info = bundle.getInfo();
3061
3062 // In case of a refresh, then we want to physically
3063 // remove the bundle's modules from the module manager.
3064 // This is necessary for two reasons: 1) because
3065 // under Windows we won't be able to delete the bundle
3066 // because files might be left open in the resource
3067 // sources of its modules and 2) we want to make sure
3068 // that no references to old modules exist since they
3069 // will all be stale after the refresh. The only other
3070 // way to do this is to remove the bundle, but that
3071 // would be incorrect, because this is a refresh operation
3072 // and should not trigger bundle REMOVE events.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003073 IModule[] modules = info.getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003074 for (int i = 0; i < modules.length; i++)
3075 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003076 m_factory.removeModule(modules[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003077 }
3078
3079 // Purge all bundle revisions, but the current one.
3080 m_cache.purge(m_cache.getArchive(info.getBundleId()));
3081 }
3082 finally
3083 {
3084 // Always release the bundle lock.
3085 releaseBundleLock(bundle);
3086 }
3087 }
3088
3089 private void garbageCollectBundle(BundleImpl bundle) throws Exception
3090 {
3091 // CONCURRENCY NOTE: There is no reason to lock this bundle,
3092 // because this method is only called during shutdown or a
3093 // refresh operation and these are already guarded by locks.
3094
3095 // Remove the bundle's associated modules from
3096 // the module manager.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003097 IModule[] modules = bundle.getInfo().getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003098 for (int i = 0; i < modules.length; i++)
3099 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003100 m_factory.removeModule(modules[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003101 }
3102
3103 // Remove the bundle from the cache.
3104 m_cache.remove(m_cache.getArchive(bundle.getInfo().getBundleId()));
3105 }
3106
3107 //
3108 // Event-related methods.
3109 //
3110
3111 /**
3112 * Fires bundle events.
3113 **/
3114 private void fireFrameworkEvent(
3115 int type, Bundle bundle, Throwable throwable)
3116 {
3117 if (m_frameworkDispatcher == null)
3118 {
3119 m_frameworkDispatcher = new Dispatcher() {
3120 public void dispatch(EventListener l, EventObject eventObj)
3121 {
3122 ((FrameworkListener) l)
3123 .frameworkEvent((FrameworkEvent) eventObj);
3124 }
3125 };
3126 }
3127 FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
3128 m_dispatchQueue.dispatch(
3129 m_frameworkDispatcher, FrameworkListener.class, event);
3130 }
3131
3132 /**
3133 * Fires bundle events.
3134 *
3135 * @param type The type of bundle event to fire.
3136 * @param bundle The bundle associated with the event.
3137 **/
3138 private void fireBundleEvent(int type, Bundle bundle)
3139 {
3140 if (m_bundleDispatcher == null)
3141 {
3142 m_bundleDispatcher = new Dispatcher() {
3143 public void dispatch(EventListener l, EventObject eventObj)
3144 {
3145 ((BundleListener) l)
3146 .bundleChanged((BundleEvent) eventObj);
3147 }
3148 };
3149 }
3150 BundleEvent event = null;
3151 event = new BundleEvent(type, bundle);
3152 m_dispatchQueue.dispatch(m_bundleDispatcher,
3153 BundleListener.class, event);
3154 }
3155
3156 /**
3157 * Fires service events.
3158 *
3159 * @param type The type of service event to fire.
3160 * @param ref The service reference associated with the event.
3161 **/
3162 private void fireServiceEvent(ServiceEvent event)
3163 {
3164 if (m_serviceDispatcher == null)
3165 {
3166 m_serviceDispatcher = new Dispatcher() {
3167 public void dispatch(EventListener l, EventObject eventObj)
3168 {
3169// TODO: Filter service events based on service permissions.
3170 if (l instanceof ListenerWrapper)
3171 {
3172 BundleImpl bundle = (BundleImpl) ((ServiceListenerWrapper) l).getBundle();
3173 if (isServiceAssignable(bundle, ((ServiceEvent) eventObj).getServiceReference()))
3174 {
3175 ((ServiceListener) l)
3176 .serviceChanged((ServiceEvent) eventObj);
3177 }
3178 }
3179 else
3180 {
3181 ((ServiceListener) l)
3182 .serviceChanged((ServiceEvent) eventObj);
3183 }
3184 }
3185 };
3186 }
3187 m_dispatchQueue.dispatch(m_serviceDispatcher,
3188 ServiceListener.class, event);
3189 }
3190
3191 //
3192 // Property related methods.
3193 //
3194
3195 private void initializeFrameworkProperties()
3196 {
3197 // Standard OSGi properties.
Richard S. Hallea415752005-12-05 19:30:28 +00003198 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003199 FelixConstants.FRAMEWORK_VERSION,
3200 FelixConstants.FRAMEWORK_VERSION_VALUE);
Richard S. Hallea415752005-12-05 19:30:28 +00003201 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003202 FelixConstants.FRAMEWORK_VENDOR,
3203 FelixConstants.FRAMEWORK_VENDOR_VALUE);
Richard S. Hallea415752005-12-05 19:30:28 +00003204 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003205 FelixConstants.FRAMEWORK_LANGUAGE,
3206 System.getProperty("user.language"));
Richard S. Hallea415752005-12-05 19:30:28 +00003207 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003208 FelixConstants.FRAMEWORK_OS_VERSION,
3209 System.getProperty("os.version"));
3210
3211 String s = null;
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003212 s = R4Library.normalizePropertyValue(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003213 FelixConstants.FRAMEWORK_OS_NAME,
3214 System.getProperty("os.name"));
Richard S. Hallea415752005-12-05 19:30:28 +00003215 m_configMutable.put(FelixConstants.FRAMEWORK_OS_NAME, s);
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003216 s = R4Library.normalizePropertyValue(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003217 FelixConstants.FRAMEWORK_PROCESSOR,
3218 System.getProperty("os.arch"));
Richard S. Hallea415752005-12-05 19:30:28 +00003219 m_configMutable.put(FelixConstants.FRAMEWORK_PROCESSOR, s);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003220
3221 // The framework version property.
Richard S. Hallea415752005-12-05 19:30:28 +00003222 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003223 FelixConstants.FELIX_VERSION_PROPERTY,
3224 FelixConstants.FELIX_VERSION_VALUE);
3225 }
3226
3227 private void processAutoProperties()
3228 {
3229 // The auto-install property specifies a space-delimited list of
3230 // bundle URLs to be automatically installed into each new profile;
3231 // the start level to which the bundles are assigned is specified by
3232 // appending a ".n" to the auto-install property name, where "n" is
3233 // the desired start level for the list of bundles.
3234 String[] keys = m_config.getKeys();
3235 for (int i = 0; (keys != null) && (i < keys.length); i++)
3236 {
3237 if (keys[i].startsWith(FelixConstants.AUTO_INSTALL_PROP))
3238 {
3239 int startLevel = 1;
3240 try
3241 {
3242 startLevel = Integer.parseInt(keys[i].substring(keys[i].lastIndexOf('.') + 1));
3243 }
3244 catch (NumberFormatException ex)
3245 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003246 m_logger.log(Logger.LOG_ERROR, "Invalid property: " + keys[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003247 }
3248 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3249 if (st.countTokens() > 0)
3250 {
3251 String location = null;
3252 do
3253 {
3254 location = nextLocation(st);
3255 if (location != null)
3256 {
3257 try
3258 {
3259 BundleImpl b = (BundleImpl) installBundle(location, null);
3260 b.getInfo().setStartLevel(startLevel);
3261 }
3262 catch (Exception ex)
3263 {
3264 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003265 Logger.LOG_ERROR, "Auto-properties install.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003266 }
3267 }
3268 }
3269 while (location != null);
3270 }
3271 }
3272 }
3273
3274 // The auto-start property specifies a space-delimited list of
3275 // bundle URLs to be automatically installed and started into each
3276 // new profile; the start level to which the bundles are assigned
3277 // is specified by appending a ".n" to the auto-start property name,
3278 // where "n" is the desired start level for the list of bundles.
3279 // The following code starts bundles in two passes, first it installs
3280 // them, then it starts them.
3281 for (int i = 0; (keys != null) && (i < keys.length); i++)
3282 {
3283 if (keys[i].startsWith(FelixConstants.AUTO_START_PROP))
3284 {
3285 int startLevel = 1;
3286 try
3287 {
3288 startLevel = Integer.parseInt(keys[i].substring(keys[i].lastIndexOf('.') + 1));
3289 }
3290 catch (NumberFormatException ex)
3291 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003292 m_logger.log(Logger.LOG_ERROR, "Invalid property: " + keys[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003293 }
3294 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3295 if (st.countTokens() > 0)
3296 {
3297 String location = null;
3298 do
3299 {
3300 location = nextLocation(st);
3301 if (location != null)
3302 {
3303 try
3304 {
3305 BundleImpl b = (BundleImpl) installBundle(location, null);
3306 b.getInfo().setStartLevel(startLevel);
3307 }
3308 catch (Exception ex)
3309 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003310 m_logger.log(Logger.LOG_ERROR, "Auto-properties install.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003311 }
3312 }
3313 }
3314 while (location != null);
3315 }
3316 }
3317 }
3318
3319 // Now loop through and start the installed bundles.
3320 for (int i = 0; (keys != null) && (i < keys.length); i++)
3321 {
3322 if (keys[i].startsWith(FelixConstants.AUTO_START_PROP))
3323 {
3324 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3325 if (st.countTokens() > 0)
3326 {
3327 String location = null;
3328 do
3329 {
3330 location = nextLocation(st);
3331 if (location != null)
3332 {
3333 // Installing twice just returns the same bundle.
3334 try
3335 {
3336 BundleImpl bundle = (BundleImpl) installBundle(location, null);
3337 if (bundle != null)
3338 {
3339 startBundle(bundle, true);
3340 }
3341 }
3342 catch (Exception ex)
3343 {
3344 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003345 Logger.LOG_ERROR, "Auto-properties start.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003346 }
3347 }
3348 }
3349 while (location != null);
3350 }
3351 }
3352 }
3353 }
3354
3355 private String nextLocation(StringTokenizer st)
3356 {
3357 String retVal = null;
3358
3359 if (st.countTokens() > 0)
3360 {
3361 String tokenList = "\" ";
3362 StringBuffer tokBuf = new StringBuffer(10);
3363 String tok = null;
3364 boolean inQuote = false;
3365 boolean tokStarted = false;
3366 boolean exit = false;
3367 while ((st.hasMoreTokens()) && (!exit))
3368 {
3369 tok = st.nextToken(tokenList);
3370 if (tok.equals("\""))
3371 {
3372 inQuote = ! inQuote;
3373 if (inQuote)
3374 {
3375 tokenList = "\"";
3376 }
3377 else
3378 {
3379 tokenList = "\" ";
3380 }
3381
3382 }
3383 else if (tok.equals(" "))
3384 {
3385 if (tokStarted)
3386 {
3387 retVal = tokBuf.toString();
3388 tokStarted=false;
3389 tokBuf = new StringBuffer(10);
3390 exit = true;
3391 }
3392 }
3393 else
3394 {
3395 tokStarted = true;
3396 tokBuf.append(tok.trim());
3397 }
3398 }
3399
3400 // Handle case where end of token stream and
3401 // still got data
3402 if ((!exit) && (tokStarted))
3403 {
3404 retVal = tokBuf.toString();
3405 }
3406 }
3407
3408 return retVal;
3409 }
3410
3411 //
3412 // Private utility methods.
3413 //
3414
3415 /**
3416 * Generated the next valid bundle identifier.
3417 **/
3418 private synchronized long getNextId()
3419 {
3420 return m_nextId++;
3421 }
3422
3423 //
3424 // Configuration methods and inner classes.
3425 //
3426
3427 public PropertyResolver getConfig()
3428 {
3429 return m_config;
3430 }
3431
3432 private class ConfigImpl implements PropertyResolver
3433 {
3434 public String get(String key)
3435 {
Richard S. Hallea415752005-12-05 19:30:28 +00003436 return (m_configMutable == null) ? null : m_configMutable.get(key);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003437 }
3438
3439 public String[] getKeys()
3440 {
Richard S. Hallea415752005-12-05 19:30:28 +00003441 return m_configMutable.getKeys();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003442 }
3443 }
3444
3445 //
3446 // Logging methods and inner classes.
3447 //
3448
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003449 public Logger getLogger()
Richard S. Hall930fecc2005-08-16 18:33:34 +00003450 {
3451 return m_logger;
3452 }
3453
3454 /**
3455 * Simple class that is used in <tt>refreshPackages()</tt> to embody
3456 * the refresh logic in order to keep the code clean. This class is
3457 * not static because it needs access to framework event firing methods.
3458 **/
3459 private class RefreshHelper
3460 {
3461 private BundleImpl m_bundle = null;
3462
3463 public RefreshHelper(Bundle bundle)
3464 {
3465 m_bundle = (BundleImpl) bundle;
3466 }
3467
3468 public void stop()
3469 {
3470 if (m_bundle.getInfo().getState() == Bundle.ACTIVE)
3471 {
3472 try
3473 {
3474 stopBundle(m_bundle, false);
3475 }
3476 catch (BundleException ex)
3477 {
3478 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3479 }
3480 }
3481 }
3482
3483 public void purgeOrRemove()
3484 {
3485 try
3486 {
3487 BundleInfo info = m_bundle.getInfo();
3488
3489 // Remove or purge the bundle depending on its
3490 // current state.
3491 if (info.getState() == Bundle.UNINSTALLED)
3492 {
3493 // This physically removes the bundle from memory
3494 // as well as the bundle cache.
3495 garbageCollectBundle(m_bundle);
3496 m_bundle = null;
3497 }
3498 else
3499 {
3500 // This physically removes all old revisions of the
3501 // bundle from memory and only maintains the newest
3502 // version in the bundle cache.
3503 purgeBundle(m_bundle);
3504 }
3505 }
3506 catch (Exception ex)
3507 {
3508 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3509 }
3510 }
3511
3512 public void reinitialize()
3513 {
3514 if (m_bundle != null)
3515 {
3516 try
3517 {
3518 BundleInfo info = m_bundle.getInfo();
3519 BundleInfo newInfo = createBundleInfo(info.getArchive());
3520 newInfo.syncLock(info);
3521 m_bundle.setInfo(newInfo);
3522 }
3523 catch (Exception ex)
3524 {
3525 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3526 }
3527 }
3528 }
3529
3530 public void restart()
3531 {
3532 if (m_bundle != null)
3533 {
3534 try
3535 {
3536 startBundle(m_bundle, false);
3537 }
3538 catch (BundleException ex)
3539 {
3540 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3541 }
3542 }
3543 }
3544 }
3545
3546 //
3547 // Locking related methods.
3548 //
3549
3550 private void rememberUninstalledBundle(BundleImpl bundle)
3551 {
3552 synchronized (m_uninstalledBundlesLock_Priority3)
3553 {
3554 // Verify that the bundle is not already in the array.
3555 for (int i = 0;
3556 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
3557 i++)
3558 {
3559 if (m_uninstalledBundles[i] == bundle)
3560 {
3561 return;
3562 }
3563 }
3564
3565 if (m_uninstalledBundles != null)
3566 {
3567 BundleImpl[] newBundles =
3568 new BundleImpl[m_uninstalledBundles.length + 1];
3569 System.arraycopy(m_uninstalledBundles, 0,
3570 newBundles, 0, m_uninstalledBundles.length);
3571 newBundles[m_uninstalledBundles.length] = bundle;
3572 m_uninstalledBundles = newBundles;
3573 }
3574 else
3575 {
3576 m_uninstalledBundles = new BundleImpl[] { bundle };
3577 }
3578 }
3579 }
3580
3581 private void forgetUninstalledBundle(BundleImpl bundle)
3582 {
3583 synchronized (m_uninstalledBundlesLock_Priority3)
3584 {
3585 if (m_uninstalledBundles == null)
3586 {
3587 return;
3588 }
3589
3590 int idx = -1;
3591 for (int i = 0; i < m_uninstalledBundles.length; i++)
3592 {
3593 if (m_uninstalledBundles[i] == bundle)
3594 {
3595 idx = i;
3596 break;
3597 }
3598 }
3599
3600 if (idx >= 0)
3601 {
3602 // If this is the only bundle, then point to empty list.
3603 if ((m_uninstalledBundles.length - 1) == 0)
3604 {
3605 m_uninstalledBundles = new BundleImpl[0];
3606 }
3607 // Otherwise, we need to do some array copying.
3608 else
3609 {
3610 BundleImpl[] newBundles =
3611 new BundleImpl[m_uninstalledBundles.length - 1];
3612 System.arraycopy(m_uninstalledBundles, 0, newBundles, 0, idx);
3613 if (idx < newBundles.length)
3614 {
3615 System.arraycopy(
3616 m_uninstalledBundles, idx + 1,
3617 newBundles, idx, newBundles.length - idx);
3618 }
3619 m_uninstalledBundles = newBundles;
3620 }
3621 }
3622 }
3623 }
3624
3625 protected void acquireInstallLock(String location)
3626 throws BundleException
3627 {
3628 synchronized (m_installRequestLock_Priority1)
3629 {
3630 while (m_installRequestMap.get(location) != null)
3631 {
3632 try
3633 {
3634 m_installRequestLock_Priority1.wait();
3635 }
3636 catch (InterruptedException ex)
3637 {
3638 throw new BundleException("Unable to install, thread interrupted.");
3639 }
3640 }
3641
3642 m_installRequestMap.put(location, location);
3643 }
3644 }
3645
3646 protected void releaseInstallLock(String location)
3647 {
3648 synchronized (m_installRequestLock_Priority1)
3649 {
3650 m_installRequestMap.remove(location);
3651 m_installRequestLock_Priority1.notifyAll();
3652 }
3653 }
3654
3655 protected void acquireBundleLock(BundleImpl bundle)
3656 throws BundleException
3657 {
3658 synchronized (m_bundleLock)
3659 {
3660 while (!bundle.getInfo().isLockable())
3661 {
3662 try
3663 {
3664 m_bundleLock.wait();
3665 }
3666 catch (InterruptedException ex)
3667 {
3668 // Ignore and just keep waiting.
3669 }
3670 }
3671 bundle.getInfo().lock();
3672 }
3673 }
3674
3675 protected void releaseBundleLock(BundleImpl bundle)
3676 {
3677 synchronized (m_bundleLock)
3678 {
3679 bundle.getInfo().unlock();
3680 m_bundleLock.notifyAll();
3681 }
3682 }
3683
3684 protected BundleImpl[] acquireBundleRefreshLocks(Bundle[] targets)
3685 {
3686 // Hold bundles to be locked.
3687 BundleImpl[] bundles = null;
3688
3689 synchronized (m_bundleLock)
3690 {
3691 boolean success = false;
3692 while (!success)
3693 {
3694 // If targets is null, then refresh all pending bundles.
3695 Bundle[] newTargets = targets;
3696 if (newTargets == null)
3697 {
3698 List list = new ArrayList();
3699
3700 // First add all uninstalled bundles.
3701 synchronized (m_uninstalledBundlesLock_Priority3)
3702 {
3703 for (int i = 0;
3704 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
3705 i++)
3706 {
3707 list.add(m_uninstalledBundles[i]);
3708 }
3709 }
3710
3711 // Then add all updated bundles.
3712 synchronized (m_installedBundleLock_Priority2)
3713 {
3714 Iterator iter = m_installedBundleMap.values().iterator();
3715 while (iter.hasNext())
3716 {
3717 BundleImpl bundle = (BundleImpl) iter.next();
3718 if (bundle.getInfo().isRemovalPending())
3719 {
3720 list.add(bundle);
3721 }
3722 }
3723 }
3724
3725 // Create an array.
3726 if (list.size() > 0)
3727 {
3728 newTargets = (Bundle[]) list.toArray(new Bundle[list.size()]);
3729 }
3730 }
3731
3732 // If there are targets, then find all dependencies
3733 // for each one.
3734 if (newTargets != null)
3735 {
3736 // Create map of bundles that import the packages
3737 // from the target bundles.
3738 Map map = new HashMap();
3739 for (int targetIdx = 0; targetIdx < newTargets.length; targetIdx++)
3740 {
3741 // Add the current target bundle to the map of
3742 // bundles to be refreshed.
3743 BundleImpl target = (BundleImpl) newTargets[targetIdx];
3744 map.put(target, target);
3745 // Add all importing bundles to map.
3746 populateImportGraph(target, map);
3747 }
3748
3749 bundles = (BundleImpl[]) map.values().toArray(new BundleImpl[map.size()]);
3750 }
3751
3752 // Check if all corresponding bundles can be locked
3753 boolean lockable = true;
3754 if (bundles != null)
3755 {
3756 for (int i = 0; lockable && (i < bundles.length); i++)
3757 {
3758 lockable = bundles[i].getInfo().isLockable();
3759 }
3760
3761 // If we can lock all bundles, then lock them.
3762 if (lockable)
3763 {
3764 for (int i = 0; i < bundles.length; i++)
3765 {
3766 bundles[i].getInfo().lock();
3767 }
3768 success = true;
3769 }
3770 // Otherwise, wait and try again.
3771 else
3772 {
3773 try
3774 {
3775 m_bundleLock.wait();
3776 }
3777 catch (InterruptedException ex)
3778 {
3779 // Ignore and just keep waiting.
3780 }
3781 }
3782 }
3783 else
3784 {
3785 // If there were no bundles to lock, then we can just
3786 // exit the lock loop.
3787 success = true;
3788 }
3789 }
3790 }
3791
3792 return bundles;
3793 }
3794
3795 protected void releaseBundleLocks(BundleImpl[] bundles)
3796 {
3797 // Always unlock any locked bundles.
3798 synchronized (m_bundleLock)
3799 {
3800 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
3801 {
3802 bundles[i].getInfo().unlock();
3803 }
3804 m_bundleLock.notifyAll();
3805 }
3806 }
Richard S. Hallc762b692006-01-27 15:18:57 +00003807}