KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ungoverned > oscar > Oscar


1 /*
2  * Oscar - An implementation of the OSGi framework.
3  * Copyright (c) 2004, Richard S. Hall
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of the ungoverned.org nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * Contact: Richard S. Hall (heavy@ungoverned.org)
33  * Contributor(s):
34  *
35 **/

36 package org.ungoverned.oscar;
37
38 import java.io.*;
39 import java.net.*;
40 import java.security.*;
41 import java.security.cert.Certificate JavaDoc;
42 import java.util.*;
43
44 import org.osgi.framework.*;
45 import org.osgi.service.packageadmin.ExportedPackage;
46 import org.ungoverned.moduleloader.*;
47 import org.ungoverned.moduleloader.search.ImportSearchPolicy;
48 import org.ungoverned.moduleloader.search.ValidationException;
49 import org.ungoverned.moduleloader.search.ValidationListener;
50 import org.ungoverned.oscar.util.*;
51
52 public class Oscar
53 {
54     // Debug output.
55
private static PrintStream m_debugOut = null;
56     // Error output.
57
private static PrintStream m_errorOut = null;
58     // Output lock.
59
private static String JavaDoc m_outputLockObj = new String JavaDoc("output lock");
60
61     // CONCURRENCY NOTE: The admin lock is used when
62
// performing an administrative operation; this prevents
63
// multiple administrative operations (e.g., install,
64
// start, refresh, etc.) from happening at the same
65
// time. The quick lock is used when performing a quick
66
// operation that potentially only needs to read
67
// information or to modify a single bundle in some
68
// small way. To ensure total isolation in the framework
69
// core (i.e., only one thread is present), it is necessary
70
// to acquire both the admin and quick locks. In such a
71
// scenario, the quick lock should only be held for the
72
// shortest possible time so that other quick operations
73
// can occur in parallel to admin operations; otherwise,
74
// the potential for deadlock is great. For admin operations,
75
// the exclusion lock should always be acquired before
76
// the quick lock.
77
//
78
// When using only the quick lock, it is important that the
79
// code inside the lock does not reference directly or
80
// indirectly (i.e., via event callbacks) code that will
81
// try to acquire the admin lock. If this happens then
82
// deadlocks can occur.
83

84     // Admin lock.
85
private String JavaDoc m_adminLock = new String JavaDoc("admin lock");
86     // Quick lock.
87
private String JavaDoc m_quickLock = new String JavaDoc("quick lock");
88
89     // MODULE MANAGER.
90
private ModuleManager m_mgr = null;
91
92     // Map of configuration properties passed into constructor.
93
private transient Map m_configPropMap = null;
94     // Next available bundle identifier.
95
private transient long m_nextId = 1L;
96     // Next available service identifier.
97
private transient long m_nextServiceId = 1L;
98     // Framework's active start level.
99
private transient int m_activeStartLevel =
100         OscarConstants.FRAMEWORK_INACTIVE_STARTLEVEL;
101
102     // List of event listeners.
103
private transient OscarDispatchQueue m_dispatchQueue = null;
104     // Re-usable event dispatchers.
105
private Dispatcher m_frameworkDispatcher = null;
106     private Dispatcher m_bundleDispatcher = null;
107     private Dispatcher m_serviceDispatcher = null;
108
109     // Maps a bundle location to a bundle.
110
private transient HashMap m_installedBundleMap = null;
111     // An array of uninstalled bundles before a refresh occurs.
112
private transient BundleImpl[] m_uninstalledBundles = null;
113
114     // Local file system cache.
115
private transient BundleCache m_cache = null;
116     // Place to store and obtain framework properties
117
private transient Map m_frameworkPropMap = null;
118
119     // Reusable admin permission object for all instances
120
// of the BundleImpl.
121
private static AdminPermission m_adminPerm = new AdminPermission();
122
123     // Reusable privileged action object for starting/stopping
124
// bundles.
125
private StartStopPrivileged m_startStopPrivileged = new StartStopPrivileged(this);
126
127     // Status flag for Oscar.
128
public static final int UNKNOWN_STATUS = -1;
129     public static final int RUNNING_STATUS = 0;
130     public static final int STARTING_STATUS = 1;
131     public static final int STOPPING_STATUS = 2;
132     private transient int m_oscarStatus = UNKNOWN_STATUS;
133
134     /**
135      * This static code block automatically loads the system
136      * properties associated with the Oscar installation as
137      * soon as the class is loaded.
138     **/

139     static
140     {
141         initializeSystemProperties();
142     }
143
144     /**
145      * <p>
146      * Creates an instance of Oscar where all configuration properties
147      * (e.g., profile name, profile directory, strictness, and
148      * embedded execution) are specified using system properties.
149      * If the <a HREF="util/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
150      * is being used, then at a minimum a profile name or profile
151      * directory must be specified in the system properties.
152      * </p>
153      * <p>
154      * System properties can be set using a <tt>system.properties</tt>
155      * file; refer to the <a HREF="http://oscar.objectweb.org/usage.html">usage
156      * document</a> for more information.
157      * </p>
158      * @see #Oscar(java.util.Properties) For information on Oscar properties.
159     **/

160     public Oscar()
161     {
162         this(null, null);
163     }
164
165     /**
166      * <p>
167      * Creates an instance of Oscar where all configuration
168      * properties (e.g., profile name, profile directory, strictness, and
169      * embedded execution) are specified in the supplied <tt>Properties</tt>
170      * instance. If <tt>Properties</tt> is a null instance, then
171      * <tt>System.getProperty()</tt> is used to find all configuration
172      * properties. If the
173      * <a HREF="util/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
174      * is being used, then at a minimum a profile name or profile
175      * directory must be specified.
176      * </p>
177      * <p>
178      * The following are framework configuration properties that can be
179      * specified in the <tt>Properties</tt> instance:
180      * </p>
181      * <ul>
182      * <li><tt>oscar.bundle.properties</tt> - The location of the
183      * <tt>bundle.properties</tt> file; by default this file is
184      * located in the <tt>lib</tt> directory of the Oscar installation
185      * directory. This file contains attribute-value property pairs and
186      * Oscar will automatically load these properties and make them
187      * available via <tt>BundleContext.getProperty()</tt>.
188      * </li>
189      * <li><tt>oscar.cache.class</tt> - The class name to be used when
190      * creating an instance for the bundle cache; this class must
191      * implement the <tt>BundleCache</tt> interface and have a default
192      * constructor. By default, Oscar will create an instance of
193      * <tt>DefaultBundleCache</tt> for the bundle cache.
194      * </li>
195      * <li><tt>oscar.auto.install</tt> - Space-delimited list of bundles
196      * to automatically install when Oscar is started. By default,
197      * the bundles specified in this property will be installed into
198      * start level 1. It is also possible to append a specific
199      * start level to this property name to assign to the specified
200      * bundles (e.g., <tt>oscar.auto.install.2</tt>). These variants
201      * will be processed in sequence until a successor cannot be
202      * found.
203      * </li>
204      * <li><tt>oscar.auto.start</tt> - Space-delimited list of bundles
205      * to automatically install and start when Oscar is started.
206      * By default, the bundles specified in this property will be
207      * installed into start level 1. It is also possible to append a
208      * specific start level to this property name to assign to the
209      * specified bundles (e.g., <tt>oscar.auto.start.2</tt>). These
210      * variants will be processed in sequence until a successor cannot
211      * be found.
212      * </li>
213      * <li><tt>oscar.startlevel.framework</tt> - The initial start level
214      * of the Oscar framework once it starts execution; the default
215      * value is 1.
216      * </li>
217      * <li><tt>oscar.startlevel.bundle</tt> - The default start level for
218      * newly installed bundles; the default value is 1.
219      * </li>
220      * <li><tt>oscar.embedded.execution</tt> - Flag to indicate whether
221      * Oscar is embedded into a host application; the default value is
222      * "<tt>false</tt>". If this flag is "<tt>true</tt>" then Oscar
223      * will not called <tt>System.exit()</tt> upon termination.
224      * </li>
225      * <li><tt>oscar.strict.osgi</tt> - Flag to indicate whether Oscar is
226      * running in strict OSGi mode; the default value is "<tt>true</tt>".
227      * If this flag is "<tt>false</tt>" it enables a non-OSGi-compliant
228      * feature by persisting <tt>BundleActivator</tt>s that implement
229      * <tt>Serializable</tt>. This feature is not recommended since
230      * it is non-compliant.
231      * </li>
232      * </ul>
233      * <p>
234      * Besides the above framework properties, it is also possible to set
235      * properties for the bundle cache via the Oscar constructor. The
236      * available bundle cache properties depend on the cache implementation
237      * being used. For the properties of the default bundle cache, refer to the
238      * <a HREF="util/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
239      * API documentation. All of these properties can specified in one
240      * of three ways:
241      * </p>
242      * <ul>
243      * <li>On the command line when starting the JVM using the "<tt>-D</tt>"
244      * option.
245      * </li>
246      * <li>In the <tt>system.properties</tt> file.
247      * </li>
248      * <li>In the <tt>Properties</tt> instance supplied to the Oscar
249      * constructor.</tt>
250      * </li>
251      * </ul>
252      * <p>
253      * The <tt>system.properties</tt> file overwrites any property values
254      * specified on the command line. If a <tt>Properties</tt> instance is
255      * passed into Oscar's constructor, then all system properties are
256      * ignored and only the <tt>Properties</tt> instance is used to locate
257      * configuration property values.
258      * </p>
259      * @param props the properties used to initialize Oscar; may also
260      * be <tt>null</tt>.
261     **/

262     public Oscar(Properties props)
263     {
264         this(props, null);
265     }
266
267     /**
268      * <p>
269      * Creates an instance of Oscar where all configuration properties
270      * (e.g., profile name, profile directory, strictness, and
271      * embedded execution) are specified using system properties.
272      * If the <a HREF="util/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
273      * is being used, then at a minimum a profile name or profile
274      * directory must be specified in the system properties.
275      * This constructor accepts a list of <tt>BundleActivator</tt>
276      * instances that will be started/stopped by the System Bundle when
277      * the framework is started/stopped; this is useful for when Oscar
278      * is embedded into a host application that wants to provide services
279      * to the bundles inside of Oscar.
280      * </p>
281      * @param activatorList list of bundle activators to be started/stopped by
282      * the system bundle; may also be <tt>null</tt>
283      * @see #Oscar(java.util.Properties) For information on Oscar properties.
284     **/

285     public Oscar(List activatorList)
286     {
287         this(null, activatorList);
288     }
289
290     /**
291      * <p>
292      * Creates an instance of Oscar where all configuration
293      * properties (e.g., profile name, profile directory, strictness, and
294      * embedded execution) are specified in the supplied <tt>Properties</tt>
295      * instance. If <tt>Properties</tt> is a null instance, then
296      * <tt>System.getProperty()</tt> is used to find all configuration
297      * properties. If the
298      * <a HREF="util/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
299      * is being used, then at a minimum a profile name or profile
300      * directory must be specified in the system properties.
301      * This constructor also accepts a list of <tt>BundleActivator</tt>
302      * instances that will be started/stopped by the System Bundle when
303      * the framework is started/stopped; this is useful for when Oscar is
304      * embedded into a host application that wants to provide services to
305      * the bundles inside of Oscar.
306      * </p>
307      * @param props the properties used to initialize Oscar; may also
308      * be <tt>null</tt>.
309      * @param activatorList list of bundle activators to be started/stopped by
310      * the system bundle; may also be <tt>null</tt>
311      * @see #Oscar(java.util.Properties) For information on Oscar properties.
312     **/

313     public Oscar(Properties props, List activatorList)
314     {
315         // Create a copy of the passed in configuration properties.
316
if (props != null)
317         {
318             m_configPropMap = new HashMap();
319             for (Enumeration e = props.propertyNames(); e.hasMoreElements(); )
320             {
321                 String JavaDoc name = (String JavaDoc) e.nextElement();
322                 m_configPropMap.put(name, props.getProperty(name));
323             }
324         }
325
326         // Create default storage system from the specified cache class
327
// or use the default cache if no custom cache was specified.
328
String JavaDoc className = getConfigProperty(OscarConstants.CACHE_CLASS_PROP);
329         if (className == null)
330         {
331             className = DefaultBundleCache.class.getName();
332         }
333
334         try
335         {
336             Class JavaDoc clazz = Class.forName(className);
337             m_cache = (BundleCache) clazz.newInstance();
338             m_cache.initialize(this);
339         }
340         catch (Exception JavaDoc ex)
341         {
342             System.err.println("Error creating bundle cache:");
343             ex.printStackTrace();
344             System.err.println("\nThis may result from the fact that Oscar 1.0 uses a");
345             System.err.println("different bundle cache format than previous versions");
346             System.err.println("of Oscar. Please read the bundle cache documentation for");
347             System.err.println("more details: http://oscar.objectweb.org/cache.html.");
348
349             // Only shutdown the JVM if Oscar is running stand-alone.
350
String JavaDoc embedded = getConfigProperty(
351                 OscarConstants.EMBEDDED_EXECUTION_PROP);
352             boolean isEmbedded = (embedded == null)
353                 ? false : embedded.equals("true");
354             if (!isEmbedded)
355             {
356                 System.exit(-1);
357             }
358             else
359             {
360                 throw new RuntimeException JavaDoc(ex.toString());
361             }
362         }
363
364         // Initialize.
365
initialize(activatorList);
366     }
367
368     /**
369      * <p>
370      * Oscar uses this method whenever it needs to access any
371      * configuration properties. This will look for a configuration
372      * property either in the system properties or in the configuration
373      * property map that was passed into the Oscar constructor; that is,
374      * Oscar will look in one place or the other, but not both. This
375      * approach was taken to simplify support for having multiple
376      * instances of Oscar in memory at the same time.
377      * </p>
378      * <p>
379      * The approach is very simple. If no <tt>Properties</tt> instance
380      * was passed into the constructor, then this method only searches
381      * <tt>System.getProperty()</tt> to find property values. If a
382      * <tt>Properties</tt> instance was passed into the constructor,
383      * then it only searches the supplied instance. When creating multiple
384      * instances of Oscar, a <tt>Properties</tt> instance should be
385      * supplied to the constructor so that all instances do not end up
386      * using the same bundle cache directory.
387      * </p>
388      * @param name the name of the configuration property to retrieve.
389      * @return the value of the specified configuration property or <tt>null</tt>
390      * if the property is not defined.
391     **/

392     public String JavaDoc getConfigProperty(String JavaDoc name)
393     {
394         if (m_configPropMap != null)
395         {
396             return (String JavaDoc) m_configPropMap.get(name);
397         }
398         return System.getProperty(name);
399     }
400
401     private void setConfigProperty(String JavaDoc name, String JavaDoc value)
402     {
403         // If there is a config property map, then
404
// set the value in it, otherwise put it in
405
// the system properties.
406
if (m_configPropMap != null)
407         {
408             m_configPropMap.put(name, value);
409         }
410         else
411         {
412             System.setProperty(name, value);
413         }
414     }
415
416     /**
417      * Called to initialize a new instance.
418     **/

419     private void initialize(List activatorList)
420     {
421         ImportSearchPolicy searchPolicy = new OSGiImportSearchPolicy(this);
422         m_mgr = new ModuleManager(searchPolicy, new OSGiURLPolicy(this));
423
424         // Add the OSGi selection policy as a module listener, since
425
// it needs to keep track of when modules are removed so that
426
// it can flush its package export cache.
427
m_mgr.addModuleListener((OSGiSelectionPolicy) searchPolicy.getSelectionPolicy());
428
429         // Add a validation listener to the import/export search policy
430
// so that we will be notified when modules are validated
431
// (i.e., resolved) in order to update the bundle state.
432
searchPolicy.addValidationListener(new ValidationListener() {
433             public void moduleValidated(ModuleEvent event)
434             {
435                 synchronized (m_quickLock)
436                 {
437                     try
438                     {
439                         long id = BundleInfo.getBundleIdFromModuleId(
440                             event.getModule().getId());
441                         if (id >= 0)
442                         {
443                             // Update the bundle's state to resolved when the
444
// current module is resolved; just ignore resolve
445
// events for older revisions since this only occurs
446
// when an update is done on an unresolved bundle
447
// and there was no refresh performed.
448
BundleImpl bundle = (BundleImpl) getBundle(id);
449                             if (bundle.getInfo().getCurrentModule() == event.getModule())
450                             {
451                                 bundle.getInfo().setState(Bundle.RESOLVED);
452                             }
453                         }
454                     }
455                     catch (NumberFormatException JavaDoc ex)
456                     {
457                         // Ignore.
458
}
459                 }
460             }
461
462             public void moduleInvalidated(ModuleEvent event)
463             {
464                 // We can ignore this, because the only time it
465
// should happen is when a refresh occurs. The
466
// refresh operation resets the bundle's state
467
// by calling BundleInfo.reset(), thus it is not
468
// necessary for us to reset the bundle's state
469
// here.
470
}
471         });
472
473         // Oscar is now in its startup sequence.
474
m_oscarStatus = STARTING_STATUS;
475
476         // Turn on error information...
477
m_errorOut = System.err;
478
479         // Initialize private members.
480
m_dispatchQueue = new OscarDispatchQueue();
481         m_installedBundleMap = new HashMap();
482
483         // Set up the framework properties.
484
m_frameworkPropMap = new CaseInsensitiveMap();
485         initializeOsgiProperties();
486         initializeBundleProperties();
487
488         // Before we reload any cached bundles, let's create a system
489
// bundle that is responsible for providing specific container
490
// related services.
491
SystemBundle systembundle = null;
492         try
493         {
494             // Create a simple bundle info for the system bundle.
495
BundleInfo info = new BundleInfo(
496                 new SystemBundleArchive(), null);
497             systembundle = new SystemBundle(this, info, activatorList);
498             systembundle.getInfo().addModule(
499                 m_mgr.addModule(
500                     "0", systembundle.getAttributes(),
501                     systembundle.getResourceSources(),
502                     systembundle.getLibrarySources()));
503             m_installedBundleMap.put(
504                 systembundle.getInfo().getLocation(), systembundle);
505
506             // Manually validate the System Bundle, which will cause its
507
// state to be set to RESOLVED.
508
try
509             {
510                 searchPolicy.validate(systembundle.getInfo().getCurrentModule());
511             }
512             catch (ValidationException ex)
513             {
514                 // This should never happen.
515
int[] v = (int[]) ex.getVersion();
516                 throw new BundleException("Unresolved package: "
517                     + ex.getIdentifier() + "; specification-version=\""
518                     + v[0] + "." + v[1] + "." + v[2] + "\"");
519             }
520
521             // Start the system bundle; this will set its state
522
// to STARTING, we must set its state to ACTIVE after
523
// all bundles are restarted below according to the spec.
524
systembundle.start();
525         }
526         catch (Exception JavaDoc ex)
527         {
528             m_mgr = null;
529             Oscar.error("Unable to start system bundle: " + ex);
530             throw new RuntimeException JavaDoc("Unable to start system bundle.");
531         }
532
533         // Reload and cached bundles.
534
BundleArchive[] archives = null;
535
536         // First get cached bundle identifiers.
537
try
538         {
539             archives = m_cache.getArchives();
540         }
541         catch (Exception JavaDoc ex)
542         {
543             Oscar.error("Oscar: Unable to list saved bundles: " + ex);
544             archives = null;
545         }
546
547         BundleImpl bundle = null;
548
549         // Now install all cached bundles.
550
for (int i = 0; (archives != null) && (i < archives.length); i++)
551         {
552             // Make sure our id generator is not going to overlap.
553
// TODO: This is not correct since it may lead to re-used
554
// ids, which is not okay according to OSGi.
555
m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
556
557             try
558             {
559                 // It is possible that a bundle in the cache was previously
560
// uninstalled, but not completely deleted (perhaps because
561
// of a crash or a locked file), so if we see an archive
562
// with an UNINSTALLED persistent state, then try to remove
563
// it now.
564
if (archives[i].getPersistentState() == Bundle.UNINSTALLED)
565                 {
566                     m_cache.remove(archives[i]);
567                 }
568                 // Otherwise re-install the cached bundle.
569
else
570                 {
571                     // Install the cached bundle.
572
bundle = (BundleImpl) installBundle(
573                         archives[i].getId(), archives[i].getLocation(), null);
574                 }
575             }
576             catch (BundleException ex)
577             {
578                 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
579                 try
580                 {
581                     Oscar.error("Oscar: Unable to re-install "
582                         + archives[i].getLocation());
583                 }
584                 catch (Exception JavaDoc ex2)
585                 {
586                     Oscar.error("Oscar: Unable to re-install bundle "
587                         + archives[i].getId());
588                 }
589                 Oscar.error("Oscar: " + ex);
590                 // TODO: Perhaps we should remove the cached bundle?
591
}
592             catch (Exception JavaDoc ex)
593             {
594                 fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
595                 try
596                 {
597                     Oscar.error("Oscar: Exception while re-installing "
598                         + archives[i].getLocation());
599                 }
600                 catch (Exception JavaDoc ex2)
601                 {
602                     Oscar.error("Oscar: Exception while re-installing bundle "
603                         + archives[i].getId());
604                 }
605                 // TODO: Perhaps we should remove the cached bundle?
606
}
607         }
608
609         // Get the framework's default start level.
610
int startLevel = OscarConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
611         String JavaDoc s = getConfigProperty(OscarConstants.FRAMEWORK_STARTLEVEL_PROP);
612         if (s != null)
613         {
614             try
615             {
616                 startLevel = Integer.parseInt(s);
617             }
618             catch (NumberFormatException JavaDoc ex)
619             {
620                 startLevel = OscarConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
621             }
622         }
623
624         // Load bundles from auto-install and auto-start properties;
625
processAutoProperties();
626
627         // This will restart bundles if necessary.
628
setStartLevelInternal(startLevel);
629
630         // Oscar is now running.
631
m_oscarStatus = RUNNING_STATUS;
632
633         // Set the system bundle state to ACTIVE.
634
systembundle.getInfo().setState(Bundle.ACTIVE);
635
636         // Fire started event for system bundle.
637
fireBundleEvent(BundleEvent.STARTED, systembundle);
638
639         // Send a framework event to indicate Oscar has started.
640
fireFrameworkEvent(FrameworkEvent.STARTED, getBundle(0), null);
641     }
642
643     /**
644      * This method cleanly shuts down Oscar, it must be called at the
645      * end of a session in order to shutdown all active bundles.
646     **/

647     public void shutdown()
648     {
649         if (System.getSecurityManager() != null)
650         {
651             AccessController.checkPermission(m_adminPerm);
652         }
653
654         // Change Oscar status from running to stopping.
655
synchronized (m_adminLock)
656         {
657             // If Oscar is not running, then just return.
658
if (m_oscarStatus != RUNNING_STATUS)
659             {
660                 return;
661             }
662
663             // Oscar is now in its shutdown sequence.
664
m_oscarStatus = STOPPING_STATUS;
665         }
666
667         // Set the start level to zero in order to stop
668
// all bundles in the framework.
669
setStartLevelInternal(0);
670
671         // Just like initialize() called the system bundle's start()
672
// method, we must call its stop() method here so that it
673
// can perform any necessary clean up.
674
try {
675             getBundle(0).stop();
676         } catch (Exception JavaDoc ex) {
677             fireFrameworkEvent(FrameworkEvent.ERROR, getBundle(0), ex);
678             Oscar.error("Error stopping system bundle.", ex);
679         }
680
681         // Loop through all bundles and update any updated bundles.
682
Bundle[] bundles = getBundles();
683         for (int i = 0; i < bundles.length; i++)
684         {
685             BundleImpl bundle = (BundleImpl) bundles[i];
686             if (bundle.getInfo().isRemovalPending())
687             {
688                 try
689                 {
690                     purgeBundle(bundle);
691                 }
692                 catch (Exception JavaDoc ex)
693                 {
694                     fireFrameworkEvent(FrameworkEvent.ERROR, bundle, ex);
695                     Oscar.error("Oscar: Unable to purge bundle "
696                         + bundle.getInfo().getLocation());
697                 }
698             }
699         }
700
701         // Remove any uninstalled bundles.
702
for (int i = 0;
703             (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
704             i++)
705         {
706             try
707             {
708                 removeBundle(m_uninstalledBundles[i]);
709             }
710             catch (Exception JavaDoc ex)
711             {
712                 Oscar.error("Oscar: Unable to remove "
713                     + m_uninstalledBundles[i].getInfo().getLocation());
714             }
715         }
716
717         // Shutdown event dispatching queue.
718
DispatchQueue.shutdown();
719
720         // Oscar is no longer in a usable state.
721
m_oscarStatus = UNKNOWN_STATUS;
722     }
723
724     /**
725      * Returns the active start level of the framework; this method
726      * implements functionality for the Start Level service.
727      * @return the active start level of the framework.
728     **/

729     protected int getStartLevel()
730     {
731         return m_activeStartLevel;
732     }
733
734     /**
735      * Implements the functionality of the <tt>setStartLevel()</tt>
736      * method for the StartLevel service, but does not do the security or
737      * parameter check. The security and parameter check are done in the
738      * StartLevel service implementation because this method is called on
739      * a separate thread and the caller's thread would already be gone if
740      * we did the checks in this method.
741      * @param requestedLevel the new start level of the framework.
742     **/

743     protected void setStartLevelInternal(int requestedLevel)
744     {
745         // Determine if we are lowering or raising the
746
// active start level.
747
boolean lowering = (requestedLevel < m_activeStartLevel);
748
749         // Record new start level.
750
m_activeStartLevel = requestedLevel;
751
752         // Get exclusion lock to make sure that no one starts
753
// an operation that might affect the bundle list.
754
synchronized (m_adminLock)
755         {
756             // Get array of all installed bundles.
757
Bundle[] bundles = getBundles();
758
759             // Sort bundle array by start level either ascending or
760
// descending depending on whether the start level is being
761
// lowered or raised.
762
Comparator comparator = null;
763             if (lowering)
764             {
765                 // Sort descending to stop highest start level first.
766
comparator = new Comparator() {
767                     public int compare(Object JavaDoc o1, Object JavaDoc o2)
768                     {
769                         BundleImpl b1 = (BundleImpl) o1;
770                         BundleImpl b2 = (BundleImpl) o2;
771                         if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
772                             < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
773                         {
774                             return 1;
775                         }
776                         else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
777                             > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
778                         {
779                             return -1;
780                         }
781                         return 0;
782                     }
783                 };
784             }
785             else
786             {
787                 // Sort ascending to start lowest start level first.
788
comparator = new Comparator() {
789                     public int compare(Object JavaDoc o1, Object JavaDoc o2)
790                     {
791                         BundleImpl b1 = (BundleImpl) o1;
792                         BundleImpl b2 = (BundleImpl) o2;
793                         if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
794                             > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
795                         {
796                             return 1;
797                         }
798                         else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
799                             < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
800                         {
801                             return -1;
802                         }
803                         return 0;
804                     }
805                 };
806             }
807
808             Arrays.sort(bundles, comparator);
809
810             // Stop or start the bundles according to the start level.
811
for (int i = 0; (bundles != null) && (i < bundles.length); i++)
812             {
813                 BundleImpl impl = (BundleImpl) bundles[i];
814
815                 // Ignore the system bundle, since its start() and
816
// stop() methods get called explicitly in initialize()
817
// and shutdown(), respectively.
818
if (impl.getInfo().getBundleId() == 0)
819                 {
820                     continue;
821                 }
822
823                 // Start or stop bundle accordingly.
824
if (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
825                     <= m_activeStartLevel)
826                 {
827                     try
828                     {
829                         startBundleWithStartLevel(impl);
830                     }
831                     catch (Throwable JavaDoc th)
832                     {
833 th.printStackTrace();
834                         fireFrameworkEvent(FrameworkEvent.ERROR, impl, th);
835                         Oscar.error("Oscar: Error starting "
836                             + impl.getInfo().getLocation());
837                     }
838                 }
839                 else if (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
840                     > m_activeStartLevel)
841                 {
842                     try
843                     {
844                         stopBundleWithStartLevel(impl);
845                     }
846                     catch (Throwable JavaDoc th)
847                     {
848                         fireFrameworkEvent(FrameworkEvent.ERROR, impl, th);
849                         Oscar.error("Oscar: Error stopping "
850                             + impl.getInfo().getLocation());
851                     }
852                 }
853             }
854         }
855
856         fireFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, getBundle(0), null);
857     }
858
859     /**
860      * Returns the start level into which newly installed bundles will
861      * be placed by default; this method implements functionality for
862      * the Start Level service.
863      * @return the default start level for newly installed bundles.
864     **/

865     protected int getInitialBundleStartLevel()
866     {
867         String JavaDoc s = getConfigProperty(OscarConstants.BUNDLE_STARTLEVEL_PROP);
868
869         if (s != null)
870         {
871             try
872             {
873                 return Integer.parseInt(s);
874             }
875             catch (NumberFormatException JavaDoc ex)
876             {
877                 // Ignore and return the default value.
878
}
879         }
880         return OscarConstants.BUNDLE_DEFAULT_STARTLEVEL;
881     }
882
883     /**
884      * Sets the default start level into which newly installed bundles
885      * will be placed; this method implements functionality for the Start
886      * Level service.
887      * @param startLevel the new default start level for newly installed
888      * bundles.
889      * @throws java.lang.IllegalArgumentException if the specified start
890      * level is not greater than zero.
891      * @throws java.security.SecurityException if the caller does not
892      * have <tt>AdminPermission</tt>.
893     **/

894     protected void setInitialBundleStartLevel(int startLevel)
895     {
896         if (System.getSecurityManager() != null)
897         {
898             AccessController.checkPermission(m_adminPerm);
899         }
900
901         if (startLevel <= 0)
902         {
903             throw new IllegalArgumentException JavaDoc(
904                 "Initial start level must be greater than zero.");
905         }
906
907         setConfigProperty(
908             OscarConstants.BUNDLE_STARTLEVEL_PROP,
909             Integer.toString(startLevel));
910     }
911
912     /**
913      * Returns the start level for the specified bundle; this method
914      * implements functionality for the Start Level service.
915      * @param bundle the bundle to examine.
916      * @return the start level of the specified bundle.
917      * @throws java.lang.IllegalArgumentException if the specified
918      * bundle has been uninstalled.
919     **/

920     protected int getBundleStartLevel(Bundle bundle)
921     {
922         if (bundle.getState() == Bundle.UNINSTALLED)
923         {
924             throw new IllegalArgumentException JavaDoc("Bundle is uninstalled.");
925         }
926
927         return ((BundleImpl) bundle).getInfo().getStartLevel(getInitialBundleStartLevel());
928     }
929
930     /**
931      * Sets the start level of the specified bundle; this method
932      * implements functionality for the Start Level service.
933      * @param bundle the bundle whose start level is to be modified.
934      * @param startLevel the new start level of the specified bundle.
935      * @throws java.lang.IllegalArgumentException if the specified
936      * bundle is the system bundle or if the bundle has been
937      * uninstalled.
938      * @throws java.security.SecurityException if the caller does not
939      * have <tt>AdminPermission</tt>.
940     **/

941     protected void setBundleStartLevel(Bundle bundle, int startLevel)
942     {
943         if (System.getSecurityManager() != null)
944         {
945             AccessController.checkPermission(m_adminPerm);
946         }
947
948         // Cannot change the system bundle.
949
if (bundle.getBundleId() == 0)
950         {
951             throw new IllegalArgumentException JavaDoc(
952                 "Cannot change system bundle start level.");
953         }
954         else if (bundle.getState() == Bundle.UNINSTALLED)
955         {
956             throw new IllegalArgumentException JavaDoc("Bundle is uninstalled.");
957         }
958
959         if (startLevel >= 1)
960         {
961             BundleImpl impl = (BundleImpl) bundle;
962             impl.getInfo().setStartLevel(startLevel);
963             try
964             {
965                 m_cache.getArchive(impl.getBundleId()).setStartLevel(startLevel);
966             }
967             catch (Exception JavaDoc ex)
968             {
969                 Oscar.error("Unable to save start level.", ex);
970             }
971
972             try
973             {
974                 // Start or stop the bundle if necessary.
975
if (impl.getInfo().getStartLevel(getInitialBundleStartLevel())
976                     <= getStartLevel())
977                 {
978                     startBundleWithStartLevel(impl);
979                 }
980                 else
981                 {
982                     stopBundleWithStartLevel(impl);
983                 }
984             }
985             catch (Throwable JavaDoc th)
986             {
987                 fireFrameworkEvent(FrameworkEvent.ERROR, impl, th);
988                 Oscar.error("Error starting/stopping bundle.", th);
989             }
990         }
991     }
992
993     /**
994      * Returns whether a bundle is persistently started; this is an
995      * method implementation for the Start Level service.
996      * @param bundle the bundle to examine.
997      * @return <tt>true</tt> if the bundle is marked as persistently
998      * started, <tt>false</tt> otherwise.
999      * @throws java.lang.IllegalArgumentException if the specified
1000     * bundle has been uninstalled.
1001    **/

1002    protected boolean isBundlePersistentlyStarted(Bundle bundle)
1003    {
1004        if (bundle.getState() == Bundle.UNINSTALLED)
1005        {
1006            throw new IllegalArgumentException JavaDoc("Bundle is uninstalled.");
1007        }
1008
1009        return
1010            (((BundleImpl) bundle).getInfo().getPersistentState()
1011                == Bundle.ACTIVE);
1012    }
1013
1014    //
1015
// Oscar framework attribute methods.
1016
//
1017

1018    /**
1019     * Returns the current status of Oscar; this information is
1020     * used to determine which actions to perform during various
1021     * execution activities. For example, during startup a bundle's
1022     * state should not be saved since the state was recorded at
1023     * and at shutdown a bundle's state should not be saved since
1024     * since the last active state is used.
1025     *
1026     * @return <tt>UNKNOWN_STATUS</tt> if Oscar is in a bad state,
1027     * <tt>RUNNING_STATUS</tt> if Oscar is up and running,
1028     * <tt>STARTING_STATUS</tt> if Oscar is in its startup sequence, or
1029     * <tt>STOPPING_STATUS</tt> if Oscar is in its shutdown sequence.
1030    **/

1031    public int getFrameworkStatus()
1032    {
1033        return m_oscarStatus;
1034    }
1035
1036    /**
1037     * Returns the framework flag that indicates whether Oscar is
1038     * strictly adhering to the OSGi specification. If this is false,
1039     * then Oscar may provides some extended functionality that is
1040     * not part of the OSGi specification.
1041     * @return <tt>true</tt> if Oscar is in strict mode, <tt>false</tt> otherwise.
1042     * @deprecate use <tt>getConfigProperty(String name)</tt> instead.
1043    **/

1044    public boolean isStrictOSGi()
1045    {
1046        String JavaDoc strict = getConfigProperty(OscarConstants.STRICT_OSGI_PROP);
1047        return (strict == null) ? true : strict.equals("true");
1048    }
1049
1050    //
1051
// Package management methods.
1052
//
1053

1054    /**
1055     * Returns the bundle that exports the specified package. If the
1056     * package is not currently resolved, then it is resolved and the
1057     * corresponding bundle is returned.
1058     *
1059     * @param name the name of the exported package to find.
1060     * @return the exported package or null if no matching package was found.
1061    **/

1062    private BundleImpl findExportingBundle(String JavaDoc pkgName)
1063    {
1064        // Use the search policy utility method to try to
1065
// resolve the package.
1066
BundleImpl bundle = null;
1067        ImportSearchPolicy search =
1068            (ImportSearchPolicy) m_mgr.getSearchPolicy();
1069        int[] version = { 0, 0, 0 };
1070        Module exporter = search.resolveImportTarget(pkgName, version);
1071        if (exporter != null)
1072        {
1073            bundle = (BundleImpl) getBundle(
1074                BundleInfo.getBundleIdFromModuleId(exporter.getId()));
1075        }
1076        return bundle;
1077    }
1078
1079    /**
1080     * Returns the exported package associated with the specified
1081     * package name. This is used by the PackageAdmin service
1082     * implementation.
1083     *
1084     * @param name the name of the exported package to find.
1085     * @return the exported package or null if no matching package was found.
1086    **/

1087    protected ExportedPackage getExportedPackage(String JavaDoc name)
1088    {
1089        BundleImpl bundle = findExportingBundle(name);
1090        if (bundle != null)
1091        {
1092            // We need to find the version of the exported package, but this
1093
// is tricky since there may be multiple versions of the package
1094
// offered by a given bundle, since multiple revisions of the
1095
// bundle JAR file may exist if the bundle was updated without
1096
// refreshing the framework. In this case, each revision of the
1097
// bundle JAR file is represented as a module in the BundleInfo
1098
// module array, which is ordered from oldest to newest. We assume
1099
// that the first module found to be exporting the package is the
1100
// provider of the package, which makes sense since it must have
1101
// been resolved first.
1102
Module[] modules = bundle.getInfo().getModules();
1103            for (int modIdx = 0; modIdx < modules.length; modIdx++)
1104            {
1105                Object JavaDoc version =
1106                    ImportSearchPolicy.getExportVersion(modules[modIdx], name);
1107                if (version != null)
1108                {
1109                    return new ExportedPackageImpl(
1110                        this, bundle, name, (int[]) version);
1111                }
1112            }
1113        }
1114
1115        return null;
1116    }
1117
1118    /**
1119     * Returns an array of all actively exported packages from the specified
1120     * bundle or if the specified bundle is <tt>null</tt> an array
1121     * containing all actively exported packages by all bundles.
1122     *
1123     * @param b the bundle whose exported packages are to be retrieved
1124     * or <tt>null</tt> if the exported packages of all bundles are
1125     * to be retrieved.
1126     * @return an array of exported packages.
1127    **/

1128    protected ExportedPackage[] getExportedPackages(Bundle b)
1129    {
1130        ExportedPackage[] pkgs = null;
1131        List list = new ArrayList();
1132
1133        // If a bundle is specified, then return its
1134
// exported packages.
1135
if (b != null)
1136        {
1137            BundleImpl bundle = (BundleImpl) b;
1138            getExportedPackages(bundle, list);
1139        }
1140        // Otherwise return all exported packages.
1141
else
1142        {
1143            // First get exported packages from uninstalled bundles.
1144
for (int bundleIdx = 0;
1145                (m_uninstalledBundles != null) && (bundleIdx < m_uninstalledBundles.length);
1146                bundleIdx++)
1147            {
1148                BundleImpl bundle = m_uninstalledBundles[bundleIdx];
1149                getExportedPackages(bundle, list);
1150            }
1151
1152            // Now get exported packages from installed bundles.
1153
Bundle[] bundles = getBundles();
1154            for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
1155            {
1156                BundleImpl bundle = (BundleImpl) bundles[bundleIdx];
1157                getExportedPackages(bundle, list);
1158            }
1159        }
1160
1161        return (ExportedPackage[]) list.toArray(new ExportedPackage[list.size()]);
1162    }
1163
1164    /**
1165     * Adds any current active exported packages from the specified bundle
1166     * to the passed in list.
1167     * @param bundle the bundle from which to retrieve exported packages.
1168     * @param list the list to which the exported packages are added
1169    **/

1170    private void getExportedPackages(BundleImpl bundle, List list)
1171    {
1172        // Since a bundle may have many modules associated with it,
1173
// one for each revision in the cache, search each module
1174
// for each revision to get all exports.
1175
Module[] modules = bundle.getInfo().getModules();
1176        for (int modIdx = 0; modIdx < modules.length; modIdx++)
1177        {
1178            Object JavaDoc[][] exports =
1179                ImportSearchPolicy.getExportsAttribute(modules[modIdx]);
1180            if (exports.length > 0)
1181            {
1182                for (int expIdx = 0; expIdx < exports.length; expIdx++)
1183                {
1184                    // If the resolving module is the same as the current
1185
// module, then this bundle exports the package so add
1186
// the package to the list of exported packages.
1187
if (exports[expIdx][ImportSearchPolicy.RESOLVING_MODULE_IDX]
1188                        == modules[modIdx])
1189                    {
1190                        list.add(new ExportedPackageImpl(
1191                            this, bundle,
1192                            (String JavaDoc) exports[expIdx][ImportSearchPolicy.IDENTIFIER_IDX],
1193                            (int[]) exports[expIdx][ImportSearchPolicy.VERSION_IDX]));
1194                    }
1195                }
1196            }
1197        }
1198    }
1199
1200    protected Bundle[] getImportingBundles(ExportedPackage ep)
1201    {
1202        // Get exporting bundle; we need to use this internal
1203
// method because the spec says ep.getExportingBundle()
1204
// should return null if the package is stale.
1205
BundleImpl exporter = (BundleImpl)
1206            ((ExportedPackageImpl) ep).getExportingBundleInternal();
1207        BundleInfo exporterInfo = exporter.getInfo();
1208        String JavaDoc exportName = ep.getName();
1209
1210        // Create list for storing importing bundles.
1211
List list = new ArrayList();
1212        Bundle[] bundles = getBundles();
1213
1214        // Check all bundles to see who imports the package.
1215
for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
1216        {
1217            BundleImpl bundle = (BundleImpl) bundles[bundleIdx];
1218
1219            // Flag to short-circuit the loops if we find that
1220
// the current bundle does import the package.
1221
boolean doesImport = false;
1222
1223            // Check the imports and exports of each bundle.
1224
String JavaDoc[] searchAttrs = {
1225                ImportSearchPolicy.IMPORTS_ATTR,
1226                ImportSearchPolicy.EXPORTS_ATTR
1227            };
1228
1229            for (int attrIdx = 0;
1230                (!doesImport) && (attrIdx < searchAttrs.length);
1231                attrIdx++)
1232            {
1233                // Check all revisions of each bundle.
1234
Module[] modules = bundle.getInfo().getModules();
1235                for (int modIdx = 0;
1236                    (!doesImport) && (modIdx < modules.length);
1237                    modIdx++)
1238                {
1239                    Object JavaDoc[][] imports =
1240                        ImportSearchPolicy.getImportsOrExports(
1241                            modules[modIdx], searchAttrs[attrIdx]);
1242                    for (int importIdx = 0;
1243                        (!doesImport) && (importIdx < imports.length);
1244                        importIdx++)
1245                    {
1246                        // Get import package name.
1247
String JavaDoc importName = (String JavaDoc)
1248                            imports[importIdx][ImportSearchPolicy.IDENTIFIER_IDX];
1249                        // Get resolving module.
1250
Module resolvingModule = (Module)
1251                            imports[importIdx][ImportSearchPolicy.RESOLVING_MODULE_IDX];
1252                        // If the export and import package names are the same
1253
// and the resolving module is associated with the
1254
// exporting, then add current bundle to list.
1255
if (exportName.equals(importName) &&
1256                            exporterInfo.hasModule(resolvingModule))
1257                        {
1258                            // Add the bundle to the list of importers.
1259
list.add(bundles[bundleIdx]);
1260                            // Set the import flag so we exit the loops.
1261
doesImport = true;
1262                        }
1263                    }
1264                }
1265            }
1266        }
1267
1268
1269        // Return the results.
1270
if (list.size() > 0)
1271        {
1272            return (Bundle[]) list.toArray(new Bundle[list.size()]);
1273        }
1274
1275        return null;
1276    }
1277
1278    protected void refreshPackages(Bundle[] targets)
1279    {
1280        if (System.getSecurityManager() != null)
1281        {
1282            AccessController.checkPermission(m_adminPerm);
1283        }
1284
1285        synchronized (m_adminLock)
1286        {
1287            // If targets is null, then refresh all pending bundles.
1288
if (targets == null)
1289            {
1290                ArrayList list = new ArrayList();
1291
1292                // First add all uninstalled bundles.
1293
for (int i = 0;
1294                    (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
1295                    i++)
1296                {
1297                    list.add(m_uninstalledBundles[i]);
1298                }
1299                m_uninstalledBundles = null;
1300
1301                // Then add all updated bundles.
1302
Iterator iter = m_installedBundleMap.values().iterator();
1303                while (iter.hasNext())
1304                {
1305                    BundleImpl bundle = (BundleImpl) iter.next();
1306                    if (bundle.getInfo().isRemovalPending())
1307                    {
1308                        list.add(bundle);
1309                    }
1310                }
1311
1312                // Create an array.
1313
if (list.size() > 0)
1314                {
1315                    targets = (Bundle[]) list.toArray(new Bundle[list.size()]);
1316                }
1317            }
1318
1319            // If there are targets, then find all dependencies
1320
// for each one.
1321
if (targets != null)
1322            {
1323                // Create map of bundles that import the packages
1324
// from the target bundles.
1325
HashMap map = new HashMap();
1326                for (int targetIdx = 0; targetIdx < targets.length; targetIdx++)
1327                {
1328                    BundleImpl target = (BundleImpl) targets[targetIdx];
1329
1330                    // Add the current target bundle to the map of
1331
// bundles to be refreshed using a RefreshHelper.
1332
// Only these target bundles should be refreshed.
1333
map.put(new Long JavaDoc(target.getBundleId()),
1334                        new RefreshHelper(target));
1335                    // Add all importing bundles to map.
1336
populateImportGraph(target, map);
1337                }
1338
1339                // At this point the map contains every bundle that has been
1340
// updated and/or removed as well as all bundles that import
1341
// packages from these bundles.
1342
for (Iterator iter = map.values().iterator(); iter.hasNext(); )
1343                {
1344                    RefreshHelper helper = (RefreshHelper) iter.next();
1345                    helper.stop();
1346                    helper.purgeOrRemove();
1347                    helper.reinitialize();
1348                }
1349
1350                // Now restart bundles that were previously running.
1351
for (Iterator iter = map.values().iterator(); iter.hasNext(); )
1352                {
1353                    RefreshHelper helper = (RefreshHelper) iter.next();
1354                    helper.restart();
1355                }
1356            }
1357        }
1358
1359        fireFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, getBundle(0), null);
1360    }
1361
1362    private void populateImportGraph(BundleImpl target, HashMap map)
1363    {
1364        // Get the exported packages for the specified bundle.
1365
ExportedPackage[] pkgs = getExportedPackages(target);
1366
1367        for (int pkgIdx = 0; (pkgs != null) && (pkgIdx < pkgs.length); pkgIdx++)
1368        {
1369            // Get all imports of this package.
1370
Bundle[] importers = getImportingBundles(pkgs[pkgIdx]);
1371
1372            // Add importing bundle to map if not already present.
1373
for (int impIdx = 0;
1374                (importers != null) && (impIdx < importers.length);
1375                impIdx++)
1376            {
1377                Long JavaDoc id = new Long JavaDoc(importers[impIdx].getBundleId());
1378                if (map.get(id) == null)
1379                {
1380                    map.put(id, new RefreshHelper(importers[impIdx]));
1381                    // Now recurse into each bundle to get its importers.
1382
populateImportGraph(
1383                        (BundleImpl) importers[impIdx], map);
1384                }
1385            }
1386        }
1387    }
1388
1389    /**
1390     * This method adds a import target to the IMPORTS_ATTR attribute
1391     * array associated with the specified module. If the module is
1392     * already validated, then this method will not add the new
1393     * import target if it will cause the specified module to become
1394     * invalidate. It is possible to "force" the method to add the
1395     * new import target, but doing so might cause module and modules
1396     * that depend on it to be invalidated.
1397     * @param module the module whose IMPORTS_ATTR meta-data is to be modified.
1398     * @param target the target to import.
1399     * @param version the version of the target to import.
1400     * @param force indicates whether to force the operation, even in the
1401     * case where the module will be invalidated.
1402     * @return <tt>true</tt> if the import target was added, <tt>false</tt>
1403     * otherwise.
1404    **/

1405    protected boolean addImport(
1406        Module module, Object JavaDoc target, Object JavaDoc version, boolean force)
1407    {
1408// TODO: Import permission check
1409
// Synchronize on the module manager, because we don't want
1410
// anything to change while we are in the middle of this
1411
// operation.
1412
// TODO: Is this lock sufficient?
1413
synchronized (m_mgr)
1414        {
1415            ImportSearchPolicy search = (ImportSearchPolicy) m_mgr.getSearchPolicy();
1416            boolean added = false;
1417            Module exporter = null;
1418
1419            // Get the valid attribute.
1420
boolean valid = ImportSearchPolicy.getValidAttribute(module).booleanValue();
1421
1422            // Only attempt to resolve the new import target if the
1423
// module is already valid.
1424
if (valid)
1425            {
1426                exporter = search.resolveImportTarget(target, version);
1427            }
1428
1429            // There are three situations that will cause us to add the
1430
// new import target to the existing module: 1) we are
1431
// being forced to do so, 2) the module is not currently
1432
// validated so adding imports is okay, or 3) the module
1433
// is currently valid and we were able to resolve the new
1434
// import target. The follow if-statement checks for these
1435
// three cases.
1436
if (force
1437                || !valid
1438                || (valid && (exporter != null)))
1439            {
1440                // Create a new imports attribute array and
1441
// copy the old values into it.
1442
Object JavaDoc[][] imports = ImportSearchPolicy.getImportsAttribute(module);
1443                Object JavaDoc[][] newImports = new Object JavaDoc[imports.length + 1][3];
1444                for (int i = 0; i < imports.length; i++)
1445                {
1446                    newImports[i] = imports[i];
1447                }
1448
1449                // This is the new import target.
1450
newImports[newImports.length - 1] = new Object JavaDoc[] { target, version, exporter };
1451                module.setAttribute(ImportSearchPolicy.IMPORTS_ATTR, newImports);
1452                added = true;
1453
1454                // If it was not possible to resolve the new import target
1455
// and the module is currently valid, then the module must
1456
// be invalidated.
1457
if ((exporter == null) && valid)
1458                {
1459                    search.invalidate(
1460                        module, module.getAttributes(), module.getResourceSources(),
1461                        module.getLibrarySources());
1462                }
1463            }
1464
1465            return added;
1466        }
1467    }
1468
1469    //
1470
// Implementations for Bundle interface methods.
1471
//
1472

1473    /**
1474     * Implementation for Bundle.getBundleId().
1475    **/

1476    protected long getBundleId(BundleImpl bundle)
1477    {
1478        return bundle.getInfo().getBundleId();
1479    }
1480
1481    /**
1482     * Implementation for Bundle.getHeaders().
1483    **/

1484    protected Dictionary getBundleHeaders(BundleImpl bundle)
1485    {
1486        if (System.getSecurityManager() != null)
1487        {
1488            AccessController.checkPermission(m_adminPerm);
1489        }
1490        return new MapToDictionary(bundle.getInfo().getCurrentHeader());
1491    }
1492
1493    /**
1494     * Implementation for Bundle.getLocation().
1495    **/

1496    protected String JavaDoc getBundleLocation(BundleImpl bundle)
1497    {
1498        if (System.getSecurityManager() != null)
1499        {
1500            AccessController.checkPermission(m_adminPerm);
1501        }
1502        return bundle.getInfo().getLocation();
1503    }
1504
1505    /**
1506     * Implementation for Bundle.getResource().
1507    **/

1508    protected URL getBundleResource(BundleImpl bundle, String JavaDoc name)
1509    {
1510        if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1511        {
1512            throw new IllegalStateException JavaDoc("The bundle is uninstalled.");
1513        }
1514        else if (System.getSecurityManager() != null)
1515        {
1516            AccessController.checkPermission(m_adminPerm);
1517        }
1518        return bundle.getInfo().getCurrentModule().getClassLoader().getResource(name);
1519// We previously search the old revisions of the bundle for resources
1520
// first, but this caused multiple resolves when a bundle was updated
1521
// but not resolved yet. I think the following is the better way, but
1522
// other frameworks do it like above.
1523
// Module[] modules = bundle.getInfo().getModules();
1524
// for (int modIdx = 0; modIdx < modules.length; modIdx++)
1525
// {
1526
// URL url = modules[modIdx].getClassLoader().getResource(name);
1527
// if (url != null)
1528
// {
1529
// return url;
1530
// }
1531
// }
1532
// return null;
1533
}
1534
1535    /**
1536     * Implementation for Bundle.getRegisteredServices().
1537    **/

1538    protected ServiceReference[] getBundleRegisteredServices(BundleImpl bundle)
1539    {
1540        if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1541        {
1542            throw new IllegalStateException JavaDoc("The bundle is uninstalled.");
1543        }
1544
1545        synchronized (m_quickLock)
1546        {
1547            BundleInfo info = bundle.getInfo();
1548
1549            if (info.getServiceRegistrationCount() > 0)
1550            {
1551                // Create a list of service references.
1552
ArrayList list = new ArrayList();
1553                for (int regIdx = 0; regIdx < info.getServiceRegistrationCount(); regIdx++)
1554                {
1555                    // Get service registration.
1556
ServiceRegistrationImpl reg = (ServiceRegistrationImpl)
1557                        info.getServiceRegistration(regIdx);
1558
1559                    // Check that the current security context has permission
1560
// to get at least one of the service interfaces; the
1561
// objectClass property of the service stores its service
1562
// interfaces.
1563
boolean hasPermission = false;
1564                    if (System.getSecurityManager() != null)
1565                    {
1566                        String JavaDoc[] objectClass = (String JavaDoc[])
1567                            reg.getProperty(Constants.OBJECTCLASS);
1568                        if (objectClass == null)
1569                        {
1570                            return null;
1571                        }
1572                        for (int ifcIdx = 0;
1573                            !hasPermission && (ifcIdx < objectClass.length);
1574                            ifcIdx++)
1575                        {
1576                            try
1577                            {
1578                                ServicePermission perm =
1579                                    new ServicePermission(
1580                                        objectClass[ifcIdx], ServicePermission.GET);
1581                                AccessController.checkPermission(perm);
1582                                hasPermission = true;
1583                            }
1584                            catch (Exception JavaDoc ex)
1585                            {
1586                            }
1587                        }
1588                    }
1589                    else
1590                    {
1591                        hasPermission = true;
1592                    }
1593
1594                    if (hasPermission)
1595                    {
1596                        list.add(reg.getReference());
1597                    }
1598                }
1599
1600                if (list.size() > 0)
1601                {
1602                    return (ServiceReference[])
1603                        list.toArray(new ServiceReference[list.size()]);
1604                }
1605            }
1606        }
1607
1608        return null;
1609    }
1610
1611    /**
1612     * Implementation for Bundle.getServicesInUse().
1613    **/

1614    protected ServiceReference[] getBundleServicesInUse(BundleImpl bundle)
1615    {
1616        synchronized (m_quickLock)
1617        {
1618            BundleInfo info = bundle.getInfo();
1619            Iterator iter = info.getServiceUsageCounters();
1620            if (iter.hasNext())
1621            {
1622                // Create a list of service references.
1623
ArrayList list = new ArrayList();
1624                while (iter.hasNext())
1625                {
1626                    // Get service reference.
1627
ServiceReference ref = (ServiceReference) iter.next();
1628
1629                    // Check that the current security context has permission
1630
// to get at least one of the service interfaces; the
1631
// objectClass property of the service stores its service
1632
// interfaces.
1633
boolean hasPermission = false;
1634                    if (System.getSecurityManager() != null)
1635                    {
1636                        String JavaDoc[] objectClass = (String JavaDoc[])
1637                            ref.getProperty(Constants.OBJECTCLASS);
1638                        if (objectClass == null)
1639                        {
1640                            return null;
1641                        }
1642                        for (int i = 0;
1643                            !hasPermission && (i < objectClass.length);
1644                            i++)
1645                        {
1646                            try
1647                            {
1648                                ServicePermission perm =
1649                                    new ServicePermission(
1650                                        objectClass[i], ServicePermission.GET);
1651                                AccessController.checkPermission(perm);
1652                                hasPermission = true;
1653                            }
1654                            catch (Exception JavaDoc ex)
1655                            {
1656                            }
1657                        }
1658                    }
1659                    else
1660                    {
1661                        hasPermission = true;
1662                    }
1663
1664                    if (hasPermission)
1665                    {
1666                        list.add(ref);
1667                    }
1668                }
1669
1670                if (list.size() > 0)
1671                {
1672                    return (ServiceReference[])
1673                        list.toArray(new ServiceReference[list.size()]);
1674                }
1675            }
1676        }
1677
1678        return null;
1679    }
1680
1681    /**
1682     * Implementation for Bundle.getState().
1683    **/

1684    protected int getBundleState(BundleImpl bundle)
1685    {
1686        return bundle.getInfo().getState();
1687    }
1688
1689    /**
1690     * Implementation for Bundle.hasPermission().
1691    **/

1692    protected boolean bundleHasPermission(BundleImpl bundle, Object JavaDoc obj)
1693    {
1694        if (bundle.getInfo().getState() == Bundle.UNINSTALLED)
1695        {
1696            throw new IllegalStateException JavaDoc("The bundle is uninstalled.");
1697        }
1698
1699        return true;
1700    }
1701
1702    /**
1703     * Implementation for Bundle.start().
1704    **/

1705    protected void startBundle(BundleImpl bundle)
1706        throws BundleException
1707    {
1708        if (System.getSecurityManager() != null)
1709        {
1710            AccessController.checkPermission(m_adminPerm);
1711        }
1712
1713        synchronized (m_adminLock)
1714        {
1715            // Set the bundle's persistent state to active.
1716
bundle.getInfo().setPersistentStateActive();
1717
1718            try
1719            {
1720                // Try to start the bundle.
1721
startBundleWithStartLevel(bundle);
1722            }
1723            catch (Throwable JavaDoc th)
1724            {
1725                // Reset the bundle's persistent state to inactive if
1726
// there was an error.
1727
bundle.getInfo().setPersistentStateInactive();
1728
1729th.printStackTrace();
1730
1731                // The spec says to expect BundleException,
1732
// IllegalStateException, or SecurityException,
1733
// so rethrow these exceptions.
1734
if (th instanceof BundleException)
1735                {
1736                    throw (BundleException) th;
1737                }
1738                else if (th instanceof IllegalStateException JavaDoc)
1739                {
1740                    throw (IllegalStateException JavaDoc) th;
1741                }
1742                else if (th instanceof SecurityException JavaDoc)
1743                {
1744                    throw (SecurityException JavaDoc) th;
1745                }
1746                // Convert a privileged action exception to the
1747
// nested exception.
1748
else if (th instanceof PrivilegedActionException)
1749                {
1750                    th = ((PrivilegedActionException) th).getException();
1751                }
1752
1753                // Rethrow all other exceptions as a BundleException.
1754
throw new BundleException("Activator start error.", th);
1755            }
1756
1757            // Save the bundle's persistent state.
1758
try
1759            {
1760                m_cache.getArchive(bundle.getInfo().getBundleId())
1761                    .setPersistentState(bundle.getInfo().getPersistentState());
1762            }
1763            catch (Exception JavaDoc ex)
1764            {
1765                Oscar.error("Oscar: Error saving persistent bundle state.");
1766                Oscar.error("Oscar: " + ex);
1767            }
1768        }
1769    }
1770
1771    /**
1772     * This method performs the actual task of starting a bundle and is
1773     * used by <tt>Oscar.startBundle()</tt> and <tt>Oscar.setStartLevel</tt>.
1774     * When <tt>Oscar.startBundle()</tt> is called, the bundle's persistent
1775     * state is set to active, while calling this method directly does
1776     * not affect the bundle's persistent state. This is necessary when
1777     * the framework's start level changes, because it may be necessary to
1778     * start bundles without affecting their persistent state. If the specified
1779     * bundle's start level is greater than the framework's start level, then
1780     * this method simply returns.
1781    **/

1782    private void startBundleWithStartLevel(BundleImpl bundle)
1783        throws Throwable JavaDoc
1784    {
1785        BundleInfo info = bundle.getInfo();
1786
1787        // Ignore bundles whose persistent state is not active
1788
// or whose start level is greater than the framework's.
1789
if ((info.getPersistentState() != Bundle.ACTIVE)
1790            || (info.getStartLevel(getInitialBundleStartLevel()) > getStartLevel()))
1791        {
1792            return;
1793        }
1794
1795        switch (info.getState())
1796        {
1797            case Bundle.UNINSTALLED:
1798                throw new IllegalStateException JavaDoc("Cannot start an uninstalled bundle.");
1799            case Bundle.STARTING:
1800            case Bundle.STOPPING:
1801                throw new BundleException("Starting a bundle that is starting or stopping is currently not supported.");
1802            case Bundle.ACTIVE:
1803                return;
1804            case Bundle.INSTALLED:
1805                resolveBundle(bundle);
1806            case Bundle.RESOLVED:
1807                info.setState(Bundle.STARTING);
1808        }
1809
1810        try
1811        {
1812            // Set the bundle's activator.
1813
bundle.getInfo().setActivator(createBundleActivator(bundle.getInfo()));
1814
1815            // Activate the bundle if it has an activator.
1816
if (bundle.getInfo().getActivator() != null)
1817            {
1818                if (info.getContext() == null)
1819                {
1820                    info.setContext(new BundleContextImpl(this, bundle));
1821                }
1822
1823                if (System.getSecurityManager() != null)
1824                {
1825                    m_startStopPrivileged.setAction(StartStopPrivileged.START_ACTION);
1826                    m_startStopPrivileged.setBundle(bundle);
1827                    AccessController.doPrivileged(m_startStopPrivileged);
1828                }
1829                else
1830                {
1831                    info.getActivator().start(info.getContext());
1832                }
1833            }
1834        }
1835        catch (Throwable JavaDoc th)
1836        {
1837            // If there was an error starting the bundle,
1838
// then reset its state to RESOLVED.
1839
info.setState(Bundle.RESOLVED);
1840
1841            // Unregister any services offered by this bundle.
1842
unregisterServices(bundle);
1843
1844            // Release any services being used by this bundle.
1845
ungetServices(bundle);
1846
1847            // Remove any listeners registered by this bundle.
1848
removeListeners(bundle);
1849
1850            throw th;
1851        }
1852
1853        info.setState(Bundle.ACTIVE);
1854
1855        fireBundleEvent(BundleEvent.STARTED, bundle);
1856    }
1857
1858    /**
1859     * Implementation for Bundle.update().
1860    **/

1861    protected void updateBundle(BundleImpl bundle, InputStream is)
1862        throws BundleException
1863    {
1864        if (System.getSecurityManager() != null)
1865        {
1866            AccessController.checkPermission(m_adminPerm);
1867        }
1868
1869        // We guarantee to close the input stream, so put it in a
1870
// finally clause.
1871

1872        try
1873        {
1874            // Variable to indicate whether bundle is active or not.
1875
boolean activated = false;
1876            Exception JavaDoc rethrow = null;
1877
1878            synchronized (m_adminLock)
1879            {
1880                BundleInfo info = bundle.getInfo();
1881
1882                if (info.getState() == Bundle.UNINSTALLED)
1883                {
1884                    throw new IllegalStateException JavaDoc("The bundle is uninstalled.");
1885                }
1886
1887                // First get the update-URL from our header.
1888
String JavaDoc updateLocation = (String JavaDoc)
1889                    info.getCurrentHeader().get(Constants.BUNDLE_UPDATELOCATION);
1890
1891                // If no update location specified, use original location.
1892
if (updateLocation == null)
1893                {
1894                    updateLocation = info.getLocation();
1895                }
1896
1897                // Remember if active.
1898
activated = (info.getState() == Bundle.ACTIVE);
1899
1900                // If the bundle is active, stop it.
1901
if (activated)
1902                {
1903                    stopBundle(bundle);
1904                }
1905
1906                try
1907                {
1908                    // Get the URL input stream if necessary.
1909
if (is == null)
1910                    {
1911                        // Do it the manual way to have a chance to
1912
// set request properties such as proxy auth.
1913
URL url = new URL(updateLocation);
1914                        URLConnection conn = url.openConnection();
1915
1916                        // Support for http proxy authentication.
1917
String JavaDoc auth = System.getProperty("http.proxyAuth");
1918                        if ((auth != null) && (auth.length() > 0))
1919                        {
1920                            if ("http".equals(url.getProtocol()) ||
1921                                "https".equals(url.getProtocol()))
1922                            {
1923                                String JavaDoc base64 = Util.base64Encode(auth);
1924                                conn.setRequestProperty(
1925                                    "Proxy-Authorization", "Basic " + base64);
1926                            }
1927                        }
1928                        is = conn.getInputStream();
1929                    }
1930                    // Get the bundle's archive.
1931
BundleArchive archive = m_cache.getArchive(info.getBundleId());
1932                    // Update the bundle; this operation will increase
1933
// the revision count for the bundle.
1934
m_cache.update(archive, is);
1935                    // Create a module for the new revision; the revision is
1936
// base zero, so subtract one from the revision count to
1937
// get the revision of the new update.
1938
Module module = createModule(
1939                        info.getBundleId(),
1940                        archive.getRevisionCount() - 1,
1941                        info.getCurrentHeader());
1942                    // Add module to bundle info.
1943
info.addModule(module);
1944                }
1945                catch (Exception JavaDoc ex)
1946                {
1947                    Oscar.error("Unable to update the bundle.");
1948                    rethrow = ex;
1949                }
1950                finally
1951                {
1952                    try
1953                    {
1954                        if (is != null) is.close();
1955                    }
1956                    catch (Exception JavaDoc ex)
1957                    {
1958                        Oscar.error("Unable to close input stream: " + ex);
1959                    }
1960                }
1961
1962                info.setState(Bundle.INSTALLED);
1963
1964                // Mark as needing a refresh.
1965
info.setRemovalPending();
1966            }
1967
1968            // Fire updated event if successful.
1969
if (rethrow == null)
1970            {
1971                // Fire bundle update event outside of synchronized block
1972
// if no error occured.
1973
fireBundleEvent(BundleEvent.UPDATED, bundle);
1974            }
1975
1976            // Start if previously active.
1977
if (activated)
1978            {
1979                // Restart bundle if previously active.
1980
startBundle(bundle);
1981            }
1982
1983            // If update failed, rethrow exception.
1984
if (rethrow != null)
1985            {
1986                throw new BundleException("Update failed.", rethrow);
1987            }
1988        }
1989        finally
1990        {
1991            try
1992            {
1993                if (is != null) is.close();
1994            }
1995            catch (IOException ex)
1996            {
1997                Oscar.error("Oscar: Could not close update stream.");
1998                // What else can we do?
1999
}
2000        }
2001    }
2002
2003    /**
2004     * Implementation for Bundle.stop().
2005    **/

2006    protected void stopBundle(BundleImpl bundle)
2007        throws BundleException
2008    {
2009        if (System.getSecurityManager() != null)
2010        {
2011            AccessController.checkPermission(m_adminPerm);
2012        }
2013
2014        Throwable JavaDoc rethrow = null;
2015
2016        synchronized (m_adminLock)
2017        {
2018            // Set the bundle's persistent state to inactive.
2019
bundle.getInfo().setPersistentStateInactive();
2020
2021            try
2022            {
2023                // Stop the bundle.
2024
stopBundleWithStartLevel(bundle);
2025            }
2026            catch (Throwable JavaDoc th)
2027            {
2028                Oscar.error("Oscar: Error calling activator.", th);
2029                rethrow = th;
2030            }
2031
2032            // Save the bundle's persistent state.
2033
try
2034            {
2035                m_cache.getArchive(bundle.getInfo().getBundleId())
2036                    .setPersistentState(bundle.getInfo().getPersistentState());
2037            }
2038            catch (Exception JavaDoc ex)
2039            {
2040                Oscar.error("Oscar: Error saving persistent bundle state.");
2041                Oscar.error("Oscar: " + ex);
2042            }
2043        }
2044
2045        // Throw activator error if there was one.
2046
if (rethrow != null)
2047        {
2048            // The spec says to expect BundleException,
2049
// IllegalStateException, or SecurityException,
2050
// so rethrow these exceptions.
2051
if (rethrow instanceof BundleException)
2052            {
2053                throw (BundleException) rethrow;
2054            }
2055            else if (rethrow instanceof IllegalStateException JavaDoc)
2056            {
2057                throw (IllegalStateException JavaDoc) rethrow;
2058            }
2059            else if (rethrow instanceof SecurityException JavaDoc)
2060            {
2061                throw (SecurityException JavaDoc) rethrow;
2062            }
2063            else if (rethrow instanceof PrivilegedActionException)
2064            {
2065                rethrow = ((PrivilegedActionException) rethrow).getException();
2066            }
2067
2068            // Rethrow all other exceptions as a BundleException.
2069
throw new BundleException("Activator stop error.", rethrow);
2070        }
2071    }
2072
2073    /**
2074     * This method performs the actual task of stopping a bundle and is
2075     * used by <tt>Oscar.stopBundle()</tt> and <tt>Oscar.setStartLevel</tt>.
2076     * When <tt>Oscar.stopBundle()</tt> is called, the bundle's persistent
2077     * state is set to inactive, while calling this method directly does
2078     * not affect the bundle's persistent state. This is necessary when
2079     * the framework's start level changes, because it may be necessary to
2080     * stop bundles without affecting their persistent state.
2081    **/

2082    private void stopBundleWithStartLevel(BundleImpl bundle)
2083        throws Throwable JavaDoc
2084    {
2085        BundleInfo info = bundle.getInfo();
2086
2087        switch (info.getState())
2088        {
2089            case Bundle.UNINSTALLED:
2090                throw new IllegalStateException JavaDoc("Cannot stop an uninstalled bundle.");
2091            case Bundle.STARTING:
2092            case Bundle.STOPPING:
2093                throw new BundleException("Stopping a bundle that is starting or stopping is currently not supported.");
2094            case Bundle.INSTALLED:
2095            case Bundle.RESOLVED:
2096                return;
2097            case Bundle.ACTIVE:
2098                // Set bundle state..
2099
info.setState(Bundle.STOPPING);
2100        }
2101
2102        Throwable JavaDoc rethrow = null;
2103
2104        try
2105        {
2106            if (bundle.getInfo().getActivator() != null)
2107            {
2108                if (System.getSecurityManager() != null)
2109                {
2110                    m_startStopPrivileged.setAction(StartStopPrivileged.STOP_ACTION);
2111                    m_startStopPrivileged.setBundle(bundle);
2112                    AccessController.doPrivileged(m_startStopPrivileged);
2113                }
2114                else
2115                {
2116                    info.getActivator().stop(info.getContext());
2117                }
2118            }
2119
2120            // Try to save the activator in the cache.
2121
// NOTE: This is non-standard OSGi behavior and only
2122
// occurs if strictness is disabled.
2123
String JavaDoc strict = getConfigProperty(OscarConstants.STRICT_OSGI_PROP);
2124            boolean isStrict = (strict == null) ? true : strict.equals("true");
2125            if (!isStrict)
2126            {
2127                try
2128                {
2129                    m_cache.getArchive(info.getBundleId())
2130                        .setActivator(info.getActivator());
2131                }
2132                catch (Exception JavaDoc ex)
2133                {
2134                    // Problem saving activator, so ignore it.
2135
// TODO: Perhaps we should handle this some other way?
2136
}
2137            }
2138        }
2139        catch (Throwable JavaDoc th)
2140        {
2141            // TODO: Make sure we clean up everything here.
2142
th.printStackTrace();
2143            rethrow = th;
2144        }
2145
2146
2147        // Unregister any services offered by this bundle.
2148
unregisterServices(bundle);
2149
2150        // Release any services being used by this bundle.
2151
ungetServices(bundle);
2152
2153        // The spec says that we must remove all event
2154
// listeners for a bundle when it is stopped.
2155
removeListeners(bundle);
2156
2157        info.setState(Bundle.RESOLVED);
2158        fireBundleEvent(BundleEvent.STOPPED, bundle);
2159
2160        if (rethrow != null)
2161        {
2162            throw rethrow;
2163        }
2164    }
2165
2166    /**
2167     * Implementation for Bundle.uninstall().
2168    **/

2169    protected void uninstallBundle(BundleImpl bundle)
2170        throws BundleException
2171    {
2172        if (System.getSecurityManager() != null)
2173        {
2174            AccessController.checkPermission(m_adminPerm);
2175        }
2176
2177        BundleException rethrow = null;
2178
2179        synchronized (m_adminLock)
2180        {
2181            BundleInfo info = bundle.getInfo();
2182            if (info.getState() == Bundle.UNINSTALLED)
2183            {
2184                throw new IllegalStateException JavaDoc("The bundle is uninstalled.");
2185            }
2186
2187            // The spec says that uninstall should always succeed, so
2188
// catch an exception here if stop() doesn't succeed and
2189
// rethrow it at the end.
2190
try
2191            {
2192                stopBundle(bundle);
2193            }
2194            catch (BundleException ex)
2195            {
2196                rethrow = ex;
2197            }
2198
2199            // Get the quick lock to remove the bundle from the
2200
// installed map.
2201
BundleImpl target = null;
2202            synchronized (m_quickLock)
2203            {
2204                target = (BundleImpl) m_installedBundleMap.remove(
2205                    info.getLocation());
2206            }
2207
2208            // Finally, put the uninstalled bundle into the
2209
// uninstalled list for subsequent refreshing.
2210
if (target != null)
2211            {
2212                // Set the bundle's persistent state to uninstalled.
2213
target.getInfo().setPersistentStateUninstalled();
2214
2215                // Mark bundle for removal.
2216
target.getInfo().setRemovalPending();
2217
2218                // Put bundle in uninstalled bundle array.
2219
BundleImpl[] bundles = null;
2220                if (m_uninstalledBundles == null)
2221                {
2222                    bundles = new BundleImpl[1];
2223                }
2224                else
2225                {
2226                    bundles = new BundleImpl[m_uninstalledBundles.length + 1];
2227                    System.arraycopy(
2228                        m_uninstalledBundles, 0, bundles, 0, bundles.length - 1);
2229                }
2230                bundles[bundles.length - 1] = target;
2231                m_uninstalledBundles = bundles;
2232            }
2233            else
2234            {
2235                Oscar.error("Unable to remove bundle from installed map!");
2236            }
2237
2238            // Set state to uninstalled.
2239
info.setState(Bundle.UNINSTALLED);
2240        }
2241
2242        // Fire bundle event outside synchronized block.
2243
fireBundleEvent(BundleEvent.UNINSTALLED, bundle);
2244
2245        if (rethrow != null)
2246        {
2247            throw rethrow;
2248        }
2249    }
2250
2251    //
2252
// Implementations for BundleContext interface methods.
2253
//
2254

2255    /**
2256     * Implementation for BundleContext.getProperty(). Returns
2257     * environment property associated with the container.
2258     *
2259     * @param key the name of the property to retrieve.
2260     * @return the value of the specified property or null.
2261    **/

2262    protected String JavaDoc getProperty(String JavaDoc key)
2263    {
2264        // Property names are case insensitive.
2265
if (m_frameworkPropMap == null)
2266        {
2267            return null;
2268        }
2269        String JavaDoc val = (String JavaDoc) m_frameworkPropMap.get(key);
2270        return substVars(val);
2271    }
2272
2273    /**
2274     * Set the environment property associated with the container.
2275     *
2276     * @param key the name of the property to set.
2277     * @param value the value of the specified property.
2278    **/

2279    protected void setProperty(String JavaDoc key, String JavaDoc value)
2280    {
2281        // Property names are case insensitive.
2282
if (m_frameworkPropMap != null)
2283        {
2284            m_frameworkPropMap.put(key, value);
2285        }
2286    }
2287
2288    /**
2289     * Implementation for BundleContext.installBundle(). Installs the bundle
2290     * associated with the location string, using the specified input stream
2291     * if is it not null; the input stream will be closed at the end of this
2292     * method.
2293     *
2294     * @param location the location string (URL) for the bundle.
2295     * @param is input stream from which to read the bundle, can be null.
2296     * @return a reference to the installed bundle.
2297     * @throws BundleException if any problems are encountered during installation.
2298    **/

2299    protected Bundle installBundle(String JavaDoc location, InputStream is)
2300        throws BundleException
2301    {
2302        return installBundle(-1, location, is);
2303    }
2304
2305    private Bundle installBundle(long id, String JavaDoc location, InputStream is)
2306        throws BundleException
2307    {
2308        if (System.getSecurityManager() != null)
2309        {
2310            AccessController.checkPermission(m_adminPerm);
2311        }
2312
2313        BundleImpl bundle = null;
2314
2315        synchronized (m_adminLock)
2316        {
2317            try
2318            {
2319                // Check to see if the framework is still running;
2320
if ((getFrameworkStatus() == Oscar.STOPPING_STATUS) ||
2321                    (getFrameworkStatus() == Oscar.UNKNOWN_STATUS))
2322                {
2323                    throw new BundleException("The framework has been shutdown.");
2324                }
2325
2326                // If bundle location is already installed, then
2327
// return it as required by the OSGi specification.
2328
bundle = (BundleImpl) getBundle(location);
2329                if (bundle != null)
2330                {
2331                    return bundle;
2332                }
2333
2334                // Determine if this is a new or existing bundle.
2335
boolean isNew = (id < 0);
2336
2337                // If the bundle is new we must cache its JAR file.
2338
if (isNew)
2339                {
2340                    // First generate an identifier for it.
2341
id = getNextId();
2342
2343                    try
2344                    {
2345                        // Get the URL input stream if necessary.
2346
if (is == null)
2347                        {
2348                            // Do it the manual way to have a chance to
2349
// set request properties such as proxy auth.
2350
URL url = new URL(location);
2351                            URLConnection conn = url.openConnection();
2352
2353                            // Support for http proxy authentication.
2354
String JavaDoc auth = System.getProperty("http.proxyAuth");
2355                            if ((auth != null) && (auth.length() > 0))
2356                            {
2357                                if ("http".equals(url.getProtocol()) ||
2358                                    "https".equals(url.getProtocol()))
2359                                {
2360                                    String JavaDoc base64 = Util.base64Encode(auth);
2361                                    conn.setRequestProperty(
2362                                        "Proxy-Authorization", "Basic " + base64);
2363                                }
2364                            }
2365                            is = conn.getInputStream();
2366                        }
2367                        // Add the bundle to the cache.
2368
m_cache.create(id, location, is);
2369                    }
2370                    catch (Exception JavaDoc ex)
2371                    {
2372                        throw new BundleException("Unable to cache bundle: " + location, ex);
2373                    }
2374                    finally
2375                    {
2376                        try
2377                        {
2378                            if (is != null) is.close();
2379                        }
2380                        catch (IOException ex)
2381                        {
2382                            Oscar.error("Unable to close input stream: " + ex);
2383                        }
2384                    }
2385                }
2386                else
2387                {
2388                    // If the bundle we are installing is not new,
2389
// then try to purge old revisions before installing
2390
// it; this is done just in case a "refresh"
2391
// didn't occur last session...this would only be
2392
// due to an error or system crash.
2393
try
2394                    {
2395                        if (m_cache.getArchive(id).getRevisionCount() > 1)
2396                        {
2397                            m_cache.purge(m_cache.getArchive(id));
2398                        }
2399                    }
2400                    catch (Exception JavaDoc ex)
2401                    {
2402                        ex.printStackTrace();
2403                        Oscar.error("Oscar: Could not purge bundle.", ex);
2404                    }
2405                }
2406
2407                try
2408                {
2409                    BundleArchive archive = m_cache.getArchive(id);
2410                    bundle = new BundleImpl(this, createBundleInfo(archive));
2411                }
2412                catch (Exception JavaDoc ex)
2413                {
2414                    // If the bundle is new, then remove it from the cache.
2415
// TODO: Perhaps it should be removed if it is not new
2416
// too.
2417
if (isNew)
2418                    {
2419                        try
2420                        {
2421                            m_cache.remove(m_cache.getArchive(id));
2422                        }
2423                        catch (Exception JavaDoc ex1)
2424                        {
2425                            Oscar.error("Could not remove from cache.", ex1);
2426                        }
2427                    }
2428                    throw new BundleException("Could not create bundle object: " + ex);
2429                }
2430
2431                // If the bundle is new, then persistently set its
2432
// start level; existing bundles already have their
2433
// start level set.
2434
if (isNew)
2435                {
2436                    setBundleStartLevel(bundle, getInitialBundleStartLevel());
2437                }
2438
2439                synchronized (m_quickLock)
2440                {
2441                    m_installedBundleMap.put(location, bundle);
2442                }
2443            }
2444            finally
2445            {
2446                // Always try to close the input stream.
2447
try
2448                {
2449                    if (is != null) is.close();
2450                }
2451                catch (IOException ex)
2452                {
2453                    Oscar.error("Oscar: Unable to close input stream.");
2454                    // Not much else we can do.
2455
}
2456            }
2457        }
2458
2459        // Fire bundle event outside synchronized block.
2460
fireBundleEvent(BundleEvent.INSTALLED, bundle);
2461
2462        // Return new bundle.
2463
return bundle;
2464    }
2465
2466    /**
2467     * Implementation for BundleContext.getBundle(). Retrieves a
2468     * bundle from its identifier.
2469     *
2470     * @param id the identifier of the bundle to retrieve.
2471     * @return the bundle associated with the identifier or null if there
2472     * is no bundle associated with the identifier.
2473    **/

2474    protected Bundle getBundle(long id)
2475    {
2476        synchronized (m_quickLock)
2477        {
2478            BundleImpl bundle = null;
2479
2480            for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
2481            {
2482                bundle = (BundleImpl) i.next();
2483                if (bundle.getInfo().getBundleId() == id)
2484                {
2485                    return bundle;
2486                }
2487            }
2488        }
2489
2490        return null;
2491    }
2492
2493    // Private member for method below.
2494
private Comparator m_comparator = null;
2495
2496    /**
2497     * Implementation for BundleContext.getBundles(). Retrieves
2498     * all installed bundles.
2499     *
2500     * @return an array containing all installed bundles or null if
2501     * there are no installed bundles.
2502    **/

2503    protected Bundle[] getBundles()
2504    {
2505        if (m_comparator == null)
2506        {
2507            m_comparator = new Comparator() {
2508                public int compare(Object JavaDoc o1, Object JavaDoc o2)
2509                {
2510                    Bundle b1 = (Bundle) o1;
2511                    Bundle b2 = (Bundle) o2;
2512                    if (b1.getBundleId() > b2.getBundleId())
2513                        return 1;
2514                    else if (b1.getBundleId() < b2.getBundleId())
2515                        return -1;
2516                    return 0;
2517                }
2518            };
2519        }
2520
2521        Bundle[] bundles = null;
2522
2523        synchronized (m_quickLock)
2524        {
2525            if (m_installedBundleMap.size() == 0)
2526            {
2527                return null;
2528            }
2529
2530            bundles = new Bundle[m_installedBundleMap.size()];
2531            int counter = 0;
2532            for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
2533            {
2534                bundles[counter++] = (Bundle) i.next();
2535            }
2536        }
2537
2538        Arrays.sort(bundles, m_comparator);
2539
2540        return bundles;
2541    }
2542
2543    /**
2544     * Implementation for BundleContext.addBundleListener().
2545     * Adds bundle listener to the listener list so that it
2546     * can listen for <code>BundleEvent</code>s.
2547     *
2548     * @param bundle the bundle that registered the listener.
2549     * @param l the bundle listener to add to the listener list.
2550    **/

2551    protected void addBundleListener(Bundle bundle, BundleListener l)
2552    {
2553        // The spec says do nothing if the listener is
2554
// already registered.
2555
BundleListenerWrapper old = (BundleListenerWrapper)
2556            m_dispatchQueue.getListener(BundleListener.class, l);
2557        if (old == null)
2558        {
2559            l = new BundleListenerWrapper(bundle, l);
2560            m_dispatchQueue.addListener(BundleListener.class, l);
2561        }
2562    }
2563
2564    /**
2565     * Implementation for BundleContext.removeBundleListener().
2566     * Removes bundle listeners from the listener list.
2567     *
2568     * @param l the bundle listener to remove from the listener list.
2569    **/

2570    protected void removeBundleListener(BundleListener l)
2571    {
2572        m_dispatchQueue.removeListener(BundleListener.class, l);
2573    }
2574
2575    /**
2576     * Implementation for BundleContext.addServiceListener().
2577     * Adds service listener to the listener list so that is
2578     * can listen for <code>ServiceEvent</code>s.
2579     *
2580     * @param bundle the bundle that registered the listener.
2581     * @param l the service listener to add to the listener list.
2582     * @param f the filter for the listener; may be null.
2583    **/

2584    protected void addServiceListener(Bundle bundle, ServiceListener l, String JavaDoc f)
2585        throws InvalidSyntaxException
2586    {
2587        // The spec says if the listener is already registered,
2588
// then replace filter.
2589
ServiceListenerWrapper old = (ServiceListenerWrapper)
2590            m_dispatchQueue.getListener(ServiceListener.class, l);
2591        if (old != null)
2592        {
2593            old.setFilter((f == null) ? null : new FilterImpl(f));
2594        }
2595        else
2596        {
2597            l = new ServiceListenerWrapper(
2598                bundle, l, (f == null) ? null : new FilterImpl(f));
2599            m_dispatchQueue.addListener(ServiceListener.class, l);
2600        }
2601    }
2602
2603    /**
2604     * Implementation for BundleContext.removeServiceListener().
2605     * Removes service listeners from the listener list.
2606     *
2607     * @param l the service listener to remove from the listener list.
2608    **/

2609    protected void removeServiceListener(ServiceListener l)
2610    {
2611        m_dispatchQueue.removeListener(ServiceListener.class, l);
2612    }
2613
2614    /**
2615     * Implementation for BundleContext.addFrameworkListener().
2616     * Adds framework listener to the listener list so that it
2617     * can listen for <code>FrameworkEvent</code>s.
2618     *
2619     * @param bundle the bundle that registered the listener.
2620     * @param l the framework listener to add to the listener list.
2621    **/

2622    protected void addFrameworkListener(Bundle bundle, FrameworkListener l)
2623    {
2624        // The spec says do nothing if the listener is
2625
// already registered.
2626
FrameworkListenerWrapper old = (FrameworkListenerWrapper)
2627            m_dispatchQueue.getListener(FrameworkListener.class, l);
2628        if (old == null)
2629        {
2630            l = new FrameworkListenerWrapper(bundle, l);
2631            m_dispatchQueue.addListener(FrameworkListener.class, l);
2632        }
2633    }
2634
2635    /**
2636     * Implementation for BundleContext.removeFrameworkListener().
2637     * Removes framework listeners from the listener list.
2638     *
2639     * @param l the framework listener to remove from the listener list.
2640    **/

2641    protected void removeFrameworkListener(FrameworkListener l)
2642    {
2643        m_dispatchQueue.removeListener(FrameworkListener.class, l);
2644    }
2645
2646    /**
2647     * Implementation for BundleContext.registerService(). Registers
2648     * a service for the specified bundle bundle.
2649     *
2650     * @param clazzes a string array containing the names of the classes
2651     * under which the new service is available.
2652     * @param svcObj the service object or <code>ServiceFactory</code>.
2653     * @param dict a dictionary of properties that further describe the
2654     * service or null.
2655     * @return a <code>ServiceRegistration</code> object or null.
2656    **/

2657    protected ServiceRegistration registerService(
2658        BundleImpl bundle, String JavaDoc[] classNames, Object JavaDoc svcObj, Dictionary dict)
2659    {
2660        if (classNames == null)
2661        {
2662            throw new NullPointerException JavaDoc("Service class names cannot be null.");
2663        }
2664        else if (svcObj == null)
2665        {
2666            throw new IllegalArgumentException JavaDoc("Service object cannot be null.");
2667        }
2668
2669        // Check for permission to register all passed in interface names.
2670
if (System.getSecurityManager() != null)
2671        {
2672            for (int i = 0; i < classNames.length; i++)
2673            {
2674                ServicePermission perm = new ServicePermission(
2675                    classNames[i], ServicePermission.REGISTER);
2676                AccessController.checkPermission(perm);
2677            }
2678        }
2679
2680        ServiceRegistrationImpl reg = null;
2681
2682        synchronized (m_quickLock)
2683        {
2684            BundleInfo info = bundle.getInfo();
2685
2686            // Can only register services if starting or active.
2687
if ((info.getState() & (Bundle.STARTING | Bundle.ACTIVE)) == 0)
2688            {
2689                throw new IllegalStateException JavaDoc(
2690                    "Can only register services while bundle is active or activating.");
2691            }
2692
2693            // Check to make sure that the service object is
2694
// an instance of all service classes; ignore if
2695
// service object is a service factory.
2696
if (!(svcObj instanceof ServiceFactory))
2697            {
2698                String JavaDoc pkgName = null;
2699                Class JavaDoc clazz = null;
2700                // Get the class loader from the service object.
2701
ClassLoader JavaDoc loader = svcObj.getClass().getClassLoader();
2702                // A null class loader represents the system class loader.
2703
loader = (loader == null)
2704                    ? ClassLoader.getSystemClassLoader() : loader;
2705                for (int i = 0; i < classNames.length; i++)
2706                {
2707                    try
2708                    {
2709                        clazz = loader.loadClass(classNames[i]);
2710                    }
2711                    catch (ClassNotFoundException JavaDoc ex)
2712                    {
2713                        throw new IllegalArgumentException JavaDoc("Class not found: " + ex);
2714                    }
2715                    if (!clazz.isAssignableFrom(svcObj.getClass()))
2716                    {
2717                        throw new IllegalArgumentException JavaDoc(
2718                            "Service object is not an instance of \""
2719                            + classNames[i] + "\".");
2720                    }
2721                }
2722            }
2723
2724            reg = new ServiceRegistrationImpl(
2725                this, bundle, classNames,
2726                new Long JavaDoc(getNextServiceId()), svcObj, dict);
2727
2728            info.addServiceRegistration(reg);
2729        }
2730
2731        // Fire event outside synchronized block.
2732
fireServiceEvent(ServiceEvent.REGISTERED, reg.getReference());
2733
2734        return reg;
2735    }
2736
2737    /**
2738     * Implementation for BundleContext.getServiceReferences().
2739    **/

2740    protected ServiceReference[] getServiceReferences(String JavaDoc className, String JavaDoc expr)
2741        throws InvalidSyntaxException
2742    {
2743        Oscar.debug("Oscar.getServiceReferences(" + className + ", " + expr + ")");
2744
2745        // If the specified class name is not null, then check for
2746
// permission to get the associated service interface.
2747
if ((className != null) && (System.getSecurityManager() != null))
2748        {
2749            try
2750            {
2751                ServicePermission perm =
2752                    new ServicePermission(className, ServicePermission.GET);
2753                AccessController.checkPermission(perm);
2754            }
2755            catch (Exception JavaDoc ex)
2756            {
2757                // We do not throw this exception since the bundle
2758
// is not supposed to know about the service at all
2759
// if it does not have permission.
2760
Oscar.error(ex.getMessage());
2761                return null;
2762            }
2763        }
2764
2765        // Define filter if expression is not null.
2766
Filter filter = null;
2767        if (expr != null)
2768        {
2769            filter = new FilterImpl(expr);
2770        }
2771
2772        synchronized (m_quickLock)
2773        {
2774            // Create a filtered list of service references.
2775
ArrayList list = new ArrayList();
2776            // Iterator over all bundles.
2777
for (Iterator i = m_installedBundleMap.values().iterator(); i.hasNext(); )
2778            {
2779                BundleImpl bundle = (BundleImpl) i.next();
2780
2781                // Do not look at bundles that are stopping.
2782
if (bundle.getInfo().getState() != Bundle.STOPPING)
2783                {
2784                    // Loop through all registered services for each bundle.
2785
ServiceReference[] refs = getBundleRegisteredServices(bundle);
2786                    for (int refIdx = 0;
2787                        (refs != null) && (refIdx < refs.length);
2788                        refIdx++)
2789                    {
2790                        // Determine if the registered services matches
2791
// the search criteria.
2792
boolean matched = false;
2793
2794                        // If className is null, then look at filter only.
2795
if ((className == null) &&
2796                            ((filter == null) || filter.match(refs[refIdx])))
2797                        {
2798                            boolean hasPermission = true;
2799                            // Since the class name is null, we need to check
2800
// here for permission to get each service interface.
2801
if (System.getSecurityManager() != null)
2802                            {
2803                                String JavaDoc[] objectClass = (String JavaDoc[])
2804                                    refs[refIdx].getProperty(OscarConstants.OBJECTCLASS);
2805                                for (int classIdx = 0;
2806                                    classIdx < objectClass.length;
2807                                    classIdx++)
2808                                {
2809                                    try
2810                                    {
2811                                        ServicePermission perm = new ServicePermission(
2812                                            objectClass[classIdx], ServicePermission.GET);
2813                                        AccessController.checkPermission(perm);
2814                                        // The bundle only needs permission for one
2815
// of the service interfaces, so break out
2816
// of the loop when permission is granted.
2817
break;
2818                                    }
2819                                    catch (Exception JavaDoc ex)
2820                                    {
2821                                        // We do not throw this exception since the bundle
2822
// is not supposed to know about the service at all
2823
// if it does not have permission.
2824
Oscar.error(ex.getMessage());
2825                                        hasPermission = false;
2826                                        break;
2827                                    }
2828                                }
2829                            }
2830                            matched = hasPermission;
2831                        }
2832                        // If className is not null, then first match the
2833
// objectClass property before looking at the
2834
// filter.
2835
else if (className != null)
2836                        {
2837                            String JavaDoc[] objectClass = (String JavaDoc[])
2838                                refs[refIdx].getProperty(OscarConstants.OBJECTCLASS);
2839                            for (int classIdx = 0;
2840                                classIdx < objectClass.length;
2841                                classIdx++)
2842                            {
2843                                if (objectClass[classIdx].equals(className) &&
2844                                    ((filter == null) || filter.match(refs[refIdx])))
2845                                {
2846                                    matched = true;
2847                                    break;
2848                                }
2849                            }
2850                        }
2851
2852                        // Add reference if it was a match.
2853
if (matched)
2854                        {
2855                            list.add(refs[refIdx]);
2856                        }
2857                    }
2858                }
2859            }
2860
2861            if (list.size() > 0)
2862            {
2863                ServiceReference[] refs = new ServiceReference[list.size()];
2864                for (int i = 0; i < list.size(); i++)
2865                {
2866                    refs[i] = (ServiceReference) list.get(i);
2867                }
2868                return refs;
2869            }
2870        }
2871
2872        return null;
2873    }
2874
2875    /**
2876     * Implementation for BundleContext.getService().
2877    **/

2878    protected Object JavaDoc getService(BundleImpl bundle, ServiceReference ref)
2879    {
2880        synchronized (m_quickLock)
2881        {
2882            // Check that the bundle has permission to get at least
2883
// one of the service interfaces; the objectClass property
2884
// of the service stores its service interfaces.
2885
String JavaDoc[] objectClass = (String JavaDoc[])
2886                ref.getProperty(Constants.OBJECTCLASS);
2887            if (objectClass == null)
2888            {
2889                return null;
2890            }
2891
2892            boolean hasPermission = false;
2893            if (System.getSecurityManager() != null)
2894            {
2895                for (int i = 0;
2896                    !hasPermission && (i < objectClass.length);
2897                    i++)
2898                {
2899                    try
2900                    {
2901                        ServicePermission perm =
2902                            new ServicePermission(
2903                                objectClass[i], ServicePermission.GET);
2904                        AccessController.checkPermission(perm);
2905                        hasPermission = true;
2906                    }
2907                    catch (Exception JavaDoc ex)
2908                    {
2909                    }
2910                }
2911            }
2912            else
2913            {
2914                hasPermission = true;
2915            }
2916
2917            // If the bundle does not permission to access the service,
2918
// then return null.
2919
if (!hasPermission)
2920            {
2921                return null;
2922            }
2923
2924            // Get the service registration if it is still valid.
2925
ServiceRegistrationImpl reg =
2926                ((ServiceReferenceImpl) ref).getServiceRegistration().isValid()
2927                ? ((ServiceReferenceImpl) ref).getServiceRegistration()
2928                : null;
2929
2930            BundleInfo clientInfo = bundle.getInfo();
2931
2932            // If the service registration is not valid, then this means
2933
// that the service provider unregistered the service. The spec
2934
// says that calls to get an unregistered service should always
2935
// return null (assumption: even if it is currently cached
2936
// by the bundle). So in this case, flush the service reference
2937
// from the cache and return null.
2938
if (reg == null)
2939            {
2940                // Remove service reference from usage cache.
2941
clientInfo.removeServiceUsageCounter(ref);
2942
2943                // It is not necessary to unget the service object from
2944
// the providing bundle, since the associated service is
2945
// unregistered and hence not in the list of registered services
2946
// of the providing bundle. This is precisely why the service
2947
// registration was not found above in the first place.
2948

2949                return null;
2950            }
2951
2952            // Get the usage count, if any.
2953
BundleInfo.UsageCounter usage = clientInfo.getServiceUsageCounter(ref);
2954
2955            // If the service object is cached, then increase the usage
2956
// count and return the cached service object.
2957
Object JavaDoc svcObj = null;
2958            if (usage != null)
2959            {
2960                usage.m_count++;
2961                svcObj = usage.m_svcObj;
2962            }
2963            else
2964            {
2965                // Get service object from service registration.
2966
svcObj = reg.getService(bundle);
2967
2968                // Cache the service object.
2969
if (svcObj != null)
2970                {
2971                    usage = new BundleInfo.UsageCounter();
2972                    usage.m_svcObj = svcObj;
2973                    usage.m_count++;
2974                    clientInfo.putServiceUsageCounter(ref, usage);
2975                }
2976            }
2977
2978            return svcObj;
2979        }
2980    }
2981
2982    /**
2983     * Implementation for BundleContext.ungetService().
2984    **/

2985    protected Object JavaDoc ungetService(BundleImpl bundle, ServiceReference ref)
2986    {
2987        synchronized (m_quickLock)
2988        {
2989            BundleInfo clientInfo = bundle.getInfo();
2990            BundleInfo.UsageCounter usage = null;
2991
2992            // Get current usage count.
2993
usage = clientInfo.getServiceUsageCounter(ref);
2994
2995            // If no usage count, then return.
2996
if (usage == null)
2997            {
2998                return null;
2999            }
3000
3001            // Decrement usage count.
3002
usage.m_count--;
3003
3004            // Remove reference when usage count goes to zero
3005
// and unget the service object from the exporting
3006
// bundle.
3007
if (usage.m_count == 0)
3008            {
3009                clientInfo.removeServiceUsageCounter(ref);
3010                ServiceRegistrationImpl reg =
3011                    ((ServiceReferenceImpl) ref).getServiceRegistration();
3012                reg.ungetService(bundle, usage.m_svcObj);
3013                usage.m_svcObj = null;
3014            }
3015
3016            // Always return the service object.
3017
return usage.m_svcObj;
3018        }
3019    }
3020
3021    /**
3022     * Implementation for BundleContext.getDataFile().
3023    **/

3024    protected File getBundleDataFile(BundleImpl bundle, String JavaDoc name)
3025        throws IllegalStateException JavaDoc
3026    {
3027        // The spec says to throw an error if the bundle
3028
// is stopped, which I assume means not active,
3029
// starting, or stopping.
3030
if ((bundle.getInfo().getState() != Bundle.ACTIVE) &&
3031            (bundle.getInfo().getState() != Bundle.STARTING) &&
3032            (bundle.getInfo().getState() != Bundle.STOPPING))
3033        {
3034            throw new IllegalStateException JavaDoc("Only active bundles can create files.");
3035        }
3036        try
3037        {
3038            return m_cache.getArchive(
3039                bundle.getInfo().getBundleId()).getDataFile(name);
3040        }
3041        catch (Exception JavaDoc ex)
3042        {
3043            Oscar.error(ex.getMessage());
3044            return null;
3045        }
3046    }
3047
3048    //
3049
// Miscellaneous management methods.
3050
//
3051

3052    private BundleInfo createBundleInfo(BundleArchive archive)
3053        throws Exception JavaDoc
3054    {
3055        // Get the bundle manifest.
3056
Map headerMap = null;
3057        try
3058        {
3059            // Although there should only ever be one revision at this
3060
// point, get the header for the current revision to be safe.
3061
headerMap = archive.getManifestHeader(archive.getRevisionCount() - 1);
3062        }
3063        catch (Exception JavaDoc ex)
3064        {
3065            throw new BundleException("Unable to read JAR manifest.", ex);
3066        }
3067
3068        // We can't do anything without the manifest header.
3069
if (headerMap == null)
3070        {
3071            throw new BundleException("Unable to read JAR manifest header.");
3072        }
3073
3074        // Create the module for the bundle; although there should only
3075
// ever be one revision at this point, create the module for
3076
// the current revision to be safe.
3077
Module module = createModule(
3078            archive.getId(), archive.getRevisionCount() - 1, headerMap);
3079
3080        // Finally, create an return the bundle info.
3081
return new BundleInfo(archive, module);
3082    }
3083
3084    /**
3085     * Creates a module for a given bundle by reading the bundle's
3086     * manifest meta-data and converting it to work with the underlying
3087     * import/export search policy of the module loader.
3088     * @param id the identifier of the bundle for which the module should
3089     * be created.
3090     * @param headers the headers map associated with the bundle.
3091     * @return the initialized and/or newly created module.
3092    **/

3093    private Module createModule(long id, int revision, Map headerMap)
3094        throws Exception JavaDoc
3095    {
3096        // Create the resource sources for the bundle. The resource sources
3097
// are comprised of the bundle's class path values (as JarResourceSources).
3098
ResourceSource[] resSources = null;
3099        try
3100        {
3101            // Get bundle class path for the specified revision from cache.
3102
String JavaDoc[] classPath = m_cache.getArchive(id).getClassPath(revision);
3103
3104            // Create resource sources for everything.
3105
resSources = new ResourceSource[classPath.length];
3106            for (int i = 0; i < classPath.length; i++)
3107            {
3108                resSources[i] = new JarResourceSource(classPath[i]);
3109            }
3110        }
3111        catch (Exception JavaDoc ex)
3112        {
3113            ex.printStackTrace();
3114            throw new BundleException("Error in class path: " + ex);
3115        }
3116
3117        // Get import packages from bundle manifest
3118
// for use by ImportSearchPolicy.
3119
Object JavaDoc[][] imports = TextUtil.parseImportExportHeader(
3120            (String JavaDoc) headerMap.get(Constants.IMPORT_PACKAGE));
3121
3122        // Get export packages from bundle manifest
3123
// for use by ImportSearchPolicy.
3124
Object JavaDoc[][] exports = TextUtil.parseImportExportHeader(
3125            (String JavaDoc) headerMap.get(Constants.EXPORT_PACKAGE));
3126
3127        // Get dynamic imports from bundle manifest
3128
// for use by OSGiImportSearchPolicy.
3129
String JavaDoc[] dynamics = TextUtil.parseCommaDelimitedString(
3130            (String JavaDoc) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
3131        dynamics = (dynamics == null) ? new String JavaDoc[0] : dynamics;
3132
3133        Object JavaDoc[][] attributes = {
3134            new Object JavaDoc[] { ImportSearchPolicy.EXPORTS_ATTR, exports },
3135            new Object JavaDoc[] { ImportSearchPolicy.IMPORTS_ATTR, imports },
3136            new Object JavaDoc[] { OSGiImportSearchPolicy.DYNAMIC_IMPORTS_ATTR, dynamics},
3137            new Object JavaDoc[] { ImportSearchPolicy.PROPAGATES_ATTR, new Object JavaDoc[0] }
3138        };
3139
3140        // Get native library entry names for module library sources.
3141
LibraryInfo[] libraries =
3142            TextUtil.parseLibraryStrings(TextUtil.parseCommaDelimitedString(
3143                (String JavaDoc) headerMap.get(Constants.BUNDLE_NATIVECODE)));
3144        LibrarySource[] libSources = {
3145            new OSGiLibrarySource(
3146                m_cache, id, revision,
3147                getProperty(Constants.FRAMEWORK_OS_NAME),
3148                getProperty(Constants.FRAMEWORK_PROCESSOR),
3149                libraries)
3150        };
3151
3152        Module module =
3153            m_mgr.addModule(
3154                Long.toString(id) + "." + Integer.toString(revision),
3155                attributes, resSources, libSources);
3156
3157        return module;
3158    }
3159
3160    //
3161
// Miscellaneous bundle management methods.
3162
//
3163

3164    /**
3165     * Retrieves a bundle from its location.
3166     *
3167     * @param location the location of the bundle to retrieve.
3168     * @return the bundle associated with the location or null if there
3169     * is no bundle associated with the location.
3170    **/

3171    private Bundle getBundle(String JavaDoc location)
3172    {
3173        synchronized (m_quickLock)
3174        {
3175            return (Bundle) m_installedBundleMap.get(location);
3176        }
3177    }
3178
3179    /**
3180     * This method removes all old revisions of the specified bundle from
3181     * the bundle cache, except the current revision and removes all modules
3182     * associated with the specified bundle from the module manager.
3183     * This method is only called during a refresh.
3184     * @param bundle the bundle whose revisions should be purged.
3185     * @throws java.lang.Exception if any error occurs.
3186    **/

3187    private void purgeBundle(BundleImpl bundle) throws Exception JavaDoc
3188    {
3189        // CONCURRENCY NOTE:
3190
// This method is called during a refresh operation, which
3191
// already holds the admin lock.
3192

3193        // Get the bundle info.
3194
BundleInfo info = bundle.getInfo();
3195
3196        // In case of a refresh, then we want to physically
3197
// remove the bundle's modules from the module manager.
3198
// This is necessary for two reasons: 1) because
3199
// under Windows we won't be able to delete the bundle
3200
// because files might be left open in the resource
3201
// sources of its modules and 2) we want to make sure
3202
// that no references to old modules exist since they
3203
// will all be stale after the refresh. The only other
3204
// way to do this is to remove the bundle, but that
3205
// would be incorrect, because this is a refresh operation
3206
// and should not trigger bundle REMOVE events.
3207
synchronized (m_quickLock)
3208        {
3209            Module[] modules = info.getModules();
3210            for (int i = 0; i < modules.length; i++)
3211            {
3212                m_mgr.removeModule(modules[i]);
3213            }
3214        }
3215
3216        // Purge all bundle revisions, but the current one.
3217
m_cache.purge(m_cache.getArchive(info.getBundleId()));
3218    }
3219
3220    /**
3221     * This method removes a bundle from the installed bundled map
3222     * and from the bundle cache. It also removes the bundle's associated
3223     * module from the module manager. This method is only used internally/
3224     * @param bundle the bundle to remove.
3225     * @throws java.lang.Exception if any error occurs.
3226    **/

3227    private void removeBundle(BundleImpl bundle) throws Exception JavaDoc
3228    {
3229        synchronized (m_adminLock)
3230        {
3231            synchronized (m_quickLock)
3232            {
3233                // Remove the bundle from the installed bundle map.
3234
m_installedBundleMap.remove(bundle.getInfo().getLocation());
3235            }
3236
3237            // Remove the bundle's associated modules from
3238
// the module manager.
3239
Module[] modules = bundle.getInfo().getModules();
3240            for (int i = 0; i < modules.length; i++)
3241            {
3242                m_mgr.removeModule(modules[i]);
3243            }
3244
3245            // Remove the bundle from the cache.
3246
m_cache.remove(m_cache.getArchive(bundle.getInfo().getBundleId()));
3247        }
3248    }
3249
3250    private BundleActivator createBundleActivator(BundleInfo info)
3251        throws Exception JavaDoc
3252    {
3253        // CONCURRENCY NOTE:
3254
// This method is only called from startBundleWithStartLevel(),
3255
// which has the exclusion lock, so there is no need to do any
3256
// locking here.
3257

3258        BundleActivator activator = null;
3259
3260        String JavaDoc strict = getConfigProperty(OscarConstants.STRICT_OSGI_PROP);
3261        boolean isStrict = (strict == null) ? true : strict.equals("true");
3262        if (!isStrict)
3263        {
3264            try
3265            {
3266                activator =
3267                    m_cache.getArchive(info.getBundleId())
3268                        .getActivator(info.getCurrentModule().getClassLoader());
3269            }
3270            catch (Exception JavaDoc ex)
3271            {
3272                activator = null;
3273            }
3274        }
3275
3276        // If there was no cached activator, then get the activator
3277
// class from the bundle manifest.
3278
if (activator == null)
3279        {
3280            // Get the associated bundle archive.
3281
BundleArchive ba = m_cache.getArchive(info.getBundleId());
3282            // Get the manifest from the current revision; revision is
3283
// base zero so subtract one from the count to get the
3284
// current revision.
3285
Map headerMap = ba.getManifestHeader(ba.getRevisionCount() - 1);
3286            // Get the activator class attribute.
3287
String JavaDoc className = (String JavaDoc) headerMap.get(Constants.BUNDLE_ACTIVATOR);
3288            // Try to instantiate activator class if present.
3289
if (className != null)
3290            {
3291                className = className.trim();
3292                Class JavaDoc clazz = info.getCurrentModule().getClassLoader().loadClass(className);
3293                if (clazz == null)
3294                {
3295                    throw new BundleException("Not found: "
3296                        + className);
3297                }
3298                activator = (BundleActivator) clazz.newInstance();
3299            }
3300        }
3301
3302        return activator;
3303    }
3304
3305    protected void resolveBundle(BundleImpl bundle)
3306        throws BundleException
3307    {
3308        synchronized (m_adminLock)
3309        {
3310            // If a security manager is installed, then check for permission
3311
// to import the necessary packages.
3312
if (System.getSecurityManager() != null)
3313            {
3314                URL url = null;
3315                try
3316                {
3317                    url = new URL(bundle.getInfo().getLocation());
3318                }
3319                catch (MalformedURLException ex)
3320                {
3321                    throw new BundleException("Cannot resolve, bad URL "
3322                        + bundle.getInfo().getLocation());
3323                }
3324
3325                try
3326                {
3327                    AccessController.doPrivileged(new CheckImportsPrivileged(url, bundle));
3328                }
3329                catch (PrivilegedActionException ex)
3330                {
3331                    Exception JavaDoc thrown = ((PrivilegedActionException) ex).getException();
3332                    if (thrown instanceof AccessControlException)
3333                    {
3334                        throw (AccessControlException) thrown;
3335                    }
3336                    else
3337                    {
3338                        throw new BundleException("Problem resolving: " + ex);
3339                    }
3340                }
3341            }
3342
3343            // Get the import search policy and try to validate
3344
// the module.
3345
ImportSearchPolicy search =
3346                (ImportSearchPolicy) m_mgr.getSearchPolicy();
3347            Module module = bundle.getInfo().getCurrentModule();
3348            try
3349            {
3350                search.validate(module);
3351            }
3352            catch (ValidationException ex)
3353            {
3354                int[] v = (int[]) ex.getVersion();
3355                throw new BundleException("Unresolved package: "
3356                    + ex.getIdentifier() + "; specification-version=\""
3357                    + v[0] + "." + v[1] + "." + v[2] + "\"");
3358            }
3359
3360            bundle.getInfo().setState(Bundle.RESOLVED);
3361        }
3362    }
3363
3364    //
3365
// Miscellaneous service management methods.
3366
//
3367

3368    /**
3369     * Sets the properties associated with the given service registration.
3370    **/

3371    protected void servicePropertiesModified(ServiceRegistration reg)
3372    {
3373        fireServiceEvent(ServiceEvent.MODIFIED, reg.getReference());
3374    }
3375
3376    /**
3377     * Returns the bundles that are using the service referenced by the
3378     * ServiceReference object. Specifically, this method returns the bundles
3379     * whose usage count for that service is greater than zero.
3380     * @return An array of bundles whose usage count for the service referenced
3381     * by this ServiceReference object is greater than zero; null if no bundles
3382     * are currently using that service.
3383    **/

3384    protected Bundle[] getUsingBundles(ServiceReference ref)
3385    {
3386        // Since this needs to look at all bundles, first
3387
// get lock the write lock object, then get the
3388
// exclusive lock on the read lock.
3389
synchronized (m_quickLock)
3390        {
3391            List list = null;
3392
3393            // Access the installed bundle map directly, since
3394
// the read lock it not recursive, we cannot call
3395
// Oscar.getBundles() or else we would deadlock.
3396
Iterator iter = m_installedBundleMap.values().iterator();
3397            while (iter.hasNext())
3398            {
3399                BundleImpl bundle = (BundleImpl) iter.next();
3400                BundleInfo info = bundle.getInfo();
3401                if (info.getServiceUsageCounter(ref) != null)
3402                {
3403                    if (list == null)
3404                    {
3405                        list = new ArrayList();
3406                    }
3407                    list.add(bundle);
3408                }
3409            }
3410
3411            if (list != null)
3412            {
3413                Bundle[] bundles = new Bundle[list.size()];
3414                return (Bundle[]) list.toArray(bundles);
3415            }
3416        }
3417
3418        return null;
3419    }
3420
3421    /**
3422     * Unregisters a service for the specified bundle.
3423     *
3424     * @param reg the service registration for the service to unregister.
3425    **/

3426    protected void unregisterService(BundleImpl bundle, ServiceRegistrationImpl reg)
3427    {
3428        synchronized (m_quickLock)
3429        {
3430            BundleInfo info = bundle.getInfo();
3431            info.removeServiceRegistration(reg);
3432        }
3433
3434        // Fire event outside synchronized block.
3435
fireServiceEvent(ServiceEvent.UNREGISTERING, reg.getReference());
3436    }
3437
3438    /**
3439     * Unregisters all services for the specified bundle.
3440    **/

3441    protected void unregisterServices(BundleImpl bundle)
3442    {
3443        synchronized (m_quickLock)
3444        {
3445            BundleInfo info = bundle.getInfo();
3446            while (info.getServiceRegistrationCount() != 0)
3447            {
3448                ServiceRegistrationImpl reg = info.getServiceRegistration(0);
3449                unregisterService(bundle, reg);
3450            }
3451        }
3452    }
3453
3454    /**
3455     * This is a utility method to release all services being
3456     * used by the specified bundle.
3457     * @param bundle the bundle whose services are to be released.
3458    **/

3459    protected void ungetServices(BundleImpl bundle)
3460    {
3461        synchronized (m_quickLock)
3462        {
3463            BundleInfo info = bundle.getInfo();
3464            if (info.getState() == Bundle.UNINSTALLED)
3465            {
3466                throw new IllegalStateException JavaDoc("The bundle is uninstalled.");
3467            }
3468
3469            // We don't directly call getServicesInUse() here
3470
// because of security checks.
3471
Iterator iter = info.getServiceUsageCounters();
3472            if (iter.hasNext())
3473            {
3474                // Create a list of services so we don't have
3475
// to worry about concurrent modification to
3476
// the iterator's collection.
3477
ArrayList list = new ArrayList();
3478                while (iter.hasNext())
3479                {
3480                    list.add(iter.next());
3481                }
3482
3483                // Remove each service object from the
3484
// service cache.
3485
for (int i = 0; i < list.size(); i++)
3486                {
3487                    ServiceReference ref = (ServiceReference) list.get(i);
3488                    // Keep ungetting until all usage count is zero.
3489
while (info.getServiceUsageCounter(ref) != null)
3490                    {
3491                        ungetService(bundle, ref);
3492                    }
3493                }
3494            }
3495        }
3496    }
3497
3498    //
3499
// Event firing methods.
3500
//
3501

3502    /**
3503     * Miscellaneous events methods.
3504    **/

3505    private void fireFrameworkEvent(
3506        int type, Bundle bundle, Throwable JavaDoc throwable)
3507    {
3508        if (m_frameworkDispatcher == null)
3509        {
3510            m_frameworkDispatcher = new Dispatcher() {
3511                public void dispatch(EventListener l, EventObject eventObj)
3512                {
3513                    ((FrameworkListener) l)
3514                        .frameworkEvent((FrameworkEvent) eventObj);
3515                }
3516            };
3517        }
3518        FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
3519        m_dispatchQueue.dispatch(
3520            m_frameworkDispatcher, FrameworkListener.class, event);
3521    }
3522
3523    /**
3524     * Fires bundle events.
3525     *
3526     * @param type the type of bundle event to fire.
3527     * @param bundle the bundle associated with the event.
3528    **/

3529    private void fireBundleEvent(int type, Bundle bundle)
3530    {
3531        if (m_bundleDispatcher == null)
3532        {
3533            m_bundleDispatcher = new Dispatcher() {
3534                public void dispatch(EventListener l, EventObject eventObj)
3535                {
3536                    ((BundleListener) l)
3537                        .bundleChanged((BundleEvent) eventObj);
3538                }
3539            };
3540        }
3541        BundleEvent event = null;
3542        event = new BundleEvent(type, bundle);
3543        m_dispatchQueue.dispatch(m_bundleDispatcher,
3544            BundleListener.class, event);
3545    }
3546
3547    /**
3548     * Fires service events.
3549     *
3550     * @param type the type of service event to fire.
3551     * @param ref the service reference associated with the event.
3552    **/

3553    private void fireServiceEvent(int type, ServiceReference ref)
3554    {
3555        if (m_serviceDispatcher == null)
3556        {
3557            m_serviceDispatcher = new Dispatcher() {
3558                public void dispatch(EventListener l, EventObject eventObj)
3559                {
3560                    ((ServiceListener) l)
3561                        .serviceChanged((ServiceEvent) eventObj);
3562                }
3563            };
3564        }
3565        ServiceEvent event = null;
3566        event = new ServiceEvent(type, ref);
3567        m_dispatchQueue.dispatch(m_serviceDispatcher,
3568            ServiceListener.class, event);
3569    }
3570
3571    /**
3572     * Remove all of the specified bundle's event listeners from
3573     * the framework.
3574     * @param bundle the bundle whose listeners are to be removed.
3575    **/

3576    private void removeListeners(BundleImpl bundle)
3577    {
3578        Oscar.debug("Removing all listeners for bundle "
3579            + bundle.getInfo().getBundleId());
3580        if (bundle == null)
3581        {
3582            return;
3583        }
3584
3585        // Remove all listeners associated with the supplied bundle;
3586
// it is only possible to know the bundle associated with a
3587
// listener if the listener was wrapper by a ListenerWrapper,
3588
// so look for those.
3589
Object JavaDoc[] listeners = m_dispatchQueue.getListeners();
3590        for (int i = listeners.length - 2; i >= 0; i -= 2)
3591        {
3592            // Check for listener wrappers and then compare the bundle.
3593
if (listeners[i + 1] instanceof ListenerWrapper)
3594            {
3595                ListenerWrapper lw = (ListenerWrapper) listeners[i + 1];
3596                if ((lw.getBundle() != null) && (lw.getBundle().equals(bundle)))
3597                {
3598                    m_dispatchQueue.removeListener(
3599                        (Class JavaDoc) listeners[i], (EventListener) listeners[i+1]);
3600                }
3601            }
3602        }
3603
3604        Oscar.debug("Removed all listeners for bundle "
3605            + bundle.getInfo().getBundleId());
3606    }
3607
3608    //
3609
// Property related methods.
3610
//
3611

3612    private static boolean s_initialized = false;
3613
3614    /**
3615     * Installs all system properties specified in the system property
3616     * file associated with the Oscar installation; these properties
3617     * will be accessible through <tt>System.getProperty()</tt> at run
3618     * time. By default, the system property file is located in the
3619     * same directory as the <tt>oscar.jar</tt> file and is called
3620     * "<tt>system.properties</tt>". This may be changed by setting the
3621     * "<tt>oscar.system.properties</tt>" system property to an
3622     * arbitrary absolute path. The properties in this file will
3623     * overwrite any existing system properties.
3624    **/

3625    public static void initializeSystemProperties()
3626    {
3627        // In theory, this should be synchronized, but it
3628
// is not that critical.
3629
if (!s_initialized)
3630        {
3631            // Set initialized flag.
3632
s_initialized = true;
3633
3634            // The system properties file is either specified by a system
3635
// property or it is in the same directory as the Oscar JAR file.
3636
// Try to load it from one of these places.
3637

3638            // See if the property file was specified as a property.
3639
File propFile = null;
3640            String JavaDoc custom = System.getProperty(OscarConstants.SYSTEM_PROPERTIES_PROP);
3641            if (custom != null)
3642            {
3643                propFile = new File(custom);
3644            }
3645            else
3646            {
3647                // Determine where oscar.jar is located by looking at the
3648
// system class path.
3649
String JavaDoc jarLoc = null;
3650                String JavaDoc classpath = System.getProperty("java.class.path");
3651                int index = classpath.toLowerCase().indexOf("oscar.jar");
3652                int start = classpath.lastIndexOf(File.pathSeparator, index) + 1;
3653                if (index > start)
3654                {
3655                    jarLoc = classpath.substring(start, index);
3656                    if (jarLoc.length() == 0)
3657                    {
3658                        jarLoc = ".";
3659                    }
3660                }
3661                else
3662                {
3663                    // Can't figure it out so use the current directory as default.
3664
jarLoc = System.getProperty("user.dir");
3665                }
3666
3667                propFile = new File(jarLoc, OscarConstants.SYSTEM_PROPERTY_FILE_VALUE);
3668            }
3669
3670            // Try to load the global properties file.
3671
Properties props = new Properties();
3672            try
3673            {
3674                // See if the default property file has been overwritten.
3675
FileInputStream fis = new FileInputStream(propFile);
3676                props.load(fis);
3677                fis.close();
3678            }
3679            catch (FileNotFoundException ex)
3680            {
3681                // Ignore file not found.
3682
}
3683            catch (Exception JavaDoc ex)
3684            {
3685                Oscar.error("Oscar: Error loading system properties from "
3686                    + OscarConstants.SYSTEM_PROPERTY_FILE_VALUE + ": " + ex);
3687            }
3688
3689            // Push all loaded properties into System.
3690
for (Enumeration e = props.propertyNames(); e.hasMoreElements(); )
3691            {
3692                String JavaDoc name = (String JavaDoc) e.nextElement();
3693                System.setProperty(name, substVars((String JavaDoc) props.getProperty(name)));
3694            }
3695        }
3696    }
3697
3698    private void initializeOsgiProperties()
3699    {
3700        setProperty(OscarConstants.FRAMEWORK_VERSION,
3701            OscarConstants.FRAMEWORK_VERSION_VALUE);
3702        setProperty(OscarConstants.FRAMEWORK_VENDOR,
3703            OscarConstants.FRAMEWORK_VENDOR_VALUE);
3704        setProperty(OscarConstants.FRAMEWORK_LANGUAGE,
3705            System.getProperty("user.language"));
3706        setProperty(OscarConstants.FRAMEWORK_OS_NAME,
3707            System.getProperty("os.name"));
3708        setProperty(OscarConstants.FRAMEWORK_OS_VERSION,
3709            System.getProperty("os.version"));
3710        setProperty(OscarConstants.FRAMEWORK_PROCESSOR,
3711            System.getProperty("os.arch"));
3712    }
3713
3714    private void initializeBundleProperties()
3715    {
3716        //
3717
// First set various default global property values.
3718
//
3719

3720        // The Oscar version property.
3721
setProperty(OscarConstants.OSCAR_VERSION_PROPERTY,
3722            OscarConstants.OSCAR_VERSION_VALUE);
3723
3724        // Oscar OSGi strictness property.
3725
String JavaDoc strict = getConfigProperty(OscarConstants.STRICT_OSGI_PROP);
3726        boolean isStrict = (strict == null) ? true : strict.equals("true");
3727        setProperty(OscarConstants.STRICT_OSGI_PROP, (isStrict) ? "true" : "false");
3728
3729        // The Java user directory property.
3730
String JavaDoc val = System.getProperty("user.home");
3731        if (val != null)
3732            setProperty("user.home", val);
3733
3734        // The Java user directory property.
3735
val = System.getProperty("user.dir");
3736        if (val != null)
3737            setProperty("user.dir", val);
3738
3739        // Push all properties from the bundle property file into
3740
// the Oscar global properties.
3741
Properties props = readBundlePropertiesFile();
3742        for (Enumeration e = props.propertyNames(); e.hasMoreElements(); )
3743        {
3744            String JavaDoc name = (String JavaDoc) e.nextElement();
3745            setProperty(name, (String JavaDoc) props.getProperty(name));
3746        }
3747    }
3748
3749    /**
3750     * Retrieves the bundle property file associated with the Oscar
3751     * installation; these properties will be accessible through
3752     * <tt>BundleContext.getProperty()</tt> at run time. By default, the
3753     * Bundle property file is located in the same directory as the
3754     * <tt>oscar.jar</tt> file and is called "<tt>bundle.properties</tt>".
3755     * This may be changed by setting the
3756     * "<tt>oscar.bundle.properties</tt>" system property to an
3757     * arbitrary absolute path.
3758     *
3759     * @return a list of properties or <tt>null</tt> if there was an error.
3760    **/

3761    private Properties readBundlePropertiesFile()
3762    {
3763        // The global properties file is either specified by a system
3764
// property or it is in the same directory as the Oscar JAR file.
3765
// Try to load it from one of these places.
3766

3767        // See if the property file was specified as a property.
3768
File propFile = null;
3769        String JavaDoc custom = getConfigProperty(OscarConstants.BUNDLE_PROPERTIES_PROP);
3770        if (custom != null)
3771        {
3772            propFile = new File(custom);
3773        }
3774        else
3775        {
3776            // Determine where oscar.jar is located by looking at the
3777
// system class path.
3778
String JavaDoc jarLoc = null;
3779            String JavaDoc classpath = System.getProperty("java.class.path");
3780            int index = classpath.toLowerCase().indexOf("oscar.jar");
3781            int start = classpath.lastIndexOf(File.pathSeparator, index) + 1;
3782            if (index > start)
3783            {
3784                jarLoc = classpath.substring(start, index);
3785                if (jarLoc.length() == 0)
3786                {
3787                    jarLoc = ".";
3788                }
3789            }
3790            else
3791            {
3792                // Can't figure it out so use the current directory as default.
3793
jarLoc = System.getProperty("user.dir");
3794            }
3795
3796            propFile = new File(jarLoc, OscarConstants.BUNDLE_PROPERTY_FILE_VALUE);
3797        }
3798
3799        // Try to load the global properties file.
3800
Properties props = new Properties();
3801        try
3802        {
3803            // See if the default property file has been overwritten.
3804
FileInputStream fis = new FileInputStream(propFile);
3805            props.load(fis);
3806            fis.close();
3807        }
3808        catch (FileNotFoundException ex)
3809        {
3810            // Ignore file not found.
3811
}
3812        catch (Exception JavaDoc ex)
3813        {
3814            Oscar.error("Oscar: Error loading bundle properties from "
3815                + OscarConstants.BUNDLE_PROPERTY_FILE_VALUE + ": " + ex);
3816            return null;
3817        }
3818
3819        return props;
3820    }
3821
3822    private static final String JavaDoc DELIM_START = "${";
3823    private static final char DELIM_STOP = '}';
3824    private static final int DELIM_START_LEN = 2;
3825    private static final int DELIM_STOP_LEN = 1;
3826
3827    private static String JavaDoc substVars(String JavaDoc val)
3828        throws IllegalArgumentException JavaDoc
3829    {
3830        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
3831
3832        if (val == null)
3833        {
3834            return val;
3835        }
3836
3837        int i = 0;
3838        int j, k;
3839
3840        while (true)
3841        {
3842            j = val.indexOf(DELIM_START, i);
3843            if (j == -1)
3844            {
3845                if (i == 0)
3846                {
3847                    return val;
3848                }
3849                else
3850                {
3851                    sbuf.append(val.substring(i, val.length()));
3852                    return sbuf.toString();
3853                }
3854            }
3855            else
3856            {
3857                sbuf.append(val.substring(i, j));
3858                k = val.indexOf(DELIM_STOP, j);
3859                if (k == -1)
3860                {
3861                    throw new IllegalArgumentException JavaDoc(
3862                    '"' + val +
3863                    "\" has no closing brace. Opening brace at position "
3864                    + j + '.');
3865                }
3866                else
3867                {
3868                    j += DELIM_START_LEN;
3869                    String JavaDoc key = val.substring(j, k);
3870                    // Try system properties.
3871
String JavaDoc replacement = System.getProperty(key, null);
3872                    if (replacement != null)
3873                    {
3874                        sbuf.append(replacement);
3875                    }
3876                    i = k + DELIM_STOP_LEN;
3877                }
3878            }
3879        }
3880    }
3881
3882    private void processAutoProperties()
3883    {
3884        // The auto-install property specifies bundles to be automatically
3885
// installed in each new profile; it is possible to specify a
3886
// start level for these bundles by appending a ".n" to the
3887
// auto-install property name, where "n" is the desired start
3888
// level for the bundle. This code will search for the auto-install
3889
// property and for subsequent variants with a specified start level
3890
// up until it finds a break in the sequence of start levels.
3891

3892        // NOTE: The following is a little bit of a hack. For legacy reasons
3893
// we still have to allow for the possibility of an auto-install
3894
// property with no start level specified. To do this, we first
3895
// try get the property with no start level and try to process it,
3896
// then we retrieve the next property at the end of the loop and
3897
// then process it, and so on.
3898
String JavaDoc prop = getConfigProperty(OscarConstants.AUTO_INSTALL_PROP);
3899        int level = 0;
3900        do
3901        {
3902            if (prop != null)
3903            {
3904                StringTokenizer st = new StringTokenizer(prop, "\" ",true);
3905                if (st.countTokens() > 0)
3906                {
3907                    String JavaDoc location = null;
3908                    do
3909                    {
3910                        location = nextLocation(st);
3911                        if (location != null)
3912                        {
3913                            try
3914                            {
3915                                BundleImpl b = (BundleImpl) installBundle(location, null);
3916                                // The legacy case defaults to level 1.
3917
b.getInfo().setStartLevel((level == 0) ? 1 : level);
3918                            }
3919                            catch (Exception JavaDoc ex)
3920                            {
3921                                System.err.println("Oscar: Auto-properties install.");
3922                                ex.printStackTrace();
3923                            }
3924                        }
3925                    }
3926                    while (location != null);
3927                }
3928            }
3929
3930            level++;
3931            prop = getConfigProperty(OscarConstants.AUTO_INSTALL_PROP + "." + level);
3932        }
3933        while (prop != null);
3934
3935        // The auto-start property specifies bundles to be automatically
3936
// installed and started in each new profile; it is possible to
3937
// specify a start level for these bundles by appending a ".n" to
3938
// the auto-start property name, where "n" is the desired start
3939
// level for the bundle. This code will search for the auto-start
3940
// property and for subsequent variants with a specified start level
3941
// up until it finds a break in the sequence of start levels.
3942

3943        // NOTE: The following is a little bit of a hack. For legacy reasons
3944
// we still have to allow for the possibility of an auto-start
3945
// property with no start level specified. To do this, we first
3946
// try get the property with no start level and try to process it,
3947
// then we retrieve the next property at the end of the loop and
3948
// then process it, and so on.
3949
prop = getConfigProperty(OscarConstants.AUTO_START_PROP);
3950        level = 0;
3951        do
3952        {
3953            if (prop != null)
3954            {
3955                // First install all autostart bundles to avoid
3956
// start-up ordering problems.
3957
StringTokenizer st = new StringTokenizer(prop, "\" ",true);
3958                if (st.countTokens() > 0)
3959                {
3960                    String JavaDoc location = null;
3961                    do
3962                    {
3963                        location = nextLocation(st);
3964                        if (location != null)
3965                        {
3966                            try
3967                            {
3968                                BundleImpl b = (BundleImpl) installBundle(location, null);
3969                                // The legacy case defaults to level 1.
3970
b.getInfo().setStartLevel((level == 0) ? 1 : level);
3971                            }
3972                            catch (Exception JavaDoc ex)
3973                            {
3974                                System.err.println("Oscar: Auto-properties install.");
3975                                ex.printStackTrace();
3976                            }
3977                        }
3978                    }
3979                    while (location != null);
3980                }
3981
3982                // Now loop through and start the installed bundles.
3983
st = new StringTokenizer(prop, "\" ",true);
3984                if (st.countTokens() > 0)
3985                {
3986                    String JavaDoc location = null;
3987                    do
3988                    {
3989                        location = nextLocation(st);
3990                        if (location != null)
3991                        {
3992                            // Installing twice just returns the same bundle.
3993
try
3994                            {
3995                                BundleImpl bundle = (BundleImpl) installBundle(location, null);
3996                                startBundle(bundle);
3997                            }
3998                            catch (Exception JavaDoc ex)
3999                            {
4000                                System.err.println("Oscar: Auto-properties start.");
4001                                ex.printStackTrace();
4002                            }
4003                        }
4004                    }
4005                    while (location != null);
4006                }
4007            }
4008
4009            level++;
4010            prop = getConfigProperty(OscarConstants.AUTO_START_PROP + "." + level);
4011        }
4012        while (prop != null);
4013    }
4014
4015    private String JavaDoc nextLocation(StringTokenizer st)
4016    {
4017        String JavaDoc retVal = null;
4018
4019        if (st.countTokens() > 0)
4020        {
4021            String JavaDoc tokenList = "\" ";
4022            StringBuffer JavaDoc tokBuf = new StringBuffer JavaDoc(10);
4023            String JavaDoc tok = null;
4024            String JavaDoc location = null;
4025            boolean inQuote = false;
4026            boolean tokStarted = false;
4027            boolean exit = false;
4028            while ((st.hasMoreTokens()) && (!exit))
4029            {
4030                tok = st.nextToken(tokenList);
4031                if (tok.equals("\""))
4032                {
4033                    inQuote = ! inQuote;
4034                    if (inQuote)
4035                    {
4036                        tokenList = "\"";
4037                    }
4038                    else
4039                    {
4040                        tokenList = "\" ";
4041                    }
4042
4043                }
4044                else if (tok.equals(" "))
4045                {
4046                    if (tokStarted)
4047                    {
4048                        retVal = tokBuf.toString();
4049                        tokStarted=false;
4050                        tokBuf = new StringBuffer JavaDoc(10);
4051                        exit = true;
4052                    }
4053                }
4054                else
4055                {
4056                    tokStarted = true;
4057                    tokBuf.append(tok.trim());
4058                }
4059            }
4060
4061            // Handle case where end of token stream and
4062
// still got data
4063
if ((!exit) && (tokStarted))
4064            {
4065                retVal = tokBuf.toString();
4066            }
4067        }
4068
4069        return retVal;
4070    }
4071
4072    //
4073
// Public static utility methods.
4074
//
4075

4076    public static void setDebug(PrintStream ps)
4077    {
4078        synchronized (m_outputLockObj)
4079        {
4080            m_debugOut = ps;
4081        }
4082    }
4083
4084    public static void debug(String JavaDoc s)
4085    {
4086        synchronized (m_outputLockObj)
4087        {
4088            if (m_debugOut != null)
4089            {
4090                m_debugOut.println(s);
4091            }
4092        }
4093    }
4094
4095    public static void error(String JavaDoc s)
4096    {
4097        synchronized (m_outputLockObj)
4098        {
4099            if (m_errorOut != null)
4100            {
4101                m_errorOut.println(s);
4102            }
4103        }
4104    }
4105
4106    public static void error(String JavaDoc s, Throwable JavaDoc th)
4107    {
4108        synchronized (m_outputLockObj)
4109        {
4110            if (m_errorOut != null)
4111            {
4112                m_errorOut.println(s);
4113                th.printStackTrace(m_errorOut);
4114            }
4115        }
4116    }
4117
4118    //
4119
// Private utility methods.
4120
//
4121

4122    /**
4123     * Generated the next valid bundle identifier.
4124    **/

4125    private synchronized long getNextId()
4126    {
4127        return m_nextId++;
4128    }
4129
4130    /**
4131     * Generated the next valid service identifier.
4132    **/

4133    private synchronized long getNextServiceId()
4134    {
4135        return m_nextServiceId++;
4136    }
4137
4138    //
4139
// Private utility classes.
4140
//
4141

4142    /**
4143     * Simple class that is used in <tt>refreshPackages()</tt> to embody
4144     * the refresh logic in order to keep the code clean. This class is
4145     * not static because it needs access to framework event firing methods.
4146    **/

4147    private class RefreshHelper
4148    {
4149        private BundleImpl m_bundle = null;
4150        private boolean m_active = false;
4151
4152        public RefreshHelper(Bundle bundle)
4153        {
4154            m_bundle = (BundleImpl) bundle;
4155        }
4156
4157        public void stop()
4158        {
4159            if (m_bundle.getInfo().getPersistentState() == Bundle.ACTIVE)
4160            {
4161                m_active = true;
4162                try
4163                {
4164                    stopBundle(m_bundle);
4165                }
4166                catch (BundleException ex)
4167                {
4168                    fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
4169                }
4170            }
4171        }
4172
4173        public void purgeOrRemove()
4174        {
4175            try
4176            {
4177                BundleInfo info = m_bundle.getInfo();
4178
4179                // Remove or purge the bundle depending on its
4180
// current state.
4181
if (info.getState() == Bundle.UNINSTALLED)
4182                {
4183                    // This physically removes the bundle from memory
4184
// as well as the bundle cache.
4185
removeBundle(m_bundle);
4186                    m_bundle = null;
4187                }
4188                else
4189                {
4190                    // This physically removes all old revisions of the
4191
// bundle from memory and only maintains the newest
4192
// version in the bundle cache.
4193
purgeBundle(m_bundle);
4194                }
4195            }
4196            catch (Exception JavaDoc ex)
4197            {
4198                fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
4199            }
4200        }
4201
4202        public void reinitialize()
4203        {
4204            if (m_bundle != null)
4205            {
4206                try
4207                {
4208                    synchronized (m_quickLock)
4209                    {
4210                        BundleInfo info = m_bundle.getInfo();
4211                        BundleInfo newInfo = createBundleInfo(info.getArchive());
4212                        m_bundle.setInfo(newInfo);
4213                    }
4214                }
4215                catch (Exception JavaDoc ex)
4216                {
4217                    fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
4218                }
4219            }
4220        }
4221
4222        public void restart()
4223        {
4224            if (m_bundle != null)
4225            {
4226                if (m_active)
4227                {
4228                    try
4229                    {
4230                        startBundle(m_bundle);
4231                    }
4232                    catch (BundleException ex)
4233                    {
4234                        fireFrameworkEvent(FrameworkEvent.ERROR, m_bundle, ex);
4235                    }
4236                }
4237            }
4238        }
4239
4240        public String JavaDoc toString()
4241        {
4242            return m_bundle.toString();
4243        }
4244    }
4245
4246    /**
4247     * This simple class is used to perform the privileged action of
4248     * checking if a bundle has permission to import its packages.
4249    **/

4250    private static class CheckImportsPrivileged implements PrivilegedExceptionAction
4251    {
4252        private URL m_url = null;
4253        private BundleImpl m_bundle = null;
4254
4255        public CheckImportsPrivileged(URL url, BundleImpl bundle)
4256        {
4257            m_url = url;
4258            m_bundle = bundle;
4259        }
4260
4261        public Object JavaDoc run() throws Exception JavaDoc
4262        {
4263            // Get permission collection for code source; we cannot
4264
// call AccessController.checkPermission() directly since
4265
// the bundle's code is not on the access context yet because
4266
// it has not started yet...we are simply resolving it to see
4267
// if we can start it. We must check for import permission
4268
// on the exports as well, since export implies import.
4269
CodeSource cs = new CodeSource(m_url, (Certificate JavaDoc[]) null);
4270            PermissionCollection pc = Policy.getPolicy().getPermissions(cs);
4271
4272            // Check import permission for all imports of the current module.
4273
Object JavaDoc[][] imports =
4274                ImportSearchPolicy.getImportsAttribute(m_bundle.getInfo().getCurrentModule());
4275            for (int i = 0; i < imports.length; i++)
4276            {
4277                PackagePermission perm = new PackagePermission(
4278                    (String JavaDoc) imports[i][ImportSearchPolicy.IDENTIFIER_IDX],
4279                    PackagePermission.IMPORT);
4280                if (!pc.implies(perm))
4281                {
4282                    throw new AccessControlException("access denied " + perm);
4283                }
4284            }
4285
4286            // Check export permission for all exports of the current module.
4287
imports =
4288                ImportSearchPolicy.getExportsAttribute(m_bundle.getInfo().getCurrentModule());
4289            for (int i = 0; i < imports.length; i++)
4290            {
4291                PackagePermission perm = new PackagePermission(
4292                    (String JavaDoc) imports[i][ImportSearchPolicy.IDENTIFIER_IDX],
4293                    PackagePermission.EXPORT);
4294                if (!pc.implies(perm))
4295                {
4296                    throw new AccessControlException("access denied " + perm);
4297                }
4298            }
4299
4300            return null;
4301        }
4302    }
4303
4304    private static class StartStopPrivileged implements PrivilegedExceptionAction
4305    {
4306        private Oscar m_oscar = null;
4307        private int m_action = 0;
4308        private BundleImpl m_bundle = null;
4309
4310        public static final int START_ACTION = 0;
4311        public static final int STOP_ACTION = 1;
4312
4313        public StartStopPrivileged(Oscar oscar)
4314        {
4315            m_oscar = oscar;
4316        }
4317
4318        public void setAction(int i)
4319        {
4320            m_action = i;
4321        }
4322
4323        public void setBundle(BundleImpl bundle)
4324        {
4325            m_bundle = bundle;
4326        }
4327
4328        public Object JavaDoc run() throws Exception JavaDoc
4329        {
4330            if (m_action == START_ACTION)
4331            {
4332                m_bundle.getInfo().getActivator().start(m_bundle.getInfo().getContext());
4333            }
4334            else
4335            {
4336                m_bundle.getInfo().getActivator().stop(m_bundle.getInfo().getContext());
4337            }
4338            return null;
4339        }
4340    }
4341}
4342
Popular Tags