blob: 16cef733c0231d9b49a021641a5517b17709f4bb [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.*;
Richard S. Hall5a031592005-08-19 19:53:58 +000028import org.apache.felix.moduleloader.*;
Richard S. Hall930fecc2005-08-16 18:33:34 +000029import org.osgi.framework.*;
30import org.osgi.service.packageadmin.ExportedPackage;
Richard S. Hall17897152006-03-02 13:43:09 +000031import org.osgi.service.startlevel.StartLevel;
Richard S. Hall930fecc2005-08-16 18:33:34 +000032
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.
Richard S. Hall04bdbb12006-03-15 14:26:15 +000084 private BundleCache m_cache = null;
Richard S. Hall930fecc2005-08-16 18:33:34 +000085
86 // Next available bundle identifier.
87 private long m_nextId = 1L;
Richard S. Hall441c7152006-02-17 11:07:10 +000088 private Object m_nextIdLock = new Object[0];
Richard S. Hall930fecc2005-08-16 18:33:34 +000089
90 // List of event listeners.
91 private FelixDispatchQueue m_dispatchQueue = null;
92 // Re-usable event dispatchers.
93 private Dispatcher m_frameworkDispatcher = null;
94 private Dispatcher m_bundleDispatcher = null;
95 private Dispatcher m_serviceDispatcher = null;
96
97 // Service registry.
98 private ServiceRegistry m_registry = null;
99
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000100 // Reusable bundle URL stream handler.
101 private URLStreamHandler m_bundleStreamHandler = null;
102
Richard S. Hall930fecc2005-08-16 18:33:34 +0000103 // Reusable admin permission object for all instances
104 // of the BundleImpl.
105 private static AdminPermission m_adminPerm = new AdminPermission();
106
107 /**
108 * <p>
109 * This method starts the framework instance; instances of the framework
110 * are dormant until this method is called. The caller may also provide
111 * <tt>MutablePropertyResolver</tt> implementations that the instance will
112 * use to obtain configuration or framework properties. Configuration
113 * properties are used internally by the framework and its extensions to alter
114 * its default behavior. Framework properties are used by bundles
115 * and are accessible from <tt>BundleContext.getProperty()</tt>.
116 * </p>
117 * <p>
118 * Configuration properties are the sole means to configure the framework's
119 * default behavior; the framework does not refer to any system properties for
120 * configuration information. If a <tt>MutablePropertyResolver</tt> is
121 * supplied to this method for configuration properties, then the framework will
122 * consult the <tt>MutablePropertyResolver</tt> instance for any and all
123 * configuration properties. It is possible to specify a <tt>null</tt>
124 * configuration property resolver, in which case the framework will use its
125 * default behavior in all cases. However, if the
126 * <a href="cache/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
127 * is used, then at a minimum a profile name or profile directory must
128 * be specified.
129 * </p>
130 * <p>
131 * The following configuration properties can be specified:
132 * </p>
133 * <ul>
Richard S. Hall930fecc2005-08-16 18:33:34 +0000134 * <li><tt>felix.auto.install.&lt;n&gt;</tt> - Space-delimited list of
135 * bundles to automatically install into start level <tt>n</tt> when
136 * the framework is started. Append a specific start level to this
137 * property name to assign the bundles' start level
138 * (e.g., <tt>felix.auto.install.2</tt>).
139 * </li>
140 * <li><tt>felix.auto.start.&lt;n&gt;</tt> - Space-delimited list of
141 * bundles to automatically install and start into start level
142 * <tt>n</tt> when the framework is started. Append a
143 * specific start level to this property name to assign the
144 * bundles' start level(e.g., <tt>felix.auto.start.2</tt>).
145 * </li>
146 * <li><tt>felix.startlevel.framework</tt> - The initial start level
147 * of the framework once it starts execution; the default
148 * value is 1.
149 * </li>
150 * <li><tt>felix.startlevel.bundle</tt> - The default start level for
151 * newly installed bundles; the default value is 1.
152 * </li>
Richard S. Hall5d226732005-11-08 09:09:05 +0000153 * <li><tt>framework.service.urlhandlers</tt> - Flag to indicate whether
154 * to activate the URL Handlers service for the framework instance;
155 * the default value is "<tt>true</tt>". Activating the URL Handlers
156 * service will result in the <tt>URL.setURLStreamHandlerFactory()</tt>
157 * and <tt>URLConnection.setContentHandlerFactory()</tt> being called.
158 * </li>
Richard S. Hall930fecc2005-08-16 18:33:34 +0000159 * <li><tt>felix.embedded.execution</tt> - Flag to indicate whether
160 * the framework is embedded into a host application; the default value is
161 * "<tt>false</tt>". If this flag is "<tt>true</tt>" then the framework
162 * will not called <tt>System.exit()</tt> upon termination.
163 * </li>
164 * <li><tt>felix.strict.osgi</tt> - Flag to indicate whether the framework is
165 * running in strict OSGi mode; the default value is "<tt>true</tt>".
166 * If this flag is "<tt>false</tt>" it enables a non-OSGi-compliant
167 * feature by persisting <tt>BundleActivator</tt>s that implement
168 * <tt>Serializable</tt>. This feature is not recommended since
169 * it is non-compliant.
170 * </li>
171 * </ul>
172 * <p>
173 * Besides the above framework configuration properties, it is also
174 * possible to specify properties for the bundle cache. The available
175 * bundle cache properties depend on the cache implementation
176 * being used. For the properties of the default bundle cache, refer to the
177 * <a href="cache/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
178 * API documentation.
179 * </p>
180 * <p>
181 * Framework properties are somewhat misnamed, since they are not used by
182 * the framework, but by bundles via <tt>BundleContext.getProperty()</tt>.
183 * Please refer to bundle documentation of your specific bundle for any
184 * available properties.
185 * </p>
186 * <p>
187 * The <a href="Main.html"><tt>Main</tt></a> class implements some
188 * functionality for default property file handling, which makes it
189 * possible to specify configuration properties and framework properties
190 * in files that are automatically loaded when starting the framework. If you
191 * plan to create your own framework instance, you may be
192 * able to take advantage of the features it provides; refer to its
193 * class documentation for more information.
194 * </p>
195 *
Richard S. Hallea415752005-12-05 19:30:28 +0000196 * @param configMutable An object for obtaining configuration properties,
Richard S. Hall930fecc2005-08-16 18:33:34 +0000197 * may be <tt>null</tt>.
198 * @param frameworkProps An object for obtaining framework properties,
199 * may be <tt>null</tt>.
200 * @param activatorList A list of System Bundle activators.
201 **/
202 public synchronized void start(
Richard S. Hallea415752005-12-05 19:30:28 +0000203 MutablePropertyResolver configMutable,
Richard S. Hall930fecc2005-08-16 18:33:34 +0000204 List activatorList)
205 {
206 if (m_frameworkStatus != INITIAL_STATUS)
207 {
208 throw new IllegalStateException("Invalid framework status: " + m_frameworkStatus);
209 }
210
211 // The framework is now in its startup sequence.
212 m_frameworkStatus = STARTING_STATUS;
213
214 // Initialize member variables.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000215 m_factory = null;
Richard S. Hallea415752005-12-05 19:30:28 +0000216 m_configMutable = (configMutable == null)
217 ? new MutablePropertyResolverImpl(new StringMap(false)) : configMutable;
Richard S. Hall930fecc2005-08-16 18:33:34 +0000218 m_activeStartLevel = FelixConstants.FRAMEWORK_INACTIVE_STARTLEVEL;
219 m_installRequestMap = new HashMap();
220 m_installedBundleMap = new HashMap();
221 m_uninstalledBundles = null;
222 m_cache = null;
223 m_nextId = 1L;
224 m_dispatchQueue = null;
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000225 m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000226 m_registry = new ServiceRegistry(m_logger);
227
228 // Add a listener to the service registry; this is
229 // used to distribute service registry events to
230 // service listeners.
231 m_registry.addServiceListener(new ServiceListener() {
232 public void serviceChanged(ServiceEvent event)
233 {
234 fireServiceEvent(event);
235 }
236 });
237
Richard S. Hall930fecc2005-08-16 18:33:34 +0000238 try
239 {
Richard S. Hall04bdbb12006-03-15 14:26:15 +0000240 m_cache = new BundleCache(m_config, m_logger);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000241 }
242 catch (Exception ex)
243 {
244 System.err.println("Error creating bundle cache:");
245 ex.printStackTrace();
246
247 // Only shutdown the JVM if the framework is running stand-alone.
248 String embedded = m_config.get(
249 FelixConstants.EMBEDDED_EXECUTION_PROP);
250 boolean isEmbedded = (embedded == null)
251 ? false : embedded.equals("true");
252 if (!isEmbedded)
253 {
254 System.exit(-1);
255 }
256 else
257 {
258 throw new RuntimeException(ex.toString());
259 }
260 }
261
262 // Create search policy for module loader.
Richard S. Halle975d4d2006-04-10 12:56:14 +0000263 m_policyCore = new R4SearchPolicyCore(m_logger, m_config);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000264
265 // Add a resolver listener to the search policy
266 // so that we will be notified when modules are resolved
267 // in order to update the bundle state.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000268 m_policyCore.addResolverListener(new ResolveListener() {
Richard S. Hall930fecc2005-08-16 18:33:34 +0000269 public void moduleResolved(ModuleEvent event)
270 {
271 BundleImpl bundle = null;
272 try
273 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000274 long id = Util.getBundleIdFromModuleId(
Richard S. Hall930fecc2005-08-16 18:33:34 +0000275 event.getModule().getId());
276 if (id >= 0)
277 {
278 // Update the bundle's state to resolved when the
279 // current module is resolved; just ignore resolve
280 // events for older revisions since this only occurs
281 // when an update is done on an unresolved bundle
282 // and there was no refresh performed.
283 bundle = (BundleImpl) getBundle(id);
284
285 // Lock the bundle first.
286 try
287 {
288 acquireBundleLock(bundle);
289 if (bundle.getInfo().getCurrentModule() == event.getModule())
290 {
291 bundle.getInfo().setState(Bundle.RESOLVED);
292 }
293 }
Richard S. Hall930fecc2005-08-16 18:33:34 +0000294 finally
295 {
296 releaseBundleLock(bundle);
297 }
298 }
299 }
300 catch (NumberFormatException ex)
301 {
302 // Ignore.
303 }
304 }
305
306 public void moduleUnresolved(ModuleEvent event)
307 {
308 // We can ignore this, because the only time it
309 // should happen is when a refresh occurs. The
310 // refresh operation resets the bundle's state
311 // by calling BundleInfo.reset(), thus it is not
312 // necessary for us to reset the bundle's state
313 // here.
314 }
315 });
316
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000317 m_factory = new ModuleFactoryImpl(m_logger);
318 m_policyCore.setModuleFactory(m_factory);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000319
320 // Initialize dispatch queue.
321 m_dispatchQueue = new FelixDispatchQueue(m_logger);
322
323 // Initialize framework properties.
324 initializeFrameworkProperties();
325
326 // Before we reload any cached bundles, let's create a system
327 // bundle that is responsible for providing specific container
328 // related services.
329 SystemBundle systembundle = null;
330 try
331 {
332 // Create a simple bundle info for the system bundle.
333 BundleInfo info = new BundleInfo(
334 m_logger, new SystemBundleArchive(), null);
335 systembundle = new SystemBundle(this, info, activatorList);
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000336 systembundle.getInfo().addModule(m_factory.createModule("0"));
337 systembundle.getContentLoader().setSearchPolicy(
338 new R4SearchPolicy(
339 m_policyCore, systembundle.getInfo().getCurrentModule()));
340 m_factory.setContentLoader(
341 systembundle.getInfo().getCurrentModule(),
342 systembundle.getContentLoader());
343 m_policyCore.setExports(
344 systembundle.getInfo().getCurrentModule(), systembundle.getExports());
345
Richard S. Hall930fecc2005-08-16 18:33:34 +0000346 m_installedBundleMap.put(
347 systembundle.getInfo().getLocation(), systembundle);
348
349 // Manually resolve the System Bundle, which will cause its
350 // state to be set to RESOLVED.
351 try
352 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000353 m_policyCore.resolve(systembundle.getInfo().getCurrentModule());
Richard S. Hall930fecc2005-08-16 18:33:34 +0000354 }
355 catch (ResolveException ex)
356 {
357 // This should never happen.
358 throw new BundleException(
359 "Unresolved package in System Bundle:"
360 + ex.getPackage());
361 }
362
363 // Start the system bundle; this will set its state
364 // to STARTING, we must set its state to ACTIVE after
365 // all bundles are restarted below according to the spec.
366 systembundle.start();
367 }
368 catch (Exception ex)
369 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000370 m_factory = null;
Richard S. Hall930fecc2005-08-16 18:33:34 +0000371 DispatchQueue.shutdown();
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000372 m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000373 throw new RuntimeException("Unable to start system bundle.");
374 }
375
376 // Reload and cached bundles.
Richard S. Hall04bdbb12006-03-15 14:26:15 +0000377 BundleArchive[] archives = null;
Richard S. Hall930fecc2005-08-16 18:33:34 +0000378
379 // First get cached bundle identifiers.
380 try
381 {
382 archives = m_cache.getArchives();
383 }
384 catch (Exception ex)
385 {
386 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000387 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +0000388 "Unable to list saved bundles: " + ex, ex);
389 archives = null;
390 }
391
392 BundleImpl bundle = null;
393
394 // Now install all cached bundles.
395 for (int i = 0; (archives != null) && (i < archives.length); i++)
396 {
Richard S. Hall930fecc2005-08-16 18:33:34 +0000397 try
398 {
Richard S. Hall7c9da3d2006-02-24 20:09:28 +0000399 // Make sure our id generator is not going to overlap.
400 // TODO: This is not correct since it may lead to re-used
401 // ids, which is not okay according to OSGi.
402 m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
403
Richard S. Hall930fecc2005-08-16 18:33:34 +0000404 // It is possible that a bundle in the cache was previously
405 // uninstalled, but not completely deleted (perhaps because
406 // of a crash or a locked file), so if we see an archive
407 // with an UNINSTALLED persistent state, then try to remove
408 // it now.
409 if (archives[i].getPersistentState() == Bundle.UNINSTALLED)
410 {
411 m_cache.remove(archives[i]);
412 }
413 // Otherwise re-install the cached bundle.
414 else
415 {
416 // Install the cached bundle.
417 bundle = (BundleImpl) installBundle(
418 archives[i].getId(), archives[i].getLocation(), null);
419 }
420 }
421 catch (Exception ex)
422 {
423 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
424 try
425 {
426 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000427 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +0000428 "Unable to re-install " + archives[i].getLocation(),
429 ex);
430 }
431 catch (Exception ex2)
432 {
433 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000434 Logger.LOG_ERROR,
Richard S. Hall7c9da3d2006-02-24 20:09:28 +0000435 "Unable to re-install cached bundle.",
Richard S. Hall930fecc2005-08-16 18:33:34 +0000436 ex);
437 }
438 // TODO: Perhaps we should remove the cached bundle?
439 }
440 }
441
442 // Get the framework's default start level.
443 int startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
444 String s = m_config.get(FelixConstants.FRAMEWORK_STARTLEVEL_PROP);
445 if (s != null)
446 {
447 try
448 {
449 startLevel = Integer.parseInt(s);
450 }
451 catch (NumberFormatException ex)
452 {
453 startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
454 }
455 }
456
457 // Load bundles from auto-install and auto-start properties;
458 processAutoProperties();
459
Richard S. Hall17897152006-03-02 13:43:09 +0000460 // Set the start level using the start level service;
461 // this ensures that all start level requests are
462 // serialized.
463 // NOTE: There is potentially a specification compliance
464 // issue here, since the start level request is asynchronous;
465 // this means that the framework will fire its STARTED event
466 // before all bundles have officially been restarted and it
467 // is not clear if this is really an issue or not.
468 try
469 {
470 StartLevel sl = (StartLevel) getService(
471 getBundle(0),
472 getServiceReferences((BundleImpl) getBundle(0), StartLevel.class.getName(), null)[0]);
473 sl.setStartLevel(startLevel);
474 }
475 catch (InvalidSyntaxException ex)
476 {
477 // Should never happen.
478 }
Richard S. Hall930fecc2005-08-16 18:33:34 +0000479
480 // The framework is now running.
481 m_frameworkStatus = RUNNING_STATUS;
482
483 // Set the system bundle state to ACTIVE.
484 systembundle.getInfo().setState(Bundle.ACTIVE);
485
486 // Fire started event for system bundle.
487 fireBundleEvent(BundleEvent.STARTED, systembundle);
488
489 // Send a framework event to indicate the framework has started.
490 fireFrameworkEvent(FrameworkEvent.STARTED, getBundle(0), null);
491 }
492
493 /**
494 * This method cleanly shuts down the framework, it must be called at the
495 * end of a session in order to shutdown all active bundles.
496 **/
497 public synchronized void shutdown()
498 {
499 if (System.getSecurityManager() != null)
500 {
501 AccessController.checkPermission(m_adminPerm);
502 }
503
504 // Change framework status from running to stopping.
505 // If framework is not running, then just return.
506 if (m_frameworkStatus != RUNNING_STATUS)
507 {
508 return;
509 }
510
511 // The framework is now in its shutdown sequence.
512 m_frameworkStatus = STOPPING_STATUS;
513
Richard S. Hall17897152006-03-02 13:43:09 +0000514 // Use the start level service to set the start level to zero
515 // in order to stop all bundles in the framework. Since framework
516 // shutdown happens on its own thread, we can wait for the start
517 // level service to finish before proceeding by calling the
518 // non-spec setStartLevelAndWait() method.
519 try
520 {
521 StartLevelImpl sl = (StartLevelImpl) getService(
522 getBundle(0),
523 getServiceReferences((BundleImpl) getBundle(0), StartLevel.class.getName(), null)[0]);
524 sl.setStartLevelAndWait(0);
525 }
526 catch (InvalidSyntaxException ex)
527 {
528 // Should never happen.
529 }
Richard S. Hall930fecc2005-08-16 18:33:34 +0000530
531 // Just like initialize() called the system bundle's start()
532 // method, we must call its stop() method here so that it
533 // can perform any necessary clean up.
534 try
535 {
536 getBundle(0).stop();
537 }
538 catch (Exception ex)
539 {
540 fireFrameworkEvent(FrameworkEvent.ERROR, getBundle(0), ex);
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000541 m_logger.log(Logger.LOG_ERROR, "Error stopping system bundle.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000542 }
543
Richard S. Hall60c26d42006-07-19 10:35:04 +0000544 // Since they may be updated and uninstalled bundles that
545 // have not been refreshed, we will take care of refreshing
546 // them during shutdown.
547
548 // First loop through all bundled and purge old revisions
549 // from updated bundles.
Richard S. Hall930fecc2005-08-16 18:33:34 +0000550 Bundle[] bundles = getBundles();
551 for (int i = 0; i < bundles.length; i++)
552 {
553 BundleImpl bundle = (BundleImpl) bundles[i];
Richard S. Hall60c26d42006-07-19 10:35:04 +0000554 if (bundle.getInfo().getArchive().getRevisionCount() > 1)
Richard S. Hall930fecc2005-08-16 18:33:34 +0000555 {
556 try
557 {
558 purgeBundle(bundle);
559 }
560 catch (Exception ex)
561 {
562 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000563 m_logger.log(Logger.LOG_ERROR, "Unable to purge bundle "
Richard S. Hall930fecc2005-08-16 18:33:34 +0000564 + bundle.getInfo().getLocation(), ex);
565 }
566 }
567 }
568
Richard S. Hall60c26d42006-07-19 10:35:04 +0000569 // Next garbage collection any uninstalled bundles.
Richard S. Hall930fecc2005-08-16 18:33:34 +0000570 for (int i = 0;
571 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
572 i++)
573 {
574 try
575 {
576 garbageCollectBundle(m_uninstalledBundles[i]);
577 }
578 catch (Exception ex)
579 {
580 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000581 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +0000582 "Unable to remove "
583 + m_uninstalledBundles[i].getInfo().getLocation(), ex);
584 }
585 }
586
587 // Shutdown event dispatching queue.
588 DispatchQueue.shutdown();
589
590 // The framework is no longer in a usable state.
591 m_frameworkStatus = INITIAL_STATUS;
Richard S. Hall3331ad72006-06-20 09:07:23 +0000592
593 // Remove all bundles from the module factory so that any
594 // open resources will be closed.
595 bundles = getBundles();
596 for (int i = 0; i < bundles.length; i++)
597 {
598 BundleImpl bundle = (BundleImpl) bundles[i];
599 try
600 {
601 IModule[] modules = bundle.getInfo().getModules();
602 for (int j = 0; j < modules.length; j++)
603 {
604 m_factory.removeModule(modules[j]);
605 }
606 }
607 catch (Exception ex)
608 {
609 m_logger.log(Logger.LOG_ERROR,
610 "Unable to clean up " + bundle.getInfo().getLocation(), ex);
611 }
612 }
Richard S. Hall930fecc2005-08-16 18:33:34 +0000613 }
614
615 public int getStatus()
616 {
617 return m_frameworkStatus;
618 }
619
620 /**
621 * Returns the active start level of the framework; this method
622 * implements functionality for the Start Level service.
623 * @return The active start level of the framework.
624 **/
625 protected int getStartLevel()
626 {
627 return m_activeStartLevel;
628 }
629
630 /**
631 * Implements the functionality of the <tt>setStartLevel()</tt>
632 * method for the StartLevel service, but does not do the security or
633 * parameter check. The security and parameter check are done in the
634 * StartLevel service implementation because this method is called on
635 * a separate thread and the caller's thread would already be gone if
Richard S. Hall17897152006-03-02 13:43:09 +0000636 * we did the checks in this method. This method should not be called
637 * directly.
Richard S. Hall930fecc2005-08-16 18:33:34 +0000638 * @param requestedLevel The new start level of the framework.
639 **/
Richard S. Hall441c7152006-02-17 11:07:10 +0000640 protected void setFrameworkStartLevel(int requestedLevel)
Richard S. Hall930fecc2005-08-16 18:33:34 +0000641 {
Richard S. Hall17897152006-03-02 13:43:09 +0000642 Bundle[] bundles = null;
643
644 // Synchronization for changing the start level is rather loose.
645 // The install lock is grabbed initially to atomically change the
646 // framework's start level and to grab a sorted snapshot of the
647 // currently installed bundles, but then this lock is freed immediately.
648 // No locks are held while processing the currently installed bundles
649 // for starting/stopping based on the new start level. The only locking
650 // that occurs is for individual bundles when startBundle()/stopBundle()
651 // is called, but this locking is done in the respective method.
652 //
653 // This approach does mean that it is possible for a for individual
654 // bundle states to change during this operation. For example, bundle
655 // start levels can be changed or bundles can be uninstalled. If a
656 // bundle's start level changes, then it is possible for it to be
657 // processed out of order. Uninstalled bundles are just logged and
658 // ignored. I had a bit of discussion with Peter Kriens about these
659 // issues and he felt they were consistent with the spec, which
660 // intended Start Level to have some leeway.
661 //
662 // Calls to this method are only made by the start level thread, which
663 // serializes framework start level changes. Thus, it is not possible
664 // for two requests to change the framework's start level to interfere
665 // with each other.
666
667 synchronized (m_installedBundleLock_Priority2)
Richard S. Hall930fecc2005-08-16 18:33:34 +0000668 {
Richard S. Hall17897152006-03-02 13:43:09 +0000669 // Determine if we are lowering or raising the
670 // active start level.
671 boolean lowering = (requestedLevel < m_activeStartLevel);
672
673 // Record new start level.
674 m_activeStartLevel = requestedLevel;
675
676 // Get a snapshot of all installed bundles.
677 bundles = getBundles();
678
679 // Sort bundle array by start level either ascending or
680 // descending depending on whether the start level is being
681 // lowered or raised to that the bundles can be efficiently
Richard S. Hall5259e3e2006-07-14 18:55:59 +0000682 // processed in order. Within a start level sort by bundle ID.
Richard S. Hall17897152006-03-02 13:43:09 +0000683 Comparator comparator = null;
684 if (lowering)
Richard S. Hall930fecc2005-08-16 18:33:34 +0000685 {
Richard S. Hall17897152006-03-02 13:43:09 +0000686 // Sort descending to stop highest start level first.
687 comparator = new Comparator() {
688 public int compare(Object o1, Object o2)
Richard S. Hall441c7152006-02-17 11:07:10 +0000689 {
Richard S. Hall17897152006-03-02 13:43:09 +0000690 BundleImpl b1 = (BundleImpl) o1;
691 BundleImpl b2 = (BundleImpl) o2;
692 if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
693 < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
694 {
695 return 1;
696 }
697 else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
698 > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
699 {
700 return -1;
701 }
Richard S. Hall5259e3e2006-07-14 18:55:59 +0000702 else if (b1.getInfo().getBundleId() < b2.getInfo().getBundleId())
703 {
704 return 1;
705 }
706 return -1;
Richard S. Hall441c7152006-02-17 11:07:10 +0000707 }
Richard S. Hall17897152006-03-02 13:43:09 +0000708 };
709 }
710 else
711 {
712 // Sort ascending to start lowest start level first.
713 comparator = new Comparator() {
714 public int compare(Object o1, Object o2)
Richard S. Hall441c7152006-02-17 11:07:10 +0000715 {
Richard S. Hall17897152006-03-02 13:43:09 +0000716 BundleImpl b1 = (BundleImpl) o1;
717 BundleImpl b2 = (BundleImpl) o2;
718 if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
719 > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
Richard S. Hall441c7152006-02-17 11:07:10 +0000720 {
Richard S. Hall17897152006-03-02 13:43:09 +0000721 return 1;
Richard S. Hall441c7152006-02-17 11:07:10 +0000722 }
Richard S. Hall17897152006-03-02 13:43:09 +0000723 else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
724 < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
Richard S. Hall441c7152006-02-17 11:07:10 +0000725 {
Richard S. Hall17897152006-03-02 13:43:09 +0000726 return -1;
Richard S. Hall441c7152006-02-17 11:07:10 +0000727 }
Richard S. Hall5259e3e2006-07-14 18:55:59 +0000728 else if (b1.getInfo().getBundleId() > b2.getInfo().getBundleId())
729 {
730 return 1;
731 }
732 return -1;
Richard S. Hall17897152006-03-02 13:43:09 +0000733 }
734 };
735 }
Richard S. Hall3c26cc02006-02-17 13:51:21 +0000736
Richard S. Hall17897152006-03-02 13:43:09 +0000737 Arrays.sort(bundles, comparator);
738 }
739
740 // Stop or start the bundles according to the start level.
741 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
742 {
743 BundleImpl impl = (BundleImpl) bundles[i];
744
745 // Ignore the system bundle, since its start() and
746 // stop() methods get called explicitly in Felix.start()
747 // and Felix.shutdown(), respectively.
748 if (impl.getInfo().getBundleId() == 0)
749 {
750 continue;
751 }
752
753 // Lock the current bundle.
754 acquireBundleLock(impl);
755
756 try
757 {
758 // Start the bundle if necessary.
759 if ((impl.getInfo().getPersistentState() == Bundle.ACTIVE) &&
760 (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
761 <= m_activeStartLevel))
762 {
763 try
764 {
765 startBundle(impl, false);
766 }
767 catch (Throwable th)
768 {
769 fireFrameworkEvent(FrameworkEvent.ERROR, impl, th);
770 m_logger.log(
771 Logger.LOG_ERROR,
772 "Error starting " + impl.getInfo().getLocation(), th);
Richard S. Hall441c7152006-02-17 11:07:10 +0000773 }
Richard S. Hall3c26cc02006-02-17 13:51:21 +0000774 }
Richard S. Hall17897152006-03-02 13:43:09 +0000775 // Stop the bundle if necessary.
776 else if (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
777 > m_activeStartLevel)
Richard S. Hall3c26cc02006-02-17 13:51:21 +0000778 {
Richard S. Hall17897152006-03-02 13:43:09 +0000779 try
Richard S. Hall441c7152006-02-17 11:07:10 +0000780 {
Richard S. Hall17897152006-03-02 13:43:09 +0000781 stopBundle(impl, false);
Richard S. Hall3c26cc02006-02-17 13:51:21 +0000782 }
Richard S. Hall17897152006-03-02 13:43:09 +0000783 catch (Throwable th)
Richard S. Hall3c26cc02006-02-17 13:51:21 +0000784 {
Richard S. Hall17897152006-03-02 13:43:09 +0000785 fireFrameworkEvent(FrameworkEvent.ERROR, impl, th);
786 m_logger.log(
787 Logger.LOG_ERROR,
788 "Error stopping " + impl.getInfo().getLocation(), th);
Richard S. Hall441c7152006-02-17 11:07:10 +0000789 }
Richard S. Hall930fecc2005-08-16 18:33:34 +0000790 }
Richard S. Hall17897152006-03-02 13:43:09 +0000791 }
792 finally
793 {
794 // Always release bundle lock.
795 releaseBundleLock(impl);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000796 }
797 }
798
799 fireFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, getBundle(0), null);
800 }
801
802 /**
803 * Returns the start level into which newly installed bundles will
804 * be placed by default; this method implements functionality for
805 * the Start Level service.
806 * @return The default start level for newly installed bundles.
807 **/
808 protected int getInitialBundleStartLevel()
809 {
810 String s = m_config.get(FelixConstants.BUNDLE_STARTLEVEL_PROP);
811
812 if (s != null)
813 {
814 try
815 {
816 int i = Integer.parseInt(s);
817 return (i > 0) ? i : FelixConstants.BUNDLE_DEFAULT_STARTLEVEL;
818 }
819 catch (NumberFormatException ex)
820 {
821 // Ignore and return the default value.
822 }
823 }
824 return FelixConstants.BUNDLE_DEFAULT_STARTLEVEL;
825 }
826
827 /**
828 * Sets the default start level into which newly installed bundles
829 * will be placed; this method implements functionality for the Start
830 * Level service.
831 * @param startLevel The new default start level for newly installed
832 * bundles.
833 * @throws java.lang.IllegalArgumentException If the specified start
834 * level is not greater than zero.
835 * @throws java.security.SecurityException If the caller does not
836 * have <tt>AdminPermission</tt>.
837 **/
838 protected void setInitialBundleStartLevel(int startLevel)
839 {
840 if (System.getSecurityManager() != null)
841 {
842 AccessController.checkPermission(m_adminPerm);
843 }
844
845 if (startLevel <= 0)
846 {
847 throw new IllegalArgumentException(
848 "Initial start level must be greater than zero.");
849 }
850
Richard S. Hallea415752005-12-05 19:30:28 +0000851 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +0000852 FelixConstants.BUNDLE_STARTLEVEL_PROP, Integer.toString(startLevel));
853 }
854
855 /**
856 * Returns the start level for the specified bundle; this method
857 * implements functionality for the Start Level service.
858 * @param bundle The bundle to examine.
859 * @return The start level of the specified bundle.
860 * @throws java.lang.IllegalArgumentException If the specified
861 * bundle has been uninstalled.
862 **/
863 protected int getBundleStartLevel(Bundle bundle)
864 {
865 if (bundle.getState() == Bundle.UNINSTALLED)
866 {
867 throw new IllegalArgumentException("Bundle is uninstalled.");
868 }
869
870 return ((BundleImpl) bundle).getInfo().getStartLevel(getInitialBundleStartLevel());
871 }
872
873 /**
874 * Sets the start level of the specified bundle; this method
875 * implements functionality for the Start Level service.
876 * @param bundle The bundle whose start level is to be modified.
877 * @param startLevel The new start level of the specified bundle.
878 * @throws java.lang.IllegalArgumentException If the specified
879 * bundle is the system bundle or if the bundle has been
880 * uninstalled.
881 * @throws java.security.SecurityException If the caller does not
882 * have <tt>AdminPermission</tt>.
883 **/
884 protected void setBundleStartLevel(Bundle bundle, int startLevel)
885 {
Richard S. Hall930fecc2005-08-16 18:33:34 +0000886 // Acquire bundle lock.
Richard S. Hall3c26cc02006-02-17 13:51:21 +0000887 acquireBundleLock((BundleImpl) bundle);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000888
889 Throwable rethrow = null;
890
891 try
892 {
893 if (bundle.getState() == Bundle.UNINSTALLED)
894 {
895 throw new IllegalArgumentException("Bundle is uninstalled.");
896 }
897
898 if (startLevel >= 1)
899 {
900 BundleImpl impl = (BundleImpl) bundle;
901 impl.getInfo().setStartLevel(startLevel);
902
903 try
904 {
905 // Start the bundle if necessary.
906 if ((impl.getInfo().getPersistentState() == Bundle.ACTIVE) &&
907 (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
908 <= m_activeStartLevel))
909 {
910 startBundle(impl, false);
911 }
912 // Stop the bundle if necessary.
913 else if (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
914 > m_activeStartLevel)
915 {
916 stopBundle(impl, false);
917 }
918 }
919 catch (Throwable th)
920 {
921 rethrow = th;
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000922 m_logger.log(Logger.LOG_ERROR, "Error starting/stopping bundle.", th);
Richard S. Hall930fecc2005-08-16 18:33:34 +0000923 }
924 }
925 else
926 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +0000927 m_logger.log(Logger.LOG_WARNING, "Bundle start level must be greater than zero.");
Richard S. Hall930fecc2005-08-16 18:33:34 +0000928 }
929 }
930 finally
931 {
932 // Always release bundle lock.
933 releaseBundleLock((BundleImpl) bundle);
934 }
935
936 if (rethrow != null)
937 {
938 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, rethrow);
939 }
940 }
941
942 /**
943 * Returns whether a bundle is persistently started; this is an
944 * method implementation for the Start Level service.
945 * @param bundle The bundle to examine.
946 * @return <tt>true</tt> if the bundle is marked as persistently
947 * started, <tt>false</tt> otherwise.
948 * @throws java.lang.IllegalArgumentException If the specified
949 * bundle has been uninstalled.
950 **/
951 protected boolean isBundlePersistentlyStarted(Bundle bundle)
952 {
953 if (bundle.getState() == Bundle.UNINSTALLED)
954 {
955 throw new IllegalArgumentException("Bundle is uninstalled.");
956 }
957
958 return (((BundleImpl) bundle).getInfo().getPersistentState() == Bundle.ACTIVE);
959 }
960
961 //
962 // Implementation of Bundle interface methods.
963 //
964
965 /**
966 * Implementation for Bundle.getHeaders().
967 **/
968 protected Dictionary getBundleHeaders(BundleImpl bundle)
969 {
970 if (System.getSecurityManager() != null)
971 {
972 AccessController.checkPermission(m_adminPerm);
973 }
974 return new MapToDictionary(bundle.getInfo().getCurrentHeader());
975 }
976
977 /**
978 * Implementation for Bundle.getLocation().
979 **/
980 protected String getBundleLocation(BundleImpl bundle)
981 {
982 if (System.getSecurityManager() != null)
983 {
984 AccessController.checkPermission(m_adminPerm);
985 }
986 return bundle.getInfo().getLocation();
987 }
988
989 /**
990 * Implementation for Bundle.getResource().
991 **/
992 protected URL getBundleResource(BundleImpl bundle, String name)
993 {
994 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
995 {
996 throw new IllegalStateException("The bundle is uninstalled.");
997 }
998 else if (System.getSecurityManager() != null)
999 {
Richard S. Hall5d7a94a2006-02-06 12:57:30 +00001000 try
1001 {
1002 AccessController.checkPermission(
1003 new AdminPermission(bundle, AdminPermission.RESOURCE));
1004 }
1005 catch (SecurityException ex)
1006 {
1007 // Spec says to return null if there is a security exception.
1008 return null;
1009 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00001010 }
Richard S. Hall001cc302006-02-03 15:03:24 +00001011 return bundle.getInfo().getCurrentModule().getResource(name);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001012 }
1013
Richard S. Hallbcbf83e2006-02-05 13:28:26 +00001014 /**
1015 * Implementation for Bundle.getEntry().
1016 **/
1017 protected URL getBundleEntry(BundleImpl bundle, String name)
1018 {
1019 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1020 {
1021 throw new IllegalStateException("The bundle is uninstalled.");
1022 }
Richard S. Hallbcbf83e2006-02-05 13:28:26 +00001023 else if (System.getSecurityManager() != null)
1024 {
Richard S. Hall5d7a94a2006-02-06 12:57:30 +00001025 try
1026 {
1027 AccessController.checkPermission(
1028 new AdminPermission(bundle, AdminPermission.RESOURCE));
1029 }
1030 catch (SecurityException ex)
1031 {
1032 // Spec says to return null if there is a security exception.
1033 return null;
1034 }
Richard S. Hallbcbf83e2006-02-05 13:28:26 +00001035 }
1036 return ((ContentLoaderImpl) bundle.getInfo().getCurrentModule()
1037 .getContentLoader()).getResourceFromContent(name);
1038 }
1039
Richard S. Hall4f09b642006-02-06 10:59:19 +00001040 /**
1041 * Implementation for Bundle.getEntryPaths().
1042 **/
1043 protected Enumeration getBundleEntryPaths(BundleImpl bundle, String path)
1044 {
1045 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1046 {
1047 throw new IllegalStateException("The bundle is uninstalled.");
1048 }
Richard S. Hall4f09b642006-02-06 10:59:19 +00001049 else if (System.getSecurityManager() != null)
1050 {
Richard S. Hall5d7a94a2006-02-06 12:57:30 +00001051 try
1052 {
1053 AccessController.checkPermission(
1054 new AdminPermission(bundle, AdminPermission.RESOURCE));
1055 }
1056 catch (SecurityException ex)
1057 {
1058 // Spec says to return null if there is a security exception.
1059 return null;
1060 }
Richard S. Hall4f09b642006-02-06 10:59:19 +00001061 }
1062 // Strip leading '/' if present.
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00001063 if ((path.length() > 0) && (path.charAt(0) == '/'))
Richard S. Hall4f09b642006-02-06 10:59:19 +00001064 {
1065 path = path.substring(1);
1066 }
1067 return bundle.getInfo().getCurrentModule()
1068 .getContentLoader().getContent().getEntryPaths(path);
1069 }
1070
Richard S. Hall930fecc2005-08-16 18:33:34 +00001071 protected ServiceReference[] getBundleRegisteredServices(BundleImpl bundle)
1072 {
1073 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1074 {
1075 throw new IllegalStateException("The bundle is uninstalled.");
1076 }
1077
1078 // Filter list of registered service references.
1079 ServiceReference[] refs = m_registry.getRegisteredServices(bundle);
1080 List list = new ArrayList();
1081 for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
1082 {
1083 // Check that the current security context has permission
1084 // to get at least one of the service interfaces; the
1085 // objectClass property of the service stores its service
1086 // interfaces.
1087 boolean hasPermission = false;
1088 if (System.getSecurityManager() != null)
1089 {
1090 String[] objectClass = (String[])
1091 refs[refIdx].getProperty(Constants.OBJECTCLASS);
1092 if (objectClass == null)
1093 {
1094 return null;
1095 }
1096 for (int ifcIdx = 0;
1097 !hasPermission && (ifcIdx < objectClass.length);
1098 ifcIdx++)
1099 {
1100 try
1101 {
1102 ServicePermission perm =
1103 new ServicePermission(
1104 objectClass[ifcIdx], ServicePermission.GET);
1105 AccessController.checkPermission(perm);
1106 hasPermission = true;
1107 }
1108 catch (Exception ex)
1109 {
1110 }
1111 }
1112 }
1113 else
1114 {
1115 hasPermission = true;
1116 }
1117
1118 if (hasPermission)
1119 {
1120 list.add(refs[refIdx]);
1121 }
1122 }
1123
1124 if (list.size() > 0)
1125 {
1126 return (ServiceReference[])
1127 list.toArray(new ServiceReference[list.size()]);
1128 }
1129
1130 return null;
1131 }
1132
1133 protected ServiceReference[] getBundleServicesInUse(Bundle bundle)
1134 {
1135 // Filter list of "in use" service references.
1136 ServiceReference[] refs = m_registry.getServicesInUse(bundle);
1137 List list = new ArrayList();
1138 for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
1139 {
1140 // Check that the current security context has permission
1141 // to get at least one of the service interfaces; the
1142 // objectClass property of the service stores its service
1143 // interfaces.
1144 boolean hasPermission = false;
1145 if (System.getSecurityManager() != null)
1146 {
1147 String[] objectClass = (String[])
1148 refs[refIdx].getProperty(Constants.OBJECTCLASS);
1149 if (objectClass == null)
1150 {
1151 return null;
1152 }
1153 for (int ifcIdx = 0;
1154 !hasPermission && (ifcIdx < objectClass.length);
1155 ifcIdx++)
1156 {
1157 try
1158 {
1159 ServicePermission perm =
1160 new ServicePermission(
1161 objectClass[ifcIdx], ServicePermission.GET);
1162 AccessController.checkPermission(perm);
1163 hasPermission = true;
1164 }
1165 catch (Exception ex)
1166 {
1167 }
1168 }
1169 }
1170 else
1171 {
1172 hasPermission = true;
1173 }
1174
1175 if (hasPermission)
1176 {
1177 list.add(refs[refIdx]);
1178 }
1179 }
1180
1181 if (list.size() > 0)
1182 {
1183 return (ServiceReference[])
1184 list.toArray(new ServiceReference[list.size()]);
1185 }
1186
1187 return null;
1188 }
1189
1190 protected boolean bundleHasPermission(BundleImpl bundle, Object obj)
1191 {
1192 if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1193 {
1194 throw new IllegalStateException("The bundle is uninstalled.");
1195 }
1196
Richard S. Hall2cf44c92006-01-23 19:23:56 +00001197 if (System.getSecurityManager() != null)
Richard S. Hall65730962006-01-23 19:23:13 +00001198 {
1199 try
1200 {
Richard S. Halld1e3cbd2006-01-27 15:23:01 +00001201 return (obj instanceof java.security.Permission)
1202 ? java.security.Policy.getPolicy().getPermissions(
Richard S. Hall65730962006-01-23 19:23:13 +00001203 new java.security.CodeSource(
Richard S. Hallc762b692006-01-27 15:18:57 +00001204 new java.net.URL(bundle.getInfo().getLocation()),
1205 (java.security.cert.Certificate[]) null))
Richard S. Hall65730962006-01-23 19:23:13 +00001206 .implies((java.security.Permission) obj)
Richard S. Halld1e3cbd2006-01-27 15:23:01 +00001207 : false;
Richard S. Hall65730962006-01-23 19:23:13 +00001208 }
1209 catch (Exception ex)
1210 {
1211 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001212 Logger.LOG_WARNING,
Richard S. Hall65730962006-01-23 19:23:13 +00001213 "Exception while evaluating the permission.",
1214 ex);
1215 return false;
1216 }
Richard S. Halld1e3cbd2006-01-27 15:23:01 +00001217 }
Richard S. Hall65730962006-01-23 19:23:13 +00001218
Richard S. Halld1e3cbd2006-01-27 15:23:01 +00001219 return true;
Richard S. Hall930fecc2005-08-16 18:33:34 +00001220 }
1221
1222 /**
Richard S. Hall74b97972005-11-30 15:51:41 +00001223 * Implementation for Bundle.loadClass().
1224 **/
1225 protected Class loadBundleClass(BundleImpl bundle, String name) throws ClassNotFoundException
1226 {
Richard S. Hallf1359482006-02-14 08:02:51 +00001227 Class clazz = bundle.getInfo().getCurrentModule().getClass(name);
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001228 if (clazz == null)
Richard S. Hall74b97972005-11-30 15:51:41 +00001229 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001230 // Throw exception.
1231 ClassNotFoundException ex = new ClassNotFoundException(name);
1232
Richard S. Hall624f8012005-11-30 15:53:43 +00001233 // The spec says we must fire a framework error.
Richard S. Hall74b97972005-11-30 15:51:41 +00001234 fireFrameworkEvent(
1235 FrameworkEvent.ERROR, bundle,
1236 new BundleException(ex.getMessage()));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001237
Richard S. Hall74b97972005-11-30 15:51:41 +00001238 throw ex;
1239 }
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001240 return clazz;
Richard S. Hall74b97972005-11-30 15:51:41 +00001241 }
1242
1243 /**
Richard S. Hall930fecc2005-08-16 18:33:34 +00001244 * Implementation for Bundle.start().
1245 **/
1246 protected void startBundle(BundleImpl bundle, boolean record)
1247 throws BundleException
1248 {
1249 if (System.getSecurityManager() != null)
1250 {
1251 AccessController.checkPermission(m_adminPerm);
1252 }
1253
1254 // CONCURRENCY NOTE:
1255 // Starting a bundle may actually impact many bundles, since
1256 // the bundle being started my need to be resolved, which in
1257 // turn may need to resolve other bundles. Despite this fact,
1258 // we only acquire the lock for the bundle being started, because
1259 // when resolve is called on this bundle, it will eventually
1260 // call resolve on the module loader search policy, which does
Richard S. Hall3c26cc02006-02-17 13:51:21 +00001261 // its own locking on the module factory instance. Since the
1262 // resolve algorithm is locking the module factory instance, it
Richard S. Hall930fecc2005-08-16 18:33:34 +00001263 // is not possible for other bundles to be installed or removed,
1264 // so we don't have to worry about these possibilities.
1265 //
1266 // Further, if other bundles are started during this operation,
1267 // then either they will resolve first because they got the lock
Richard S. Hall3c26cc02006-02-17 13:51:21 +00001268 // on the module factory or we will resolve first since we got
1269 // the lock on the module factory, so there should be no interference.
Richard S. Hall930fecc2005-08-16 18:33:34 +00001270 // If other bundles are stopped or uninstalled, this should pose
1271 // no problems, since this does not impact their resolved state.
1272 // If a refresh occurs, then the refresh algorithm ulimately has
Richard S. Hall3c26cc02006-02-17 13:51:21 +00001273 // to acquire the module factory instance lock too before it can
Richard S. Hall930fecc2005-08-16 18:33:34 +00001274 // completely purge old modules, so it should also complete either
1275 // before or after this bundle is started. At least that's the
1276 // theory.
1277
1278 // Acquire bundle lock.
1279 acquireBundleLock(bundle);
1280
1281 try
1282 {
1283 _startBundle(bundle, record);
1284 }
1285 finally
1286 {
1287 // Release bundle lock.
1288 releaseBundleLock(bundle);
1289 }
1290 }
1291
1292 private void _startBundle(BundleImpl bundle, boolean record)
1293 throws BundleException
1294 {
1295 // Set and save the bundle's persistent state to active
1296 // if we are supposed to record state change.
1297 if (record)
1298 {
1299 bundle.getInfo().setPersistentStateActive();
1300 }
1301
1302 // Try to start the bundle.
1303 BundleInfo info = bundle.getInfo();
1304
1305 // Ignore bundles whose persistent state is not active
1306 // or whose start level is greater than the framework's.
1307 if ((info.getPersistentState() != Bundle.ACTIVE)
1308 || (info.getStartLevel(getInitialBundleStartLevel()) > getStartLevel()))
1309 {
1310 return;
1311 }
1312
1313 switch (info.getState())
1314 {
1315 case Bundle.UNINSTALLED:
1316 throw new IllegalStateException("Cannot start an uninstalled bundle.");
1317 case Bundle.STARTING:
1318 case Bundle.STOPPING:
1319 throw new BundleException("Starting a bundle that is starting or stopping is currently not supported.");
1320 case Bundle.ACTIVE:
1321 return;
1322 case Bundle.INSTALLED:
1323 _resolveBundle(bundle);
1324 case Bundle.RESOLVED:
1325 info.setState(Bundle.STARTING);
1326 }
1327
1328 try
1329 {
1330 // Set the bundle's activator.
1331 bundle.getInfo().setActivator(createBundleActivator(bundle.getInfo()));
1332
1333 // Activate the bundle if it has an activator.
1334 if (bundle.getInfo().getActivator() != null)
1335 {
1336 if (info.getContext() == null)
1337 {
1338 info.setContext(new BundleContextImpl(this, bundle));
1339 }
1340
1341 if (System.getSecurityManager() != null)
1342 {
1343// m_startStopPrivileged.setAction(StartStopPrivileged.START_ACTION);
1344// m_startStopPrivileged.setBundle(bundle);
1345// AccessController.doPrivileged(m_startStopPrivileged);
1346 }
1347 else
1348 {
1349 info.getActivator().start(info.getContext());
1350 }
1351 }
1352
1353 info.setState(Bundle.ACTIVE);
1354
Richard S. Hall3c26cc02006-02-17 13:51:21 +00001355 // TODO: CONCURRENCY - Reconsider firing event outside of the
1356 // bundle lock.
1357
Richard S. Hall930fecc2005-08-16 18:33:34 +00001358 fireBundleEvent(BundleEvent.STARTED, bundle);
1359 }
1360 catch (Throwable th)
1361 {
1362 // If there was an error starting the bundle,
1363 // then reset its state to RESOLVED.
1364 info.setState(Bundle.RESOLVED);
1365
Richard S. Hall187b87a2006-07-03 09:13:18 +00001366 // Clean up the bundle context, if necessary.
1367 if (info.getContext() != null)
1368 {
1369 ((BundleContextImpl) info.getContext()).invalidate();
1370 info.setContext(null);
1371 }
1372
Richard S. Hall930fecc2005-08-16 18:33:34 +00001373 // Unregister any services offered by this bundle.
1374 m_registry.unregisterServices(bundle);
1375
1376 // Release any services being used by this bundle.
1377 m_registry.ungetServices(bundle);
1378
1379 // Remove any listeners registered by this bundle.
1380 removeListeners(bundle);
1381
1382 // The spec says to expect BundleException or
1383 // SecurityException, so rethrow these exceptions.
1384 if (th instanceof BundleException)
1385 {
1386 throw (BundleException) th;
1387 }
1388 else if (th instanceof SecurityException)
1389 {
1390 throw (SecurityException) th;
1391 }
1392 // Convert a privileged action exception to the
1393 // nested exception.
1394 else if (th instanceof PrivilegedActionException)
1395 {
1396 th = ((PrivilegedActionException) th).getException();
1397 }
1398
1399 // Rethrow all other exceptions as a BundleException.
1400 throw new BundleException("Activator start error.", th);
1401 }
1402 }
1403
1404 protected void _resolveBundle(BundleImpl bundle)
1405 throws BundleException
1406 {
1407 // If a security manager is installed, then check for permission
1408 // to import the necessary packages.
1409 if (System.getSecurityManager() != null)
1410 {
1411 URL url = null;
1412 try
1413 {
1414 url = new URL(bundle.getInfo().getLocation());
1415 }
1416 catch (MalformedURLException ex)
1417 {
1418 throw new BundleException("Cannot resolve, bad URL "
1419 + bundle.getInfo().getLocation());
1420 }
1421
1422// try
1423// {
1424// AccessController.doPrivileged(new CheckImportsPrivileged(url, bundle));
1425// }
1426// catch (PrivilegedActionException ex)
1427// {
1428// Exception thrown = ((PrivilegedActionException) ex).getException();
1429// if (thrown instanceof AccessControlException)
1430// {
1431// throw (AccessControlException) thrown;
1432// }
1433// else
1434// {
1435// throw new BundleException("Problem resolving: " + ex);
1436// }
1437// }
1438 }
1439
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001440 IModule module = bundle.getInfo().getCurrentModule();
Richard S. Hall930fecc2005-08-16 18:33:34 +00001441 try
1442 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001443 m_policyCore.resolve(module);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001444 }
1445 catch (ResolveException ex)
1446 {
1447 if (ex.getModule() != null)
1448 {
1449 throw new BundleException(
1450 "Unresolved package in bundle "
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001451 + Util.getBundleIdFromModuleId(ex.getModule().getId())
Richard S. Hall930fecc2005-08-16 18:33:34 +00001452 + ": " + ex.getPackage());
1453 }
1454 else
1455 {
1456 throw new BundleException(ex.getMessage());
1457 }
1458 }
1459
1460 bundle.getInfo().setState(Bundle.RESOLVED);
1461 }
1462
1463 protected void updateBundle(BundleImpl bundle, InputStream is)
1464 throws BundleException
1465 {
1466 if (System.getSecurityManager() != null)
1467 {
1468 AccessController.checkPermission(m_adminPerm);
1469 }
1470
1471 // Acquire bundle lock.
1472 acquireBundleLock(bundle);
1473
1474 try
1475 {
1476 _updateBundle(bundle, is);
1477 }
1478 finally
1479 {
1480 // Release bundle lock.
1481 releaseBundleLock(bundle);
1482 }
1483 }
1484
1485 protected void _updateBundle(BundleImpl bundle, InputStream is)
1486 throws BundleException
1487 {
1488 // We guarantee to close the input stream, so put it in a
1489 // finally clause.
1490
1491 try
1492 {
1493 // Variable to indicate whether bundle is active or not.
1494 Exception rethrow = null;
1495
1496 // Cannot update an uninstalled bundle.
1497 BundleInfo info = bundle.getInfo();
1498 if (info.getState() == Bundle.UNINSTALLED)
1499 {
1500 throw new IllegalStateException("The bundle is uninstalled.");
1501 }
1502
1503 // First get the update-URL from our header.
1504 String updateLocation = (String)
1505 info.getCurrentHeader().get(Constants.BUNDLE_UPDATELOCATION);
1506
1507 // If no update location specified, use original location.
1508 if (updateLocation == null)
1509 {
1510 updateLocation = info.getLocation();
1511 }
1512
1513 // Stop the bundle, but do not change the persistent state.
1514 stopBundle(bundle, false);
1515
1516 try
1517 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00001518 // Get the bundle's archive.
Richard S. Hall04bdbb12006-03-15 14:26:15 +00001519 BundleArchive archive = m_cache.getArchive(info.getBundleId());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001520 // Update the bundle; this operation will increase
1521 // the revision count for the bundle.
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00001522 archive.revise(updateLocation, is);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001523 // Create a module for the new revision; the revision is
1524 // base zero, so subtract one from the revision count to
1525 // get the revision of the new update.
Karl Pauls9ff9bb82006-07-19 12:46:01 +00001526 try
1527 {
1528 IModule module = createModule(
1529 info.getBundleId(),
1530 archive.getRevisionCount() - 1,
1531 info.getCurrentHeader());
1532 // Add module to bundle info.
1533 info.addModule(module);
1534 }
1535 catch (Exception ex)
1536 {
Karl Pauls9ff9bb82006-07-19 12:46:01 +00001537 try
1538 {
1539 archive.undoRevise();
1540 }
1541 catch (Exception busted)
1542 {
1543 m_logger.log(Logger.LOG_ERROR, "Unable to rollback.", busted);
1544 }
1545
Karl Pauls3390b4a2006-07-19 13:20:32 +00001546 throw ex;
Karl Pauls9ff9bb82006-07-19 12:46:01 +00001547 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00001548 }
1549 catch (Exception ex)
1550 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001551 m_logger.log(Logger.LOG_ERROR, "Unable to update the bundle.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001552 rethrow = ex;
1553 }
1554
Karl Pauls9ff9bb82006-07-19 12:46:01 +00001555 // Set new state, mark as needing a refresh, and fire updated event
1556 // if successful.
Richard S. Hall930fecc2005-08-16 18:33:34 +00001557 if (rethrow == null)
1558 {
Karl Pauls9ff9bb82006-07-19 12:46:01 +00001559 info.setState(Bundle.INSTALLED);
1560 info.setLastModified(System.currentTimeMillis());
1561
Richard S. Hall60c26d42006-07-19 10:35:04 +00001562 // Mark previous the bundle's old module for removal since
1563 // it can no longer be used to resolve other modules per the spec.
1564 IModule module = info.getModules()[info.getModules().length - 2];
1565 m_policyCore.setRemovalPending(module, true);
1566
Richard S. Hall930fecc2005-08-16 18:33:34 +00001567 fireBundleEvent(BundleEvent.UPDATED, bundle);
1568 }
1569
1570 // Restart bundle, but do not change the persistent state.
1571 // This will not start the bundle if it was not previously
1572 // active.
1573 startBundle(bundle, false);
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00001574
Richard S. Hall930fecc2005-08-16 18:33:34 +00001575 // If update failed, rethrow exception.
1576 if (rethrow != null)
1577 {
1578 throw new BundleException("Update failed.", rethrow);
1579 }
1580 }
1581 finally
1582 {
1583 try
1584 {
1585 if (is != null) is.close();
1586 }
1587 catch (IOException ex)
1588 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001589 m_logger.log(Logger.LOG_ERROR, "Unable to close input stream.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001590 }
1591 }
1592 }
1593
1594 protected void stopBundle(BundleImpl bundle, boolean record)
1595 throws BundleException
1596 {
1597 if (System.getSecurityManager() != null)
1598 {
1599 AccessController.checkPermission(m_adminPerm);
1600 }
1601
1602 // Acquire bundle lock.
1603 acquireBundleLock(bundle);
1604
1605 try
1606 {
1607 _stopBundle(bundle, record);
1608 }
1609 finally
1610 {
1611 // Always release bundle lock.
1612 releaseBundleLock(bundle);
1613 }
1614 }
1615
1616 private void _stopBundle(BundleImpl bundle, boolean record)
1617 throws BundleException
1618 {
1619 Throwable rethrow = null;
1620
1621 // Set the bundle's persistent state to inactive if necessary.
1622 if (record)
1623 {
1624 bundle.getInfo().setPersistentStateInactive();
1625 }
1626
1627 BundleInfo info = bundle.getInfo();
1628
1629 switch (info.getState())
1630 {
1631 case Bundle.UNINSTALLED:
1632 throw new IllegalStateException("Cannot stop an uninstalled bundle.");
1633 case Bundle.STARTING:
1634 case Bundle.STOPPING:
1635 throw new BundleException("Stopping a bundle that is starting or stopping is currently not supported.");
1636 case Bundle.INSTALLED:
1637 case Bundle.RESOLVED:
1638 return;
1639 case Bundle.ACTIVE:
1640 // Set bundle state..
1641 info.setState(Bundle.STOPPING);
1642 }
1643
1644 try
1645 {
1646 if (bundle.getInfo().getActivator() != null)
1647 {
1648 if (System.getSecurityManager() != null)
1649 {
1650// m_startStopPrivileged.setAction(StartStopPrivileged.STOP_ACTION);
1651// m_startStopPrivileged.setBundle(bundle);
1652// AccessController.doPrivileged(m_startStopPrivileged);
1653 }
1654 else
1655 {
1656 info.getActivator().stop(info.getContext());
1657 }
1658 }
1659
1660 // Try to save the activator in the cache.
1661 // NOTE: This is non-standard OSGi behavior and only
1662 // occurs if strictness is disabled.
1663 String strict = m_config.get(FelixConstants.STRICT_OSGI_PROP);
1664 boolean isStrict = (strict == null) ? true : strict.equals("true");
1665 if (!isStrict)
1666 {
1667 try
1668 {
1669 m_cache.getArchive(info.getBundleId())
1670 .setActivator(info.getActivator());
1671 }
1672 catch (Exception ex)
1673 {
1674 // Problem saving activator, so ignore it.
1675 // TODO: Perhaps we should handle this some other way?
1676 }
1677 }
1678 }
1679 catch (Throwable th)
1680 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001681 m_logger.log(Logger.LOG_ERROR, "Error stopping bundle.", th);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001682 rethrow = th;
1683 }
1684
Richard S. Hall187b87a2006-07-03 09:13:18 +00001685 // Clean up the bundle context, if necessary.
1686 if (info.getContext() != null)
1687 {
1688 ((BundleContextImpl) info.getContext()).invalidate();
1689 info.setContext(null);
1690 }
1691
Richard S. Hall930fecc2005-08-16 18:33:34 +00001692 // Unregister any services offered by this bundle.
1693 m_registry.unregisterServices(bundle);
1694
1695 // Release any services being used by this bundle.
1696 m_registry.ungetServices(bundle);
1697
1698 // The spec says that we must remove all event
1699 // listeners for a bundle when it is stopped.
1700 removeListeners(bundle);
1701
1702 info.setState(Bundle.RESOLVED);
1703 fireBundleEvent(BundleEvent.STOPPED, bundle);
1704
1705 // Throw activator error if there was one.
1706 if (rethrow != null)
1707 {
1708 // The spec says to expect BundleException or
1709 // SecurityException, so rethrow these exceptions.
1710 if (rethrow instanceof BundleException)
1711 {
1712 throw (BundleException) rethrow;
1713 }
1714 else if (rethrow instanceof SecurityException)
1715 {
1716 throw (SecurityException) rethrow;
1717 }
1718 else if (rethrow instanceof PrivilegedActionException)
1719 {
1720 rethrow = ((PrivilegedActionException) rethrow).getException();
1721 }
1722
1723 // Rethrow all other exceptions as a BundleException.
1724 throw new BundleException("Activator stop error.", rethrow);
1725 }
1726 }
1727
1728 protected void uninstallBundle(BundleImpl bundle) throws BundleException
1729 {
1730 if (System.getSecurityManager() != null)
1731 {
1732 AccessController.checkPermission(m_adminPerm);
1733 }
1734
1735 // Acquire bundle lock.
1736 acquireBundleLock(bundle);
1737
1738 try
1739 {
1740 _uninstallBundle(bundle);
1741 }
1742 finally
1743 {
1744 // Always release bundle lock.
1745 releaseBundleLock(bundle);
1746 }
1747 }
1748
1749 private void _uninstallBundle(BundleImpl bundle) throws BundleException
1750 {
1751 if (System.getSecurityManager() != null)
1752 {
1753 AccessController.checkPermission(m_adminPerm);
1754 }
1755
Richard S. Hall930fecc2005-08-16 18:33:34 +00001756 BundleInfo info = bundle.getInfo();
1757 if (info.getState() == Bundle.UNINSTALLED)
1758 {
1759 throw new IllegalStateException("The bundle is uninstalled.");
1760 }
1761
1762 // The spec says that uninstall should always succeed, so
1763 // catch an exception here if stop() doesn't succeed and
1764 // rethrow it at the end.
1765 try
1766 {
1767 stopBundle(bundle, true);
1768 }
1769 catch (BundleException ex)
1770 {
Richard S. Hall447c52f2006-07-04 09:25:07 +00001771 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001772 }
1773
1774 // Remove the bundle from the installed map.
1775 BundleImpl target = null;
1776 synchronized (m_installedBundleLock_Priority2)
1777 {
1778 target = (BundleImpl) m_installedBundleMap.remove(info.getLocation());
1779 }
1780
1781 // Finally, put the uninstalled bundle into the
1782 // uninstalled list for subsequent refreshing.
1783 if (target != null)
1784 {
1785 // Set the bundle's persistent state to uninstalled.
1786 target.getInfo().setPersistentStateUninstalled();
1787
Richard S. Hall60c26d42006-07-19 10:35:04 +00001788 // Mark current module for removal since it can no longer
1789 // be used to resolve other modules per the spec.
1790 m_policyCore.setRemovalPending(target.getInfo().getCurrentModule(), true);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001791
1792 // Put bundle in uninstalled bundle array.
1793 rememberUninstalledBundle(bundle);
1794 }
1795 else
1796 {
1797 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001798 Logger.LOG_ERROR, "Unable to remove bundle from installed map!");
Richard S. Hall930fecc2005-08-16 18:33:34 +00001799 }
1800
1801 // Set state to uninstalled.
1802 info.setState(Bundle.UNINSTALLED);
Richard S. Hall69d84792006-01-13 13:55:13 +00001803 info.setLastModified(System.currentTimeMillis());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001804
1805 // Fire bundle event.
1806 fireBundleEvent(BundleEvent.UNINSTALLED, bundle);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001807 }
1808
1809 //
1810 // Implementation of BundleContext interface methods.
1811 //
1812
1813 /**
1814 * Implementation for BundleContext.getProperty(). Returns
1815 * environment property associated with the framework.
1816 *
1817 * @param key The name of the property to retrieve.
1818 * @return The value of the specified property or null.
1819 **/
1820 protected String getProperty(String key)
1821 {
1822 // First, check the config properties.
Richard S. Hallea415752005-12-05 19:30:28 +00001823 String val = (String) m_config.get(key);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001824 // If not found, then try the system properties.
1825 return (val == null) ? System.getProperty(key) : val;
1826 }
1827
1828 protected Bundle installBundle(String location, InputStream is)
1829 throws BundleException
1830 {
1831 return installBundle(-1, location, is);
1832 }
1833
1834 private Bundle installBundle(long id, String location, InputStream is)
1835 throws BundleException
1836 {
1837 if (System.getSecurityManager() != null)
1838 {
1839 AccessController.checkPermission(m_adminPerm);
1840 }
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00001841
Richard S. Hall930fecc2005-08-16 18:33:34 +00001842 BundleImpl bundle = null;
1843
1844 // Acquire an install lock.
1845 acquireInstallLock(location);
1846
1847 try
1848 {
1849 // Check to see if the framework is still running;
1850 if ((getStatus() == Felix.STOPPING_STATUS) ||
1851 (getStatus() == Felix.INITIAL_STATUS))
1852 {
1853 throw new BundleException("The framework has been shutdown.");
1854 }
1855
1856 // If bundle location is already installed, then
1857 // return it as required by the OSGi specification.
1858 bundle = (BundleImpl) getBundle(location);
1859 if (bundle != null)
1860 {
1861 return bundle;
1862 }
1863
1864 // Determine if this is a new or existing bundle.
1865 boolean isNew = (id < 0);
1866
1867 // If the bundle is new we must cache its JAR file.
1868 if (isNew)
1869 {
1870 // First generate an identifier for it.
1871 id = getNextId();
1872
1873 try
1874 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00001875 // Add the bundle to the cache.
Richard S. Hall9a3e9852006-03-04 03:44:05 +00001876 m_cache.create(id, location, is);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001877 }
1878 catch (Exception ex)
1879 {
1880 throw new BundleException(
1881 "Unable to cache bundle: " + location, ex);
1882 }
1883 finally
1884 {
1885 try
1886 {
1887 if (is != null) is.close();
1888 }
1889 catch (IOException ex)
1890 {
1891 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001892 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +00001893 "Unable to close input stream.", ex);
1894 }
1895 }
1896 }
1897 else
1898 {
1899 // If the bundle we are installing is not new,
1900 // then try to purge old revisions before installing
1901 // it; this is done just in case a "refresh"
1902 // didn't occur last session...this would only be
1903 // due to an error or system crash.
1904 try
1905 {
1906 if (m_cache.getArchive(id).getRevisionCount() > 1)
1907 {
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00001908 m_cache.getArchive(id).purge();
Richard S. Hall930fecc2005-08-16 18:33:34 +00001909 }
1910 }
1911 catch (Exception ex)
1912 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00001913 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001914 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +00001915 "Could not purge bundle.", ex);
1916 }
1917 }
1918
1919 try
1920 {
Richard S. Hall04bdbb12006-03-15 14:26:15 +00001921 BundleArchive archive = m_cache.getArchive(id);
Richard S. Hall930fecc2005-08-16 18:33:34 +00001922 bundle = new BundleImpl(this, createBundleInfo(archive));
1923 }
1924 catch (Exception ex)
1925 {
1926 // If the bundle is new, then remove it from the cache.
1927 // TODO: Perhaps it should be removed if it is not new too.
1928 if (isNew)
1929 {
1930 try
1931 {
1932 m_cache.remove(m_cache.getArchive(id));
1933 }
1934 catch (Exception ex1)
1935 {
1936 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001937 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +00001938 "Could not remove from cache.", ex1);
1939 }
1940 }
1941 throw new BundleException("Could not create bundle object.", ex);
1942 }
1943
1944 // If the bundle is new, then set its start level; existing
1945 // bundles already have their start level set.
1946 if (isNew)
1947 {
1948 // This will persistently set the bundle's start level.
1949 bundle.getInfo().setStartLevel(getInitialBundleStartLevel());
Richard S. Hall69d84792006-01-13 13:55:13 +00001950 bundle.getInfo().setLastModified(System.currentTimeMillis());
Richard S. Hall930fecc2005-08-16 18:33:34 +00001951 }
1952
1953 synchronized (m_installedBundleLock_Priority2)
1954 {
1955 m_installedBundleMap.put(location, bundle);
1956 }
1957 }
1958 finally
1959 {
1960 // Always release install lock.
1961 releaseInstallLock(location);
1962
1963 // Always try to close the input stream.
1964 try
1965 {
1966 if (is != null) is.close();
1967 }
1968 catch (IOException ex)
1969 {
1970 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00001971 Logger.LOG_ERROR,
Richard S. Hall930fecc2005-08-16 18:33:34 +00001972 "Unable to close input stream.", ex);
1973 // Not much else we can do.
1974 }
1975 }
1976
1977 // Fire bundle event.
1978 fireBundleEvent(BundleEvent.INSTALLED, bundle);
1979
1980 // Return new bundle.
1981 return bundle;
1982 }
1983
1984 /**
1985 * Retrieves a bundle from its location.
1986 *
1987 * @param location The location of the bundle to retrieve.
1988 * @return The bundle associated with the location or null if there
1989 * is no bundle associated with the location.
1990 **/
1991 protected Bundle getBundle(String location)
1992 {
1993 synchronized (m_installedBundleLock_Priority2)
1994 {
1995 return (Bundle) m_installedBundleMap.get(location);
1996 }
1997 }
1998
1999 /**
2000 * Implementation for BundleContext.getBundle(). Retrieves a
2001 * bundle from its identifier.
2002 *
2003 * @param id The identifier of the bundle to retrieve.
2004 * @return The bundle associated with the identifier or null if there
2005 * is no bundle associated with the identifier.
2006 **/
2007 protected Bundle getBundle(long id)
2008 {
2009 synchronized (m_installedBundleLock_Priority2)
2010 {
2011 BundleImpl bundle = null;
2012
2013 for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
2014 {
2015 bundle = (BundleImpl) i.next();
2016 if (bundle.getInfo().getBundleId() == id)
2017 {
2018 return bundle;
2019 }
2020 }
2021 }
2022
Richard S. Halla6443462006-06-29 15:16:05 +00002023 synchronized (m_uninstalledBundlesLock_Priority3)
2024 {
2025 for (int i = 0; i < m_uninstalledBundles.length; i++)
2026 {
2027 if (m_uninstalledBundles[i].getInfo().getBundleId() == id)
2028 {
2029 return m_uninstalledBundles[i];
2030 }
2031 }
2032 }
2033
Richard S. Hall930fecc2005-08-16 18:33:34 +00002034 return null;
2035 }
2036
2037 // Private member for method below.
2038 private Comparator m_comparator = null;
2039
2040 /**
2041 * Implementation for BundleContext.getBundles(). Retrieves
2042 * all installed bundles.
2043 *
2044 * @return An array containing all installed bundles or null if
2045 * there are no installed bundles.
2046 **/
2047 protected Bundle[] getBundles()
2048 {
2049 if (m_comparator == null)
2050 {
2051 m_comparator = new Comparator() {
2052 public int compare(Object o1, Object o2)
2053 {
2054 Bundle b1 = (Bundle) o1;
2055 Bundle b2 = (Bundle) o2;
2056 if (b1.getBundleId() > b2.getBundleId())
2057 return 1;
2058 else if (b1.getBundleId() < b2.getBundleId())
2059 return -1;
2060 return 0;
2061 }
2062 };
2063 }
2064
2065 Bundle[] bundles = null;
2066
2067 synchronized (m_installedBundleLock_Priority2)
2068 {
2069 if (m_installedBundleMap.size() == 0)
2070 {
2071 return null;
2072 }
2073
2074 bundles = new Bundle[m_installedBundleMap.size()];
2075 int counter = 0;
2076 for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
2077 {
2078 bundles[counter++] = (Bundle) i.next();
2079 }
2080 }
2081
2082 Arrays.sort(bundles, m_comparator);
2083
2084 return bundles;
2085 }
2086
2087 protected void addBundleListener(Bundle bundle, BundleListener l)
2088 {
2089 // The spec says do nothing if the listener is
2090 // already registered.
2091 BundleListenerWrapper old = (BundleListenerWrapper)
2092 m_dispatchQueue.getListener(BundleListener.class, l);
2093 if (old == null)
2094 {
2095 l = new BundleListenerWrapper(bundle, l);
2096 m_dispatchQueue.addListener(BundleListener.class, l);
2097 }
2098 }
2099
Karl Pauls3390b4a2006-07-19 13:20:32 +00002100 protected void removeBundleListener(Bundle bundle, BundleListener l)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002101 {
Karl Pauls3390b4a2006-07-19 13:20:32 +00002102 BundleListenerWrapper old = (BundleListenerWrapper)
2103 m_dispatchQueue.getListener(BundleListener.class, l);
2104 if ((old != null) && old.getBundle().equals(bundle))
2105 {
2106 m_dispatchQueue.removeListener(BundleListener.class, l);
2107 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002108 }
2109
2110 /**
2111 * Implementation for BundleContext.addServiceListener().
2112 * Adds service listener to the listener list so that is
2113 * can listen for <code>ServiceEvent</code>s.
2114 *
2115 * @param bundle The bundle that registered the listener.
2116 * @param l The service listener to add to the listener list.
2117 * @param f The filter for the listener; may be null.
2118 **/
2119 protected void addServiceListener(Bundle bundle, ServiceListener l, String f)
2120 throws InvalidSyntaxException
2121 {
2122 // The spec says if the listener is already registered,
2123 // then replace filter.
2124 ServiceListenerWrapper old = (ServiceListenerWrapper)
2125 m_dispatchQueue.getListener(ServiceListener.class, l);
2126 if (old != null)
2127 {
2128 old.setFilter((f == null) ? null : new FilterImpl(m_logger, f));
2129 }
2130 else
2131 {
2132 l = new ServiceListenerWrapper(
2133 bundle, l, (f == null) ? null : new FilterImpl(m_logger, f));
2134 m_dispatchQueue.addListener(ServiceListener.class, l);
2135 }
2136 }
2137
2138 /**
2139 * Implementation for BundleContext.removeServiceListener().
2140 * Removes service listeners from the listener list.
Karl Pauls3390b4a2006-07-19 13:20:32 +00002141 *
2142 * @param bundle The context bundle of the listener
Richard S. Hall930fecc2005-08-16 18:33:34 +00002143 * @param l The service listener to remove from the listener list.
2144 **/
Karl Pauls3390b4a2006-07-19 13:20:32 +00002145 protected void removeServiceListener(Bundle bundle, ServiceListener l)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002146 {
Karl Pauls3390b4a2006-07-19 13:20:32 +00002147 ServiceListenerWrapper old = (ServiceListenerWrapper)
2148 m_dispatchQueue.getListener(ServiceListener.class, l);
2149 if ((old != null) && old.getBundle().equals(bundle))
2150 {
2151 m_dispatchQueue.removeListener(ServiceListener.class, l);
2152 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002153 }
2154
2155 protected void addFrameworkListener(Bundle bundle, FrameworkListener l)
2156 {
2157 // The spec says do nothing if the listener is
2158 // already registered.
2159 FrameworkListenerWrapper old = (FrameworkListenerWrapper)
2160 m_dispatchQueue.getListener(FrameworkListener.class, l);
2161 if (old == null)
2162 {
2163 l = new FrameworkListenerWrapper(bundle, l);
2164 m_dispatchQueue.addListener(FrameworkListener.class, l);
2165 }
2166 }
2167
Karl Pauls3390b4a2006-07-19 13:20:32 +00002168 protected void removeFrameworkListener(Bundle bundle, FrameworkListener l)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002169 {
Karl Pauls3390b4a2006-07-19 13:20:32 +00002170 FrameworkListenerWrapper old = (FrameworkListenerWrapper)
2171 m_dispatchQueue.getListener(FrameworkListener.class, l);
2172 if ((old != null) && old.getBundle().equals(bundle))
2173 {
2174 m_dispatchQueue.removeListener(FrameworkListener.class, l);
2175 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002176 }
2177
2178 /**
2179 * Remove all of the specified bundle's event listeners from
2180 * the framework.
2181 * @param bundle The bundle whose listeners are to be removed.
2182 **/
2183 private void removeListeners(Bundle bundle)
2184 {
2185 if (bundle == null)
2186 {
2187 return;
2188 }
2189
2190 // Remove all listeners associated with the supplied bundle;
2191 // it is only possible to know the bundle associated with a
2192 // listener if the listener was wrapper by a ListenerWrapper,
2193 // so look for those.
2194 Object[] listeners = m_dispatchQueue.getListeners();
2195 for (int i = listeners.length - 2; i >= 0; i -= 2)
2196 {
2197 // Check for listener wrappers and then compare the bundle.
2198 if (listeners[i + 1] instanceof ListenerWrapper)
2199 {
2200 ListenerWrapper lw = (ListenerWrapper) listeners[i + 1];
2201 if ((lw.getBundle() != null) && (lw.getBundle().equals(bundle)))
2202 {
2203 m_dispatchQueue.removeListener(
2204 (Class) listeners[i], (EventListener) listeners[i+1]);
2205 }
2206 }
2207 }
2208 }
2209
2210 /**
2211 * Implementation for BundleContext.registerService(). Registers
2212 * a service for the specified bundle bundle.
2213 *
2214 * @param classNames A string array containing the names of the classes
2215 * under which the new service is available.
2216 * @param svcObj The service object or <code>ServiceFactory</code>.
2217 * @param dict A dictionary of properties that further describe the
2218 * service or null.
2219 * @return A <code>ServiceRegistration</code> object or null.
2220 **/
2221 protected ServiceRegistration registerService(
2222 BundleImpl bundle, String[] classNames, Object svcObj, Dictionary dict)
2223 {
2224 if (classNames == null)
2225 {
2226 throw new NullPointerException("Service class names cannot be null.");
2227 }
2228 else if (svcObj == null)
2229 {
2230 throw new IllegalArgumentException("Service object cannot be null.");
2231 }
2232
2233 // Check for permission to register all passed in interface names.
2234 if (System.getSecurityManager() != null)
2235 {
2236 for (int i = 0; i < classNames.length; i++)
2237 {
2238 ServicePermission perm = new ServicePermission(
2239 classNames[i], ServicePermission.REGISTER);
2240 AccessController.checkPermission(perm);
2241 }
2242 }
2243
2244 // Acquire bundle lock.
Richard S. Hall3c26cc02006-02-17 13:51:21 +00002245 acquireBundleLock(bundle);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002246
2247 ServiceRegistration reg = null;
2248
2249 try
2250 {
2251 BundleInfo info = bundle.getInfo();
2252
2253 // Can only register services if starting or active.
2254 if ((info.getState() & (Bundle.STARTING | Bundle.ACTIVE)) == 0)
2255 {
2256 throw new IllegalStateException(
2257 "Can only register services while bundle is active or activating.");
2258 }
2259
2260 // Check to make sure that the service object is
2261 // an instance of all service classes; ignore if
2262 // service object is a service factory.
2263 if (!(svcObj instanceof ServiceFactory))
2264 {
2265 for (int i = 0; i < classNames.length; i++)
2266 {
Richard S. Hallbc549622006-07-13 13:33:22 +00002267 Class clazz = Util.loadClassUsingClass(svcObj.getClass(), classNames[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002268 if (clazz == null)
2269 {
2270 throw new IllegalArgumentException(
2271 "Cannot cast service: " + classNames[i]);
2272 }
2273 else if (!clazz.isAssignableFrom(svcObj.getClass()))
2274 {
2275 throw new IllegalArgumentException(
2276 "Service object is not an instance of \""
2277 + classNames[i] + "\".");
2278 }
2279 }
2280 }
2281
2282 reg = m_registry.registerService(bundle, classNames, svcObj, dict);
2283 }
2284 finally
2285 {
2286 // Always release bundle lock.
2287 releaseBundleLock(bundle);
2288 }
Richard S. Hall3c26cc02006-02-17 13:51:21 +00002289
2290 // TODO: CONCURRENCY - Reconsider firing event here, outside of the
2291 // bundle lock.
2292
Richard S. Hall930fecc2005-08-16 18:33:34 +00002293 // NOTE: The service registered event is fired from the service
2294 // registry to the framework, where it is then redistributed to
2295 // interested service event listeners.
2296
2297 return reg;
2298 }
2299
Richard S. Hall930fecc2005-08-16 18:33:34 +00002300 protected ServiceReference[] getServiceReferences(
2301 BundleImpl bundle, String className, String expr)
2302 throws InvalidSyntaxException
2303 {
2304 // Define filter if expression is not null.
2305 Filter filter = null;
2306 if (expr != null)
2307 {
2308 filter = new FilterImpl(m_logger, expr);
2309 }
2310
2311 // Ask the service registry for all matching service references.
2312 List refList = m_registry.getServiceReferences(className, filter);
2313
2314 // The returned reference list must be filtered for two cases:
2315 // 1) The requesting bundle may not be wired to the same class
2316 // as the providing bundle (i.e, different versions), so filter
2317 // any services for which the requesting bundle might get a
2318 // class cast exception.
2319 // 2) Security is enabled and the requesting bundle does not have
2320 // permission access the service.
2321 for (int refIdx = 0; (refList != null) && (refIdx < refList.size()); refIdx++)
2322 {
2323 // Get the current service reference.
2324 ServiceReference ref = (ServiceReference) refList.get(refIdx);
2325
2326 // Get the service's objectClass property.
2327 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
2328
2329 // Boolean flag.
2330 boolean allow = false;
2331
2332 // Filter the service reference if the requesting bundle
2333 // does not have permission.
2334 if (System.getSecurityManager() != null)
2335 {
2336 for (int classIdx = 0;
2337 !allow && (classIdx < objectClass.length);
2338 classIdx++)
2339 {
2340 try
2341 {
2342 ServicePermission perm = new ServicePermission(
2343 objectClass[classIdx], ServicePermission.GET);
2344 AccessController.checkPermission(perm);
2345 // The bundle only needs permission for one
2346 // of the service interfaces, so break out
2347 // of the loop when permission is granted.
2348 allow = true;
2349 }
2350 catch (Exception ex)
2351 {
2352 // We do not throw this exception since the bundle
2353 // is not supposed to know about the service at all
2354 // if it does not have permission.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002355 m_logger.log(Logger.LOG_ERROR, ex.getMessage());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002356 }
2357 }
2358
2359 if (!allow)
2360 {
2361 refList.remove(refIdx);
2362 refIdx--;
2363 continue;
2364 }
2365 }
2366
2367 // Now check for castability.
2368 if (!isServiceAssignable(bundle, ref))
2369 {
2370 refList.remove(refIdx);
2371 refIdx--;
2372 }
2373 }
2374
2375 if (refList.size() > 0)
2376 {
2377 return (ServiceReference[]) refList.toArray(new ServiceReference[refList.size()]);
2378 }
2379
2380 return null;
2381 }
2382
2383 /**
2384 * This method determines if the requesting bundle is able to cast
2385 * the specified service reference based on class visibility rules
2386 * of the underlying modules.
2387 * @param requester The bundle requesting the service.
2388 * @param ref The service in question.
2389 * @return <tt>true</tt> if the requesting bundle is able to case
2390 * the service object to a known type.
2391 **/
2392 protected boolean isServiceAssignable(BundleImpl requester, ServiceReference ref)
2393 {
2394 // Boolean flag.
2395 boolean allow = true;
2396 // Get the service's objectClass property.
2397 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
2398
2399 // The the service reference is not assignable when the requesting
2400 // bundle is wired to a different version of the service object.
2401 // NOTE: We are pessimistic here, if any class in the service's
2402 // objectClass is not usable by the requesting bundle, then we
2403 // disallow the service reference.
2404 for (int classIdx = 0; (allow) && (classIdx < objectClass.length); classIdx++)
2405 {
2406 if (!ref.isAssignableTo(requester, objectClass[classIdx]))
2407 {
2408 allow = false;
2409 }
2410 }
2411 return allow;
2412 }
2413
2414 protected Object getService(Bundle bundle, ServiceReference ref)
2415 {
2416 // Check that the bundle has permission to get at least
2417 // one of the service interfaces; the objectClass property
2418 // of the service stores its service interfaces.
2419 String[] objectClass = (String[])
2420 ref.getProperty(Constants.OBJECTCLASS);
2421 if (objectClass == null)
2422 {
2423 return null;
2424 }
2425
2426 boolean hasPermission = false;
2427 if (System.getSecurityManager() != null)
2428 {
2429 for (int i = 0;
2430 !hasPermission && (i < objectClass.length);
2431 i++)
2432 {
2433 try
2434 {
2435 ServicePermission perm =
2436 new ServicePermission(
2437 objectClass[i], ServicePermission.GET);
2438 AccessController.checkPermission(perm);
2439 hasPermission = true;
2440 }
2441 catch (Exception ex)
2442 {
2443 }
2444 }
2445 }
2446 else
2447 {
2448 hasPermission = true;
2449 }
2450
2451 // If the bundle does not permission to access the service,
2452 // then return null.
2453 if (!hasPermission)
2454 {
2455 return null;
2456 }
2457
2458 return m_registry.getService(bundle, ref);
2459 }
2460
2461 protected boolean ungetService(Bundle bundle, ServiceReference ref)
2462 {
2463 return m_registry.ungetService(bundle, ref);
2464 }
2465
2466 protected File getDataFile(BundleImpl bundle, String s)
2467 {
2468 // The spec says to throw an error if the bundle
2469 // is stopped, which I assume means not active,
2470 // starting, or stopping.
2471 if ((bundle.getInfo().getState() != Bundle.ACTIVE) &&
2472 (bundle.getInfo().getState() != Bundle.STARTING) &&
2473 (bundle.getInfo().getState() != Bundle.STOPPING))
2474 {
2475 throw new IllegalStateException("Only active bundles can create files.");
2476 }
2477 try
2478 {
2479 return m_cache.getArchive(
2480 bundle.getInfo().getBundleId()).getDataFile(s);
2481 }
2482 catch (Exception ex)
2483 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002484 m_logger.log(Logger.LOG_ERROR, ex.getMessage());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002485 return null;
2486 }
2487 }
2488
2489 //
2490 // PackageAdmin related methods.
2491 //
2492
2493 /**
Richard S. Hallc87d41a2006-07-03 07:57:28 +00002494 * This method returns the bundle associated with the specified class if
2495 * the class was loaded from a bundle from this framework instance. If the
2496 * class was not loaded from a bundle or was loaded by a bundle in another
2497 * framework instance, then <tt>null</tt> is returned.
2498 *
2499 * @param clazz the class for which to find its associated bundle.
2500 * @return the bundle associated with the specified class or <tt>null</tt>
2501 * if the class was not loaded by a bundle or its associated
2502 * bundle belongs to a different framework instance.
2503 **/
2504 protected Bundle getBundle(Class clazz)
2505 {
2506 if (clazz.getClassLoader() instanceof ContentClassLoader)
2507 {
2508 IContentLoader contentLoader =
2509 ((ContentClassLoader) clazz.getClassLoader()).getContentLoader();
2510 IModule[] modules = m_factory.getModules();
2511 for (int i = 0; i < modules.length; i++)
2512 {
2513 if (modules[i].getContentLoader() == contentLoader)
2514 {
2515 long id = Util.getBundleIdFromModuleId(modules[i].getId());
2516 return getBundle(id);
2517 }
2518 }
2519 }
2520 return null;
2521 }
2522
2523 /**
Richard S. Hall1bdc3722006-07-03 09:52:07 +00002524 * Returns the exported packages associated with the specified
Richard S. Hallafc52d42006-02-09 13:04:32 +00002525 * package name. This is used by the PackageAdmin service
2526 * implementation.
2527 *
2528 * @param name The name of the exported package to find.
2529 * @return The exported package or null if no matching package was found.
2530 **/
2531 protected ExportedPackage[] getExportedPackages(String name)
2532 {
2533 // First, get all exporters of the package.
2534 ExportedPackage[] pkgs = null;
2535 IModule[] exporters = m_policyCore.getInUseExporters(new R4Import(name, null, null));
Richard S. Hall930fecc2005-08-16 18:33:34 +00002536 if (exporters != null)
2537 {
Richard S. Hall88e0ae62006-02-09 13:22:21 +00002538 pkgs = new ExportedPackage[exporters.length];
Richard S. Hallafc52d42006-02-09 13:04:32 +00002539 for (int pkgIdx = 0; pkgIdx < pkgs.length; pkgIdx++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002540 {
Richard S. Hallafc52d42006-02-09 13:04:32 +00002541 // Get the bundle associated with the current exporting module.
2542 BundleImpl bundle = (BundleImpl) getBundle(
2543 Util.getBundleIdFromModuleId(exporters[pkgIdx].getId()));
2544
2545 // We need to find the version of the exported package, but this
2546 // is tricky since there may be multiple versions of the package
2547 // offered by a given bundle, since multiple revisions of the
2548 // bundle JAR file may exist if the bundle was updated without
2549 // refreshing the framework. In this case, each revision of the
2550 // bundle JAR file is represented as a module in the BundleInfo
2551 // module array, which is ordered from oldest to newest. We assume
2552 // that the first module found to be exporting the package is the
2553 // provider of the package, which makes sense since it must have
2554 // been resolved first.
2555 IModule[] modules = bundle.getInfo().getModules();
2556 for (int modIdx = 0; modIdx < modules.length; modIdx++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002557 {
Richard S. Hallafc52d42006-02-09 13:04:32 +00002558 R4Export export = Util.getExportPackage(modules[modIdx], name);
2559 if (export != null)
2560 {
2561 pkgs[pkgIdx] =
2562 new ExportedPackageImpl(
Richard S. Hall60c26d42006-07-19 10:35:04 +00002563 this, bundle, modules[modIdx], export);
Richard S. Hallafc52d42006-02-09 13:04:32 +00002564 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002565 }
2566 }
2567 }
2568
Richard S. Hallafc52d42006-02-09 13:04:32 +00002569 return pkgs;
Richard S. Hall930fecc2005-08-16 18:33:34 +00002570 }
2571
2572 /**
2573 * Returns an array of all actively exported packages from the specified
2574 * bundle or if the specified bundle is <tt>null</tt> an array
2575 * containing all actively exported packages by all bundles.
2576 *
2577 * @param b The bundle whose exported packages are to be retrieved
2578 * or <tt>null</tt> if the exported packages of all bundles are
2579 * to be retrieved.
2580 * @return An array of exported packages.
2581 **/
2582 protected ExportedPackage[] getExportedPackages(Bundle b)
2583 {
2584 List list = new ArrayList();
2585
2586 // If a bundle is specified, then return its
2587 // exported packages.
2588 if (b != null)
2589 {
2590 BundleImpl bundle = (BundleImpl) b;
2591 getExportedPackages(bundle, list);
2592 }
2593 // Otherwise return all exported packages.
2594 else
2595 {
2596 // To create a list of all exported packages, we must look
2597 // in the installed and uninstalled sets of bundles. To
2598 // ensure a somewhat consistent view, we will gather all
2599 // of this information from within the installed bundle
2600 // lock.
2601 synchronized (m_installedBundleLock_Priority2)
2602 {
2603 // First get exported packages from uninstalled bundles.
2604 synchronized (m_uninstalledBundlesLock_Priority3)
2605 {
2606 for (int bundleIdx = 0;
2607 (m_uninstalledBundles != null) && (bundleIdx < m_uninstalledBundles.length);
2608 bundleIdx++)
2609 {
2610 BundleImpl bundle = m_uninstalledBundles[bundleIdx];
2611 getExportedPackages(bundle, list);
2612 }
2613 }
2614
2615 // Now get exported packages from installed bundles.
2616 Bundle[] bundles = getBundles();
2617 for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
2618 {
2619 BundleImpl bundle = (BundleImpl) bundles[bundleIdx];
2620 getExportedPackages(bundle, list);
2621 }
2622 }
2623 }
2624
2625 return (ExportedPackage[]) list.toArray(new ExportedPackage[list.size()]);
2626 }
2627
2628 /**
2629 * Adds any current active exported packages from the specified bundle
2630 * to the passed in list.
2631 * @param bundle The bundle from which to retrieve exported packages.
2632 * @param list The list to which the exported packages are added
2633 **/
2634 private void getExportedPackages(BundleImpl bundle, List list)
2635 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00002636 // Since a bundle may have many modules associated with it,
2637 // one for each revision in the cache, search each module
2638 // for each revision to get all exports.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002639 IModule[] modules = bundle.getInfo().getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002640 for (int modIdx = 0; modIdx < modules.length; modIdx++)
2641 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002642 R4Export[] exports = m_policyCore.getExports(modules[modIdx]);
2643 if ((exports != null) && (exports.length > 0))
Richard S. Hall930fecc2005-08-16 18:33:34 +00002644 {
2645 for (int expIdx = 0; expIdx < exports.length; expIdx++)
2646 {
2647 // See if the target bundle's module is one of the
2648 // "in use" exporters of the package.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002649 IModule[] inUseModules =
2650 m_policyCore.getInUseExporters(
2651 new R4Import(exports[expIdx].getName(), null, null));
2652 // Search through the current providers to find the target
2653 // module.
2654 for (int i = 0; (inUseModules != null) && (i < inUseModules.length); i++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002655 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002656 if (inUseModules[i] == modules[modIdx])
2657 {
2658 list.add(new ExportedPackageImpl(
Richard S. Hall60c26d42006-07-19 10:35:04 +00002659 this, bundle, modules[modIdx], exports[expIdx]));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002660 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002661 }
2662 }
2663 }
2664 }
2665 }
2666
2667 protected Bundle[] getImportingBundles(ExportedPackage ep)
2668 {
Richard S. Halle1f53a52006-07-17 11:06:59 +00002669 // Get exporting bundle.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002670 BundleImpl exporter = (BundleImpl)
Richard S. Halle1f53a52006-07-17 11:06:59 +00002671 ((ExportedPackage) ep).getExportingBundle();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002672 BundleInfo exporterInfo = exporter.getInfo();
2673
2674 // Create list for storing importing bundles.
2675 List list = new ArrayList();
2676 Bundle[] bundles = getBundles();
2677
2678 // Check all bundles to see who imports the package.
2679 for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
2680 {
2681 BundleImpl importer = (BundleImpl) bundles[bundleIdx];
2682
Richard S. Hall82699422006-07-19 14:37:45 +00002683 // Check the import wires of all modules for all bundles.
2684 IModule[] modules = importer.getInfo().getModules();
2685 for (int modIdx = 0; modIdx < modules.length; modIdx++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002686 {
Richard S. Hall82699422006-07-19 14:37:45 +00002687 R4Wire wire = Util.getWire(modules[modIdx], ep.getName());
2688
2689 // If the resolving module is associated with the
2690 // exporting bundle, then add current bundle to
2691 // import list.
2692 if ((wire != null) && exporterInfo.hasModule(wire.getExportingModule()))
Richard S. Hall930fecc2005-08-16 18:33:34 +00002693 {
Richard S. Hall82699422006-07-19 14:37:45 +00002694 // Add the bundle to the list of importers.
2695 list.add(bundles[bundleIdx]);
2696 break;
Richard S. Hall930fecc2005-08-16 18:33:34 +00002697 }
2698 }
2699 }
2700
2701 // Return the results.
2702 if (list.size() > 0)
2703 {
2704 return (Bundle[]) list.toArray(new Bundle[list.size()]);
2705 }
2706
2707 return null;
2708 }
2709
Richard S. Hall1a4ab602006-05-24 13:46:06 +00002710 protected boolean resolveBundles(Bundle[] targets)
2711 {
2712 if (System.getSecurityManager() != null)
2713 {
2714// TODO: FW SECURITY - Perform proper security check.
2715 AccessController.checkPermission(m_adminPerm);
2716 }
2717
2718 // Acquire locks for all bundles to be resolved.
2719 BundleImpl[] bundles = acquireBundleResolveLocks(targets);
2720
2721 try
2722 {
2723 boolean result = true;
2724
2725 // If there are targets, then resolve each one.
2726 if (bundles != null)
2727 {
2728 for (int i = 0; i < bundles.length; i++)
2729 {
2730 try
2731 {
2732 _resolveBundle(bundles[i]);
2733 }
2734 catch (BundleException ex)
2735 {
2736 result = false;
2737 m_logger.log(
2738 Logger.LOG_WARNING,
2739 "Unable to resolve bundle " + bundles[i].getBundleId(),
2740 ex);
2741 }
2742 }
2743 }
2744
2745 return result;
2746 }
2747 finally
2748 {
2749 // Always release all bundle locks.
2750 releaseBundleLocks(bundles);
2751 }
2752 }
2753
Richard S. Hall930fecc2005-08-16 18:33:34 +00002754 protected void refreshPackages(Bundle[] targets)
2755 {
2756 if (System.getSecurityManager() != null)
2757 {
Richard S. Hall1a4ab602006-05-24 13:46:06 +00002758// TODO: FW SECURITY - Perform proper security check.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002759 AccessController.checkPermission(m_adminPerm);
2760 }
2761
2762 // Acquire locks for all impacted bundles.
2763 BundleImpl[] bundles = acquireBundleRefreshLocks(targets);
2764
2765 // Remove any targeted bundles from the uninstalled bundles
2766 // array, since they will be removed from the system after
2767 // the refresh.
2768 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
2769 {
2770 forgetUninstalledBundle(bundles[i]);
2771 }
2772
2773 try
2774 {
2775 // If there are targets, then refresh each one.
2776 if (bundles != null)
2777 {
2778 // At this point the map contains every bundle that has been
2779 // updated and/or removed as well as all bundles that import
2780 // packages from these bundles.
2781
2782 // Create refresh helpers for each bundle.
2783 RefreshHelper[] helpers = new RefreshHelper[bundles.length];
2784 for (int i = 0; i < bundles.length; i++)
2785 {
2786 helpers[i] = new RefreshHelper(bundles[i]);
2787 }
2788
2789 // Stop, purge or remove, and reinitialize all bundles first.
2790 for (int i = 0; i < helpers.length; i++)
2791 {
2792 helpers[i].stop();
2793 helpers[i].purgeOrRemove();
2794 helpers[i].reinitialize();
2795 }
2796
2797 // Then restart all bundles that were previously running.
2798 for (int i = 0; i < helpers.length; i++)
2799 {
2800 helpers[i].restart();
2801 }
2802 }
2803 }
2804 finally
2805 {
2806 // Always release all bundle locks.
2807 releaseBundleLocks(bundles);
2808 }
2809
2810 fireFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, getBundle(0), null);
2811 }
2812
2813 private void populateImportGraph(BundleImpl target, Map map)
2814 {
2815 // Get the exported packages for the specified bundle.
2816 ExportedPackage[] pkgs = getExportedPackages(target);
2817
2818 for (int pkgIdx = 0; (pkgs != null) && (pkgIdx < pkgs.length); pkgIdx++)
2819 {
2820 // Get all imports of this package.
2821 Bundle[] importers = getImportingBundles(pkgs[pkgIdx]);
2822
2823 for (int impIdx = 0;
2824 (importers != null) && (impIdx < importers.length);
2825 impIdx++)
2826 {
Richard S. Halle34df092005-10-06 17:03:05 +00002827 // Avoid cycles if the bundle is already in map.
2828 if (!map.containsKey(importers[impIdx]))
2829 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00002830 // Add each importing bundle to map.
2831 map.put(importers[impIdx], importers[impIdx]);
2832 // Now recurse into each bundle to get its importers.
2833 populateImportGraph(
2834 (BundleImpl) importers[impIdx], map);
Richard S. Halle34df092005-10-06 17:03:05 +00002835 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002836 }
2837 }
2838 }
2839
2840 //
2841 // Miscellaneous private methods.
2842 //
2843
Richard S. Hall04bdbb12006-03-15 14:26:15 +00002844 private BundleInfo createBundleInfo(BundleArchive archive)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002845 throws Exception
2846 {
2847 // Get the bundle manifest.
2848 Map headerMap = null;
2849 try
2850 {
2851 // Although there should only ever be one revision at this
2852 // point, get the header for the current revision to be safe.
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00002853 headerMap = archive.getRevision(archive.getRevisionCount() - 1).getManifestHeader();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002854 }
2855 catch (Exception ex)
2856 {
2857 throw new BundleException("Unable to read JAR manifest.", ex);
2858 }
2859
2860 // We can't do anything without the manifest header.
2861 if (headerMap == null)
2862 {
2863 throw new BundleException("Unable to read JAR manifest header.");
2864 }
2865
2866 // Create the module for the bundle; although there should only
2867 // ever be one revision at this point, create the module for
2868 // the current revision to be safe.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002869 IModule module = createModule(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002870 archive.getId(), archive.getRevisionCount() - 1, headerMap);
2871
2872 // Finally, create an return the bundle info.
2873 return new BundleInfo(m_logger, archive, module);
2874 }
2875
2876 /**
2877 * Creates a module for a given bundle by reading the bundle's
2878 * manifest meta-data and converting it to work with the underlying
2879 * import/export search policy of the module loader.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002880 * @param targetId The identifier of the bundle for which the module should
Richard S. Hall930fecc2005-08-16 18:33:34 +00002881 * be created.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002882 * @param headerMap The headers map associated with the bundle.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002883 * @return The initialized and/or newly created module.
2884 **/
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002885 private IModule createModule(long targetId, int revision, Map headerMap)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002886 throws Exception
2887 {
2888 // Get the manifest version.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002889 String manifestVersion = (String) headerMap.get(FelixConstants.BUNDLE_MANIFESTVERSION);
2890 manifestVersion = (manifestVersion == null) ? "1" : manifestVersion;
2891 if (!manifestVersion.equals("1") && !manifestVersion.equals("2"))
Richard S. Hall930fecc2005-08-16 18:33:34 +00002892 {
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002893 throw new BundleException("Unknown 'Bundle-ManifestVersion' value: " + manifestVersion);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002894 }
2895
Richard S. Hallb72001a2006-02-20 15:10:33 +00002896 // Create map to check for duplicate imports/exports.
2897 Map dupeMap = new HashMap();
2898
Richard S. Hall930fecc2005-08-16 18:33:34 +00002899 // Get export packages from bundle manifest.
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002900 R4Package[] pkgs = R4Package.parseImportOrExportHeader(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002901 (String) headerMap.get(Constants.EXPORT_PACKAGE));
Richard S. Hallb72001a2006-02-20 15:10:33 +00002902
2903 // Create non-duplicated export array.
2904 dupeMap.clear();
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002905 for (int i = 0; i < pkgs.length; i++)
2906 {
Richard S. Hallb72001a2006-02-20 15:10:33 +00002907 if (dupeMap.get(pkgs[i].getName()) == null)
2908 {
2909 dupeMap.put(pkgs[i].getName(), new R4Export(pkgs[i]));
2910 }
2911 else
2912 {
2913 // TODO: FRAMEWORK - Exports can be duplicated, so fix this.
2914 m_logger.log(Logger.LOG_WARNING,
2915 "Duplicate export - " + pkgs[i].getName());
2916 }
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002917 }
Richard S. Hallb72001a2006-02-20 15:10:33 +00002918 R4Export[] exports =
2919 (R4Export[]) dupeMap.values().toArray(new R4Export[dupeMap.size()]);
2920
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002921 // Get import packages from bundle manifest.
2922 pkgs = R4Package.parseImportOrExportHeader(
2923 (String) headerMap.get(Constants.IMPORT_PACKAGE));
Richard S. Hall930fecc2005-08-16 18:33:34 +00002924
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002925 // Create non-duplicated import array.
2926 dupeMap.clear();
2927 for (int i = 0; i < pkgs.length; i++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002928 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002929 if (dupeMap.get(pkgs[i].getName()) == null)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002930 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002931 dupeMap.put(pkgs[i].getName(), new R4Import(pkgs[i]));
Richard S. Hall930fecc2005-08-16 18:33:34 +00002932 }
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002933 else
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002934 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002935 // TODO: FRAMEWORK - Determine if we should error here.
2936 m_logger.log(Logger.LOG_WARNING,
2937 "Duplicate import - " + pkgs[i].getName());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002938 }
2939 }
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002940 R4Import[] imports =
2941 (R4Import[]) dupeMap.values().toArray(new R4Import[dupeMap.size()]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002942
Richard S. Hall930fecc2005-08-16 18:33:34 +00002943 // Get dynamic import packages from bundle manifest.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002944 pkgs = R4Package.parseImportOrExportHeader(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002945 (String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
Richard S. Hallb72001a2006-02-20 15:10:33 +00002946
2947 // Create non-duplicated dynamic import array.
2948 dupeMap.clear();
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002949 for (int i = 0; i < pkgs.length; i++)
2950 {
Richard S. Hallb72001a2006-02-20 15:10:33 +00002951 if (dupeMap.get(pkgs[i].getName()) == null)
2952 {
2953 dupeMap.put(pkgs[i].getName(), new R4Import(pkgs[i]));
2954 }
2955 else
2956 {
2957 // TODO: FRAMEWORK - Determine if we should error here.
2958 m_logger.log(Logger.LOG_WARNING,
2959 "Duplicate import - " + pkgs[i].getName());
2960 }
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002961 }
Richard S. Hallb72001a2006-02-20 15:10:33 +00002962 R4Import[] dynamics =
2963 (R4Import[]) dupeMap.values().toArray(new R4Import[dupeMap.size()]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002964
Richard S. Hall8bebf412006-07-03 12:54:50 +00002965 // Do some validity checking on bundles with R4 headers.
Richard S. Hallc6c4e7e2006-07-03 14:39:55 +00002966// TODO: FRAMEWORK - Perhaps these verifications and conversions can be done more efficiently.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002967 if (manifestVersion.equals("2"))
Richard S. Hall8bebf412006-07-03 12:54:50 +00002968 {
2969 // Verify that bundle symbolic name is specified.
2970 String targetSym = (String) headerMap.get(FelixConstants.BUNDLE_SYMBOLICNAME);
2971 if (targetSym == null)
2972 {
2973 throw new BundleException("R4 bundle manifests must include bundle symbolic name.");
2974 }
Richard S. Hallc6c4e7e2006-07-03 14:39:55 +00002975
Richard S. Hall8bebf412006-07-03 12:54:50 +00002976 // Verify that the bundle symbolic name and version is unique.
2977 String targetVer = (String) headerMap.get(FelixConstants.BUNDLE_VERSION);
2978 targetVer = (targetVer == null) ? "0.0.0" : targetVer;
2979 Bundle[] bundles = getBundles();
2980 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
2981 {
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002982 long id = ((BundleImpl) bundles[i]).getInfo().getBundleId();
Richard S. Hall8bebf412006-07-03 12:54:50 +00002983 String sym = (String) ((BundleImpl) bundles[i])
2984 .getInfo().getCurrentHeader().get(Constants.BUNDLE_SYMBOLICNAME);
2985 String ver = (String) ((BundleImpl) bundles[i])
2986 .getInfo().getCurrentHeader().get(Constants.BUNDLE_VERSION);
2987 ver = (ver == null) ? "0.0.0" : ver;
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002988 if (targetSym.equals(sym) && targetVer.equals(ver) && (targetId != id))
Richard S. Hall8bebf412006-07-03 12:54:50 +00002989 {
2990 throw new BundleException("Bundle symbolic name and version are not unique.");
2991 }
2992 }
Richard S. Hallc6c4e7e2006-07-03 14:39:55 +00002993
2994 // Need to add symbolic name and bundle version to all R4 exports.
2995 for (int i = 0; (exports != null) && (i < exports.length); i++)
2996 {
2997 R4Attribute[] attrs = exports[i].getAttributes();
2998 R4Attribute[] newAttrs = new R4Attribute[attrs.length + 2];
2999 System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
3000 newAttrs[attrs.length] = new R4Attribute(
3001 Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, targetSym, false);
3002 newAttrs[attrs.length + 1] = new R4Attribute(
3003 Constants.BUNDLE_VERSION_ATTRIBUTE, targetVer, false);
3004 exports[i] = new R4Export(
3005 exports[i].getName(), exports[i].getDirectives(), newAttrs);
3006 }
Richard S. Hall8bebf412006-07-03 12:54:50 +00003007 }
3008 // Do some validity checking and conversion on bundles with R3 headers.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003009 else if (manifestVersion.equals("1"))
Richard S. Hall930fecc2005-08-16 18:33:34 +00003010 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003011 // Check to make sure that R3 bundles have only specified
3012 // the 'specification-version' attribute and no directives
3013 // on their exports.
3014 for (int i = 0; (exports != null) && (i < exports.length); i++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00003015 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003016 if (exports[i].getDirectives().length != 0)
Richard S. Hall930fecc2005-08-16 18:33:34 +00003017 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003018 throw new BundleException("R3 exports cannot contain directives.");
Richard S. Hall930fecc2005-08-16 18:33:34 +00003019 }
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003020 // NOTE: This is checking for "version" rather than "specification-version"
3021 // because the package class normalizes to "version" to avoid having
3022 // future special cases. This could be changed if more strict behavior
3023 // is required.
3024 if ((exports[i].getAttributes().length > 1) ||
3025 ((exports[i].getAttributes().length == 1) &&
3026 (!exports[i].getAttributes()[0].getName().equals(FelixConstants.VERSION_ATTRIBUTE))))
Richard S. Hall930fecc2005-08-16 18:33:34 +00003027 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003028 throw new BundleException(
Richard S. Hall4c7d68e2006-07-11 13:15:47 +00003029 "Export does not conform to R3 syntax: " + exports[i]);
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003030 }
3031 }
3032
3033 // Check to make sure that R3 bundles have only specified
3034 // the 'specification-version' attribute and no directives
3035 // on their imports.
3036 for (int i = 0; (imports != null) && (i < imports.length); i++)
3037 {
3038 if (imports[i].getDirectives().length != 0)
3039 {
3040 throw new BundleException("R3 imports cannot contain directives.");
3041 }
3042 // NOTE: This is checking for "version" rather than "specification-version"
3043 // because the package class normalizes to "version" to avoid having
3044 // future special cases. This could be changed if more strict behavior
3045 // is required.
3046 if ((imports[i].getVersionHigh() != null) ||
3047 (imports[i].getAttributes().length > 1) ||
3048 ((imports[i].getAttributes().length == 1) &&
3049 (!imports[i].getAttributes()[0].getName().equals(FelixConstants.VERSION_ATTRIBUTE))))
3050 {
3051 throw new BundleException(
3052 "Import does not conform to R3 syntax: " + imports[i]);
3053 }
3054 }
3055
3056 // Since all R3 exports imply an import, add a corresponding
3057 // import for each existing export. Create non-duplicated import array.
3058 dupeMap.clear();
3059 // Add existing imports.
3060 for (int i = 0; i < imports.length; i++)
3061 {
3062 dupeMap.put(imports[i].getName(), imports[i]);
3063 }
3064 // Add import for each export.
3065 for (int i = 0; i < exports.length; i++)
3066 {
3067 if (dupeMap.get(exports[i].getName()) == null)
3068 {
3069 dupeMap.put(exports[i].getName(), new R4Import(exports[i]));
3070 }
3071 }
3072 imports =
3073 (R4Import[]) dupeMap.values().toArray(new R4Import[dupeMap.size()]);
3074
3075 // Add a "uses" directive onto each export of R3 bundles
3076 // that references every other import (which will include
3077 // exports, since export implies import); this is
3078 // necessary since R3 bundles assumed a single class space,
3079 // but R4 allows for multiple class spaces.
3080 String usesValue = "";
3081 for (int i = 0; (imports != null) && (i < imports.length); i++)
3082 {
3083 usesValue = usesValue
3084 + ((usesValue.length() > 0) ? "," : "")
3085 + imports[i].getName();
3086 }
3087 R4Directive uses = new R4Directive(
3088 FelixConstants.USES_DIRECTIVE, usesValue);
3089 for (int i = 0; (exports != null) && (i < exports.length); i++)
3090 {
3091 exports[i] = new R4Export(
3092 exports[i].getName(),
3093 new R4Directive[] { uses },
3094 exports[i].getAttributes());
3095 }
3096
3097 // Check to make sure that R3 bundles have no attributes or
3098 // directives on their dynamic imports.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003099 for (int i = 0; (dynamics != null) && (i < dynamics.length); i++)
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003100 {
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003101 if (dynamics[i].getDirectives().length != 0)
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003102 {
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003103 throw new BundleException("R3 dynamic imports cannot contain directives.");
3104 }
3105 if (dynamics[i].getAttributes().length != 0)
3106 {
3107 throw new BundleException("R3 dynamic imports cannot contain attributes.");
Richard S. Hall930fecc2005-08-16 18:33:34 +00003108 }
3109 }
3110 }
3111
Richard S. Hall930fecc2005-08-16 18:33:34 +00003112 // Get native library entry names for module library sources.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003113 R4LibraryHeader[] libraryHeaders =
Richard S. Hall930fecc2005-08-16 18:33:34 +00003114 Util.parseLibraryStrings(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003115 m_logger,
Richard S. Hall930fecc2005-08-16 18:33:34 +00003116 Util.parseDelimitedString(
3117 (String) headerMap.get(Constants.BUNDLE_NATIVECODE), ","));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003118 R4Library[] libraries = new R4Library[libraryHeaders.length];
3119 for (int i = 0; i < libraries.length; i++)
3120 {
3121 libraries[i] = new R4Library(
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003122 m_logger, m_cache, targetId, revision,
Richard S. Hall930fecc2005-08-16 18:33:34 +00003123 getProperty(Constants.FRAMEWORK_OS_NAME),
3124 getProperty(Constants.FRAMEWORK_PROCESSOR),
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003125 libraryHeaders[i]);
3126 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00003127
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003128 // Now that we have all of the metadata associated with the
3129 // module, we need to create the module itself. This is somewhat
3130 // complicated because a module is constructed out of several
3131 // interrelated pieces (e.g., content loader, search policy,
3132 // url policy). We need to create all of these pieces and bind
3133 // them together.
Richard S. Hall930fecc2005-08-16 18:33:34 +00003134
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003135 // First, create the module.
3136 IModule module = m_factory.createModule(
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003137 Long.toString(targetId) + "." + Integer.toString(revision));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003138 // Attach the R4 search policy metadata to the module.
3139 m_policyCore.setExports(module, exports);
3140 m_policyCore.setImports(module, imports);
3141 m_policyCore.setDynamicImports(module, dynamics);
3142 m_policyCore.setLibraries(module, libraries);
3143
3144 // Create the content loader associated with the module archive.
3145 IContentLoader contentLoader = new ContentLoaderImpl(
3146 m_logger,
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003147 m_cache.getArchive(targetId).getRevision(revision).getContent(),
3148 m_cache.getArchive(targetId).getRevision(revision).getContentPath());
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003149 // Set the content loader's search policy.
3150 contentLoader.setSearchPolicy(
3151 new R4SearchPolicy(m_policyCore, module));
3152 // Set the content loader's URL policy.
3153 contentLoader.setURLPolicy(
Richard S. Hallfb5221e2006-02-20 10:22:28 +00003154// TODO: ML - SUCKS NEEDING URL POLICY PER MODULE.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003155 new URLPolicyImpl(
3156 m_logger, m_bundleStreamHandler, module));
3157
3158 // Set the module's content loader to the created content loader.
3159 m_factory.setContentLoader(module, contentLoader);
3160
3161 // Done, so return the module.
Richard S. Hall930fecc2005-08-16 18:33:34 +00003162 return module;
3163 }
3164
3165 private BundleActivator createBundleActivator(BundleInfo info)
3166 throws Exception
3167 {
3168 // CONCURRENCY NOTE:
3169 // This method is called indirectly from startBundle() (via _startBundle()),
3170 // which has the exclusion lock, so there is no need to do any locking here.
3171
3172 BundleActivator activator = null;
3173
3174 String strict = m_config.get(FelixConstants.STRICT_OSGI_PROP);
3175 boolean isStrict = (strict == null) ? true : strict.equals("true");
3176 if (!isStrict)
3177 {
3178 try
3179 {
3180 activator =
3181 m_cache.getArchive(info.getBundleId())
Richard S. Hallf1359482006-02-14 08:02:51 +00003182 .getActivator(info.getCurrentModule());
Richard S. Hall930fecc2005-08-16 18:33:34 +00003183 }
3184 catch (Exception ex)
3185 {
3186 activator = null;
3187 }
3188 }
3189
3190 // If there was no cached activator, then get the activator
3191 // class from the bundle manifest.
3192 if (activator == null)
3193 {
3194 // Get the associated bundle archive.
Richard S. Hall04bdbb12006-03-15 14:26:15 +00003195 BundleArchive ba = m_cache.getArchive(info.getBundleId());
Richard S. Hall930fecc2005-08-16 18:33:34 +00003196 // Get the manifest from the current revision; revision is
3197 // base zero so subtract one from the count to get the
3198 // current revision.
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00003199 Map headerMap = ba.getRevision(ba.getRevisionCount() - 1).getManifestHeader();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003200 // Get the activator class attribute.
3201 String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
3202 // Try to instantiate activator class if present.
3203 if (className != null)
3204 {
3205 className = className.trim();
Richard S. Hall6b5f96c2006-02-10 15:57:15 +00003206 Class clazz = info.getCurrentModule().getClass(className);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003207 if (clazz == null)
3208 {
3209 throw new BundleException("Not found: "
Richard S. Hall56c68832006-06-23 15:27:31 +00003210 + className, new ClassNotFoundException(className));
Richard S. Hall930fecc2005-08-16 18:33:34 +00003211 }
3212 activator = (BundleActivator) clazz.newInstance();
3213 }
3214 }
3215
3216 return activator;
3217 }
3218
3219 private void purgeBundle(BundleImpl bundle) throws Exception
3220 {
3221 // Acquire bundle lock.
3222 acquireBundleLock(bundle);
3223
3224 try
3225 {
3226 BundleInfo info = bundle.getInfo();
3227
3228 // In case of a refresh, then we want to physically
3229 // remove the bundle's modules from the module manager.
3230 // This is necessary for two reasons: 1) because
3231 // under Windows we won't be able to delete the bundle
3232 // because files might be left open in the resource
3233 // sources of its modules and 2) we want to make sure
3234 // that no references to old modules exist since they
3235 // will all be stale after the refresh. The only other
3236 // way to do this is to remove the bundle, but that
3237 // would be incorrect, because this is a refresh operation
3238 // and should not trigger bundle REMOVE events.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003239 IModule[] modules = info.getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003240 for (int i = 0; i < modules.length; i++)
3241 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003242 m_factory.removeModule(modules[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003243 }
3244
3245 // Purge all bundle revisions, but the current one.
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00003246 m_cache.getArchive(info.getBundleId()).purge();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003247 }
3248 finally
3249 {
3250 // Always release the bundle lock.
3251 releaseBundleLock(bundle);
3252 }
3253 }
3254
3255 private void garbageCollectBundle(BundleImpl bundle) throws Exception
3256 {
3257 // CONCURRENCY NOTE: There is no reason to lock this bundle,
3258 // because this method is only called during shutdown or a
3259 // refresh operation and these are already guarded by locks.
3260
3261 // Remove the bundle's associated modules from
3262 // the module manager.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003263 IModule[] modules = bundle.getInfo().getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003264 for (int i = 0; i < modules.length; i++)
3265 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003266 m_factory.removeModule(modules[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003267 }
3268
3269 // Remove the bundle from the cache.
3270 m_cache.remove(m_cache.getArchive(bundle.getInfo().getBundleId()));
3271 }
3272
3273 //
3274 // Event-related methods.
3275 //
3276
3277 /**
3278 * Fires bundle events.
3279 **/
3280 private void fireFrameworkEvent(
3281 int type, Bundle bundle, Throwable throwable)
3282 {
3283 if (m_frameworkDispatcher == null)
3284 {
3285 m_frameworkDispatcher = new Dispatcher() {
3286 public void dispatch(EventListener l, EventObject eventObj)
3287 {
3288 ((FrameworkListener) l)
3289 .frameworkEvent((FrameworkEvent) eventObj);
3290 }
3291 };
3292 }
3293 FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
3294 m_dispatchQueue.dispatch(
3295 m_frameworkDispatcher, FrameworkListener.class, event);
3296 }
3297
3298 /**
3299 * Fires bundle events.
3300 *
3301 * @param type The type of bundle event to fire.
3302 * @param bundle The bundle associated with the event.
3303 **/
3304 private void fireBundleEvent(int type, Bundle bundle)
3305 {
3306 if (m_bundleDispatcher == null)
3307 {
3308 m_bundleDispatcher = new Dispatcher() {
3309 public void dispatch(EventListener l, EventObject eventObj)
3310 {
3311 ((BundleListener) l)
3312 .bundleChanged((BundleEvent) eventObj);
3313 }
3314 };
3315 }
3316 BundleEvent event = null;
3317 event = new BundleEvent(type, bundle);
3318 m_dispatchQueue.dispatch(m_bundleDispatcher,
3319 BundleListener.class, event);
3320 }
3321
3322 /**
3323 * Fires service events.
3324 *
3325 * @param type The type of service event to fire.
3326 * @param ref The service reference associated with the event.
3327 **/
3328 private void fireServiceEvent(ServiceEvent event)
3329 {
3330 if (m_serviceDispatcher == null)
3331 {
3332 m_serviceDispatcher = new Dispatcher() {
3333 public void dispatch(EventListener l, EventObject eventObj)
3334 {
3335// TODO: Filter service events based on service permissions.
3336 if (l instanceof ListenerWrapper)
3337 {
3338 BundleImpl bundle = (BundleImpl) ((ServiceListenerWrapper) l).getBundle();
3339 if (isServiceAssignable(bundle, ((ServiceEvent) eventObj).getServiceReference()))
3340 {
3341 ((ServiceListener) l)
3342 .serviceChanged((ServiceEvent) eventObj);
3343 }
3344 }
3345 else
3346 {
3347 ((ServiceListener) l)
3348 .serviceChanged((ServiceEvent) eventObj);
3349 }
3350 }
3351 };
3352 }
3353 m_dispatchQueue.dispatch(m_serviceDispatcher,
3354 ServiceListener.class, event);
3355 }
3356
3357 //
3358 // Property related methods.
3359 //
3360
3361 private void initializeFrameworkProperties()
3362 {
3363 // Standard OSGi properties.
Richard S. Hallea415752005-12-05 19:30:28 +00003364 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003365 FelixConstants.FRAMEWORK_VERSION,
3366 FelixConstants.FRAMEWORK_VERSION_VALUE);
Richard S. Hallea415752005-12-05 19:30:28 +00003367 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003368 FelixConstants.FRAMEWORK_VENDOR,
3369 FelixConstants.FRAMEWORK_VENDOR_VALUE);
Richard S. Hallea415752005-12-05 19:30:28 +00003370 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003371 FelixConstants.FRAMEWORK_LANGUAGE,
3372 System.getProperty("user.language"));
Richard S. Hallea415752005-12-05 19:30:28 +00003373 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003374 FelixConstants.FRAMEWORK_OS_VERSION,
3375 System.getProperty("os.version"));
3376
3377 String s = null;
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003378 s = R4Library.normalizePropertyValue(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003379 FelixConstants.FRAMEWORK_OS_NAME,
3380 System.getProperty("os.name"));
Richard S. Hallea415752005-12-05 19:30:28 +00003381 m_configMutable.put(FelixConstants.FRAMEWORK_OS_NAME, s);
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003382 s = R4Library.normalizePropertyValue(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003383 FelixConstants.FRAMEWORK_PROCESSOR,
3384 System.getProperty("os.arch"));
Richard S. Hallea415752005-12-05 19:30:28 +00003385 m_configMutable.put(FelixConstants.FRAMEWORK_PROCESSOR, s);
Richard S. Hallea415752005-12-05 19:30:28 +00003386 m_configMutable.put(
Richard S. Hall03ddc842006-03-09 14:50:16 +00003387 FelixConstants.FELIX_VERSION_PROPERTY, getFrameworkVersion());
Richard S. Hall930fecc2005-08-16 18:33:34 +00003388 }
3389
Richard S. Hall03ddc842006-03-09 14:50:16 +00003390 /**
3391 * Read the framework version from the property file.
3392 * @return the framework version as a string.
3393 **/
3394 private static String getFrameworkVersion()
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003395 {
3396 // The framework version property.
3397 Properties props = new Properties();
Richard S. Hall03ddc842006-03-09 14:50:16 +00003398 InputStream in = Felix.class.getResourceAsStream("Felix.properties");
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003399 try
3400 {
Richard S. Hall03ddc842006-03-09 14:50:16 +00003401 props.load(in);
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003402 }
Richard S. Hall03ddc842006-03-09 14:50:16 +00003403 catch (IOException ex)
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003404 {
Richard S. Hall03ddc842006-03-09 14:50:16 +00003405 ex.printStackTrace();
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003406 }
Richard S. Halla155e2e2006-03-09 21:20:02 +00003407
3408 // Maven uses a '-' to separate the version qualifier,
3409 // while OSGi uses a '.', so we need to convert to a '.'
3410 StringBuffer sb =
3411 new StringBuffer(
3412 props.getProperty(
3413 FelixConstants.FELIX_VERSION_PROPERTY, "unknown"));
Richard S. Hall1615e552006-05-30 14:10:06 +00003414 if (sb.toString().indexOf("-") >= 0)
Richard S. Halla155e2e2006-03-09 21:20:02 +00003415 {
Richard S. Hall1615e552006-05-30 14:10:06 +00003416 sb.setCharAt(sb.toString().indexOf("-"), '.');
Richard S. Halla155e2e2006-03-09 21:20:02 +00003417 }
3418 return sb.toString();
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003419 }
3420
Richard S. Hall930fecc2005-08-16 18:33:34 +00003421 private void processAutoProperties()
3422 {
3423 // The auto-install property specifies a space-delimited list of
3424 // bundle URLs to be automatically installed into each new profile;
3425 // the start level to which the bundles are assigned is specified by
3426 // appending a ".n" to the auto-install property name, where "n" is
3427 // the desired start level for the list of bundles.
3428 String[] keys = m_config.getKeys();
3429 for (int i = 0; (keys != null) && (i < keys.length); i++)
3430 {
3431 if (keys[i].startsWith(FelixConstants.AUTO_INSTALL_PROP))
3432 {
3433 int startLevel = 1;
3434 try
3435 {
3436 startLevel = Integer.parseInt(keys[i].substring(keys[i].lastIndexOf('.') + 1));
3437 }
3438 catch (NumberFormatException ex)
3439 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003440 m_logger.log(Logger.LOG_ERROR, "Invalid property: " + keys[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003441 }
3442 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3443 if (st.countTokens() > 0)
3444 {
3445 String location = null;
3446 do
3447 {
3448 location = nextLocation(st);
3449 if (location != null)
3450 {
3451 try
3452 {
3453 BundleImpl b = (BundleImpl) installBundle(location, null);
3454 b.getInfo().setStartLevel(startLevel);
3455 }
3456 catch (Exception ex)
3457 {
3458 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003459 Logger.LOG_ERROR, "Auto-properties install.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003460 }
3461 }
3462 }
3463 while (location != null);
3464 }
3465 }
3466 }
3467
3468 // The auto-start property specifies a space-delimited list of
3469 // bundle URLs to be automatically installed and started into each
3470 // new profile; the start level to which the bundles are assigned
3471 // is specified by appending a ".n" to the auto-start property name,
3472 // where "n" is the desired start level for the list of bundles.
3473 // The following code starts bundles in two passes, first it installs
3474 // them, then it starts them.
3475 for (int i = 0; (keys != null) && (i < keys.length); i++)
3476 {
3477 if (keys[i].startsWith(FelixConstants.AUTO_START_PROP))
3478 {
3479 int startLevel = 1;
3480 try
3481 {
3482 startLevel = Integer.parseInt(keys[i].substring(keys[i].lastIndexOf('.') + 1));
3483 }
3484 catch (NumberFormatException ex)
3485 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003486 m_logger.log(Logger.LOG_ERROR, "Invalid property: " + keys[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003487 }
3488 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3489 if (st.countTokens() > 0)
3490 {
3491 String location = null;
3492 do
3493 {
3494 location = nextLocation(st);
3495 if (location != null)
3496 {
3497 try
3498 {
3499 BundleImpl b = (BundleImpl) installBundle(location, null);
3500 b.getInfo().setStartLevel(startLevel);
3501 }
3502 catch (Exception ex)
3503 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003504 m_logger.log(Logger.LOG_ERROR, "Auto-properties install.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003505 }
3506 }
3507 }
3508 while (location != null);
3509 }
3510 }
3511 }
3512
3513 // Now loop through and start the installed bundles.
3514 for (int i = 0; (keys != null) && (i < keys.length); i++)
3515 {
3516 if (keys[i].startsWith(FelixConstants.AUTO_START_PROP))
3517 {
3518 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3519 if (st.countTokens() > 0)
3520 {
3521 String location = null;
3522 do
3523 {
3524 location = nextLocation(st);
3525 if (location != null)
3526 {
3527 // Installing twice just returns the same bundle.
3528 try
3529 {
3530 BundleImpl bundle = (BundleImpl) installBundle(location, null);
3531 if (bundle != null)
3532 {
3533 startBundle(bundle, true);
3534 }
3535 }
3536 catch (Exception ex)
3537 {
3538 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003539 Logger.LOG_ERROR, "Auto-properties start.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003540 }
3541 }
3542 }
3543 while (location != null);
3544 }
3545 }
3546 }
3547 }
3548
3549 private String nextLocation(StringTokenizer st)
3550 {
3551 String retVal = null;
3552
3553 if (st.countTokens() > 0)
3554 {
3555 String tokenList = "\" ";
3556 StringBuffer tokBuf = new StringBuffer(10);
3557 String tok = null;
3558 boolean inQuote = false;
3559 boolean tokStarted = false;
3560 boolean exit = false;
3561 while ((st.hasMoreTokens()) && (!exit))
3562 {
3563 tok = st.nextToken(tokenList);
3564 if (tok.equals("\""))
3565 {
3566 inQuote = ! inQuote;
3567 if (inQuote)
3568 {
3569 tokenList = "\"";
3570 }
3571 else
3572 {
3573 tokenList = "\" ";
3574 }
3575
3576 }
3577 else if (tok.equals(" "))
3578 {
3579 if (tokStarted)
3580 {
3581 retVal = tokBuf.toString();
3582 tokStarted=false;
3583 tokBuf = new StringBuffer(10);
3584 exit = true;
3585 }
3586 }
3587 else
3588 {
3589 tokStarted = true;
3590 tokBuf.append(tok.trim());
3591 }
3592 }
3593
3594 // Handle case where end of token stream and
3595 // still got data
3596 if ((!exit) && (tokStarted))
3597 {
3598 retVal = tokBuf.toString();
3599 }
3600 }
3601
3602 return retVal;
3603 }
3604
3605 //
3606 // Private utility methods.
3607 //
3608
3609 /**
3610 * Generated the next valid bundle identifier.
3611 **/
Richard S. Hall441c7152006-02-17 11:07:10 +00003612 private long getNextId()
Richard S. Hall930fecc2005-08-16 18:33:34 +00003613 {
Richard S. Hall441c7152006-02-17 11:07:10 +00003614 synchronized (m_nextIdLock)
3615 {
3616 return m_nextId++;
3617 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00003618 }
3619
3620 //
3621 // Configuration methods and inner classes.
3622 //
3623
3624 public PropertyResolver getConfig()
3625 {
3626 return m_config;
3627 }
3628
3629 private class ConfigImpl implements PropertyResolver
3630 {
3631 public String get(String key)
3632 {
Richard S. Hallea415752005-12-05 19:30:28 +00003633 return (m_configMutable == null) ? null : m_configMutable.get(key);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003634 }
3635
3636 public String[] getKeys()
3637 {
Richard S. Hallea415752005-12-05 19:30:28 +00003638 return m_configMutable.getKeys();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003639 }
3640 }
3641
3642 //
3643 // Logging methods and inner classes.
3644 //
3645
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003646 public Logger getLogger()
Richard S. Hall930fecc2005-08-16 18:33:34 +00003647 {
3648 return m_logger;
3649 }
3650
3651 /**
3652 * Simple class that is used in <tt>refreshPackages()</tt> to embody
3653 * the refresh logic in order to keep the code clean. This class is
3654 * not static because it needs access to framework event firing methods.
3655 **/
3656 private class RefreshHelper
3657 {
3658 private BundleImpl m_bundle = null;
3659
3660 public RefreshHelper(Bundle bundle)
3661 {
3662 m_bundle = (BundleImpl) bundle;
3663 }
3664
3665 public void stop()
3666 {
3667 if (m_bundle.getInfo().getState() == Bundle.ACTIVE)
3668 {
3669 try
3670 {
3671 stopBundle(m_bundle, false);
3672 }
3673 catch (BundleException ex)
3674 {
3675 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3676 }
3677 }
3678 }
3679
3680 public void purgeOrRemove()
3681 {
3682 try
3683 {
3684 BundleInfo info = m_bundle.getInfo();
3685
Richard S. Halle1f53a52006-07-17 11:06:59 +00003686 // Mark the bundle as stale.
3687 info.setStale();
3688
Richard S. Hall930fecc2005-08-16 18:33:34 +00003689 // Remove or purge the bundle depending on its
3690 // current state.
3691 if (info.getState() == Bundle.UNINSTALLED)
3692 {
3693 // This physically removes the bundle from memory
3694 // as well as the bundle cache.
3695 garbageCollectBundle(m_bundle);
3696 m_bundle = null;
3697 }
3698 else
3699 {
3700 // This physically removes all old revisions of the
3701 // bundle from memory and only maintains the newest
3702 // version in the bundle cache.
3703 purgeBundle(m_bundle);
3704 }
3705 }
3706 catch (Exception ex)
3707 {
3708 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3709 }
3710 }
3711
3712 public void reinitialize()
3713 {
3714 if (m_bundle != null)
3715 {
3716 try
3717 {
3718 BundleInfo info = m_bundle.getInfo();
3719 BundleInfo newInfo = createBundleInfo(info.getArchive());
3720 newInfo.syncLock(info);
3721 m_bundle.setInfo(newInfo);
3722 }
3723 catch (Exception ex)
3724 {
3725 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3726 }
3727 }
3728 }
3729
3730 public void restart()
3731 {
3732 if (m_bundle != null)
3733 {
3734 try
3735 {
3736 startBundle(m_bundle, false);
3737 }
3738 catch (BundleException ex)
3739 {
3740 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3741 }
3742 }
3743 }
3744 }
3745
3746 //
3747 // Locking related methods.
3748 //
3749
3750 private void rememberUninstalledBundle(BundleImpl bundle)
3751 {
3752 synchronized (m_uninstalledBundlesLock_Priority3)
3753 {
3754 // Verify that the bundle is not already in the array.
3755 for (int i = 0;
3756 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
3757 i++)
3758 {
3759 if (m_uninstalledBundles[i] == bundle)
3760 {
3761 return;
3762 }
3763 }
3764
3765 if (m_uninstalledBundles != null)
3766 {
3767 BundleImpl[] newBundles =
3768 new BundleImpl[m_uninstalledBundles.length + 1];
3769 System.arraycopy(m_uninstalledBundles, 0,
3770 newBundles, 0, m_uninstalledBundles.length);
3771 newBundles[m_uninstalledBundles.length] = bundle;
3772 m_uninstalledBundles = newBundles;
3773 }
3774 else
3775 {
3776 m_uninstalledBundles = new BundleImpl[] { bundle };
3777 }
3778 }
3779 }
3780
3781 private void forgetUninstalledBundle(BundleImpl bundle)
3782 {
3783 synchronized (m_uninstalledBundlesLock_Priority3)
3784 {
3785 if (m_uninstalledBundles == null)
3786 {
3787 return;
3788 }
3789
3790 int idx = -1;
3791 for (int i = 0; i < m_uninstalledBundles.length; i++)
3792 {
3793 if (m_uninstalledBundles[i] == bundle)
3794 {
3795 idx = i;
3796 break;
3797 }
3798 }
3799
3800 if (idx >= 0)
3801 {
3802 // If this is the only bundle, then point to empty list.
3803 if ((m_uninstalledBundles.length - 1) == 0)
3804 {
3805 m_uninstalledBundles = new BundleImpl[0];
3806 }
3807 // Otherwise, we need to do some array copying.
3808 else
3809 {
3810 BundleImpl[] newBundles =
3811 new BundleImpl[m_uninstalledBundles.length - 1];
3812 System.arraycopy(m_uninstalledBundles, 0, newBundles, 0, idx);
3813 if (idx < newBundles.length)
3814 {
3815 System.arraycopy(
3816 m_uninstalledBundles, idx + 1,
3817 newBundles, idx, newBundles.length - idx);
3818 }
3819 m_uninstalledBundles = newBundles;
3820 }
3821 }
3822 }
3823 }
3824
3825 protected void acquireInstallLock(String location)
3826 throws BundleException
3827 {
3828 synchronized (m_installRequestLock_Priority1)
3829 {
3830 while (m_installRequestMap.get(location) != null)
3831 {
3832 try
3833 {
3834 m_installRequestLock_Priority1.wait();
3835 }
3836 catch (InterruptedException ex)
3837 {
3838 throw new BundleException("Unable to install, thread interrupted.");
3839 }
3840 }
3841
3842 m_installRequestMap.put(location, location);
3843 }
3844 }
3845
3846 protected void releaseInstallLock(String location)
3847 {
3848 synchronized (m_installRequestLock_Priority1)
3849 {
3850 m_installRequestMap.remove(location);
3851 m_installRequestLock_Priority1.notifyAll();
3852 }
3853 }
3854
3855 protected void acquireBundleLock(BundleImpl bundle)
Richard S. Hall930fecc2005-08-16 18:33:34 +00003856 {
3857 synchronized (m_bundleLock)
3858 {
3859 while (!bundle.getInfo().isLockable())
3860 {
3861 try
3862 {
3863 m_bundleLock.wait();
3864 }
3865 catch (InterruptedException ex)
3866 {
3867 // Ignore and just keep waiting.
3868 }
3869 }
3870 bundle.getInfo().lock();
3871 }
3872 }
3873
Richard S. Hall3c26cc02006-02-17 13:51:21 +00003874 protected boolean acquireBundleLockOrFail(BundleImpl bundle)
3875 {
3876 synchronized (m_bundleLock)
3877 {
3878 if (!bundle.getInfo().isLockable())
3879 {
3880 return false;
3881 }
3882 bundle.getInfo().lock();
3883 return true;
3884 }
3885 }
3886
Richard S. Hall930fecc2005-08-16 18:33:34 +00003887 protected void releaseBundleLock(BundleImpl bundle)
3888 {
3889 synchronized (m_bundleLock)
3890 {
3891 bundle.getInfo().unlock();
3892 m_bundleLock.notifyAll();
3893 }
3894 }
3895
Richard S. Hall1a4ab602006-05-24 13:46:06 +00003896 protected BundleImpl[] acquireBundleResolveLocks(Bundle[] targets)
3897 {
3898 // Hold bundles to be locked.
3899 BundleImpl[] bundles = null;
3900 // Convert existing target bundle array to bundle impl array.
3901 if (targets != null)
3902 {
3903 bundles = new BundleImpl[targets.length];
3904 for (int i = 0; i < targets.length; i++)
3905 {
3906 bundles[i] = (BundleImpl) targets[i];
3907 }
3908 }
3909
3910 synchronized (m_bundleLock)
3911 {
3912 boolean success = false;
3913 while (!success)
3914 {
3915 // If targets is null, then resolve all unresolved bundles.
3916 if (targets == null)
3917 {
3918 List list = new ArrayList();
3919
3920 // Add all unresolved bundles to the list.
3921 synchronized (m_installedBundleLock_Priority2)
3922 {
3923 Iterator iter = m_installedBundleMap.values().iterator();
3924 while (iter.hasNext())
3925 {
3926 BundleImpl bundle = (BundleImpl) iter.next();
3927 if (bundle.getInfo().getState() == Bundle.INSTALLED)
3928 {
3929 list.add(bundle);
3930 }
3931 }
3932 }
3933
3934 // Create an array.
3935 if (list.size() > 0)
3936 {
3937 bundles = (BundleImpl[]) list.toArray(new BundleImpl[list.size()]);
3938 }
3939 }
3940
3941 // Check if all unresolved bundles can be locked.
3942 boolean lockable = true;
3943 if (bundles != null)
3944 {
3945 for (int i = 0; lockable && (i < bundles.length); i++)
3946 {
3947 lockable = bundles[i].getInfo().isLockable();
3948 }
3949
3950 // If we can lock all bundles, then lock them.
3951 if (lockable)
3952 {
3953 for (int i = 0; i < bundles.length; i++)
3954 {
3955 bundles[i].getInfo().lock();
3956 }
3957 success = true;
3958 }
3959 // Otherwise, wait and try again.
3960 else
3961 {
3962 try
3963 {
3964 m_bundleLock.wait();
3965 }
3966 catch (InterruptedException ex)
3967 {
3968 // Ignore and just keep waiting.
3969 }
3970 }
3971 }
3972 else
3973 {
3974 // If there were no bundles to lock, then we can just
3975 // exit the lock loop.
3976 success = true;
3977 }
3978 }
3979 }
3980
3981 return bundles;
3982 }
3983
Richard S. Hall930fecc2005-08-16 18:33:34 +00003984 protected BundleImpl[] acquireBundleRefreshLocks(Bundle[] targets)
3985 {
3986 // Hold bundles to be locked.
3987 BundleImpl[] bundles = null;
3988
3989 synchronized (m_bundleLock)
3990 {
3991 boolean success = false;
3992 while (!success)
3993 {
3994 // If targets is null, then refresh all pending bundles.
3995 Bundle[] newTargets = targets;
3996 if (newTargets == null)
3997 {
3998 List list = new ArrayList();
3999
4000 // First add all uninstalled bundles.
4001 synchronized (m_uninstalledBundlesLock_Priority3)
4002 {
4003 for (int i = 0;
4004 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
4005 i++)
4006 {
4007 list.add(m_uninstalledBundles[i]);
4008 }
4009 }
4010
4011 // Then add all updated bundles.
4012 synchronized (m_installedBundleLock_Priority2)
4013 {
4014 Iterator iter = m_installedBundleMap.values().iterator();
4015 while (iter.hasNext())
4016 {
4017 BundleImpl bundle = (BundleImpl) iter.next();
Richard S. Hall60c26d42006-07-19 10:35:04 +00004018 if (bundle.getInfo().getArchive().getRevisionCount() > 1)
Richard S. Hall930fecc2005-08-16 18:33:34 +00004019 {
4020 list.add(bundle);
4021 }
4022 }
4023 }
4024
4025 // Create an array.
4026 if (list.size() > 0)
4027 {
4028 newTargets = (Bundle[]) list.toArray(new Bundle[list.size()]);
4029 }
4030 }
4031
4032 // If there are targets, then find all dependencies
4033 // for each one.
4034 if (newTargets != null)
4035 {
4036 // Create map of bundles that import the packages
4037 // from the target bundles.
4038 Map map = new HashMap();
4039 for (int targetIdx = 0; targetIdx < newTargets.length; targetIdx++)
4040 {
4041 // Add the current target bundle to the map of
4042 // bundles to be refreshed.
4043 BundleImpl target = (BundleImpl) newTargets[targetIdx];
4044 map.put(target, target);
4045 // Add all importing bundles to map.
4046 populateImportGraph(target, map);
4047 }
4048
4049 bundles = (BundleImpl[]) map.values().toArray(new BundleImpl[map.size()]);
4050 }
4051
Richard S. Hall1a4ab602006-05-24 13:46:06 +00004052 // Check if all corresponding bundles can be locked.
Richard S. Hall930fecc2005-08-16 18:33:34 +00004053 boolean lockable = true;
4054 if (bundles != null)
4055 {
4056 for (int i = 0; lockable && (i < bundles.length); i++)
4057 {
4058 lockable = bundles[i].getInfo().isLockable();
4059 }
4060
4061 // If we can lock all bundles, then lock them.
4062 if (lockable)
4063 {
4064 for (int i = 0; i < bundles.length; i++)
4065 {
4066 bundles[i].getInfo().lock();
4067 }
4068 success = true;
4069 }
4070 // Otherwise, wait and try again.
4071 else
4072 {
4073 try
4074 {
4075 m_bundleLock.wait();
4076 }
4077 catch (InterruptedException ex)
4078 {
4079 // Ignore and just keep waiting.
4080 }
4081 }
4082 }
4083 else
4084 {
4085 // If there were no bundles to lock, then we can just
4086 // exit the lock loop.
4087 success = true;
4088 }
4089 }
4090 }
4091
4092 return bundles;
4093 }
4094
4095 protected void releaseBundleLocks(BundleImpl[] bundles)
4096 {
4097 // Always unlock any locked bundles.
4098 synchronized (m_bundleLock)
4099 {
4100 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
4101 {
4102 bundles[i].getInfo().unlock();
4103 }
4104 m_bundleLock.notifyAll();
4105 }
4106 }
Richard S. Hall9a3e9852006-03-04 03:44:05 +00004107}