blob: 44539ba329571efc456b7fdb54e2fba8e60955c1 [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 {
Richard S. Hall5a1baf02006-07-20 08:02:42 +00002025 for (int i = 0;
2026 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
2027 i++)
Richard S. Halla6443462006-06-29 15:16:05 +00002028 {
2029 if (m_uninstalledBundles[i].getInfo().getBundleId() == id)
2030 {
2031 return m_uninstalledBundles[i];
2032 }
2033 }
2034 }
2035
Richard S. Hall930fecc2005-08-16 18:33:34 +00002036 return null;
2037 }
2038
2039 // Private member for method below.
2040 private Comparator m_comparator = null;
2041
2042 /**
2043 * Implementation for BundleContext.getBundles(). Retrieves
2044 * all installed bundles.
2045 *
2046 * @return An array containing all installed bundles or null if
2047 * there are no installed bundles.
2048 **/
2049 protected Bundle[] getBundles()
2050 {
2051 if (m_comparator == null)
2052 {
2053 m_comparator = new Comparator() {
2054 public int compare(Object o1, Object o2)
2055 {
2056 Bundle b1 = (Bundle) o1;
2057 Bundle b2 = (Bundle) o2;
2058 if (b1.getBundleId() > b2.getBundleId())
2059 return 1;
2060 else if (b1.getBundleId() < b2.getBundleId())
2061 return -1;
2062 return 0;
2063 }
2064 };
2065 }
2066
2067 Bundle[] bundles = null;
2068
2069 synchronized (m_installedBundleLock_Priority2)
2070 {
2071 if (m_installedBundleMap.size() == 0)
2072 {
2073 return null;
2074 }
2075
2076 bundles = new Bundle[m_installedBundleMap.size()];
2077 int counter = 0;
2078 for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
2079 {
2080 bundles[counter++] = (Bundle) i.next();
2081 }
2082 }
2083
2084 Arrays.sort(bundles, m_comparator);
2085
2086 return bundles;
2087 }
2088
2089 protected void addBundleListener(Bundle bundle, BundleListener l)
2090 {
2091 // The spec says do nothing if the listener is
2092 // already registered.
2093 BundleListenerWrapper old = (BundleListenerWrapper)
2094 m_dispatchQueue.getListener(BundleListener.class, l);
2095 if (old == null)
2096 {
2097 l = new BundleListenerWrapper(bundle, l);
2098 m_dispatchQueue.addListener(BundleListener.class, l);
2099 }
2100 }
2101
Karl Pauls3390b4a2006-07-19 13:20:32 +00002102 protected void removeBundleListener(Bundle bundle, BundleListener l)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002103 {
Karl Pauls3390b4a2006-07-19 13:20:32 +00002104 BundleListenerWrapper old = (BundleListenerWrapper)
2105 m_dispatchQueue.getListener(BundleListener.class, l);
2106 if ((old != null) && old.getBundle().equals(bundle))
2107 {
2108 m_dispatchQueue.removeListener(BundleListener.class, l);
2109 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002110 }
2111
2112 /**
2113 * Implementation for BundleContext.addServiceListener().
2114 * Adds service listener to the listener list so that is
2115 * can listen for <code>ServiceEvent</code>s.
2116 *
2117 * @param bundle The bundle that registered the listener.
2118 * @param l The service listener to add to the listener list.
2119 * @param f The filter for the listener; may be null.
2120 **/
2121 protected void addServiceListener(Bundle bundle, ServiceListener l, String f)
2122 throws InvalidSyntaxException
2123 {
2124 // The spec says if the listener is already registered,
2125 // then replace filter.
2126 ServiceListenerWrapper old = (ServiceListenerWrapper)
2127 m_dispatchQueue.getListener(ServiceListener.class, l);
2128 if (old != null)
2129 {
2130 old.setFilter((f == null) ? null : new FilterImpl(m_logger, f));
2131 }
2132 else
2133 {
2134 l = new ServiceListenerWrapper(
2135 bundle, l, (f == null) ? null : new FilterImpl(m_logger, f));
2136 m_dispatchQueue.addListener(ServiceListener.class, l);
2137 }
2138 }
2139
2140 /**
2141 * Implementation for BundleContext.removeServiceListener().
2142 * Removes service listeners from the listener list.
Karl Pauls3390b4a2006-07-19 13:20:32 +00002143 *
2144 * @param bundle The context bundle of the listener
Richard S. Hall930fecc2005-08-16 18:33:34 +00002145 * @param l The service listener to remove from the listener list.
2146 **/
Karl Pauls3390b4a2006-07-19 13:20:32 +00002147 protected void removeServiceListener(Bundle bundle, ServiceListener l)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002148 {
Karl Pauls3390b4a2006-07-19 13:20:32 +00002149 ServiceListenerWrapper old = (ServiceListenerWrapper)
2150 m_dispatchQueue.getListener(ServiceListener.class, l);
2151 if ((old != null) && old.getBundle().equals(bundle))
2152 {
2153 m_dispatchQueue.removeListener(ServiceListener.class, l);
2154 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002155 }
2156
2157 protected void addFrameworkListener(Bundle bundle, FrameworkListener l)
2158 {
2159 // The spec says do nothing if the listener is
2160 // already registered.
2161 FrameworkListenerWrapper old = (FrameworkListenerWrapper)
2162 m_dispatchQueue.getListener(FrameworkListener.class, l);
2163 if (old == null)
2164 {
2165 l = new FrameworkListenerWrapper(bundle, l);
2166 m_dispatchQueue.addListener(FrameworkListener.class, l);
2167 }
2168 }
2169
Karl Pauls3390b4a2006-07-19 13:20:32 +00002170 protected void removeFrameworkListener(Bundle bundle, FrameworkListener l)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002171 {
Karl Pauls3390b4a2006-07-19 13:20:32 +00002172 FrameworkListenerWrapper old = (FrameworkListenerWrapper)
2173 m_dispatchQueue.getListener(FrameworkListener.class, l);
2174 if ((old != null) && old.getBundle().equals(bundle))
2175 {
2176 m_dispatchQueue.removeListener(FrameworkListener.class, l);
2177 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002178 }
2179
2180 /**
2181 * Remove all of the specified bundle's event listeners from
2182 * the framework.
2183 * @param bundle The bundle whose listeners are to be removed.
2184 **/
2185 private void removeListeners(Bundle bundle)
2186 {
2187 if (bundle == null)
2188 {
2189 return;
2190 }
2191
2192 // Remove all listeners associated with the supplied bundle;
2193 // it is only possible to know the bundle associated with a
2194 // listener if the listener was wrapper by a ListenerWrapper,
2195 // so look for those.
2196 Object[] listeners = m_dispatchQueue.getListeners();
2197 for (int i = listeners.length - 2; i >= 0; i -= 2)
2198 {
2199 // Check for listener wrappers and then compare the bundle.
2200 if (listeners[i + 1] instanceof ListenerWrapper)
2201 {
2202 ListenerWrapper lw = (ListenerWrapper) listeners[i + 1];
2203 if ((lw.getBundle() != null) && (lw.getBundle().equals(bundle)))
2204 {
2205 m_dispatchQueue.removeListener(
2206 (Class) listeners[i], (EventListener) listeners[i+1]);
2207 }
2208 }
2209 }
2210 }
2211
2212 /**
2213 * Implementation for BundleContext.registerService(). Registers
2214 * a service for the specified bundle bundle.
2215 *
2216 * @param classNames A string array containing the names of the classes
2217 * under which the new service is available.
2218 * @param svcObj The service object or <code>ServiceFactory</code>.
2219 * @param dict A dictionary of properties that further describe the
2220 * service or null.
2221 * @return A <code>ServiceRegistration</code> object or null.
2222 **/
2223 protected ServiceRegistration registerService(
2224 BundleImpl bundle, String[] classNames, Object svcObj, Dictionary dict)
2225 {
2226 if (classNames == null)
2227 {
2228 throw new NullPointerException("Service class names cannot be null.");
2229 }
2230 else if (svcObj == null)
2231 {
2232 throw new IllegalArgumentException("Service object cannot be null.");
2233 }
2234
2235 // Check for permission to register all passed in interface names.
2236 if (System.getSecurityManager() != null)
2237 {
2238 for (int i = 0; i < classNames.length; i++)
2239 {
2240 ServicePermission perm = new ServicePermission(
2241 classNames[i], ServicePermission.REGISTER);
2242 AccessController.checkPermission(perm);
2243 }
2244 }
2245
2246 // Acquire bundle lock.
Richard S. Hall3c26cc02006-02-17 13:51:21 +00002247 acquireBundleLock(bundle);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002248
2249 ServiceRegistration reg = null;
2250
2251 try
2252 {
2253 BundleInfo info = bundle.getInfo();
2254
2255 // Can only register services if starting or active.
2256 if ((info.getState() & (Bundle.STARTING | Bundle.ACTIVE)) == 0)
2257 {
2258 throw new IllegalStateException(
2259 "Can only register services while bundle is active or activating.");
2260 }
2261
2262 // Check to make sure that the service object is
2263 // an instance of all service classes; ignore if
2264 // service object is a service factory.
2265 if (!(svcObj instanceof ServiceFactory))
2266 {
2267 for (int i = 0; i < classNames.length; i++)
2268 {
Richard S. Hallbc549622006-07-13 13:33:22 +00002269 Class clazz = Util.loadClassUsingClass(svcObj.getClass(), classNames[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002270 if (clazz == null)
2271 {
2272 throw new IllegalArgumentException(
2273 "Cannot cast service: " + classNames[i]);
2274 }
2275 else if (!clazz.isAssignableFrom(svcObj.getClass()))
2276 {
2277 throw new IllegalArgumentException(
2278 "Service object is not an instance of \""
2279 + classNames[i] + "\".");
2280 }
2281 }
2282 }
2283
2284 reg = m_registry.registerService(bundle, classNames, svcObj, dict);
2285 }
2286 finally
2287 {
2288 // Always release bundle lock.
2289 releaseBundleLock(bundle);
2290 }
Richard S. Hall3c26cc02006-02-17 13:51:21 +00002291
2292 // TODO: CONCURRENCY - Reconsider firing event here, outside of the
2293 // bundle lock.
2294
Richard S. Hall930fecc2005-08-16 18:33:34 +00002295 // NOTE: The service registered event is fired from the service
2296 // registry to the framework, where it is then redistributed to
2297 // interested service event listeners.
2298
2299 return reg;
2300 }
2301
Richard S. Hall930fecc2005-08-16 18:33:34 +00002302 protected ServiceReference[] getServiceReferences(
2303 BundleImpl bundle, String className, String expr)
2304 throws InvalidSyntaxException
2305 {
2306 // Define filter if expression is not null.
2307 Filter filter = null;
2308 if (expr != null)
2309 {
2310 filter = new FilterImpl(m_logger, expr);
2311 }
2312
2313 // Ask the service registry for all matching service references.
2314 List refList = m_registry.getServiceReferences(className, filter);
2315
2316 // The returned reference list must be filtered for two cases:
2317 // 1) The requesting bundle may not be wired to the same class
2318 // as the providing bundle (i.e, different versions), so filter
2319 // any services for which the requesting bundle might get a
2320 // class cast exception.
2321 // 2) Security is enabled and the requesting bundle does not have
2322 // permission access the service.
2323 for (int refIdx = 0; (refList != null) && (refIdx < refList.size()); refIdx++)
2324 {
2325 // Get the current service reference.
2326 ServiceReference ref = (ServiceReference) refList.get(refIdx);
2327
2328 // Get the service's objectClass property.
2329 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
2330
2331 // Boolean flag.
2332 boolean allow = false;
2333
2334 // Filter the service reference if the requesting bundle
2335 // does not have permission.
2336 if (System.getSecurityManager() != null)
2337 {
2338 for (int classIdx = 0;
2339 !allow && (classIdx < objectClass.length);
2340 classIdx++)
2341 {
2342 try
2343 {
2344 ServicePermission perm = new ServicePermission(
2345 objectClass[classIdx], ServicePermission.GET);
2346 AccessController.checkPermission(perm);
2347 // The bundle only needs permission for one
2348 // of the service interfaces, so break out
2349 // of the loop when permission is granted.
2350 allow = true;
2351 }
2352 catch (Exception ex)
2353 {
2354 // We do not throw this exception since the bundle
2355 // is not supposed to know about the service at all
2356 // if it does not have permission.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002357 m_logger.log(Logger.LOG_ERROR, ex.getMessage());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002358 }
2359 }
2360
2361 if (!allow)
2362 {
2363 refList.remove(refIdx);
2364 refIdx--;
2365 continue;
2366 }
2367 }
2368
2369 // Now check for castability.
2370 if (!isServiceAssignable(bundle, ref))
2371 {
2372 refList.remove(refIdx);
2373 refIdx--;
2374 }
2375 }
2376
2377 if (refList.size() > 0)
2378 {
2379 return (ServiceReference[]) refList.toArray(new ServiceReference[refList.size()]);
2380 }
2381
2382 return null;
2383 }
2384
2385 /**
2386 * This method determines if the requesting bundle is able to cast
2387 * the specified service reference based on class visibility rules
2388 * of the underlying modules.
2389 * @param requester The bundle requesting the service.
2390 * @param ref The service in question.
2391 * @return <tt>true</tt> if the requesting bundle is able to case
2392 * the service object to a known type.
2393 **/
2394 protected boolean isServiceAssignable(BundleImpl requester, ServiceReference ref)
2395 {
2396 // Boolean flag.
2397 boolean allow = true;
2398 // Get the service's objectClass property.
2399 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
2400
2401 // The the service reference is not assignable when the requesting
2402 // bundle is wired to a different version of the service object.
2403 // NOTE: We are pessimistic here, if any class in the service's
2404 // objectClass is not usable by the requesting bundle, then we
2405 // disallow the service reference.
2406 for (int classIdx = 0; (allow) && (classIdx < objectClass.length); classIdx++)
2407 {
2408 if (!ref.isAssignableTo(requester, objectClass[classIdx]))
2409 {
2410 allow = false;
2411 }
2412 }
2413 return allow;
2414 }
2415
2416 protected Object getService(Bundle bundle, ServiceReference ref)
2417 {
2418 // Check that the bundle has permission to get at least
2419 // one of the service interfaces; the objectClass property
2420 // of the service stores its service interfaces.
2421 String[] objectClass = (String[])
2422 ref.getProperty(Constants.OBJECTCLASS);
2423 if (objectClass == null)
2424 {
2425 return null;
2426 }
2427
2428 boolean hasPermission = false;
2429 if (System.getSecurityManager() != null)
2430 {
2431 for (int i = 0;
2432 !hasPermission && (i < objectClass.length);
2433 i++)
2434 {
2435 try
2436 {
2437 ServicePermission perm =
2438 new ServicePermission(
2439 objectClass[i], ServicePermission.GET);
2440 AccessController.checkPermission(perm);
2441 hasPermission = true;
2442 }
2443 catch (Exception ex)
2444 {
2445 }
2446 }
2447 }
2448 else
2449 {
2450 hasPermission = true;
2451 }
2452
2453 // If the bundle does not permission to access the service,
2454 // then return null.
2455 if (!hasPermission)
2456 {
2457 return null;
2458 }
2459
2460 return m_registry.getService(bundle, ref);
2461 }
2462
2463 protected boolean ungetService(Bundle bundle, ServiceReference ref)
2464 {
2465 return m_registry.ungetService(bundle, ref);
2466 }
2467
2468 protected File getDataFile(BundleImpl bundle, String s)
2469 {
2470 // The spec says to throw an error if the bundle
2471 // is stopped, which I assume means not active,
2472 // starting, or stopping.
2473 if ((bundle.getInfo().getState() != Bundle.ACTIVE) &&
2474 (bundle.getInfo().getState() != Bundle.STARTING) &&
2475 (bundle.getInfo().getState() != Bundle.STOPPING))
2476 {
2477 throw new IllegalStateException("Only active bundles can create files.");
2478 }
2479 try
2480 {
2481 return m_cache.getArchive(
2482 bundle.getInfo().getBundleId()).getDataFile(s);
2483 }
2484 catch (Exception ex)
2485 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002486 m_logger.log(Logger.LOG_ERROR, ex.getMessage());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002487 return null;
2488 }
2489 }
2490
2491 //
2492 // PackageAdmin related methods.
2493 //
2494
2495 /**
Richard S. Hallc87d41a2006-07-03 07:57:28 +00002496 * This method returns the bundle associated with the specified class if
2497 * the class was loaded from a bundle from this framework instance. If the
2498 * class was not loaded from a bundle or was loaded by a bundle in another
2499 * framework instance, then <tt>null</tt> is returned.
2500 *
2501 * @param clazz the class for which to find its associated bundle.
2502 * @return the bundle associated with the specified class or <tt>null</tt>
2503 * if the class was not loaded by a bundle or its associated
2504 * bundle belongs to a different framework instance.
2505 **/
2506 protected Bundle getBundle(Class clazz)
2507 {
2508 if (clazz.getClassLoader() instanceof ContentClassLoader)
2509 {
2510 IContentLoader contentLoader =
2511 ((ContentClassLoader) clazz.getClassLoader()).getContentLoader();
2512 IModule[] modules = m_factory.getModules();
2513 for (int i = 0; i < modules.length; i++)
2514 {
2515 if (modules[i].getContentLoader() == contentLoader)
2516 {
2517 long id = Util.getBundleIdFromModuleId(modules[i].getId());
2518 return getBundle(id);
2519 }
2520 }
2521 }
2522 return null;
2523 }
2524
2525 /**
Richard S. Hall1bdc3722006-07-03 09:52:07 +00002526 * Returns the exported packages associated with the specified
Richard S. Hallafc52d42006-02-09 13:04:32 +00002527 * package name. This is used by the PackageAdmin service
2528 * implementation.
2529 *
2530 * @param name The name of the exported package to find.
2531 * @return The exported package or null if no matching package was found.
2532 **/
2533 protected ExportedPackage[] getExportedPackages(String name)
2534 {
2535 // First, get all exporters of the package.
2536 ExportedPackage[] pkgs = null;
2537 IModule[] exporters = m_policyCore.getInUseExporters(new R4Import(name, null, null));
Richard S. Hall930fecc2005-08-16 18:33:34 +00002538 if (exporters != null)
2539 {
Richard S. Hall88e0ae62006-02-09 13:22:21 +00002540 pkgs = new ExportedPackage[exporters.length];
Richard S. Hallafc52d42006-02-09 13:04:32 +00002541 for (int pkgIdx = 0; pkgIdx < pkgs.length; pkgIdx++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002542 {
Richard S. Hallafc52d42006-02-09 13:04:32 +00002543 // Get the bundle associated with the current exporting module.
2544 BundleImpl bundle = (BundleImpl) getBundle(
2545 Util.getBundleIdFromModuleId(exporters[pkgIdx].getId()));
2546
2547 // We need to find the version of the exported package, but this
2548 // is tricky since there may be multiple versions of the package
2549 // offered by a given bundle, since multiple revisions of the
2550 // bundle JAR file may exist if the bundle was updated without
2551 // refreshing the framework. In this case, each revision of the
2552 // bundle JAR file is represented as a module in the BundleInfo
2553 // module array, which is ordered from oldest to newest. We assume
2554 // that the first module found to be exporting the package is the
2555 // provider of the package, which makes sense since it must have
2556 // been resolved first.
2557 IModule[] modules = bundle.getInfo().getModules();
2558 for (int modIdx = 0; modIdx < modules.length; modIdx++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002559 {
Richard S. Hallafc52d42006-02-09 13:04:32 +00002560 R4Export export = Util.getExportPackage(modules[modIdx], name);
2561 if (export != null)
2562 {
2563 pkgs[pkgIdx] =
2564 new ExportedPackageImpl(
Richard S. Hall60c26d42006-07-19 10:35:04 +00002565 this, bundle, modules[modIdx], export);
Richard S. Hallafc52d42006-02-09 13:04:32 +00002566 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002567 }
2568 }
2569 }
2570
Richard S. Hallafc52d42006-02-09 13:04:32 +00002571 return pkgs;
Richard S. Hall930fecc2005-08-16 18:33:34 +00002572 }
2573
2574 /**
2575 * Returns an array of all actively exported packages from the specified
2576 * bundle or if the specified bundle is <tt>null</tt> an array
2577 * containing all actively exported packages by all bundles.
2578 *
2579 * @param b The bundle whose exported packages are to be retrieved
2580 * or <tt>null</tt> if the exported packages of all bundles are
2581 * to be retrieved.
2582 * @return An array of exported packages.
2583 **/
2584 protected ExportedPackage[] getExportedPackages(Bundle b)
2585 {
2586 List list = new ArrayList();
2587
2588 // If a bundle is specified, then return its
2589 // exported packages.
2590 if (b != null)
2591 {
2592 BundleImpl bundle = (BundleImpl) b;
2593 getExportedPackages(bundle, list);
2594 }
2595 // Otherwise return all exported packages.
2596 else
2597 {
2598 // To create a list of all exported packages, we must look
2599 // in the installed and uninstalled sets of bundles. To
2600 // ensure a somewhat consistent view, we will gather all
2601 // of this information from within the installed bundle
2602 // lock.
2603 synchronized (m_installedBundleLock_Priority2)
2604 {
2605 // First get exported packages from uninstalled bundles.
2606 synchronized (m_uninstalledBundlesLock_Priority3)
2607 {
2608 for (int bundleIdx = 0;
2609 (m_uninstalledBundles != null) && (bundleIdx < m_uninstalledBundles.length);
2610 bundleIdx++)
2611 {
2612 BundleImpl bundle = m_uninstalledBundles[bundleIdx];
2613 getExportedPackages(bundle, list);
2614 }
2615 }
2616
2617 // Now get exported packages from installed bundles.
2618 Bundle[] bundles = getBundles();
2619 for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
2620 {
2621 BundleImpl bundle = (BundleImpl) bundles[bundleIdx];
2622 getExportedPackages(bundle, list);
2623 }
2624 }
2625 }
2626
2627 return (ExportedPackage[]) list.toArray(new ExportedPackage[list.size()]);
2628 }
2629
2630 /**
2631 * Adds any current active exported packages from the specified bundle
2632 * to the passed in list.
2633 * @param bundle The bundle from which to retrieve exported packages.
2634 * @param list The list to which the exported packages are added
2635 **/
2636 private void getExportedPackages(BundleImpl bundle, List list)
2637 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00002638 // Since a bundle may have many modules associated with it,
2639 // one for each revision in the cache, search each module
2640 // for each revision to get all exports.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002641 IModule[] modules = bundle.getInfo().getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002642 for (int modIdx = 0; modIdx < modules.length; modIdx++)
2643 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002644 R4Export[] exports = m_policyCore.getExports(modules[modIdx]);
2645 if ((exports != null) && (exports.length > 0))
Richard S. Hall930fecc2005-08-16 18:33:34 +00002646 {
2647 for (int expIdx = 0; expIdx < exports.length; expIdx++)
2648 {
2649 // See if the target bundle's module is one of the
2650 // "in use" exporters of the package.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002651 IModule[] inUseModules =
2652 m_policyCore.getInUseExporters(
2653 new R4Import(exports[expIdx].getName(), null, null));
2654 // Search through the current providers to find the target
2655 // module.
2656 for (int i = 0; (inUseModules != null) && (i < inUseModules.length); i++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002657 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002658 if (inUseModules[i] == modules[modIdx])
2659 {
2660 list.add(new ExportedPackageImpl(
Richard S. Hall60c26d42006-07-19 10:35:04 +00002661 this, bundle, modules[modIdx], exports[expIdx]));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002662 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002663 }
2664 }
2665 }
2666 }
2667 }
2668
2669 protected Bundle[] getImportingBundles(ExportedPackage ep)
2670 {
Richard S. Halle1f53a52006-07-17 11:06:59 +00002671 // Get exporting bundle.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002672 BundleImpl exporter = (BundleImpl)
Richard S. Halle1f53a52006-07-17 11:06:59 +00002673 ((ExportedPackage) ep).getExportingBundle();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002674 BundleInfo exporterInfo = exporter.getInfo();
2675
2676 // Create list for storing importing bundles.
2677 List list = new ArrayList();
2678 Bundle[] bundles = getBundles();
2679
2680 // Check all bundles to see who imports the package.
2681 for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
2682 {
2683 BundleImpl importer = (BundleImpl) bundles[bundleIdx];
2684
Richard S. Hall82699422006-07-19 14:37:45 +00002685 // Check the import wires of all modules for all bundles.
2686 IModule[] modules = importer.getInfo().getModules();
2687 for (int modIdx = 0; modIdx < modules.length; modIdx++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002688 {
Richard S. Hall82699422006-07-19 14:37:45 +00002689 R4Wire wire = Util.getWire(modules[modIdx], ep.getName());
2690
2691 // If the resolving module is associated with the
2692 // exporting bundle, then add current bundle to
2693 // import list.
2694 if ((wire != null) && exporterInfo.hasModule(wire.getExportingModule()))
Richard S. Hall930fecc2005-08-16 18:33:34 +00002695 {
Richard S. Hall82699422006-07-19 14:37:45 +00002696 // Add the bundle to the list of importers.
2697 list.add(bundles[bundleIdx]);
2698 break;
Richard S. Hall930fecc2005-08-16 18:33:34 +00002699 }
2700 }
2701 }
2702
2703 // Return the results.
2704 if (list.size() > 0)
2705 {
2706 return (Bundle[]) list.toArray(new Bundle[list.size()]);
2707 }
2708
2709 return null;
2710 }
2711
Richard S. Hall1a4ab602006-05-24 13:46:06 +00002712 protected boolean resolveBundles(Bundle[] targets)
2713 {
2714 if (System.getSecurityManager() != null)
2715 {
2716// TODO: FW SECURITY - Perform proper security check.
2717 AccessController.checkPermission(m_adminPerm);
2718 }
2719
2720 // Acquire locks for all bundles to be resolved.
2721 BundleImpl[] bundles = acquireBundleResolveLocks(targets);
2722
2723 try
2724 {
2725 boolean result = true;
2726
2727 // If there are targets, then resolve each one.
2728 if (bundles != null)
2729 {
2730 for (int i = 0; i < bundles.length; i++)
2731 {
2732 try
2733 {
2734 _resolveBundle(bundles[i]);
2735 }
2736 catch (BundleException ex)
2737 {
2738 result = false;
2739 m_logger.log(
2740 Logger.LOG_WARNING,
2741 "Unable to resolve bundle " + bundles[i].getBundleId(),
2742 ex);
2743 }
2744 }
2745 }
2746
2747 return result;
2748 }
2749 finally
2750 {
2751 // Always release all bundle locks.
2752 releaseBundleLocks(bundles);
2753 }
2754 }
2755
Richard S. Hall930fecc2005-08-16 18:33:34 +00002756 protected void refreshPackages(Bundle[] targets)
2757 {
2758 if (System.getSecurityManager() != null)
2759 {
Richard S. Hall1a4ab602006-05-24 13:46:06 +00002760// TODO: FW SECURITY - Perform proper security check.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002761 AccessController.checkPermission(m_adminPerm);
2762 }
2763
2764 // Acquire locks for all impacted bundles.
2765 BundleImpl[] bundles = acquireBundleRefreshLocks(targets);
2766
2767 // Remove any targeted bundles from the uninstalled bundles
2768 // array, since they will be removed from the system after
2769 // the refresh.
2770 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
2771 {
2772 forgetUninstalledBundle(bundles[i]);
2773 }
2774
2775 try
2776 {
2777 // If there are targets, then refresh each one.
2778 if (bundles != null)
2779 {
2780 // At this point the map contains every bundle that has been
2781 // updated and/or removed as well as all bundles that import
2782 // packages from these bundles.
2783
2784 // Create refresh helpers for each bundle.
2785 RefreshHelper[] helpers = new RefreshHelper[bundles.length];
2786 for (int i = 0; i < bundles.length; i++)
2787 {
2788 helpers[i] = new RefreshHelper(bundles[i]);
2789 }
2790
2791 // Stop, purge or remove, and reinitialize all bundles first.
2792 for (int i = 0; i < helpers.length; i++)
2793 {
2794 helpers[i].stop();
2795 helpers[i].purgeOrRemove();
2796 helpers[i].reinitialize();
2797 }
2798
2799 // Then restart all bundles that were previously running.
2800 for (int i = 0; i < helpers.length; i++)
2801 {
2802 helpers[i].restart();
2803 }
2804 }
2805 }
2806 finally
2807 {
2808 // Always release all bundle locks.
2809 releaseBundleLocks(bundles);
2810 }
2811
2812 fireFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, getBundle(0), null);
2813 }
2814
2815 private void populateImportGraph(BundleImpl target, Map map)
2816 {
2817 // Get the exported packages for the specified bundle.
2818 ExportedPackage[] pkgs = getExportedPackages(target);
2819
2820 for (int pkgIdx = 0; (pkgs != null) && (pkgIdx < pkgs.length); pkgIdx++)
2821 {
2822 // Get all imports of this package.
2823 Bundle[] importers = getImportingBundles(pkgs[pkgIdx]);
2824
2825 for (int impIdx = 0;
2826 (importers != null) && (impIdx < importers.length);
2827 impIdx++)
2828 {
Richard S. Halle34df092005-10-06 17:03:05 +00002829 // Avoid cycles if the bundle is already in map.
2830 if (!map.containsKey(importers[impIdx]))
2831 {
Richard S. Hall930fecc2005-08-16 18:33:34 +00002832 // Add each importing bundle to map.
2833 map.put(importers[impIdx], importers[impIdx]);
2834 // Now recurse into each bundle to get its importers.
2835 populateImportGraph(
2836 (BundleImpl) importers[impIdx], map);
Richard S. Halle34df092005-10-06 17:03:05 +00002837 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00002838 }
2839 }
2840 }
2841
2842 //
2843 // Miscellaneous private methods.
2844 //
2845
Richard S. Hall04bdbb12006-03-15 14:26:15 +00002846 private BundleInfo createBundleInfo(BundleArchive archive)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002847 throws Exception
2848 {
2849 // Get the bundle manifest.
2850 Map headerMap = null;
2851 try
2852 {
2853 // Although there should only ever be one revision at this
2854 // point, get the header for the current revision to be safe.
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00002855 headerMap = archive.getRevision(archive.getRevisionCount() - 1).getManifestHeader();
Richard S. Hall930fecc2005-08-16 18:33:34 +00002856 }
2857 catch (Exception ex)
2858 {
2859 throw new BundleException("Unable to read JAR manifest.", ex);
2860 }
2861
2862 // We can't do anything without the manifest header.
2863 if (headerMap == null)
2864 {
2865 throw new BundleException("Unable to read JAR manifest header.");
2866 }
2867
2868 // Create the module for the bundle; although there should only
2869 // ever be one revision at this point, create the module for
2870 // the current revision to be safe.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002871 IModule module = createModule(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002872 archive.getId(), archive.getRevisionCount() - 1, headerMap);
2873
2874 // Finally, create an return the bundle info.
2875 return new BundleInfo(m_logger, archive, module);
2876 }
2877
2878 /**
2879 * Creates a module for a given bundle by reading the bundle's
2880 * manifest meta-data and converting it to work with the underlying
2881 * import/export search policy of the module loader.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002882 * @param targetId The identifier of the bundle for which the module should
Richard S. Hall930fecc2005-08-16 18:33:34 +00002883 * be created.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002884 * @param headerMap The headers map associated with the bundle.
Richard S. Hall930fecc2005-08-16 18:33:34 +00002885 * @return The initialized and/or newly created module.
2886 **/
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002887 private IModule createModule(long targetId, int revision, Map headerMap)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002888 throws Exception
2889 {
2890 // Get the manifest version.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002891 String manifestVersion = (String) headerMap.get(FelixConstants.BUNDLE_MANIFESTVERSION);
2892 manifestVersion = (manifestVersion == null) ? "1" : manifestVersion;
2893 if (!manifestVersion.equals("1") && !manifestVersion.equals("2"))
Richard S. Hall930fecc2005-08-16 18:33:34 +00002894 {
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002895 throw new BundleException("Unknown 'Bundle-ManifestVersion' value: " + manifestVersion);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002896 }
2897
Richard S. Hallb72001a2006-02-20 15:10:33 +00002898 // Create map to check for duplicate imports/exports.
2899 Map dupeMap = new HashMap();
2900
Richard S. Hall930fecc2005-08-16 18:33:34 +00002901 // Get export packages from bundle manifest.
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002902 R4Package[] pkgs = R4Package.parseImportOrExportHeader(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002903 (String) headerMap.get(Constants.EXPORT_PACKAGE));
Richard S. Hallb72001a2006-02-20 15:10:33 +00002904
2905 // Create non-duplicated export array.
2906 dupeMap.clear();
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002907 for (int i = 0; i < pkgs.length; i++)
2908 {
Richard S. Hallb72001a2006-02-20 15:10:33 +00002909 if (dupeMap.get(pkgs[i].getName()) == null)
2910 {
2911 dupeMap.put(pkgs[i].getName(), new R4Export(pkgs[i]));
2912 }
2913 else
2914 {
2915 // TODO: FRAMEWORK - Exports can be duplicated, so fix this.
2916 m_logger.log(Logger.LOG_WARNING,
2917 "Duplicate export - " + pkgs[i].getName());
2918 }
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002919 }
Richard S. Hallb72001a2006-02-20 15:10:33 +00002920 R4Export[] exports =
2921 (R4Export[]) dupeMap.values().toArray(new R4Export[dupeMap.size()]);
2922
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002923 // Get import packages from bundle manifest.
2924 pkgs = R4Package.parseImportOrExportHeader(
2925 (String) headerMap.get(Constants.IMPORT_PACKAGE));
Richard S. Hall930fecc2005-08-16 18:33:34 +00002926
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002927 // Create non-duplicated import array.
2928 dupeMap.clear();
2929 for (int i = 0; i < pkgs.length; i++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002930 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002931 if (dupeMap.get(pkgs[i].getName()) == null)
Richard S. Hall930fecc2005-08-16 18:33:34 +00002932 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002933 dupeMap.put(pkgs[i].getName(), new R4Import(pkgs[i]));
Richard S. Hall930fecc2005-08-16 18:33:34 +00002934 }
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002935 else
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002936 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002937 // TODO: FRAMEWORK - Determine if we should error here.
2938 m_logger.log(Logger.LOG_WARNING,
2939 "Duplicate import - " + pkgs[i].getName());
Richard S. Hall930fecc2005-08-16 18:33:34 +00002940 }
2941 }
Richard S. Hallad24d9f2006-07-03 09:09:18 +00002942 R4Import[] imports =
2943 (R4Import[]) dupeMap.values().toArray(new R4Import[dupeMap.size()]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002944
Richard S. Hall930fecc2005-08-16 18:33:34 +00002945 // Get dynamic import packages from bundle manifest.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002946 pkgs = R4Package.parseImportOrExportHeader(
Richard S. Hall930fecc2005-08-16 18:33:34 +00002947 (String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
Richard S. Hallb72001a2006-02-20 15:10:33 +00002948
2949 // Create non-duplicated dynamic import array.
2950 dupeMap.clear();
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002951 for (int i = 0; i < pkgs.length; i++)
2952 {
Richard S. Hallb72001a2006-02-20 15:10:33 +00002953 if (dupeMap.get(pkgs[i].getName()) == null)
2954 {
2955 dupeMap.put(pkgs[i].getName(), new R4Import(pkgs[i]));
2956 }
2957 else
2958 {
2959 // TODO: FRAMEWORK - Determine if we should error here.
2960 m_logger.log(Logger.LOG_WARNING,
2961 "Duplicate import - " + pkgs[i].getName());
2962 }
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00002963 }
Richard S. Hallb72001a2006-02-20 15:10:33 +00002964 R4Import[] dynamics =
2965 (R4Import[]) dupeMap.values().toArray(new R4Import[dupeMap.size()]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00002966
Richard S. Hall8bebf412006-07-03 12:54:50 +00002967 // Do some validity checking on bundles with R4 headers.
Richard S. Hallc6c4e7e2006-07-03 14:39:55 +00002968// TODO: FRAMEWORK - Perhaps these verifications and conversions can be done more efficiently.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002969 if (manifestVersion.equals("2"))
Richard S. Hall8bebf412006-07-03 12:54:50 +00002970 {
2971 // Verify that bundle symbolic name is specified.
2972 String targetSym = (String) headerMap.get(FelixConstants.BUNDLE_SYMBOLICNAME);
2973 if (targetSym == null)
2974 {
2975 throw new BundleException("R4 bundle manifests must include bundle symbolic name.");
2976 }
Richard S. Hallc6c4e7e2006-07-03 14:39:55 +00002977
Richard S. Hall8bebf412006-07-03 12:54:50 +00002978 // Verify that the bundle symbolic name and version is unique.
2979 String targetVer = (String) headerMap.get(FelixConstants.BUNDLE_VERSION);
2980 targetVer = (targetVer == null) ? "0.0.0" : targetVer;
2981 Bundle[] bundles = getBundles();
2982 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
2983 {
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002984 long id = ((BundleImpl) bundles[i]).getInfo().getBundleId();
Richard S. Hall8bebf412006-07-03 12:54:50 +00002985 String sym = (String) ((BundleImpl) bundles[i])
2986 .getInfo().getCurrentHeader().get(Constants.BUNDLE_SYMBOLICNAME);
2987 String ver = (String) ((BundleImpl) bundles[i])
2988 .getInfo().getCurrentHeader().get(Constants.BUNDLE_VERSION);
2989 ver = (ver == null) ? "0.0.0" : ver;
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00002990 if (targetSym.equals(sym) && targetVer.equals(ver) && (targetId != id))
Richard S. Hall8bebf412006-07-03 12:54:50 +00002991 {
2992 throw new BundleException("Bundle symbolic name and version are not unique.");
2993 }
2994 }
Richard S. Hallc6c4e7e2006-07-03 14:39:55 +00002995
2996 // Need to add symbolic name and bundle version to all R4 exports.
2997 for (int i = 0; (exports != null) && (i < exports.length); i++)
2998 {
2999 R4Attribute[] attrs = exports[i].getAttributes();
3000 R4Attribute[] newAttrs = new R4Attribute[attrs.length + 2];
3001 System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
3002 newAttrs[attrs.length] = new R4Attribute(
3003 Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, targetSym, false);
3004 newAttrs[attrs.length + 1] = new R4Attribute(
3005 Constants.BUNDLE_VERSION_ATTRIBUTE, targetVer, false);
3006 exports[i] = new R4Export(
3007 exports[i].getName(), exports[i].getDirectives(), newAttrs);
3008 }
Richard S. Hall8bebf412006-07-03 12:54:50 +00003009 }
3010 // Do some validity checking and conversion on bundles with R3 headers.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003011 else if (manifestVersion.equals("1"))
Richard S. Hall930fecc2005-08-16 18:33:34 +00003012 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003013 // Check to make sure that R3 bundles have only specified
3014 // the 'specification-version' attribute and no directives
3015 // on their exports.
3016 for (int i = 0; (exports != null) && (i < exports.length); i++)
Richard S. Hall930fecc2005-08-16 18:33:34 +00003017 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003018 if (exports[i].getDirectives().length != 0)
Richard S. Hall930fecc2005-08-16 18:33:34 +00003019 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003020 throw new BundleException("R3 exports cannot contain directives.");
Richard S. Hall930fecc2005-08-16 18:33:34 +00003021 }
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003022 // NOTE: This is checking for "version" rather than "specification-version"
3023 // because the package class normalizes to "version" to avoid having
3024 // future special cases. This could be changed if more strict behavior
3025 // is required.
3026 if ((exports[i].getAttributes().length > 1) ||
3027 ((exports[i].getAttributes().length == 1) &&
3028 (!exports[i].getAttributes()[0].getName().equals(FelixConstants.VERSION_ATTRIBUTE))))
Richard S. Hall930fecc2005-08-16 18:33:34 +00003029 {
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003030 throw new BundleException(
Richard S. Hall4c7d68e2006-07-11 13:15:47 +00003031 "Export does not conform to R3 syntax: " + exports[i]);
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003032 }
3033 }
3034
3035 // Check to make sure that R3 bundles have only specified
3036 // the 'specification-version' attribute and no directives
3037 // on their imports.
3038 for (int i = 0; (imports != null) && (i < imports.length); i++)
3039 {
3040 if (imports[i].getDirectives().length != 0)
3041 {
3042 throw new BundleException("R3 imports cannot contain directives.");
3043 }
3044 // NOTE: This is checking for "version" rather than "specification-version"
3045 // because the package class normalizes to "version" to avoid having
3046 // future special cases. This could be changed if more strict behavior
3047 // is required.
3048 if ((imports[i].getVersionHigh() != null) ||
3049 (imports[i].getAttributes().length > 1) ||
3050 ((imports[i].getAttributes().length == 1) &&
3051 (!imports[i].getAttributes()[0].getName().equals(FelixConstants.VERSION_ATTRIBUTE))))
3052 {
3053 throw new BundleException(
3054 "Import does not conform to R3 syntax: " + imports[i]);
3055 }
3056 }
3057
3058 // Since all R3 exports imply an import, add a corresponding
3059 // import for each existing export. Create non-duplicated import array.
3060 dupeMap.clear();
3061 // Add existing imports.
3062 for (int i = 0; i < imports.length; i++)
3063 {
3064 dupeMap.put(imports[i].getName(), imports[i]);
3065 }
3066 // Add import for each export.
3067 for (int i = 0; i < exports.length; i++)
3068 {
3069 if (dupeMap.get(exports[i].getName()) == null)
3070 {
3071 dupeMap.put(exports[i].getName(), new R4Import(exports[i]));
3072 }
3073 }
3074 imports =
3075 (R4Import[]) dupeMap.values().toArray(new R4Import[dupeMap.size()]);
3076
3077 // Add a "uses" directive onto each export of R3 bundles
3078 // that references every other import (which will include
3079 // exports, since export implies import); this is
3080 // necessary since R3 bundles assumed a single class space,
3081 // but R4 allows for multiple class spaces.
3082 String usesValue = "";
3083 for (int i = 0; (imports != null) && (i < imports.length); i++)
3084 {
3085 usesValue = usesValue
3086 + ((usesValue.length() > 0) ? "," : "")
3087 + imports[i].getName();
3088 }
3089 R4Directive uses = new R4Directive(
3090 FelixConstants.USES_DIRECTIVE, usesValue);
3091 for (int i = 0; (exports != null) && (i < exports.length); i++)
3092 {
3093 exports[i] = new R4Export(
3094 exports[i].getName(),
3095 new R4Directive[] { uses },
3096 exports[i].getAttributes());
3097 }
3098
3099 // Check to make sure that R3 bundles have no attributes or
3100 // directives on their dynamic imports.
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003101 for (int i = 0; (dynamics != null) && (i < dynamics.length); i++)
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003102 {
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003103 if (dynamics[i].getDirectives().length != 0)
Richard S. Hallad24d9f2006-07-03 09:09:18 +00003104 {
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003105 throw new BundleException("R3 dynamic imports cannot contain directives.");
3106 }
3107 if (dynamics[i].getAttributes().length != 0)
3108 {
3109 throw new BundleException("R3 dynamic imports cannot contain attributes.");
Richard S. Hall930fecc2005-08-16 18:33:34 +00003110 }
3111 }
3112 }
3113
Richard S. Hall930fecc2005-08-16 18:33:34 +00003114 // Get native library entry names for module library sources.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003115 R4LibraryHeader[] libraryHeaders =
Richard S. Hall930fecc2005-08-16 18:33:34 +00003116 Util.parseLibraryStrings(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003117 m_logger,
Richard S. Hall930fecc2005-08-16 18:33:34 +00003118 Util.parseDelimitedString(
3119 (String) headerMap.get(Constants.BUNDLE_NATIVECODE), ","));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003120 R4Library[] libraries = new R4Library[libraryHeaders.length];
3121 for (int i = 0; i < libraries.length; i++)
3122 {
3123 libraries[i] = new R4Library(
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003124 m_logger, m_cache, targetId, revision,
Richard S. Hall930fecc2005-08-16 18:33:34 +00003125 getProperty(Constants.FRAMEWORK_OS_NAME),
3126 getProperty(Constants.FRAMEWORK_PROCESSOR),
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003127 libraryHeaders[i]);
3128 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00003129
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003130 // Now that we have all of the metadata associated with the
3131 // module, we need to create the module itself. This is somewhat
3132 // complicated because a module is constructed out of several
3133 // interrelated pieces (e.g., content loader, search policy,
3134 // url policy). We need to create all of these pieces and bind
3135 // them together.
Richard S. Hall930fecc2005-08-16 18:33:34 +00003136
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003137 // First, create the module.
3138 IModule module = m_factory.createModule(
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003139 Long.toString(targetId) + "." + Integer.toString(revision));
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003140 // Attach the R4 search policy metadata to the module.
3141 m_policyCore.setExports(module, exports);
3142 m_policyCore.setImports(module, imports);
3143 m_policyCore.setDynamicImports(module, dynamics);
3144 m_policyCore.setLibraries(module, libraries);
3145
3146 // Create the content loader associated with the module archive.
3147 IContentLoader contentLoader = new ContentLoaderImpl(
3148 m_logger,
Richard S. Hallbf6bc8a2006-07-03 13:43:46 +00003149 m_cache.getArchive(targetId).getRevision(revision).getContent(),
3150 m_cache.getArchive(targetId).getRevision(revision).getContentPath());
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003151 // Set the content loader's search policy.
3152 contentLoader.setSearchPolicy(
3153 new R4SearchPolicy(m_policyCore, module));
3154 // Set the content loader's URL policy.
3155 contentLoader.setURLPolicy(
Richard S. Hallfb5221e2006-02-20 10:22:28 +00003156// TODO: ML - SUCKS NEEDING URL POLICY PER MODULE.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003157 new URLPolicyImpl(
3158 m_logger, m_bundleStreamHandler, module));
3159
3160 // Set the module's content loader to the created content loader.
3161 m_factory.setContentLoader(module, contentLoader);
3162
3163 // Done, so return the module.
Richard S. Hall930fecc2005-08-16 18:33:34 +00003164 return module;
3165 }
3166
3167 private BundleActivator createBundleActivator(BundleInfo info)
3168 throws Exception
3169 {
3170 // CONCURRENCY NOTE:
3171 // This method is called indirectly from startBundle() (via _startBundle()),
3172 // which has the exclusion lock, so there is no need to do any locking here.
3173
3174 BundleActivator activator = null;
3175
3176 String strict = m_config.get(FelixConstants.STRICT_OSGI_PROP);
3177 boolean isStrict = (strict == null) ? true : strict.equals("true");
3178 if (!isStrict)
3179 {
3180 try
3181 {
3182 activator =
3183 m_cache.getArchive(info.getBundleId())
Richard S. Hallf1359482006-02-14 08:02:51 +00003184 .getActivator(info.getCurrentModule());
Richard S. Hall930fecc2005-08-16 18:33:34 +00003185 }
3186 catch (Exception ex)
3187 {
3188 activator = null;
3189 }
3190 }
3191
3192 // If there was no cached activator, then get the activator
3193 // class from the bundle manifest.
3194 if (activator == null)
3195 {
3196 // Get the associated bundle archive.
Richard S. Hall04bdbb12006-03-15 14:26:15 +00003197 BundleArchive ba = m_cache.getArchive(info.getBundleId());
Richard S. Hall930fecc2005-08-16 18:33:34 +00003198 // Get the manifest from the current revision; revision is
3199 // base zero so subtract one from the count to get the
3200 // current revision.
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00003201 Map headerMap = ba.getRevision(ba.getRevisionCount() - 1).getManifestHeader();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003202 // Get the activator class attribute.
3203 String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
3204 // Try to instantiate activator class if present.
3205 if (className != null)
3206 {
3207 className = className.trim();
Richard S. Hall6b5f96c2006-02-10 15:57:15 +00003208 Class clazz = info.getCurrentModule().getClass(className);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003209 if (clazz == null)
3210 {
3211 throw new BundleException("Not found: "
Richard S. Hall56c68832006-06-23 15:27:31 +00003212 + className, new ClassNotFoundException(className));
Richard S. Hall930fecc2005-08-16 18:33:34 +00003213 }
3214 activator = (BundleActivator) clazz.newInstance();
3215 }
3216 }
3217
3218 return activator;
3219 }
3220
3221 private void purgeBundle(BundleImpl bundle) throws Exception
3222 {
3223 // Acquire bundle lock.
3224 acquireBundleLock(bundle);
3225
3226 try
3227 {
3228 BundleInfo info = bundle.getInfo();
3229
3230 // In case of a refresh, then we want to physically
3231 // remove the bundle's modules from the module manager.
3232 // This is necessary for two reasons: 1) because
3233 // under Windows we won't be able to delete the bundle
3234 // because files might be left open in the resource
3235 // sources of its modules and 2) we want to make sure
3236 // that no references to old modules exist since they
3237 // will all be stale after the refresh. The only other
3238 // way to do this is to remove the bundle, but that
3239 // would be incorrect, because this is a refresh operation
3240 // and should not trigger bundle REMOVE events.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003241 IModule[] modules = info.getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003242 for (int i = 0; i < modules.length; i++)
3243 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003244 m_factory.removeModule(modules[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003245 }
3246
3247 // Purge all bundle revisions, but the current one.
Richard S. Hall7c9da3d2006-02-24 20:09:28 +00003248 m_cache.getArchive(info.getBundleId()).purge();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003249 }
3250 finally
3251 {
3252 // Always release the bundle lock.
3253 releaseBundleLock(bundle);
3254 }
3255 }
3256
3257 private void garbageCollectBundle(BundleImpl bundle) throws Exception
3258 {
3259 // CONCURRENCY NOTE: There is no reason to lock this bundle,
3260 // because this method is only called during shutdown or a
3261 // refresh operation and these are already guarded by locks.
3262
3263 // Remove the bundle's associated modules from
3264 // the module manager.
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003265 IModule[] modules = bundle.getInfo().getModules();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003266 for (int i = 0; i < modules.length; i++)
3267 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003268 m_factory.removeModule(modules[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003269 }
3270
3271 // Remove the bundle from the cache.
3272 m_cache.remove(m_cache.getArchive(bundle.getInfo().getBundleId()));
3273 }
3274
3275 //
3276 // Event-related methods.
3277 //
3278
3279 /**
3280 * Fires bundle events.
3281 **/
3282 private void fireFrameworkEvent(
3283 int type, Bundle bundle, Throwable throwable)
3284 {
3285 if (m_frameworkDispatcher == null)
3286 {
3287 m_frameworkDispatcher = new Dispatcher() {
3288 public void dispatch(EventListener l, EventObject eventObj)
3289 {
3290 ((FrameworkListener) l)
3291 .frameworkEvent((FrameworkEvent) eventObj);
3292 }
3293 };
3294 }
3295 FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
3296 m_dispatchQueue.dispatch(
3297 m_frameworkDispatcher, FrameworkListener.class, event);
3298 }
3299
3300 /**
3301 * Fires bundle events.
3302 *
3303 * @param type The type of bundle event to fire.
3304 * @param bundle The bundle associated with the event.
3305 **/
3306 private void fireBundleEvent(int type, Bundle bundle)
3307 {
3308 if (m_bundleDispatcher == null)
3309 {
3310 m_bundleDispatcher = new Dispatcher() {
3311 public void dispatch(EventListener l, EventObject eventObj)
3312 {
3313 ((BundleListener) l)
3314 .bundleChanged((BundleEvent) eventObj);
3315 }
3316 };
3317 }
3318 BundleEvent event = null;
3319 event = new BundleEvent(type, bundle);
3320 m_dispatchQueue.dispatch(m_bundleDispatcher,
3321 BundleListener.class, event);
3322 }
3323
3324 /**
3325 * Fires service events.
3326 *
3327 * @param type The type of service event to fire.
3328 * @param ref The service reference associated with the event.
3329 **/
3330 private void fireServiceEvent(ServiceEvent event)
3331 {
3332 if (m_serviceDispatcher == null)
3333 {
3334 m_serviceDispatcher = new Dispatcher() {
3335 public void dispatch(EventListener l, EventObject eventObj)
3336 {
3337// TODO: Filter service events based on service permissions.
3338 if (l instanceof ListenerWrapper)
3339 {
3340 BundleImpl bundle = (BundleImpl) ((ServiceListenerWrapper) l).getBundle();
3341 if (isServiceAssignable(bundle, ((ServiceEvent) eventObj).getServiceReference()))
3342 {
3343 ((ServiceListener) l)
3344 .serviceChanged((ServiceEvent) eventObj);
3345 }
3346 }
3347 else
3348 {
3349 ((ServiceListener) l)
3350 .serviceChanged((ServiceEvent) eventObj);
3351 }
3352 }
3353 };
3354 }
3355 m_dispatchQueue.dispatch(m_serviceDispatcher,
3356 ServiceListener.class, event);
3357 }
3358
3359 //
3360 // Property related methods.
3361 //
3362
3363 private void initializeFrameworkProperties()
3364 {
3365 // Standard OSGi properties.
Richard S. Hallea415752005-12-05 19:30:28 +00003366 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003367 FelixConstants.FRAMEWORK_VERSION,
3368 FelixConstants.FRAMEWORK_VERSION_VALUE);
Richard S. Hallea415752005-12-05 19:30:28 +00003369 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003370 FelixConstants.FRAMEWORK_VENDOR,
3371 FelixConstants.FRAMEWORK_VENDOR_VALUE);
Richard S. Hallea415752005-12-05 19:30:28 +00003372 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003373 FelixConstants.FRAMEWORK_LANGUAGE,
3374 System.getProperty("user.language"));
Richard S. Hallea415752005-12-05 19:30:28 +00003375 m_configMutable.put(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003376 FelixConstants.FRAMEWORK_OS_VERSION,
3377 System.getProperty("os.version"));
3378
3379 String s = null;
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003380 s = R4Library.normalizePropertyValue(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003381 FelixConstants.FRAMEWORK_OS_NAME,
3382 System.getProperty("os.name"));
Richard S. Hallea415752005-12-05 19:30:28 +00003383 m_configMutable.put(FelixConstants.FRAMEWORK_OS_NAME, s);
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003384 s = R4Library.normalizePropertyValue(
Richard S. Hall930fecc2005-08-16 18:33:34 +00003385 FelixConstants.FRAMEWORK_PROCESSOR,
3386 System.getProperty("os.arch"));
Richard S. Hallea415752005-12-05 19:30:28 +00003387 m_configMutable.put(FelixConstants.FRAMEWORK_PROCESSOR, s);
Richard S. Hallea415752005-12-05 19:30:28 +00003388 m_configMutable.put(
Richard S. Hall03ddc842006-03-09 14:50:16 +00003389 FelixConstants.FELIX_VERSION_PROPERTY, getFrameworkVersion());
Richard S. Hall930fecc2005-08-16 18:33:34 +00003390 }
3391
Richard S. Hall03ddc842006-03-09 14:50:16 +00003392 /**
3393 * Read the framework version from the property file.
3394 * @return the framework version as a string.
3395 **/
3396 private static String getFrameworkVersion()
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003397 {
3398 // The framework version property.
3399 Properties props = new Properties();
Richard S. Hall03ddc842006-03-09 14:50:16 +00003400 InputStream in = Felix.class.getResourceAsStream("Felix.properties");
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003401 try
3402 {
Richard S. Hall03ddc842006-03-09 14:50:16 +00003403 props.load(in);
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003404 }
Richard S. Hall03ddc842006-03-09 14:50:16 +00003405 catch (IOException ex)
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003406 {
Richard S. Hall03ddc842006-03-09 14:50:16 +00003407 ex.printStackTrace();
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003408 }
Richard S. Halla155e2e2006-03-09 21:20:02 +00003409
3410 // Maven uses a '-' to separate the version qualifier,
3411 // while OSGi uses a '.', so we need to convert to a '.'
3412 StringBuffer sb =
3413 new StringBuffer(
3414 props.getProperty(
3415 FelixConstants.FELIX_VERSION_PROPERTY, "unknown"));
Richard S. Hall1615e552006-05-30 14:10:06 +00003416 if (sb.toString().indexOf("-") >= 0)
Richard S. Halla155e2e2006-03-09 21:20:02 +00003417 {
Richard S. Hall1615e552006-05-30 14:10:06 +00003418 sb.setCharAt(sb.toString().indexOf("-"), '.');
Richard S. Halla155e2e2006-03-09 21:20:02 +00003419 }
3420 return sb.toString();
Alex Karasulu5cfd2a02006-03-09 14:15:38 +00003421 }
3422
Richard S. Hall930fecc2005-08-16 18:33:34 +00003423 private void processAutoProperties()
3424 {
3425 // The auto-install property specifies a space-delimited list of
3426 // bundle URLs to be automatically installed into each new profile;
3427 // the start level to which the bundles are assigned is specified by
3428 // appending a ".n" to the auto-install property name, where "n" is
3429 // the desired start level for the list of bundles.
3430 String[] keys = m_config.getKeys();
3431 for (int i = 0; (keys != null) && (i < keys.length); i++)
3432 {
3433 if (keys[i].startsWith(FelixConstants.AUTO_INSTALL_PROP))
3434 {
3435 int startLevel = 1;
3436 try
3437 {
3438 startLevel = Integer.parseInt(keys[i].substring(keys[i].lastIndexOf('.') + 1));
3439 }
3440 catch (NumberFormatException ex)
3441 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003442 m_logger.log(Logger.LOG_ERROR, "Invalid property: " + keys[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003443 }
3444 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3445 if (st.countTokens() > 0)
3446 {
3447 String location = null;
3448 do
3449 {
3450 location = nextLocation(st);
3451 if (location != null)
3452 {
3453 try
3454 {
3455 BundleImpl b = (BundleImpl) installBundle(location, null);
3456 b.getInfo().setStartLevel(startLevel);
3457 }
3458 catch (Exception ex)
3459 {
3460 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003461 Logger.LOG_ERROR, "Auto-properties install.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003462 }
3463 }
3464 }
3465 while (location != null);
3466 }
3467 }
3468 }
3469
3470 // The auto-start property specifies a space-delimited list of
3471 // bundle URLs to be automatically installed and started into each
3472 // new profile; the start level to which the bundles are assigned
3473 // is specified by appending a ".n" to the auto-start property name,
3474 // where "n" is the desired start level for the list of bundles.
3475 // The following code starts bundles in two passes, first it installs
3476 // them, then it starts them.
3477 for (int i = 0; (keys != null) && (i < keys.length); i++)
3478 {
3479 if (keys[i].startsWith(FelixConstants.AUTO_START_PROP))
3480 {
3481 int startLevel = 1;
3482 try
3483 {
3484 startLevel = Integer.parseInt(keys[i].substring(keys[i].lastIndexOf('.') + 1));
3485 }
3486 catch (NumberFormatException ex)
3487 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003488 m_logger.log(Logger.LOG_ERROR, "Invalid property: " + keys[i]);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003489 }
3490 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3491 if (st.countTokens() > 0)
3492 {
3493 String location = null;
3494 do
3495 {
3496 location = nextLocation(st);
3497 if (location != null)
3498 {
3499 try
3500 {
3501 BundleImpl b = (BundleImpl) installBundle(location, null);
3502 b.getInfo().setStartLevel(startLevel);
3503 }
3504 catch (Exception ex)
3505 {
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003506 m_logger.log(Logger.LOG_ERROR, "Auto-properties install.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003507 }
3508 }
3509 }
3510 while (location != null);
3511 }
3512 }
3513 }
3514
3515 // Now loop through and start the installed bundles.
3516 for (int i = 0; (keys != null) && (i < keys.length); i++)
3517 {
3518 if (keys[i].startsWith(FelixConstants.AUTO_START_PROP))
3519 {
3520 StringTokenizer st = new StringTokenizer(m_config.get(keys[i]), "\" ",true);
3521 if (st.countTokens() > 0)
3522 {
3523 String location = null;
3524 do
3525 {
3526 location = nextLocation(st);
3527 if (location != null)
3528 {
3529 // Installing twice just returns the same bundle.
3530 try
3531 {
3532 BundleImpl bundle = (BundleImpl) installBundle(location, null);
3533 if (bundle != null)
3534 {
3535 startBundle(bundle, true);
3536 }
3537 }
3538 catch (Exception ex)
3539 {
3540 m_logger.log(
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003541 Logger.LOG_ERROR, "Auto-properties start.", ex);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003542 }
3543 }
3544 }
3545 while (location != null);
3546 }
3547 }
3548 }
3549 }
3550
3551 private String nextLocation(StringTokenizer st)
3552 {
3553 String retVal = null;
3554
3555 if (st.countTokens() > 0)
3556 {
3557 String tokenList = "\" ";
3558 StringBuffer tokBuf = new StringBuffer(10);
3559 String tok = null;
3560 boolean inQuote = false;
3561 boolean tokStarted = false;
3562 boolean exit = false;
3563 while ((st.hasMoreTokens()) && (!exit))
3564 {
3565 tok = st.nextToken(tokenList);
3566 if (tok.equals("\""))
3567 {
3568 inQuote = ! inQuote;
3569 if (inQuote)
3570 {
3571 tokenList = "\"";
3572 }
3573 else
3574 {
3575 tokenList = "\" ";
3576 }
3577
3578 }
3579 else if (tok.equals(" "))
3580 {
3581 if (tokStarted)
3582 {
3583 retVal = tokBuf.toString();
3584 tokStarted=false;
3585 tokBuf = new StringBuffer(10);
3586 exit = true;
3587 }
3588 }
3589 else
3590 {
3591 tokStarted = true;
3592 tokBuf.append(tok.trim());
3593 }
3594 }
3595
3596 // Handle case where end of token stream and
3597 // still got data
3598 if ((!exit) && (tokStarted))
3599 {
3600 retVal = tokBuf.toString();
3601 }
3602 }
3603
3604 return retVal;
3605 }
3606
3607 //
3608 // Private utility methods.
3609 //
3610
3611 /**
3612 * Generated the next valid bundle identifier.
3613 **/
Richard S. Hall441c7152006-02-17 11:07:10 +00003614 private long getNextId()
Richard S. Hall930fecc2005-08-16 18:33:34 +00003615 {
Richard S. Hall441c7152006-02-17 11:07:10 +00003616 synchronized (m_nextIdLock)
3617 {
3618 return m_nextId++;
3619 }
Richard S. Hall930fecc2005-08-16 18:33:34 +00003620 }
3621
3622 //
3623 // Configuration methods and inner classes.
3624 //
3625
3626 public PropertyResolver getConfig()
3627 {
3628 return m_config;
3629 }
3630
3631 private class ConfigImpl implements PropertyResolver
3632 {
3633 public String get(String key)
3634 {
Richard S. Hallea415752005-12-05 19:30:28 +00003635 return (m_configMutable == null) ? null : m_configMutable.get(key);
Richard S. Hall930fecc2005-08-16 18:33:34 +00003636 }
3637
3638 public String[] getKeys()
3639 {
Richard S. Hallea415752005-12-05 19:30:28 +00003640 return m_configMutable.getKeys();
Richard S. Hall930fecc2005-08-16 18:33:34 +00003641 }
3642 }
3643
3644 //
3645 // Logging methods and inner classes.
3646 //
3647
Richard S. Hall29a4fbc2006-02-03 12:54:52 +00003648 public Logger getLogger()
Richard S. Hall930fecc2005-08-16 18:33:34 +00003649 {
3650 return m_logger;
3651 }
3652
3653 /**
3654 * Simple class that is used in <tt>refreshPackages()</tt> to embody
3655 * the refresh logic in order to keep the code clean. This class is
3656 * not static because it needs access to framework event firing methods.
3657 **/
3658 private class RefreshHelper
3659 {
3660 private BundleImpl m_bundle = null;
3661
3662 public RefreshHelper(Bundle bundle)
3663 {
3664 m_bundle = (BundleImpl) bundle;
3665 }
3666
3667 public void stop()
3668 {
3669 if (m_bundle.getInfo().getState() == Bundle.ACTIVE)
3670 {
3671 try
3672 {
3673 stopBundle(m_bundle, false);
3674 }
3675 catch (BundleException ex)
3676 {
3677 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3678 }
3679 }
3680 }
3681
3682 public void purgeOrRemove()
3683 {
3684 try
3685 {
3686 BundleInfo info = m_bundle.getInfo();
3687
Richard S. Halle1f53a52006-07-17 11:06:59 +00003688 // Mark the bundle as stale.
3689 info.setStale();
3690
Richard S. Hall930fecc2005-08-16 18:33:34 +00003691 // Remove or purge the bundle depending on its
3692 // current state.
3693 if (info.getState() == Bundle.UNINSTALLED)
3694 {
3695 // This physically removes the bundle from memory
3696 // as well as the bundle cache.
3697 garbageCollectBundle(m_bundle);
3698 m_bundle = null;
3699 }
3700 else
3701 {
3702 // This physically removes all old revisions of the
3703 // bundle from memory and only maintains the newest
3704 // version in the bundle cache.
3705 purgeBundle(m_bundle);
3706 }
3707 }
3708 catch (Exception ex)
3709 {
3710 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3711 }
3712 }
3713
3714 public void reinitialize()
3715 {
3716 if (m_bundle != null)
3717 {
3718 try
3719 {
3720 BundleInfo info = m_bundle.getInfo();
3721 BundleInfo newInfo = createBundleInfo(info.getArchive());
3722 newInfo.syncLock(info);
3723 m_bundle.setInfo(newInfo);
3724 }
3725 catch (Exception ex)
3726 {
3727 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3728 }
3729 }
3730 }
3731
3732 public void restart()
3733 {
3734 if (m_bundle != null)
3735 {
3736 try
3737 {
3738 startBundle(m_bundle, false);
3739 }
3740 catch (BundleException ex)
3741 {
3742 fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
3743 }
3744 }
3745 }
3746 }
3747
3748 //
3749 // Locking related methods.
3750 //
3751
3752 private void rememberUninstalledBundle(BundleImpl bundle)
3753 {
3754 synchronized (m_uninstalledBundlesLock_Priority3)
3755 {
3756 // Verify that the bundle is not already in the array.
3757 for (int i = 0;
3758 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
3759 i++)
3760 {
3761 if (m_uninstalledBundles[i] == bundle)
3762 {
3763 return;
3764 }
3765 }
3766
3767 if (m_uninstalledBundles != null)
3768 {
3769 BundleImpl[] newBundles =
3770 new BundleImpl[m_uninstalledBundles.length + 1];
3771 System.arraycopy(m_uninstalledBundles, 0,
3772 newBundles, 0, m_uninstalledBundles.length);
3773 newBundles[m_uninstalledBundles.length] = bundle;
3774 m_uninstalledBundles = newBundles;
3775 }
3776 else
3777 {
3778 m_uninstalledBundles = new BundleImpl[] { bundle };
3779 }
3780 }
3781 }
3782
3783 private void forgetUninstalledBundle(BundleImpl bundle)
3784 {
3785 synchronized (m_uninstalledBundlesLock_Priority3)
3786 {
3787 if (m_uninstalledBundles == null)
3788 {
3789 return;
3790 }
3791
3792 int idx = -1;
3793 for (int i = 0; i < m_uninstalledBundles.length; i++)
3794 {
3795 if (m_uninstalledBundles[i] == bundle)
3796 {
3797 idx = i;
3798 break;
3799 }
3800 }
3801
3802 if (idx >= 0)
3803 {
3804 // If this is the only bundle, then point to empty list.
3805 if ((m_uninstalledBundles.length - 1) == 0)
3806 {
3807 m_uninstalledBundles = new BundleImpl[0];
3808 }
3809 // Otherwise, we need to do some array copying.
3810 else
3811 {
3812 BundleImpl[] newBundles =
3813 new BundleImpl[m_uninstalledBundles.length - 1];
3814 System.arraycopy(m_uninstalledBundles, 0, newBundles, 0, idx);
3815 if (idx < newBundles.length)
3816 {
3817 System.arraycopy(
3818 m_uninstalledBundles, idx + 1,
3819 newBundles, idx, newBundles.length - idx);
3820 }
3821 m_uninstalledBundles = newBundles;
3822 }
3823 }
3824 }
3825 }
3826
3827 protected void acquireInstallLock(String location)
3828 throws BundleException
3829 {
3830 synchronized (m_installRequestLock_Priority1)
3831 {
3832 while (m_installRequestMap.get(location) != null)
3833 {
3834 try
3835 {
3836 m_installRequestLock_Priority1.wait();
3837 }
3838 catch (InterruptedException ex)
3839 {
3840 throw new BundleException("Unable to install, thread interrupted.");
3841 }
3842 }
3843
3844 m_installRequestMap.put(location, location);
3845 }
3846 }
3847
3848 protected void releaseInstallLock(String location)
3849 {
3850 synchronized (m_installRequestLock_Priority1)
3851 {
3852 m_installRequestMap.remove(location);
3853 m_installRequestLock_Priority1.notifyAll();
3854 }
3855 }
3856
3857 protected void acquireBundleLock(BundleImpl bundle)
Richard S. Hall930fecc2005-08-16 18:33:34 +00003858 {
3859 synchronized (m_bundleLock)
3860 {
3861 while (!bundle.getInfo().isLockable())
3862 {
3863 try
3864 {
3865 m_bundleLock.wait();
3866 }
3867 catch (InterruptedException ex)
3868 {
3869 // Ignore and just keep waiting.
3870 }
3871 }
3872 bundle.getInfo().lock();
3873 }
3874 }
3875
Richard S. Hall3c26cc02006-02-17 13:51:21 +00003876 protected boolean acquireBundleLockOrFail(BundleImpl bundle)
3877 {
3878 synchronized (m_bundleLock)
3879 {
3880 if (!bundle.getInfo().isLockable())
3881 {
3882 return false;
3883 }
3884 bundle.getInfo().lock();
3885 return true;
3886 }
3887 }
3888
Richard S. Hall930fecc2005-08-16 18:33:34 +00003889 protected void releaseBundleLock(BundleImpl bundle)
3890 {
3891 synchronized (m_bundleLock)
3892 {
3893 bundle.getInfo().unlock();
3894 m_bundleLock.notifyAll();
3895 }
3896 }
3897
Richard S. Hall1a4ab602006-05-24 13:46:06 +00003898 protected BundleImpl[] acquireBundleResolveLocks(Bundle[] targets)
3899 {
3900 // Hold bundles to be locked.
3901 BundleImpl[] bundles = null;
3902 // Convert existing target bundle array to bundle impl array.
3903 if (targets != null)
3904 {
3905 bundles = new BundleImpl[targets.length];
3906 for (int i = 0; i < targets.length; i++)
3907 {
3908 bundles[i] = (BundleImpl) targets[i];
3909 }
3910 }
3911
3912 synchronized (m_bundleLock)
3913 {
3914 boolean success = false;
3915 while (!success)
3916 {
3917 // If targets is null, then resolve all unresolved bundles.
3918 if (targets == null)
3919 {
3920 List list = new ArrayList();
3921
3922 // Add all unresolved bundles to the list.
3923 synchronized (m_installedBundleLock_Priority2)
3924 {
3925 Iterator iter = m_installedBundleMap.values().iterator();
3926 while (iter.hasNext())
3927 {
3928 BundleImpl bundle = (BundleImpl) iter.next();
3929 if (bundle.getInfo().getState() == Bundle.INSTALLED)
3930 {
3931 list.add(bundle);
3932 }
3933 }
3934 }
3935
3936 // Create an array.
3937 if (list.size() > 0)
3938 {
3939 bundles = (BundleImpl[]) list.toArray(new BundleImpl[list.size()]);
3940 }
3941 }
3942
3943 // Check if all unresolved bundles can be locked.
3944 boolean lockable = true;
3945 if (bundles != null)
3946 {
3947 for (int i = 0; lockable && (i < bundles.length); i++)
3948 {
3949 lockable = bundles[i].getInfo().isLockable();
3950 }
3951
3952 // If we can lock all bundles, then lock them.
3953 if (lockable)
3954 {
3955 for (int i = 0; i < bundles.length; i++)
3956 {
3957 bundles[i].getInfo().lock();
3958 }
3959 success = true;
3960 }
3961 // Otherwise, wait and try again.
3962 else
3963 {
3964 try
3965 {
3966 m_bundleLock.wait();
3967 }
3968 catch (InterruptedException ex)
3969 {
3970 // Ignore and just keep waiting.
3971 }
3972 }
3973 }
3974 else
3975 {
3976 // If there were no bundles to lock, then we can just
3977 // exit the lock loop.
3978 success = true;
3979 }
3980 }
3981 }
3982
3983 return bundles;
3984 }
3985
Richard S. Hall930fecc2005-08-16 18:33:34 +00003986 protected BundleImpl[] acquireBundleRefreshLocks(Bundle[] targets)
3987 {
3988 // Hold bundles to be locked.
3989 BundleImpl[] bundles = null;
3990
3991 synchronized (m_bundleLock)
3992 {
3993 boolean success = false;
3994 while (!success)
3995 {
3996 // If targets is null, then refresh all pending bundles.
3997 Bundle[] newTargets = targets;
3998 if (newTargets == null)
3999 {
4000 List list = new ArrayList();
4001
4002 // First add all uninstalled bundles.
4003 synchronized (m_uninstalledBundlesLock_Priority3)
4004 {
4005 for (int i = 0;
4006 (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
4007 i++)
4008 {
4009 list.add(m_uninstalledBundles[i]);
4010 }
4011 }
4012
4013 // Then add all updated bundles.
4014 synchronized (m_installedBundleLock_Priority2)
4015 {
4016 Iterator iter = m_installedBundleMap.values().iterator();
4017 while (iter.hasNext())
4018 {
4019 BundleImpl bundle = (BundleImpl) iter.next();
Richard S. Hall60c26d42006-07-19 10:35:04 +00004020 if (bundle.getInfo().getArchive().getRevisionCount() > 1)
Richard S. Hall930fecc2005-08-16 18:33:34 +00004021 {
4022 list.add(bundle);
4023 }
4024 }
4025 }
4026
4027 // Create an array.
4028 if (list.size() > 0)
4029 {
4030 newTargets = (Bundle[]) list.toArray(new Bundle[list.size()]);
4031 }
4032 }
4033
4034 // If there are targets, then find all dependencies
4035 // for each one.
4036 if (newTargets != null)
4037 {
4038 // Create map of bundles that import the packages
4039 // from the target bundles.
4040 Map map = new HashMap();
4041 for (int targetIdx = 0; targetIdx < newTargets.length; targetIdx++)
4042 {
4043 // Add the current target bundle to the map of
4044 // bundles to be refreshed.
4045 BundleImpl target = (BundleImpl) newTargets[targetIdx];
4046 map.put(target, target);
4047 // Add all importing bundles to map.
4048 populateImportGraph(target, map);
4049 }
4050
4051 bundles = (BundleImpl[]) map.values().toArray(new BundleImpl[map.size()]);
4052 }
4053
Richard S. Hall1a4ab602006-05-24 13:46:06 +00004054 // Check if all corresponding bundles can be locked.
Richard S. Hall930fecc2005-08-16 18:33:34 +00004055 boolean lockable = true;
4056 if (bundles != null)
4057 {
4058 for (int i = 0; lockable && (i < bundles.length); i++)
4059 {
4060 lockable = bundles[i].getInfo().isLockable();
4061 }
4062
4063 // If we can lock all bundles, then lock them.
4064 if (lockable)
4065 {
4066 for (int i = 0; i < bundles.length; i++)
4067 {
4068 bundles[i].getInfo().lock();
4069 }
4070 success = true;
4071 }
4072 // Otherwise, wait and try again.
4073 else
4074 {
4075 try
4076 {
4077 m_bundleLock.wait();
4078 }
4079 catch (InterruptedException ex)
4080 {
4081 // Ignore and just keep waiting.
4082 }
4083 }
4084 }
4085 else
4086 {
4087 // If there were no bundles to lock, then we can just
4088 // exit the lock loop.
4089 success = true;
4090 }
4091 }
4092 }
4093
4094 return bundles;
4095 }
4096
4097 protected void releaseBundleLocks(BundleImpl[] bundles)
4098 {
4099 // Always unlock any locked bundles.
4100 synchronized (m_bundleLock)
4101 {
4102 for (int i = 0; (bundles != null) && (i < bundles.length); i++)
4103 {
4104 bundles[i].getInfo().unlock();
4105 }
4106 m_bundleLock.notifyAll();
4107 }
4108 }
Richard S. Hall9a3e9852006-03-04 03:44:05 +00004109}