Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bundles/org.eclipse.osgi.services/.classpath7
-rw-r--r--bundles/org.eclipse.osgi.services/.cvsignore1
-rw-r--r--bundles/org.eclipse.osgi.services/.project17
-rw-r--r--bundles/org.eclipse.osgi.services/META-INF/MANIFEST.MF20
-rw-r--r--bundles/org.eclipse.osgi.services/about.html30
-rw-r--r--bundles/org.eclipse.osgi.services/build.properties6
-rw-r--r--bundles/org.eclipse.osgi.services/plugin.xml13
-rw-r--r--bundles/org.eclipse.osgi.services/services.jarbin0 -> 32783 bytes
-rw-r--r--bundles/org.eclipse.osgi.services/servicessrc.zipbin0 -> 214094 bytes
-rw-r--r--bundles/org.eclipse.osgi.util/.classpath7
-rw-r--r--bundles/org.eclipse.osgi.util/.cvsignore1
-rw-r--r--bundles/org.eclipse.osgi.util/.project17
-rw-r--r--bundles/org.eclipse.osgi.util/META-INF/MANIFEST.MF17
-rw-r--r--bundles/org.eclipse.osgi.util/about.html30
-rw-r--r--bundles/org.eclipse.osgi.util/build.properties6
-rw-r--r--bundles/org.eclipse.osgi.util/plugin.xml13
-rw-r--r--bundles/org.eclipse.osgi.util/util.jarbin0 -> 15801 bytes
-rw-r--r--bundles/org.eclipse.osgi.util/utilsrc.zipbin0 -> 28293 bytes
-rw-r--r--bundles/org.eclipse.osgi/.classpath14
-rw-r--r--bundles/org.eclipse.osgi/.cvsignore9
-rw-r--r--bundles/org.eclipse.osgi/.project18
-rw-r--r--bundles/org.eclipse.osgi/build.properties13
-rw-r--r--bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/ConsoleMessages.properties148
-rw-r--r--bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/ConsoleMsg.java43
-rw-r--r--bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkCommandInterpreter.java529
-rw-r--r--bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkCommandProvider.java1636
-rw-r--r--bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkConsole.java497
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleClassLoader.java297
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java160
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleOperation.java75
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ClassLoaderDelegate.java159
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/EventPublisher.java33
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/FrameworkAdaptor.java268
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/IBundleStats.java21
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ImportClassNotFoundException.java41
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ImportResourceNotFoundException.java41
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/PermissionStorage.java81
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ServiceRegistry.java72
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/Version.java521
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/AbstractBundleData.java91
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/AbstractFrameworkAdaptor.java264
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/ServiceRegistry.java224
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/Headers.java312
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/ManifestElement.java592
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/Tokenizer.java172
-rw-r--r--bundles/org.eclipse.osgi/core/framework/META-INF/SYSTEMBUNDLE.MF30
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/console/CommandInterpreter.java89
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/console/CommandProvider.java46
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/debug/Debug.java166
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/debug/DebugOptions.java123
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventListeners.java72
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventManager.java128
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventQueue.java115
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventSource.java44
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventThread.java190
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/ListenerList.java164
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AliasMapper.java195
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Bundle.java1591
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleCombinedPermissions.java173
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleContext.java1629
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleFragment.java474
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java728
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleLoader.java1045
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleLoaderProxy.java155
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleNativeCode.java530
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundlePermissionCollection.java30
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundlePermissions.java344
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleRepository.java162
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleResourcePermission.java106
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleSource.java43
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Constants.java131
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ExportedPackageImpl.java83
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ExternalMessages.properties78
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Filter.java1837
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/FilteredServiceListener.java79
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java1859
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/KeyedElement.java17
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/KeyedHashSet.java375
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Msg.java41
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/MultiSourcePackage.java23
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/NothingToUpdateException.java22
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/NullPackageSource.java28
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/OSGi.java118
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdmin.java896
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageSource.java25
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PermissionAdmin.java723
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PermissionsHash.java112
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceReference.java232
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceRegistration.java779
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceUse.java333
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SingleSourcePackage.java26
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevel.java216
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelEvent.java100
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelFactory.java68
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelImpl.java487
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelListener.java384
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundle.java329
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleActivator.java124
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleData.java132
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleLoader.java121
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/UnresolvedPermission.java196
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/UnresolvedPermissionCollection.java100
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Util.java217
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/default.permissions25
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/implied.permissions48
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/osname.aliases35
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/processor.aliases23
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ContentHandlerFactory.java123
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ContentHandlerProxy.java186
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/NullURLStreamHandlerService.java135
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ProtocolActivator.java22
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/StreamHandlerFactory.java160
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/URLStreamHandlerProxy.java287
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/URLStreamHandlerSetter.java57
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/launcher/EclipseLauncher.java733
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/launcher/Launcher.java423
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/msg/MessageFormat.java280
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/CreateThread.java35
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/GetProperty.java44
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/PermissionStorage.java98
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/tracker/ServiceTracker.java884
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/tracker/ServiceTrackerCustomizer.java74
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/AdaptorElementFactory.java32
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/AdaptorMsg.java43
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/BundleEntry.java215
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/BundleFile.java438
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultAdaptor.java1138
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultBundleData.java713
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultClassLoader.java567
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/ExternalMessages.properties25
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/MetaData.java166
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/PermissionStorage.java380
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/ReferenceInputStream.java42
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/bundle/BundleURLConnection.java242
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/bundle/Handler.java103
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/reference/Handler.java63
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/reference/ReferenceURLConnection.java109
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFile.java536
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileInputStream.java101
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileOutputStream.java141
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/META-INF/ADAPTOR.MF3
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/CachedManifest.java64
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseAdaptor.java318
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseBundleData.java353
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseClassLoader.java76
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseElementFactory.java20
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseStarter.java535
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EnvironmentInfo.java152
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/Semaphore.java62
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/BundleStats.java145
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ClassStats.java126
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ClassloaderStats.java224
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ResourceBundleStats.java123
-rw-r--r--bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/StatsManager.java108
-rw-r--r--bundles/org.eclipse.osgi/launch/.cvsignore4
-rw-r--r--bundles/org.eclipse.osgi/launch/.options11
-rw-r--r--bundles/org.eclipse.osgi/launch/DevFramework.launch16
-rw-r--r--bundles/org.eclipse.osgi/launch/EclipseFramework.launch16
-rw-r--r--bundles/org.eclipse.osgi/launch/osgi.properties116
-rw-r--r--bundles/org.eclipse.osgi/osgi/ee.foundation.jarbin0 -> 521334 bytes
-rw-r--r--bundles/org.eclipse.osgi/osgi/ee.minimum.jarbin0 -> 298251 bytes
-rw-r--r--bundles/org.eclipse.osgi/osgi/osgi-r3-jdk131-notsigned.jarbin0 -> 323815 bytes
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/copyright.txt64
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/Constants.java122
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/DebugOptions.java8
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/EnvironmentInfo.java79
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleDelta.java72
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleDescription.java146
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleSpecification.java14
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/HostSpecification.java11
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/PackageSpecification.java11
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/PlatformAdmin.java46
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/Resolver.java70
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/State.java265
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateChangeEvent.java19
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateChangeListener.java17
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateDelta.java28
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateObjectFactory.java28
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/Version.java520
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/VersionConstraint.java31
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java213
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java693
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleActivator.java92
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleContext.java643
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleEvent.java159
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleException.java84
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleListener.java57
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundlePermission.java596
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Configurable.java61
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java564
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Filter.java109
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkEvent.java181
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkListener.java58
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/InvalidSyntaxException.java78
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java572
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceEvent.java139
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceFactory.java96
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceListener.java66
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java548
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceReference.java115
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceRegistration.java112
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/SynchronousBundleListener.java55
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/ExportedPackage.java101
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/PackageAdmin.java164
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionAdmin.java133
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionInfo.java453
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/startlevel/StartLevel.java237
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/AbstractURLStreamHandlerService.java163
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLConstants.java47
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerService.java96
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerSetter.java65
-rw-r--r--bundles/org.eclipse.osgi/plugin.xml24
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IDependency.java25
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IDependencySystem.java55
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElement.java25
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElementChange.java35
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElementSet.java60
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IMatchRule.java19
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IResolutionDelta.java23
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/ISelectionPolicy.java31
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Assert.java52
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/AssertionFailedException.java34
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ComputeNodeOrder.java521
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Dependency.java83
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/DependencySystem.java312
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Element.java65
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ElementChange.java64
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ElementSet.java251
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/IElementSetVisitor.java24
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ResolutionDelta.java77
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ResolutionVisitor.java66
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SatisfactionVisitor.java71
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SelectionVisitor.java56
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SimpleSelectionPolicy.java36
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDeltaImpl.java58
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java202
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleSpecificationImpl.java30
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/Eclipse21SelectionPolicy.java107
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/Eclipse30SelectionPolicy.java130
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/HostSpecificationImpl.java28
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/PackageSpecificationImpl.java26
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverHelper.java157
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverImpl.java190
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java127
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateDeltaImpl.java89
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java336
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateManager.java114
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java129
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java170
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java164
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java84
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/debug/Debug.java166
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventListeners.java72
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventManager.java128
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventQueue.java115
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventSource.java44
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventThread.java190
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/ListenerList.java164
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/ExternalMessages.properties78
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/Msg.java41
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFile.java536
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileInputStream.java101
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileOutputStream.java141
263 files changed, 50604 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi.services/.classpath b/bundles/org.eclipse.osgi.services/.classpath
new file mode 100644
index 000000000..6c5aa836e
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry exported="true" sourcepath="servicessrc.zip" kind="lib" path="services.jar"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.osgi.services/.cvsignore b/bundles/org.eclipse.osgi.services/.cvsignore
new file mode 100644
index 000000000..ba077a403
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/bundles/org.eclipse.osgi.services/.project b/bundles/org.eclipse.osgi.services/.project
new file mode 100644
index 000000000..3c3b0970f
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.osgi.services</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.osgi.services/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.services/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..36a3f4eeb
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-Name: OSGi-Service-Interfaces
+Bundle-GlobalName: org.eclipse.osgi.services
+Bundle-Version: 3.0
+Bundle-Description: OSGi Service Platform Release 3 Service Interfaces
+ and Classes
+Bundle-Vendor: Eclipse
+Bundle-DocUrl: http://www.eclipse.org
+Bundle-ContactAddress: www.eclipse.org
+Bundle-Copyright: Copyright (c) The Open Services Gateway Initiative (
+ 2001, 2003). All Rights Reserved.
+Bundle-ClassPath: services.jar
+Export-Package: org.osgi.service.cm; specification-version=1.1,
+ org.osgi.service.device; specification-version=1.1,
+ org.osgi.service.log; specification-version=1.2,
+ org.osgi.service.metatype; specification-version=1.0,
+ org.osgi.service.prefs; specification-version=1.0,
+ org.osgi.service.useradmin; specification-version=1.0,
+ org.osgi.service.wireadmin; specification-version=1.0
+Import-Package: org.osgi.framework; specification-version=1.2
diff --git a/bundles/org.eclipse.osgi.services/about.html b/bundles/org.eclipse.osgi.services/about.html
new file mode 100644
index 000000000..9db411aab
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/about.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<title>About</title>
+<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>20th June, 2002</p>
+<h3>License</h3>
+<p>Eclipse.org makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+Common Public License Version 1.0 (&quot;CPL&quot;). A copy of the CPL is available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>.
+For purposes of the CPL, &quot;Program&quot; will mean the Content.</p>
+
+<h3>Contributions</h3>
+
+<p>If this Content is licensed to you under the terms and conditions of the CPL, any Contributions, as defined in the CPL, uploaded, submitted, or otherwise
+made available to Eclipse.org, members of Eclipse.org and/or the host of Eclipse.org web site, by you that relate to such
+Content are provided under the terms and conditions of the CPL and can be made available to others under the terms of the CPL.</p>
+
+<p>If this Content is licensed to you under license terms and conditions other than the CPL (&quot;Other License&quot;), any modifications, enhancements and/or
+other code and/or documentation (&quot;Modifications&quot;) uploaded, submitted, or otherwise made available to Eclipse.org, members of Eclipse.org and/or the
+host of Eclipse.org, by you that relate to such Content are provided under terms and conditions of the Other License and can be made available
+to others under the terms of the Other License. In addition, with regard to Modifications for which you are the copyright holder, you are also
+providing the Modifications under the terms and conditions of the CPL and such Modifications can be made available to others under the terms of
+the CPL.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi.services/build.properties b/bundles/org.eclipse.osgi.services/build.properties
new file mode 100644
index 000000000..72666c7d0
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/build.properties
@@ -0,0 +1,6 @@
+bin.includes = plugin.xml,\
+ services.jar,\
+ about.html,\
+ META-INF/
+src.includes = *.zip,\
+ about.html
diff --git a/bundles/org.eclipse.osgi.services/plugin.xml b/bundles/org.eclipse.osgi.services/plugin.xml
new file mode 100644
index 000000000..b0acc2a61
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/plugin.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin
+ id="org.eclipse.osgi.services"
+ name="org.eclipse.osgi.services"
+ version="1.0.0">
+
+ <runtime>
+ <library name="services.jar">
+ <export name="*"/>
+ </library>
+ </runtime>
+
+</plugin>
diff --git a/bundles/org.eclipse.osgi.services/services.jar b/bundles/org.eclipse.osgi.services/services.jar
new file mode 100644
index 000000000..fe22c9acb
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/services.jar
Binary files differ
diff --git a/bundles/org.eclipse.osgi.services/servicessrc.zip b/bundles/org.eclipse.osgi.services/servicessrc.zip
new file mode 100644
index 000000000..3474afde4
--- /dev/null
+++ b/bundles/org.eclipse.osgi.services/servicessrc.zip
Binary files differ
diff --git a/bundles/org.eclipse.osgi.util/.classpath b/bundles/org.eclipse.osgi.util/.classpath
new file mode 100644
index 000000000..e9a51753e
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry exported="true" sourcepath="utilsrc.zip" kind="lib" path="util.jar"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.osgi.util/.cvsignore b/bundles/org.eclipse.osgi.util/.cvsignore
new file mode 100644
index 000000000..ba077a403
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/bundles/org.eclipse.osgi.util/.project b/bundles/org.eclipse.osgi.util/.project
new file mode 100644
index 000000000..2d0ce3af1
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.osgi.util</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.osgi.util/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.util/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..3788b03f5
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Bundle-Name: OSGi-Utility-Classes
+Bundle-GlobalName: org.eclipse.osgi.util
+Bundle-Version: 3.0
+Bundle-Description: OSGi Service Platform Release 3 Utility Classes
+Bundle-Vendor: Eclipse
+Bundle-DocUrl: http://www.eclipse.org
+Bundle-ContactAddress: www.eclipse.org
+Bundle-Copyright: Copyright (c) The Open Services Gateway Initiative (
+ 2001, 2003). All Rights Reserved.
+Bundle-ClassPath: util.jar
+Export-Package: org.osgi.util.tracker; specification-version=1.2,
+ org.osgi.util.measurement; specification-version=1.0,
+ org.osgi.util.position; specification-version=1.0,
+ org.osgi.util.xml; specification-version=1.0
+Import-Package: org.osgi.framework; specification-version=1.1
+Build-Information: ${build-information}
diff --git a/bundles/org.eclipse.osgi.util/about.html b/bundles/org.eclipse.osgi.util/about.html
new file mode 100644
index 000000000..9db411aab
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/about.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<title>About</title>
+<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>20th June, 2002</p>
+<h3>License</h3>
+<p>Eclipse.org makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+Common Public License Version 1.0 (&quot;CPL&quot;). A copy of the CPL is available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>.
+For purposes of the CPL, &quot;Program&quot; will mean the Content.</p>
+
+<h3>Contributions</h3>
+
+<p>If this Content is licensed to you under the terms and conditions of the CPL, any Contributions, as defined in the CPL, uploaded, submitted, or otherwise
+made available to Eclipse.org, members of Eclipse.org and/or the host of Eclipse.org web site, by you that relate to such
+Content are provided under the terms and conditions of the CPL and can be made available to others under the terms of the CPL.</p>
+
+<p>If this Content is licensed to you under license terms and conditions other than the CPL (&quot;Other License&quot;), any modifications, enhancements and/or
+other code and/or documentation (&quot;Modifications&quot;) uploaded, submitted, or otherwise made available to Eclipse.org, members of Eclipse.org and/or the
+host of Eclipse.org, by you that relate to such Content are provided under terms and conditions of the Other License and can be made available
+to others under the terms of the Other License. In addition, with regard to Modifications for which you are the copyright holder, you are also
+providing the Modifications under the terms and conditions of the CPL and such Modifications can be made available to others under the terms of
+the CPL.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi.util/build.properties b/bundles/org.eclipse.osgi.util/build.properties
new file mode 100644
index 000000000..f110ed9b2
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/build.properties
@@ -0,0 +1,6 @@
+bin.includes = plugin.xml,\
+ util.jar,\
+ about.html,\
+ META-INF/
+src.includes = *.zip,\
+ about.html
diff --git a/bundles/org.eclipse.osgi.util/plugin.xml b/bundles/org.eclipse.osgi.util/plugin.xml
new file mode 100644
index 000000000..1b9d43dfe
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/plugin.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin
+ id="org.eclipse.osgi.util"
+ name="org.eclipse.osgi.util"
+ version="1.0.0">
+
+ <runtime>
+ <library name="util.jar">
+ <export name="*"/>
+ </library>
+ </runtime>
+
+</plugin>
diff --git a/bundles/org.eclipse.osgi.util/util.jar b/bundles/org.eclipse.osgi.util/util.jar
new file mode 100644
index 000000000..3a80ae0aa
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/util.jar
Binary files differ
diff --git a/bundles/org.eclipse.osgi.util/utilsrc.zip b/bundles/org.eclipse.osgi.util/utilsrc.zip
new file mode 100644
index 000000000..afa095392
--- /dev/null
+++ b/bundles/org.eclipse.osgi.util/utilsrc.zip
Binary files differ
diff --git a/bundles/org.eclipse.osgi/.classpath b/bundles/org.eclipse.osgi/.classpath
new file mode 100644
index 000000000..d85b7fc4f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/.classpath
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="console/src"/>
+ <classpathentry kind="src" path="core/adaptor"/>
+ <classpathentry kind="src" path="core/framework"/>
+ <classpathentry kind="src" path="defaultAdaptor/src"/>
+ <classpathentry kind="src" path="eclipseAdaptor/src"/>
+ <classpathentry kind="src" path="osgi/src"/>
+ <classpathentry kind="src" path="resolver/src"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="src" path="/org.eclipse.osgi.services"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.osgi/.cvsignore b/bundles/org.eclipse.osgi/.cvsignore
new file mode 100644
index 000000000..533dc387a
--- /dev/null
+++ b/bundles/org.eclipse.osgi/.cvsignore
@@ -0,0 +1,9 @@
+bin
+temp.folder
+build.xml
+console.jar
+core.jar
+defaultAdaptor.jar
+eclipseAdaptor.jar
+osgi.jar
+resolver.jar
diff --git a/bundles/org.eclipse.osgi/.project b/bundles/org.eclipse.osgi/.project
new file mode 100644
index 000000000..ffcb87342
--- /dev/null
+++ b/bundles/org.eclipse.osgi/.project
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.osgi</name>
+ <comment></comment>
+ <projects>
+ <project>org.eclipse.osgi.services</project>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.osgi/build.properties b/bundles/org.eclipse.osgi/build.properties
new file mode 100644
index 000000000..f10d04561
--- /dev/null
+++ b/bundles/org.eclipse.osgi/build.properties
@@ -0,0 +1,13 @@
+bin.includes = plugin.properties, plugin.xml, *.jar, about.html, META-INF/MANIFEST.MF
+
+jars.compile.order = osgi.jar, core.jar, resolver.jar, defaultAdaptor.jar, eclipseAdaptor.jar, console.jar
+
+source.osgi.jar = osgi/src
+source.core.jar = core/adaptor/, core/framework/
+source.defaultAdaptor.jar = defaultAdaptor/src/
+source.eclipseAdaptor.jar = eclipseAdaptor/src/
+source.console.jar = console/src/
+source.resolver.jar = resolver/src/
+
+extra.core.jar = ../org.eclipse.osgi.services/services.jar
+extra.console.jar = ../org.eclipse.osgi.services/services.jar
diff --git a/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/ConsoleMessages.properties b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/ConsoleMessages.properties
new file mode 100644
index 000000000..0a554d848
--- /dev/null
+++ b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/ConsoleMessages.properties
@@ -0,0 +1,148 @@
+###############################################################################
+# Copyright (c) 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+
+#External Messages for EN locale
+CONSOLE_PROMPT=osgi>
+CONSOLE_ID=id
+CONSOLE_TYPE=Type
+CONSOLE_LOCATION=location
+CONSOLE_STARTLEVEL=start level
+CONSOLE_MORE=-- More...Press Enter to Continue...
+CONSOLE_HELP_CONTROLLING_CONSOLE_HEADING=---Controlling the Console---
+CONSOLE_HELP_MORE=More prompt for console output
+CONSOLE_HELP_DISCONNECT=Disconnects from telnet session
+CONSOLE_CONFIRM_MORE=Use 'more' prompt?
+CONSOLE_MORE_ENTER_LINES=Enter maximum number of lines to scroll:
+CONSOLE_CONFIRM_DISCONNECT=Disconnect from console?
+CONSOLE_CONFIRM=Confirm
+CONSOLE_CONFIRM_VALUES=y/n; default=
+CONSOLE_Y=y
+CONSOLE_N=n
+CONSOLE_ERROR_READING_RESOURCE=Error reading {0}
+CONSOLE_RESOURCE_NOT_IN_BUNDLE={0} not found in {1}
+CONSOLE_NESTED_EXCEPTION=Nested Exception:
+CONSOLE_PROMPT_DEFAULT=default
+CONSOLE_INVALID_INPUT=Invalid input.
+CONSOLE_TOO_MUCH_INVALID_INPUT=Too much invalid input.
+CONSOLE_LINES_TO_SCROLL_NEGATIVE_ERROR=Lines to scroll is set to a negative number
+CONSOLE_HELP_VALID_COMMANDS_HEADER=Valid commands:
+CONSOLE_HELP_CONTROLLING_FRAMEWORK_HEADER=Controlling the OSGi framework
+CONSOLE_HELP_LAUNCH_COMMAND_DESCRIPTION=start the OSGi Framework
+CONSOLE_HELP_SHUTDOWN_COMMAND_DESCRIPTION=shutdown the OSGi Framework
+CONSOLE_HELP_CLOSE_COMMAND_DESCRIPTION=shutdown and exit
+CONSOLE_HELP_EXIT_COMMAND_DESCRIPTION=exit immediately (System.exit)
+CONSOLE_HELP_GC_COMMAND_DESCRIPTION=perform a garbage collection
+CONSOLE_HELP_INIT_COMMAND_DESCRIPTION=uninstall all bundles
+CONSOLE_HELP_SETPROP_COMMAND_DESCRIPTION=set the OSGi property
+CONSOLE_HELP_CONTROLLING_BUNDLES_HEADER=Controlling Bundles
+CONSOLE_HELP_INSTALL_COMMAND_DESCRIPTION=install and optionally start bundle from the given URL
+CONSOLE_HELP_UNINSTALL_COMMAND_DESCRIPTION=uninstall the specified bundle(s)
+CONSOLE_HELP_START_COMMAND_DESCRIPTION=start the specified bundle(s)
+CONSOLE_HELP_STOP_COMMAND_DESCRIPTION=stop the specified bundle(s)
+CONSOLE_HELP_REFRESH_COMMAND_DESCRIPTION=refresh the packages of the specified bundles
+CONSOLE_HELP_UPDATE_COMMAND_DESCRIPTION=update the specified bundle(s)
+CONSOLE_HELP_DISPLAYING_STATUS_HEADER=Displaying Status
+CONSOLE_HELP_STATUS_COMMAND_DESCRIPTION=display installed bundles and registered services
+CONSOLE_HELP_SS_COMMAND_DESCRIPTION=display installed bundles (short status)
+CONSOLE_HELP_SERVICES_COMMAND_DESCRIPTION=display registered service details
+CONSOLE_HELP_PACKAGES_COMMAND_DESCRIPTION=display imported/exported package details
+CONSOLE_HELP_BUNDLES_COMMAND_DESCRIPTION=display details for all installed bundles
+CONSOLE_HELP_BUNDLE_COMMAND_DESCRIPTION=display details for the specified bundle(s)
+CONSOLE_HELP_HEADERS_COMMAND_DESCRIPTION=print bundle headers
+CONSOLE_HELP_PROPS_COMMAND_DESCRIPTION=display System properties
+CONSOLE_HELP_THREADS_COMMAND_DESCRIPTION=display threads and thread groups
+CONSOLE_HELP_LOG_COMMAND_DESCRIPTION=display log entries
+CONSOLE_HELP_EXTRAS_HEADER=Extras
+CONSOLE_HELP_EXEC_COMMAND_DESCRIPTION=execute a command in a separate process and wait
+CONSOLE_HELP_FORK_COMMAND_DESCRIPTION=execute a command in a separate process
+CONSOLE_HELP_KEYVALUE_ARGUMENT_DESCRIPTION=<key>=<value>
+CONSOLE_HELP_FILTER_ARGUMENT_DESCRIPTION={filter}
+CONSOLE_HELP_PACKAGES_ARGUMENT_DESCRIPTION={<pkgname>|<id>|<location>}
+CONSOLE_HELP_IDLOCATION_ARGUMENT_DESCRIPTION=(<id>|<location>)
+CONSOLE_HELP_OPTIONAL_IDLOCATION_ARGUMENT_DESCRIPTION={(<id>|<location>)}
+CONSOLE_HELP_COMMAND_ARGUMENT_DESCRIPTION=<command>
+CONSOLE_NO_BUNDLE_SPECIFIED_ERROR=No bundle(s) specified!
+CONSOLE_NOTHING_TO_INSTALL_ERROR=Nothing to install!
+CONSOLE_NO_INSTALLED_BUNDLES_ERROR=No installed bundles.
+CONSOLE_BUNDLE_ID_MESSAGE=Bundle id is
+CONSOLE_FRAMEWORK_IS_LAUNCHED_MESSAGE=Framework is launched.
+CONSOLE_FRAMEWORK_IS_SHUTDOWN_MESSAGE=Framework is shutdown.
+CONSOLE_ID_MESSAGE=Id={0}
+CONSOLE_BUNDLE_LOCATION_MESSAGE=Bundle Location
+CONSOLE_STATE_BUNDLE_FILE_NAME_HEADER=State Bundle File Name
+CONSOLE_REGISTERED_SERVICES_MESSAGE=Registered Services
+CONSOLE_REGISTERED_BY_BUNDLE_MESSAGE=Registered by bundle:
+CONSOLE_BUNDLES_USING_SERVICE_MESSAGE=Bundles using service:
+CONSOLE_NO_BUNDLES_USING_SERVICE_MESSAGE=No bundles using service.
+CONSOLE_NO_REGISTERED_SERVICES_MESSAGE=No registered services.
+CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE=No exported packages
+CONSOLE_NO_IMPORTED_PACKAGES_MESSAGE=No imported packages
+CONSOLE_REMOVAL_PENDING_MESSAGE=removal pending
+CONSOLE_IMPORTS_MESSAGE=imports
+CONSOLE_STALE_MESSAGE=stale
+CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE=No exported packages [PackageAdmin service is not registered]
+CONSOLE_SERVICES_IN_USE_MESSAGE=Services in use:
+CONSOLE_NO_SERVICES_IN_USE_MESSAGE=No services in use.
+CONSOLE_STATUS_MESSAGE=Status={0}
+CONSOLE_DATA_ROOT_MESSAGE=Data Root={0}
+CONSOLE_EXPORTED_PACKAGES_MESSAGE=Exported packages
+CONSOLE_IMPORTED_PACKAGES_MESSAGE=Imported packages
+CONSOLE_EXPORTED_REMOVAL_PENDING_MESSAGE=[exported(removal pending)]
+CONSOLE_EXPORTED_MESSAGE=[exported]
+CONSOLE_STALE_MESSAGE=stale
+CONSOLE_DEBUG_MESSAGE=Debug
+CONSOLE_INFO_MESSAGE=Info
+CONSOLE_WARNING_MESSAGE=Warn
+CONSOLE_ERROR_MESSAGE=Error
+CONSOLE_LOGSERVICE_NOT_REGISTERED_MESSAGE=The LogReaderService is not registered.
+CONSOLE_FREE_MEMORY_BEFORE_GARBAGE_COLLECTION_MESSAGE=Free memory before GC:
+CONSOLE_FREE_MEMORY_AFTER_GARBAGE_COLLECTION_MESSAGE=Free memory after GC:
+CONSOLE_MEMORY_GAINED_WITH_GARBAGE_COLLECTION_MESSAGE=Memory gained with GC:
+CONSOLE_FRAMEWORK_LAUNCHED_PLEASE_SHUTDOWN_MESSAGE=Framework is launched. Please shutdown framework first.
+CONSOLE_INVALID_BUNDLE_SPECIFICATION_ERROR=Invalid bundle specification.
+CONSOLE_CAN_NOT_REFRESH_NO_PACKAGE_ADMIN_ERROR=Cannot refresh [PackageAdmin service is not registered]
+CONSOLE_CAN_NOT_USE_STARTLEVEL_NO_STARTLEVEL_SVC_ERROR=Cannot use Startlevel commands [Startlevel service is not registered]
+CONSOLE_NO_COMMAND_SPECIFIED_ERROR=No command specified
+CONSOLE_EXECUTED_RESULT_CODE_MESSAGE=Executed ({0}); result code = {1}
+CONSOLE_STARTED_IN_MESSAGE=Started({0}) in {1}
+CONSOLE_BUNDLE_HEADERS_TITLE=Bundle headers:
+CONSOLE_SYSTEM_PROPERTIES_TITLE=System properties:
+CONSOLE_NO_PARAMETERS_SPECIFIED_TITLE=No parameters specified:
+CONSOLE_SETTING_PROPERTIES_TITLE=Setting Properties:
+CONSOLE_STATE_BUNDLE_TITLE=State Bundle
+CONSOLE_THREADGROUP_TITLE=ThreadGroupType: Name: ParentGroup: MaxP: Threads:
+CONSOLE_THREADTYPE_TITLE=ThreadType: Name: ThreadGroup: Prio:
+CONSOLE_CANNOT_FIND_BUNDLE_ERROR=Cannot find bundle {0}
+CONSOLE_INSTALLED_MESSAGE=INSTALLED
+CONSOLE_UNINSTALLED_MESSAGE=UNINSTALLED
+CONSOLE_RESOLVED_MESSAGE=RESOLVED
+CONSOLE_STARTING_MESSAGE=STARTING
+CONSOLE_STOPPING_MESSAGE=STOPPING
+CONSOLE_ACTIVE_MESSAGE=ACTIVE
+CONSOLE_LISTENING_ON_PORT=Listening on port {0} ...
+CONSOLE_TELNET_CONNECTION_REFUSED=Connection refused.
+CONSOLE_TELNET_CURRENTLY_USED=Telnet is currently being used.
+CONSOLE_TELNET_ONE_CLIENT_ONLY=Only one client can access at a time.
+STARTLEVEL_HELP_HEADING=Controlling Start Level
+STARTLEVEL_HELP_SL=display the start level for the specified bundle, or for the framework if no bundle specified
+STARTLEVEL_HELP_SETFWSL=set the framework start level
+STARTLEVEL_HELP_SETBSL=set the start level for the bundle(s)
+STARTLEVEL_HELP_SETIBSL=set the initial bundle start level
+STARTLEVEL_CANT_CHANGE_SYSTEMBUNDLE_STARTLEVEL=The System Bundle's start level can not be modified.
+STARTLEVEL_FRAMEWORK_ACTIVE_STARTLEVEL=Framework Active Start Level = {0}
+STARTLEVEL_NO_STARTLEVEL_GIVEN=No Start Level given.
+STARTLEVEL_NO_STARTLEVEL_OR_BUNDLE_GIVEN=No Bundle or Start Level given.
+STARTLEVEL_INITIAL_BUNDLE_STARTLEVEL=Initial Bundle Start Level = {0}
+STARTLEVEL_BUNDLE_STARTLEVEL=Bundle {0} Start Level = {1}
+STARTLEVEL_ARGUMENT_DESCRIPTION = <start level>
+STARTLEVEL_IDLOCATION_ARGUMENT_DESCRIPTION = <start level> (<id>|<location>)
+BUNDLE_CANT_FIND_BUNDLE=Can not find bundle.
+STARTLEVEL_POSITIVE_INTEGER=Startlevel must be a positive integer. \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/ConsoleMsg.java b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/ConsoleMsg.java
new file mode 100644
index 000000000..f7d4f8a0e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/ConsoleMsg.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import org.eclipse.osgi.framework.msg.MessageFormat;
+
+/**
+ * This class retrieves strings from a resource bundle
+ * and returns them, formatting them with MessageFormat
+ * when required.
+ * <p>
+ * It is used by the system classes to provide national
+ * language support, by looking up messages in the
+ * <code>
+ * org.eclipse.osgi.framework.internal.core.ExternalMessages
+ * </code>
+ * resource bundle. Note that if this file is not available,
+ * or an invalid key is looked up, or resource bundle support
+ * is not available, the key itself will be returned as the
+ * associated message. This means that the <em>KEY</em> should
+ * a reasonable human-readable (english) string.
+ */
+
+public class ConsoleMsg
+{
+
+ static public MessageFormat formatter;
+
+ // Attempt to load the message bundle.
+ static
+ {
+ formatter = new MessageFormat("org.eclipse.osgi.framework.internal.core.ConsoleMessages");
+ }
+}
diff --git a/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkCommandInterpreter.java b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkCommandInterpreter.java
new file mode 100644
index 000000000..16318fc54
--- /dev/null
+++ b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkCommandInterpreter.java
@@ -0,0 +1,529 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+;
+
+/**
+ * This class knows how to parse and execute the command line arguments to the FrameworkConsole.
+ * It attempts to pass the command to each registered CommandProvider until it finds one
+ * that knows what to do with it.
+ *
+ * FrameworkCommandInterpreter provides support for the "More" command which allows the operator to configure
+ * the number of lines to display before being prompted to continue.
+ *
+ * FrameworkCommandInterpreter provides several print methods which handle the "More" command.
+ */
+public class FrameworkCommandInterpreter implements CommandInterpreter {
+
+ /** The command line in StringTokenizer form */
+ private StringTokenizer tok;
+ /** The active CommandProviders */
+ private Object[] commandProviders;
+ /** The FrameworkConsole */
+ private FrameworkConsole con;
+ /** The stream to send output to */
+ private PrintWriter out;
+
+ /** Strings used to format other strings */
+ private String tab = "\t";
+ private String newline = "\r\n";
+
+
+ /**
+ * The maximum number of lines to print without user prompt.
+ * 0 means no user prompt is required, the window is scrollable.
+ */
+ protected static int maxLineCount;
+
+ /** The number of lines printed without user prompt.*/
+ protected int currentLineCount;
+
+ /**
+ * The constructor. It turns the cmdline string into a StringTokenizer and remembers
+ * the input parms.
+ */
+ public FrameworkCommandInterpreter(String cmdline, Object[] commandProviders, FrameworkConsole con) {
+ tok = new StringTokenizer(cmdline);
+ this.commandProviders = commandProviders;
+ this.con = con;
+ this.out = con.getWriter();
+ }
+
+ /**
+ Get the next argument in the input.
+
+ E.g. if the commandline is hello world, the _hello method
+ will get "world" as the first argument.
+
+ @return A string containing the next argument on the command line
+ */
+ public String nextArgument() {
+ if (tok==null || !tok.hasMoreElements()) {
+ return null;
+ }
+ String token = tok.nextToken();
+ //check for quotes
+ int index = token.indexOf('"');
+ if (index != -1)
+ {
+ //if we only have one quote, find the second quote
+ if(index == token.lastIndexOf('"'))
+ {
+ token += tok.nextToken("\"");
+ }
+ StringBuffer buf = new StringBuffer(token);
+
+ //strip quotes
+ while(index != -1)
+ {
+ buf.deleteCharAt(index);
+ token = buf.toString();
+ index = token.indexOf('"');
+ }
+ return buf.toString();
+ }
+ return (token);
+ }
+
+ /**
+ Execute a command line as if it came from the end user.
+
+ Searches the list of command providers using introspection until
+ it finds one that contains a matching method. It searches for a method
+ with the name "_cmd" where cmd is the command to execute. For example,
+ for a command of "launch" execute searches for a method called "_launch".
+
+ @param The name of the command to execute.
+ @return The object returned by the method executed.
+ */
+ public Object execute(String cmd) {
+ resetLineCount();
+ Object retval=null;
+ // handle "more" command here
+ if (cmd.equalsIgnoreCase("more")) {
+ try {
+ _more();
+ } catch (Exception e) {
+ printStackTrace(e);
+ }
+ return retval;
+ }
+ // handle "disconnect" command here
+ if (cmd.equalsIgnoreCase("disconnect") && con.getUseSocketStream()){
+ try{
+ _disconnect();
+ } catch (Exception e){
+ printStackTrace(e);
+ }
+ return retval;
+ }
+ Class[] parameterTypes = new Class[] { CommandInterpreter.class };
+ Object[] parameters = new Object[] { this };
+ boolean executed = false;
+ int size = commandProviders.length;
+ for (int i = 0; !executed && (i < size); i++)
+ {
+ try {
+ Object target = commandProviders[i];
+ Method method = target.getClass().getMethod( "_" + cmd, parameterTypes );
+ retval = method.invoke( target, parameters );
+ executed = true; // stop after the command has been found
+ }
+ catch( NoSuchMethodException ite ) {
+ // keep going - maybe another command provider will be able to execute this command
+ }
+ catch (InvocationTargetException ite) {
+ executed = true; // don't want to keep trying - we found the method but got an error
+ printStackTrace(ite.getTargetException());
+ }
+ catch( Exception ee ) {
+ executed = true; // don't want to keep trying - we got an error we don't understand
+ printStackTrace(ee);
+ }
+ }
+ // if no command was found to execute, display help for all registered command providers
+ if (!executed) {
+ for (int i = 0; i < size; i++)
+ {
+ try {
+ CommandProvider commandProvider = (CommandProvider) commandProviders[i];
+ out.print(commandProvider.getHelp());
+ out.flush();
+ } catch (Exception ee) {
+ printStackTrace(ee);
+ }
+ }
+ // call help for the more command provided by this class
+ out.print(getHelp());
+ out.flush();
+ }
+ return retval;
+ }
+
+ /**
+ * Answers the number of lines output to the console
+ * window should scroll without user interaction.
+ *
+ * @return The number of lines to scroll.
+ */
+ private int getMaximumLinesToScroll() {
+ return maxLineCount;
+ }
+
+ /**
+ * Sets the number of lines output to the console
+ * window will scroll without user interaction.
+ * <p>
+ * Note that this number does not include the line
+ * for the 'more' prompt itself.
+ * <p>
+ * If the number of lines is 0 then no 'more' prompt
+ * is disabled.
+ *
+ * @param lines the number of lines to scroll
+ */
+ private void setMaximumLinesToScroll(int lines) {
+ if (lines < 0) {
+ throw new IllegalArgumentException(ConsoleMsg.formatter.getString("CONSOLE_LINES_TO_SCROLL_NEGATIVE_ERROR"));
+ }
+
+ maxLineCount = lines;
+ }
+
+ /**
+ * Resets the line counter for the 'more' prompt.
+ */
+ private void resetLineCount() {
+ currentLineCount = 0;
+ }
+
+ /**
+ * Prints a string to the output medium (appended with newline character).
+ * <p>
+ * This method does not increment the line counter for the 'more' prompt.
+ *
+ * @param string the string to be printed
+ */
+ private void printline(Object o) {
+ print(o + newline);
+ }
+
+ /**
+ * Prints an object to the outputstream
+ *
+ * @param o the object to be printed
+ */
+ public void print(Object o) {
+ synchronized(out) {
+ check4More();
+ out.print(o);
+ out.flush();
+ }
+ }
+
+ /**
+ * Prints a empty line to the outputstream
+ */
+ public void println() {
+ println("");
+ }
+
+ /**
+ * Print a stack trace including nested exceptions.
+ * @param The offending exception
+ */
+ public void printStackTrace(Throwable t)
+ {
+ t.printStackTrace(out);
+
+ Method[] methods = t.getClass().getMethods();
+
+ int size = methods.length;
+ Class throwable = Throwable.class;
+
+ for (int i = 0; i < size; i++)
+ {
+ Method method = methods[i];
+
+ if (Modifier.isPublic(method.getModifiers()) &&
+ method.getName().startsWith("get") &&
+ throwable.isAssignableFrom(method.getReturnType()) &&
+ (method.getParameterTypes().length == 0))
+ {
+ try
+ {
+ Throwable nested = (Throwable) method.invoke(t, null);
+
+ if ((nested != null) && (nested != t))
+ {
+ out.println(ConsoleMsg.formatter.getString("CONSOLE_NESTED_EXCEPTION"));
+ printStackTrace(nested);
+ }
+ }
+ catch (IllegalAccessException e)
+ {
+ }
+ catch (InvocationTargetException e)
+ {
+ }
+ }
+ }
+ }
+
+ /**
+ * Prints an object to the output medium (appended with newline character).
+ * <p>
+ * If running on the target environment, the user is prompted with '--more'
+ * if more than the configured number of lines have been printed without user prompt.
+ * This enables the user of the program to have control over scrolling.
+ * <p>
+ * For this to work properly you should not embed "\n" etc. into the string.
+ *
+ * @param o the object to be printed
+ */
+ public void println(Object o) {
+ if(o == null)
+ {
+ return;
+ }
+ synchronized (out) {
+ check4More();
+ printline(o);
+ currentLineCount++;
+ currentLineCount += o.toString().length() / 80;
+ }
+ }
+
+ /**
+ * Prints the given dictionary sorted by keys.
+ *
+ * @param dic the dictionary to print
+ * @param title the header to print above the key/value pairs
+ */
+ public void printDictionary(Dictionary dic, String title) {
+ if (dic == null)
+ return;
+
+ int count = dic.size();
+ String[] keys = new String[count];
+ Enumeration enum = dic.keys();
+ int i = 0;
+ while (enum.hasMoreElements()) {
+ keys[i++] = (String) enum.nextElement();
+ }
+ Util.sort(keys);
+
+ if (title != null) {
+ println(title);
+ }
+ for (i = 0; i < count; i++) {
+ println(" " + keys[i] + " = " + dic.get(keys[i]));
+ }
+ println();
+ }
+
+ /**
+ * Prints the given bundle resource if it exists
+ *
+ * @param bundle the bundle containing the resource
+ * @param resource the resource to print
+ */
+ public void printBundleResource(Bundle bundle, String resource) {
+ URL entry=null;
+ try {
+ entry = bundle.getEntry(resource);
+ } catch (IOException e1) {
+ }
+ if (entry != null) {
+ try {
+ println(resource);
+ InputStream in = entry.openStream();
+ byte[] buffer = new byte[1024];
+ int read = 0;
+ try {
+ while ((read = in.read(buffer)) != -1)
+ print(new String(buffer, 0, read));
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {}
+ }
+ }
+ } catch (Exception e) {
+ System.err.println(ConsoleMsg.formatter.getString("CONSOLE_ERROR_READING_RESOURCE", resource));
+ }
+ } else {
+ println(ConsoleMsg.formatter.getString("CONSOLE_RESOURCE_NOT_IN_BUNDLE", resource, bundle.toString()));
+ }
+ }
+
+ /**
+ * Displays the more... prompt if the max line count has been reached
+ * and waits for the operator to hit enter.
+ *
+ * @param The Object to be printed - used to determine how many lines the object takes
+ */
+ private void check4More() {
+ int max = getMaximumLinesToScroll();
+ if (max > 0) {
+ if (currentLineCount >= max) {
+ out.print(ConsoleMsg.formatter.getString("CONSOLE_MORE"));
+ out.flush();
+ con.getInput(); // wait for user entry
+ resetLineCount(); //Reset the line counter for the 'more' prompt
+ }
+ }
+ }
+
+ /**
+ Answer a string (may be as many lines as you like) with help
+ texts that explain the command.
+ */
+ public String getHelp() {
+ StringBuffer help = new StringBuffer(256);
+ help.append(newline);
+ help.append(ConsoleMsg.formatter.getString("CONSOLE_HELP_CONTROLLING_CONSOLE_HEADING"));
+ help.append(newline);
+ help.append(tab);
+ help.append("more - ");
+ help.append(ConsoleMsg.formatter.getString("CONSOLE_HELP_MORE"));
+ if( con.getUseSocketStream() ){
+ help.append(newline);
+ help.append(tab);
+ help.append("disconnect - ");
+ help.append(ConsoleMsg.formatter.getString("CONSOLE_HELP_DISCONNECT"));
+ }
+ return help.toString();
+ }
+
+ /**
+ * Toggles the use of the more prompt for displayed output.
+ *
+ */
+ public void _more() throws Exception {
+ if (confirm(ConsoleMsg.formatter.getString("CONSOLE_CONFIRM_MORE"), true)) {
+ int lines = prompt(newline + ConsoleMsg.formatter.getString("CONSOLE_MORE_ENTER_LINES"), 24);
+ setMaximumLinesToScroll(lines);
+ } else {
+ setMaximumLinesToScroll(0);
+ }
+ }
+
+ private void _disconnect() throws Exception {
+ if( confirm(ConsoleMsg.formatter.getString("CONSOLE_CONFIRM_DISCONNECT"), true)){
+ con.disconnect();
+ }
+ }
+
+ /**
+ * Prompts the user for confirmation.
+ *
+ * @param string the message to present to the user to confirm
+ * @param defaultAnswer the default result
+ *
+ * @return <code>true</code> if the user confirms; <code>false</code> otherwise.
+ */
+ protected boolean confirm(String string, boolean defaultAnswer) {
+ synchronized (out) {
+ if (string.length() > 0) {
+ print(string);
+ } else {
+ print(ConsoleMsg.formatter.getString("CONSOLE_CONFIRM"));
+ }
+ print(" ("+ConsoleMsg.formatter.getString("CONSOLE_CONFIRM_VALUES"));
+ if (defaultAnswer) {
+ print(ConsoleMsg.formatter.getString("CONSOLE_Y") + ") ");
+ } else {
+ print(ConsoleMsg.formatter.getString("CONSOLE_N") + ") ");
+ }
+ }
+ String input = con.getInput();
+ resetLineCount();
+ if (input.length() == 0) {
+ return defaultAnswer;
+ }
+ return input.toLowerCase().charAt(0) == ConsoleMsg.formatter.getString("CONSOLE_Y").charAt(0);
+ }
+
+ /**
+ * Prompts the user for input from the input medium providing a default value.
+ *
+ * @param string the message to present to the user
+ * @param defaultAnswer the string to use as a default return value
+ *
+ * @return The user provided string or the defaultAnswer,
+ * if user provided string was empty.
+ */
+ protected String prompt(String string, String defaultAnswer) {
+ if (string.length() > 0) {
+ if (defaultAnswer.length() > 0) {
+ StringBuffer buf = new StringBuffer(256);
+ buf.append(string);
+ buf.append(" ");
+ buf.append(ConsoleMsg.formatter.getString("CONSOLE_PROMPT_DEFAULT"));
+ buf.append("=");
+ buf.append(defaultAnswer);
+ buf.append(") ");
+ print(buf.toString());
+ } else {
+ print(string);
+ }
+ }
+ String input = con.getInput();
+ resetLineCount();
+ if (input.length() > 0) {
+ return input;
+ }
+ return defaultAnswer;
+ }
+
+ /**
+ * Prompts the user for input of a positive integer.
+ *
+ * @param string the message to present to the user
+ * @param defaultAnswer the integer to use as a default return value
+ *
+ * @return The user provided integer or the defaultAnswer,
+ * if user provided an empty input.
+ */
+ protected int prompt(String string, int defaultAnswer) {
+ Integer i = new Integer(defaultAnswer);
+ int answer;
+ for (int j = 0; j < 3; j++) {
+ String s = prompt(string, i.toString());
+ try {
+ answer = Integer.parseInt(s);
+ if (answer >= 0) {
+ return answer;
+ }
+ } catch (NumberFormatException e) {}
+ println(ConsoleMsg.formatter.getString("CONSOLE_INVALID_INPUT"));
+ }
+ println(ConsoleMsg.formatter.getString("CONSOLE_TOO_MUCH_INVALID_INPUT"));
+ return defaultAnswer;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkCommandProvider.java b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkCommandProvider.java
new file mode 100644
index 000000000..3f4471aa1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkCommandProvider.java
@@ -0,0 +1,1636 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.eclipse.osgi.framework.launcher.Launcher;
+import org.osgi.framework.BundleException;
+
+/**
+ * This class provides methods to execute commands from the command line. It registers
+ * itself as a CommandProvider so it can be invoked by a CommandInterpreter. The
+ * FrameworkCommandProvider registers itself with the highest ranking (Integer.MAXVALUE) so it will always be
+ * called first. Other CommandProviders should register with lower rankings.
+ *
+ * The commands provided by this class are:
+ ---Controlling the OSGi framework---
+ close - shutdown and exit
+ exit - exit immediately (System.exit)
+ gc - perform a garbage collection
+ init - uninstall all bundles
+ launch - start the Service Management Framework
+ setprop <key>=<value> - set the OSGI property
+ shutdown - shutdown the Service Management Framework
+ ---Controlliing Bundles---
+ install <url> {s[tart]} - install and optionally start bundle from the given URL
+ refresh (<id>|<location>) - refresh the packages of the specified bundles
+ start (<id>|<location>) - start the specified bundle(s)
+ stop (<id>|<location>) - stop the specified bundle(s)
+ uninstall (<id>|<location>) - uninstall the specified bundle(s)
+ update (<id>|<location>|<*>) - update the specified bundle(s)
+ ---Displaying Status---
+ bundle (<id>|<location>) - display details for the specified bundle(s)
+ bundles - display details for all installed bundles
+ headers (<id>|<location>) - print bundle headers
+ packages {<pkgname>|<id>|<location>} - display imported/exported package details
+ props - display System properties
+ services {filter} - display registered service details
+ ss - display installed bundles (short status)
+ status - display installed bundles and registered services
+ threads - display threads and thread groups
+ ---Log Commands---
+ log {(<id>|<location>)} - display log entries
+ ---Extras---
+ exec <command> - execute a command in a separate process and wait
+ fork <command> - execute a command in a separate process
+ ---Controlling StartLevel---
+ sl {(<id>|<location>)} - display the start level for the specified bundle, or for the framework if no bundle specified
+ setfwsl <start level> - set the framework start level
+ setbsl <start level> (<id>|<location>) - set the start level for the bundle(s)
+ setibsl <start level> - set the initial bundle start level
+
+ *
+ * There is a method for each command which is named '_'+method. The methods are
+ * invoked by a CommandInterpreter's execute method.
+ */
+public class FrameworkCommandProvider implements CommandProvider {
+
+ /** An instance of the OSGi framework */
+ private OSGi osgi;
+ /** The system bundle context */
+ private org.osgi.framework.BundleContext context;
+ /** The start level implementation */
+ private StartLevelImpl slImpl;
+
+ /** Strings used to format other strings */
+ private String tab = "\t";
+ private String newline = "\r\n";
+
+ /**
+ * Constructor.
+ *
+ * It registers itself as a CommandProvider with the highest ranking possible.
+ *
+ * @param osgi The current instance of OSGi
+ */
+ public FrameworkCommandProvider(OSGi osgi) {
+ this.osgi = osgi;
+ context = osgi.getBundleContext();
+ slImpl = osgi.framework.startLevelImpl;
+ Dictionary props = new Hashtable();
+ props.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+ context.registerService( CommandProvider.class.getName(), this, props );
+ }
+
+
+ /**
+ Answer a string (may be as many lines as you like) with help
+ texts that explain the command. This getHelp() method uses the
+ ConsoleMsg class to obtain the correct NLS data to display to the user.
+
+ @return The help string
+ */
+ public String getHelp() {
+ StringBuffer help = new StringBuffer(1024);
+ help.append(newline);
+ help.append(ConsoleMsg.formatter.getString("CONSOLE_HELP_VALID_COMMANDS_HEADER"));
+ help.append(newline);
+ addHeader("CONSOLE_HELP_CONTROLLING_FRAMEWORK_HEADER",help);
+ addCommand("launch","CONSOLE_HELP_LAUNCH_COMMAND_DESCRIPTION",help);
+ addCommand("shutdown","CONSOLE_HELP_SHUTDOWN_COMMAND_DESCRIPTION",help);
+ addCommand("close","CONSOLE_HELP_CLOSE_COMMAND_DESCRIPTION",help);
+ addCommand("exit","CONSOLE_HELP_EXIT_COMMAND_DESCRIPTION",help);
+ addCommand("gc","CONSOLE_HELP_GC_COMMAND_DESCRIPTION",help);
+ addCommand("init","CONSOLE_HELP_INIT_COMMAND_DESCRIPTION",help);
+ addCommand("setprop","CONSOLE_HELP_KEYVALUE_ARGUMENT_DESCRIPTION","CONSOLE_HELP_SETPROP_COMMAND_DESCRIPTION",help);
+ addHeader("CONSOLE_HELP_CONTROLLING_BUNDLES_HEADER",help);
+ addCommand("install","CONSOLE_HELP_INSTALL_COMMAND_DESCRIPTION",help);
+ addCommand("uninstall","CONSOLE_HELP_UNINSTALL_COMMAND_DESCRIPTION",help);
+ addCommand("start","CONSOLE_HELP_START_COMMAND_DESCRIPTION",help);
+ addCommand("stop","CONSOLE_HELP_STOP_COMMAND_DESCRIPTION",help);
+ addCommand("refresh","CONSOLE_HELP_REFRESH_COMMAND_DESCRIPTION",help);
+ addCommand("update","CONSOLE_HELP_UPDATE_COMMAND_DESCRIPTION",help);
+ addHeader("CONSOLE_HELP_DISPLAYING_STATUS_HEADER",help);
+ addCommand("status","CONSOLE_HELP_STATUS_COMMAND_DESCRIPTION",help);
+ addCommand("ss","CONSOLE_HELP_SS_COMMAND_DESCRIPTION",help);
+ addCommand("services","CONSOLE_HELP_FILTER_ARGUMENT_DESCRIPTION","CONSOLE_HELP_SERVICES_COMMAND_DESCRIPTION",help);
+ addCommand("packages","CONSOLE_HELP_PACKAGES_ARGUMENT_DESCRIPTION","CONSOLE_HELP_PACKAGES_COMMAND_DESCRIPTION",help);
+ addCommand("bundles","CONSOLE_HELP_BUNDLES_COMMAND_DESCRIPTION",help);
+ addCommand("bundle","CONSOLE_HELP_IDLOCATION_ARGUMENT_DESCRIPTION","CONSOLE_HELP_BUNDLE_COMMAND_DESCRIPTION",help);
+ addCommand("headers","CONSOLE_HELP_IDLOCATION_ARGUMENT_DESCRIPTION","CONSOLE_HELP_HEADERS_COMMAND_DESCRIPTION",help);
+ addCommand("log","CONSOLE_HELP_IDLOCATION_ARGUMENT_DESCRIPTION","CONSOLE_HELP_LOG_COMMAND_DESCRIPTION",help);
+ addHeader("CONSOLE_HELP_EXTRAS_HEADER",help);
+ addCommand("exec","CONSOLE_HELP_COMMAND_ARGUMENT_DESCRIPTION","CONSOLE_HELP_EXEC_COMMAND_DESCRIPTION",help);
+ addCommand("fork","CONSOLE_HELP_COMMAND_ARGUMENT_DESCRIPTION","CONSOLE_HELP_FORK_COMMAND_DESCRIPTION",help);
+ addHeader("STARTLEVEL_HELP_HEADING", help);
+ addCommand("sl", "CONSOLE_HELP_OPTIONAL_IDLOCATION_ARGUMENT_DESCRIPTION","STARTLEVEL_HELP_SL", help);
+ addCommand("setfwsl", "STARTLEVEL_ARGUMENT_DESCRIPTION", "STARTLEVEL_HELP_SETFWSL", help);
+ addCommand("setbsl", "STARTLEVEL_IDLOCATION_ARGUMENT_DESCRIPTION","STARTLEVEL_HELP_SETBSL",help);
+ addCommand("setibsl", "STARTLEVEL_ARGUMENT_DESCRIPTION", "STARTLEVEL_HELP_SETIBSL",help);
+ return help.toString();
+ }
+
+
+ /** Private helper method for getHelp. Formats the help headers. */
+ private void addHeader(String header, StringBuffer help)
+ {
+ help.append("---");
+ help.append(ConsoleMsg.formatter.getString(header));
+ help.append("---");
+ help.append(newline);
+ }
+
+ /** Private helper method for getHelp. Formats the command descriptions. */
+ private void addCommand(String command,String description,StringBuffer help)
+ {
+ help.append(tab);
+ help.append(command);
+ help.append(" - ");
+ help.append(ConsoleMsg.formatter.getString(description));
+ help.append(newline);
+ }
+
+ /** Private helper method for getHelp. Formats the command descriptions with command arguements. */
+ private void addCommand(String command,String parameters, String description,StringBuffer help)
+ {
+ help.append(tab);
+ help.append(command);
+ help.append(" ");
+ help.append(ConsoleMsg.formatter.getString(parameters));
+ help.append(" - ");
+ help.append(ConsoleMsg.formatter.getString(description));
+ help.append(newline);
+ }
+
+ /**
+ * Handle the exit command. Exit immediately (System.exit)
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _exit(CommandInterpreter intp) throws Exception
+ {
+ intp.println();
+ System.exit(0);
+ }
+
+ /**
+ * Handle the launch command. Start the OSGi framework.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _launch(CommandInterpreter intp) throws Exception
+ {
+ osgi.launch();
+ }
+
+ /**
+ * Handle the shutdown command. Shutdown the OSGi framework.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _shutdown(CommandInterpreter intp) throws Exception
+ {
+ osgi.shutdown();
+ }
+
+ /**
+ * Handle the start command's abbreviation. Invoke _start()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _sta(CommandInterpreter intp) throws Exception
+ {
+ _start(intp);
+ }
+
+ /**
+ * Handle the start command. Start the specified bundle(s).
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _start(CommandInterpreter intp) throws Exception
+ {
+ String nextArg = intp.nextArgument();
+ if (nextArg==null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_BUNDLE_SPECIFIED_ERROR"));
+ }
+ while (nextArg!=null) {
+ Bundle bundle = getBundleFromToken(intp, nextArg, true);
+ if (bundle != null) {
+ bundle.start();
+ }
+ nextArg = intp.nextArgument();
+ }
+ }
+
+ /**
+ * Handle the stop command's abbreviation. Invoke _stop()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _sto(CommandInterpreter intp) throws Exception
+ {
+ _stop(intp);
+ }
+
+ /**
+ * Handle the stop command. Stop the specified bundle(s).
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _stop(CommandInterpreter intp) throws Exception
+ {
+ String nextArg = intp.nextArgument();
+ if (nextArg==null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_BUNDLE_SPECIFIED_ERROR"));
+ }
+ while (nextArg!=null) {
+ Bundle bundle = getBundleFromToken(intp, nextArg, true);
+ if (bundle != null) {
+ bundle.stop();
+ }
+ nextArg = intp.nextArgument();
+ }
+ }
+
+ /**
+ * Handle the install command's abbreviation. Invoke _install()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _i(CommandInterpreter intp) throws Exception
+ {
+ _install(intp);
+ }
+
+ /**
+ * Handle the install command. Install and optionally start bundle from the given URL\r\n"
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _install(CommandInterpreter intp) throws Exception
+ {
+ String url = intp.nextArgument();
+ if (url==null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NOTHING_TO_INSTALL_ERROR"));
+ } else {
+ Bundle bundle = (Bundle)context.installBundle(url);
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_BUNDLE_ID_MESSAGE"));
+ intp.println(new Long(bundle.getBundleId()));
+
+ String nextArg = intp.nextArgument();
+ if (nextArg != null) {
+ String start = nextArg.toLowerCase();
+
+ if (Launcher.matchCommand("start", start, 1)) {
+ bundle.start();
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Handle the update command's abbreviation. Invoke _update()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _up(CommandInterpreter intp) throws Exception
+ {
+ _update(intp);
+ }
+
+ /**
+ * Handle the update command. Update the specified bundle(s).
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _update(CommandInterpreter intp) throws Exception
+ {
+ String token = intp.nextArgument();
+ if (token==null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_BUNDLE_SPECIFIED_ERROR"));
+ }
+ while (token!=null) {
+
+ if ("*".equals(token)) {
+ Bundle[] bundles = (Bundle[])context.getBundles();
+
+ int size = bundles.length;
+
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ Bundle bundle = bundles[i];
+
+ if (bundle.getBundleId() != 0) {
+ try {
+ bundle.update();
+ } catch (BundleException e) {
+ intp.printStackTrace(e);
+ }
+ }
+ }
+ } else {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_INSTALLED_BUNDLES_ERROR"));
+ }
+ } else {
+ Bundle bundle = getBundleFromToken(intp, token, true);
+ if (bundle != null) {
+ String source = intp.nextArgument();
+ try {
+ if (source != null) {
+ bundle.update(new URL(source).openStream());
+ }
+ else{
+ bundle.update();
+ }
+ } catch (BundleException e) {
+ intp.printStackTrace(e);
+ }
+ }
+ }
+ token = intp.nextArgument();
+ }
+ }
+
+
+ /**
+ * Handle the uninstall command's abbreviation. Invoke _uninstall()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _un(CommandInterpreter intp) throws Exception
+ {
+ _uninstall(intp);
+ }
+
+ /**
+ * Handle the uninstall command. Uninstall the specified bundle(s).
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _uninstall(CommandInterpreter intp) throws Exception
+ {
+ String nextArg = intp.nextArgument();
+ if (nextArg==null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_BUNDLE_SPECIFIED_ERROR"));
+ }
+ while (nextArg!=null) {
+ Bundle bundle = getBundleFromToken(intp, nextArg, true);
+ if (bundle != null) {
+ bundle.uninstall();
+ }
+ nextArg = intp.nextArgument();
+ }
+ }
+
+
+ /**
+ * Handle the status command's abbreviation. Invoke _status()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _s(CommandInterpreter intp) throws Exception
+ {
+ _status(intp);
+ }
+
+ /**
+ * Handle the status command. Display installed bundles and registered services.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _status(CommandInterpreter intp) throws Exception
+ {
+ if (osgi.isActive()) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_FRAMEWORK_IS_LAUNCHED_MESSAGE"));
+ } else {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_FRAMEWORK_IS_SHUTDOWN_MESSAGE"));
+ }
+ intp.println();
+
+ Bundle[] bundles = (Bundle[])context.getBundles();
+ int size = bundles.length;
+
+ if (size == 0) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_INSTALLED_BUNDLES_ERROR"));
+ return;
+ }
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_ID"));
+ intp.print(tab);
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_BUNDLE_LOCATION_MESSAGE"));
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_STATE_BUNDLE_FILE_NAME_HEADER"));
+ for (int i = 0; i < size; i++) {
+ Bundle bundle = bundles[i];
+ intp.print(new Long(bundle.getBundleId()));
+ intp.print(tab);
+ intp.println(bundle.getLocation());
+ intp.print(" ");
+ intp.print(getStateName(bundle.getState()));
+ intp.println(bundle.bundledata);
+ }
+
+ ServiceReference[] services = (ServiceReference[])context.getServiceReferences(null, null);
+ if (services != null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_REGISTERED_SERVICES_MESSAGE"));
+ size = services.length;
+ for (int i = 0; i < size; i++) {
+ intp.println(services[i]);
+ }
+ }
+ }
+
+
+ /**
+ * Handle the services command's abbreviation. Invoke _services()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _se(CommandInterpreter intp) throws Exception
+ {
+ _services(intp);
+ }
+
+ /**
+ * Handle the services command. Display registered service details.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _services(CommandInterpreter intp) throws Exception
+ {
+ String filter = null;
+
+ String nextArg = intp.nextArgument();
+ if (nextArg!=null) {
+ StringBuffer buf = new StringBuffer();
+ while (nextArg!=null) {
+ buf.append(' ');
+ buf.append(nextArg);
+ nextArg = intp.nextArgument();
+ }
+ filter = buf.toString();
+ }
+
+ ServiceReference[] services = (ServiceReference[])context.getServiceReferences(null, filter);
+ if (services != null) {
+ int size = services.length;
+ if (size > 0) {
+ for (int j = 0; j < size; j++) {
+ ServiceReference service = services[j];
+ intp.println(service);
+ intp.print(" ");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_REGISTERED_BY_BUNDLE_MESSAGE")); intp.print(" ");
+ intp.println(service.getBundle());
+ Bundle[] users = (Bundle[])service.getUsingBundles();
+ if (users != null) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_BUNDLES_USING_SERVICE_MESSAGE"));
+ for (int k = 0; k < users.length; k++) {
+ intp.print(" ");
+ intp.println(users[k]);
+ }
+ } else {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_BUNDLES_USING_SERVICE_MESSAGE"));
+ }
+ }
+ return;
+ }
+ }
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_REGISTERED_SERVICES_MESSAGE"));
+ }
+
+
+ /**
+ * Handle the packages command's abbreviation. Invoke _packages()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _p(CommandInterpreter intp) throws Exception
+ {
+ _packages(intp);
+ }
+
+ /**
+ * Handle the packages command. Display imported/exported package details.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _packages(CommandInterpreter intp) throws Exception
+ {
+ org.osgi.framework.Bundle bundle = null;
+
+ String token = intp.nextArgument();
+ if (token!=null) {
+ bundle = getBundleFromToken(intp, token, false);
+ }
+
+ org.osgi.framework.ServiceReference packageAdminRef = context.getServiceReference("org.osgi.service.packageadmin.PackageAdmin");
+ if (packageAdminRef != null) {
+ org.osgi.service.packageadmin.PackageAdmin packageAdmin = (org.osgi.service.packageadmin.PackageAdmin)context.getService(packageAdminRef);
+ if (packageAdmin != null) {
+ try {
+ org.osgi.service.packageadmin.ExportedPackage[] packages = null;
+
+ if ((token != null) && (bundle == null)) {
+ org.osgi.service.packageadmin.ExportedPackage pkg = packageAdmin.getExportedPackage(token);
+
+ if (pkg != null) {
+ packages = new org.osgi.service.packageadmin.ExportedPackage[] {pkg};
+ }
+ } else {
+ packages = packageAdmin.getExportedPackages(bundle);
+ }
+
+ if (packages == null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE"));
+ } else {
+ for (int i = 0; i < packages.length; i++) {
+ org.osgi.service.packageadmin.ExportedPackage pkg = packages[i];
+ intp.print(pkg);
+
+ boolean removalPending = pkg.isRemovalPending();
+ if (removalPending) {
+ intp.print("(");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_REMOVAL_PENDING_MESSAGE"));
+ intp.println(")");
+ }
+
+ org.osgi.framework.Bundle exporter = pkg.getExportingBundle();
+ if (exporter != null) {
+ intp.print("<");
+ intp.print(exporter);
+ intp.println(">");
+
+ org.osgi.framework.Bundle[] importers = pkg.getImportingBundles();
+ for (int j = 0; j < importers.length; j++) {
+ intp.print(" ");
+ intp.print(importers[j]);
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_IMPORTS_MESSAGE"));
+ }
+ } else {
+ intp.print("<");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_STALE_MESSAGE"));
+ intp.println(">");
+ }
+
+ }
+ }
+ } finally {
+ context.ungetService(packageAdminRef);
+ }
+ }
+ } else {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE"));
+ }
+ }
+
+ /**
+ * Handle the bundles command. Display details for all installed bundles.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _bundles(CommandInterpreter intp) throws Exception
+ {
+ Bundle[] bundles = (Bundle[])context.getBundles();
+ int size = bundles.length;
+
+ if (size == 0) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_INSTALLED_BUNDLES_ERROR"));
+ return;
+ }
+
+ for (int i = 0; i < size; i++) {
+ Bundle bundle = bundles[i];
+ long id = bundle.getBundleId();
+ intp.println(bundle);
+ intp.print(" ");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_ID_MESSAGE",String.valueOf(id)));
+ intp.print(", ");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_STATUS_MESSAGE",getStateName(bundle.getState())));
+ if (id != 0) {
+ File dataRoot = osgi.framework.getDataFile(bundle, "");
+
+ String root = (dataRoot == null) ? null : dataRoot.getAbsolutePath();
+
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_DATA_ROOT_MESSAGE",root));
+ } else {
+ intp.println();
+ }
+
+ ServiceReference[] services = (ServiceReference[])bundle.getRegisteredServices();
+ if (services != null) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_REGISTERED_SERVICES_MESSAGE"));
+ for (int j = 0; j < services.length; j++) {
+ intp.print(" ");
+ intp.println(services[j]);
+ }
+ } else {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_REGISTERED_SERVICES_MESSAGE"));
+ }
+
+ services = (ServiceReference[])bundle.getServicesInUse();
+ if (services != null) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_SERVICES_IN_USE_MESSAGE"));
+ for (int j = 0; j < services.length; j++) {
+ intp.print(" ");
+ intp.println(services[j]);
+ }
+ } else {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_SERVICES_IN_USE_MESSAGE"));
+ }
+ }
+ }
+
+
+ /**
+ * Handle the bundle command's abbreviation. Invoke _bundle()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _b(CommandInterpreter intp) throws Exception
+ {
+ _bundle(intp);
+ }
+
+ /**
+ * Handle the bundle command. Display details for the specified bundle(s).
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _bundle(CommandInterpreter intp) throws Exception
+ {
+ String nextArg = intp.nextArgument();
+ if (nextArg==null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_BUNDLE_SPECIFIED_ERROR"));
+ }
+ while (nextArg!=null) {
+ Bundle bundle = getBundleFromToken(intp, nextArg, true);
+ if (bundle != null) {
+ long id = bundle.getBundleId();
+ intp.println(bundle);
+ intp.print(" ");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_ID_MESSAGE",String.valueOf(id)));
+ intp.print(", ");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_STATUS_MESSAGE",getStateName(bundle.getState())));
+ if (id != 0) {
+ File dataRoot = osgi.framework.getDataFile(bundle, "");
+
+ String root = (dataRoot == null) ? null : dataRoot.getAbsolutePath();
+
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_DATA_ROOT_MESSAGE",root));
+ } else {
+ intp.println();
+ }
+
+ ServiceReference[] services = (ServiceReference[])bundle.getRegisteredServices();
+ if (services != null) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_REGISTERED_SERVICES_MESSAGE"));
+ for (int j = 0; j < services.length; j++) {
+ intp.print(" ");
+ intp.println(services[j]);
+ }
+ } else {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_REGISTERED_SERVICES_MESSAGE"));
+ }
+
+ services = (ServiceReference[])bundle.getServicesInUse();
+ if (services != null) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_SERVICES_IN_USE_MESSAGE"));
+ for (int j = 0; j < services.length; j++) {
+ intp.print(" ");
+ intp.println(services[j]);
+ }
+ } else {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_SERVICES_IN_USE_MESSAGE"));
+ }
+
+ org.osgi.framework.ServiceReference packageAdminRef = context.getServiceReference("org.osgi.service.packageadmin.PackageAdmin");
+ if (packageAdminRef != null) {
+ org.osgi.service.packageadmin.PackageAdmin packageAdmin = (org.osgi.service.packageadmin.PackageAdmin)context.getService(packageAdminRef);
+ if (packageAdmin != null) {
+ try {
+ org.osgi.service.packageadmin.ExportedPackage exportedpkgs[] = packageAdmin.getExportedPackages(null);
+
+ if (exportedpkgs == null) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE"));
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_IMPORTED_PACKAGES_MESSAGE"));
+ } else {
+ boolean title = true;
+
+ for (int i = 0; i < exportedpkgs.length; i++) {
+ org.osgi.service.packageadmin.ExportedPackage exportedpkg = exportedpkgs[i];
+
+ if (exportedpkg.getExportingBundle() == bundle) {
+ if (title) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_EXPORTED_PACKAGES_MESSAGE"));
+ title = false;
+ }
+ intp.print(" ");
+ intp.print(exportedpkg);
+ if (exportedpkg.isRemovalPending()) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_EXPORTED_REMOVAL_PENDING_MESSAGE"));
+ } else {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_EXPORTED_MESSAGE"));
+ }
+ }
+ }
+
+ if (title) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_EXPORTED_PACKAGES_MESSAGE"));
+ }
+
+ title = true;
+
+ for (int i = 0; i < exportedpkgs.length; i++) {
+ org.osgi.service.packageadmin.ExportedPackage exportedpkg = exportedpkgs[i];
+
+ org.osgi.framework.Bundle[] importers = exportedpkg.getImportingBundles();
+ for (int j = 0; j < importers.length; j++) {
+ if (importers[j] == bundle) {
+ if (title) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_IMPORTED_PACKAGES_MESSAGE"));
+ title = false;
+ }
+ intp.print(" ");
+ intp.print(exportedpkg);
+ org.osgi.framework.Bundle exporter = exportedpkg.getExportingBundle();
+ if (exporter != null) {
+ intp.print("<");
+ intp.print(exporter);
+ intp.println(">");
+ } else {
+ intp.print("<");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_STALE_MESSAGE"));
+ intp.println(">");
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (title) {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_IMPORTED_PACKAGES_MESSAGE"));
+ }
+
+ }
+ } finally {
+ context.ungetService(packageAdminRef);
+ }
+ }
+ } else {
+ intp.print(" ");
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_EXPORTED_PACKAGES_NO_PACKAGE_ADMIN_MESSAGE"));
+ }
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ ProtectionDomain domain = bundle.getProtectionDomain();
+
+ intp.println(domain);
+ }
+ }
+ nextArg = intp.nextArgument();
+ }
+ }
+
+
+ /**
+ * Handle the log command's abbreviation. Invoke _log()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _l(CommandInterpreter intp) throws Exception
+ {
+ _log(intp);
+ }
+
+ /**
+ * Handle the log command. Display log entries.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _log(CommandInterpreter intp) throws Exception
+ {
+ long logid = -1;
+ String token = intp.nextArgument();
+ if (token!=null) {
+ Bundle bundle = getBundleFromToken(intp, token, false);
+
+ if (bundle == null) {
+ try {
+ logid = Long.parseLong(token);
+ } catch (NumberFormatException e) {
+ return;
+ }
+ } else {
+ logid = bundle.getBundleId();
+ }
+ }
+
+ org.osgi.framework.ServiceReference logreaderRef = context.getServiceReference("org.osgi.service.log.LogReaderService");
+ if (logreaderRef != null) {
+ Object logreader = context.getService(logreaderRef);
+ if (logreader != null) {
+ try {
+ Enumeration logentries = (Enumeration) (logreader.getClass().getMethod("getLog", null).invoke(logreader, null));
+
+ if (logentries.hasMoreElements()) {
+ Object logentry = logentries.nextElement();
+ Class clazz = logentry.getClass();
+ Method getBundle = clazz.getMethod("getBundle", null);
+ Method getLevel = clazz.getMethod("getLevel", null);
+ Method getMessage = clazz.getMethod("getMessage", null);
+ Method getServiceReference = clazz.getMethod("getServiceReference", null);
+ Method getException = clazz.getMethod("getException", null);
+
+ while (true) {
+ Bundle bundle = (Bundle) getBundle.invoke(logentry, null);
+
+ if ((logid == -1) || ((bundle != null) && (logid == bundle.getBundleId()))) {
+ Integer level = (Integer) getLevel.invoke(logentry, null);
+ switch (level.intValue()) {
+ case org.osgi.service.log.LogService.LOG_DEBUG:
+ intp.print(">");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_DEBUG_MESSAGE"));
+ intp.print(" ");
+ break;
+ case org.osgi.service.log.LogService.LOG_INFO:
+ intp.print(">");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_INFO_MESSAGE")); intp.print(" ");
+ break;
+ case org.osgi.service.log.LogService.LOG_WARNING:
+ intp.print(">");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_WARNING_MESSAGE"));
+ intp.print(" ");
+ break;
+ case org.osgi.service.log.LogService.LOG_ERROR:
+ intp.print(">");
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_ERROR_MESSAGE"));
+ intp.print(" ");
+ break;
+ default:
+ intp.print(">");
+ intp.print(level);
+ intp.print(" ");
+ break;
+ }
+
+ if (bundle != null) {
+ intp.print("[");
+ intp.print(new Long(bundle.getBundleId()));
+ intp.print("] ");
+ }
+
+ intp.print(getMessage.invoke(logentry, null));
+ intp.print(" ");
+
+ ServiceReference svcref = (ServiceReference) getServiceReference.invoke(logentry, null);
+ if (svcref != null) {
+ intp.print("{");
+ intp.print(Constants.SERVICE_ID);
+ intp.print("=");
+ intp.print(svcref.getProperty(Constants.SERVICE_ID).toString());
+ intp.println("}");
+ } else {
+ if (bundle != null) {
+ intp.println(bundle.getLocation());
+ } else {
+ intp.println();
+ }
+ }
+
+ Throwable t = (Throwable) getException.invoke(logentry, null);
+ if (t != null) {
+ intp.printStackTrace(t);
+ }
+ }
+
+
+ if (logentries.hasMoreElements()) {
+ logentry = logentries.nextElement();
+ } else {
+ break;
+ }
+ }
+ }
+ } finally {
+ context.ungetService(logreaderRef);
+ }
+ return;
+ }
+ }
+
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_LOGSERVICE_NOT_REGISTERED_MESSAGE"));
+ }
+
+ /**
+ * Handle the gc command. Perform a garbage collection.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _gc(CommandInterpreter intp) throws Exception
+ {
+ long before = Runtime.getRuntime().freeMemory();
+
+ /* Let the finilizer finish its work and remove objects from its queue */
+ System.gc(); /* asyncronous garbage collector might already run */
+ System.gc(); /* to make sure it does a full gc call it twice */
+ System.runFinalization();
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+
+ long after = Runtime.getRuntime().freeMemory();
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_FREE_MEMORY_BEFORE_GARBAGE_COLLECTION_MESSAGE"));
+ intp.println(String.valueOf(before));
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_FREE_MEMORY_AFTER_GARBAGE_COLLECTION_MESSAGE"));
+ intp.println(String.valueOf(after));
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_MEMORY_GAINED_WITH_GARBAGE_COLLECTION_MESSAGE"));
+ intp.println(String.valueOf(after - before));
+ }
+
+ /**
+ * Handle the init command. Uninstall all bundles.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _init(CommandInterpreter intp) throws Exception
+ {
+ if (osgi.isActive()) {
+ intp.print(newline);
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_FRAMEWORK_LAUNCHED_PLEASE_SHUTDOWN_MESSAGE"));
+ return;
+ }
+
+ Bundle[] bundles = (Bundle[])context.getBundles();
+
+ int size = bundles.length;
+
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ Bundle bundle = bundles[i];
+
+ if (bundle.getBundleId() != 0) {
+ try {
+ bundle.uninstall();
+ } catch (BundleException e) {
+ intp.printStackTrace(e);
+ }
+ }
+ }
+ } else {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_INSTALLED_BUNDLES_ERROR"));
+ }
+ }
+
+ /**
+ * Handle the close command. Shutdown and exit.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _close(CommandInterpreter intp) throws Exception
+ {
+ intp.println();
+ osgi.close();
+ System.exit(0);
+ }
+
+
+ /**
+ * Handle the refresh command's abbreviation. Invoke _refresh()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _r(CommandInterpreter intp) throws Exception
+ {
+ _refresh(intp);
+ }
+
+ /**
+ * Handle the refresh command. Refresh the packages of the specified bundles.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _refresh(CommandInterpreter intp) throws Exception
+ {
+ org.osgi.framework.ServiceReference packageAdminRef = context.getServiceReference("org.osgi.service.packageadmin.PackageAdmin");
+ if (packageAdminRef != null) {
+ org.osgi.service.packageadmin.PackageAdmin packageAdmin = (org.osgi.service.packageadmin.PackageAdmin)context.getService(packageAdminRef);
+ if (packageAdmin != null) {
+ try {
+ Bundle[] refresh = null;
+
+ String token = intp.nextArgument();
+ if (token!=null) {
+ Vector bundles = new Vector();
+
+ while (token!=null) {
+ Bundle bundle = getBundleFromToken(intp, token, true);
+
+ if (bundle != null) {
+ bundles.addElement(bundle);
+ }
+ token = intp.nextArgument();
+ }
+
+ int size = bundles.size();
+
+ if (size == 0) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_INVALID_BUNDLE_SPECIFICATION_ERROR"));
+ return;
+ }
+
+ refresh = new Bundle[size];
+ bundles.copyInto(refresh);
+ }
+
+ packageAdmin.refreshPackages(refresh);
+ } finally {
+ context.ungetService(packageAdminRef);
+ }
+ }
+ } else {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_CAN_NOT_REFRESH_NO_PACKAGE_ADMIN_ERROR"));
+ }
+ }
+
+
+ /**
+ * Executes the given system command in a separate system process
+ * and waits for it to finish.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _exec(CommandInterpreter intp) throws Exception {
+ String command = intp.nextArgument();
+ if (command == null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_COMMAND_SPECIFIED_ERROR"));
+ return;
+ }
+
+ Process p = Runtime.getRuntime().exec(command);
+
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_STARTED_IN_MESSAGE",command,String.valueOf(p)));
+ int result = p.waitFor();
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_EXECUTED_RESULT_CODE_MESSAGE",command,String.valueOf(result)));
+ }
+
+ /**
+ * Executes the given system command in a separate system process. It does
+ * not wait for a result.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _fork(CommandInterpreter intp) throws Exception {
+ String command = intp.nextArgument();
+ if (command == null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_COMMAND_SPECIFIED_ERROR"));
+ return;
+ }
+
+ Process p = Runtime.getRuntime().exec(command);
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_STARTED_IN_MESSAGE",command,String.valueOf(p)));
+ }
+
+ /**
+ * Handle the headers command's abbreviation. Invoke _headers()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _h(CommandInterpreter intp) throws Exception {
+ _headers(intp);
+ }
+
+ /**
+ * Handle the headers command. Display headers for the specified bundle(s).
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _headers(CommandInterpreter intp) throws Exception {
+
+ String nextArg = intp.nextArgument();
+ if (nextArg==null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_BUNDLE_SPECIFIED_ERROR"));
+ }
+ while (nextArg!=null) {
+ Bundle bundle = getBundleFromToken(intp, nextArg, true);
+ if (bundle != null) {
+ intp.printDictionary(bundle.getHeaders(),ConsoleMsg.formatter.getString("CONSOLE_BUNDLE_HEADERS_TITLE"));
+ intp.printBundleResource(bundle, "META-INF/IVEATTRS.XML");
+ intp.printBundleResource(bundle, "META-INF/IVERES.XML");
+ }
+ nextArg = intp.nextArgument();
+ }
+ }
+
+ /**
+ * Prints the the meta data of bundles.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _meta(CommandInterpreter intp) throws Exception
+ {
+ String nextArg = intp.nextArgument();
+ if (nextArg == null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_BUNDLE_SPECIFIED_ERROR"));
+ }
+
+ /* TODO figure out if we can remove the meta command.
+ while (nextArg != null) {
+ Bundle bundle = getBundleFromToken(intp, nextArg, true);
+ if (bundle != null) {
+ intp.printDictionary(bundle.getMetadata(), bundle.toString());
+ }
+ nextArg = intp.nextArgument();
+ }
+ */
+ }
+
+ /**
+ * Handles the props command's abbreviation. Invokes _props()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _pr(CommandInterpreter intp) throws Exception {
+ _props(intp);
+ }
+
+ /**
+ * Handles the _props command. Prints the system properties sorted.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _props(CommandInterpreter intp) throws Exception {
+ intp.printDictionary(System.getProperties(), ConsoleMsg.formatter.getString("CONSOLE_SYSTEM_PROPERTIES_TITLE"));
+ }
+
+ /**
+ * Handles the setprop command's abbreviation. Invokes _setprop()
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _setp(CommandInterpreter intp) throws Exception {
+ _setprop(intp);
+ }
+
+ /**
+ * Handles the setprop command. Sets the CDS property in the given argument.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _setprop(CommandInterpreter intp) throws Exception {
+ String argument = intp.nextArgument();
+ if (argument == null) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_PARAMETERS_SPECIFIED_TITLE"));
+ _props(intp);
+ } else {
+ InputStream in = new ByteArrayInputStream(argument.getBytes());
+ try {
+ Properties sysprops = System.getProperties();
+ Properties newprops = new Properties();
+ newprops.load(in);
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_SETTING_PROPERTIES_TITLE"));
+ Enumeration keys = newprops.propertyNames();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ String value = (String) newprops.get(key);
+ sysprops.put(key, value);
+ intp.println(tab + key + " = " + value);
+ }
+ } catch (IOException e) {
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Prints the short version of the status.
+ * For the long version use "status".
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _ss(CommandInterpreter intp) throws Exception {
+ if (osgi.isActive()) {
+ intp.println();
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_FRAMEWORK_IS_LAUNCHED_MESSAGE"));
+ } else {
+ intp.println();
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_FRAMEWORK_IS_SHUTDOWN_MESSAGE"));
+ }
+
+ Bundle[] bundles = (Bundle[]) context.getBundles();
+ if (bundles.length == 0) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_NO_INSTALLED_BUNDLES_ERROR"));
+ } else {
+ intp.print(newline);
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_ID")); intp.print(tab);
+ intp.print(ConsoleMsg.formatter.getString("CONSOLE_TYPE"));
+ intp.print(tab);
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_STATE_BUNDLE_TITLE"));
+ for (int i = 0; i < bundles.length; i++) {
+ Bundle b = (Bundle) bundles[i];
+ String type = " ";
+ // :TODO need to determine the type?
+ intp.println(b.getBundleId() + "\t" + type + " \t" + getStateName(b.getState()) + b);
+ if (b.isFragment()) {
+ Bundle master = (Bundle)b.getHost();
+ if (master!=null)
+ intp.println("\t\tMaster="+master.getBundleId());
+ } else {
+ Bundle fragment;
+ org.osgi.framework.Bundle fragments[] = b.getFragments();
+ if (fragments!=null) {
+ for (int f=0;f<fragments.length;f++) {
+ fragment = (Bundle)fragments[f];
+ intp.println("\t\tFragment="+fragment.getBundleId());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles the threads command abbreviation. Invokes _threads().
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _t(CommandInterpreter intp) throws Exception {
+ _threads(intp);
+ }
+
+ /**
+ * Prints the information about the currently running threads
+ * in the embedded system.
+ *
+ * @param intp A CommandInterpreter object containing the command
+ * and it's arguments.
+ */
+ public void _threads(CommandInterpreter intp) throws Exception
+ {
+
+ ThreadGroup[] threadGroups = getThreadGroups();
+ Util.sort(threadGroups);
+
+ ThreadGroup tg = getTopThreadGroup();
+ Thread[] threads = new Thread[tg.activeCount()];
+ int count = tg.enumerate(threads, true);
+ Util.sort(threads);
+
+ StringBuffer sb = new StringBuffer(120);
+ intp.println();
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_THREADGROUP_TITLE"));
+ for (int i = 0; i < threadGroups.length; i++) {
+ tg = threadGroups[i];
+ int all = tg.activeCount(); //tg.allThreadsCount();
+ int local = tg.enumerate(new Thread[all], false); //tg.threadsCount();
+ ThreadGroup p = tg.getParent();
+ String parent = (p == null) ? "-none-" : p.getName();
+ sb.setLength(0);
+ sb.append(Util.toString(simpleClassName(tg), 18))
+ .append(" ")
+ .append(Util.toString(tg.getName(), 21))
+ .append(" ")
+ .append(Util.toString(parent, 16))
+ .append(Util.toString(new Integer(tg.getMaxPriority()), 3))
+ .append(Util.toString(new Integer(local), 4))
+ .append("/")
+ .append(Util.toString(String.valueOf(all), 6));
+ intp.println(sb.toString());
+ }
+ intp.print(newline);
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_THREADTYPE_TITLE"));
+ for (int j = 0; j < count; j++) {
+ Thread t = threads[j];
+ if (t != null) {
+ sb.setLength(0);
+ sb.append(Util.toString(simpleClassName(t), 18))
+ .append(" ")
+ .append(Util.toString(t.getName(), 21))
+ .append(" ")
+ .append(Util.toString(t.getThreadGroup().getName(), 16))
+ .append(Util.toString(new Integer(t.getPriority()), 3));
+ intp.println(sb.toString());
+ }
+ }
+ }
+
+ /**
+ * Handles the sl (startlevel) command.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _sl(CommandInterpreter intp) throws Exception {
+ if (isStartLevelSvcPresent(intp)) {
+ org.osgi.framework.Bundle bundle = null;
+ String token = intp.nextArgument();
+ int value = 0;
+ if (token != null) {
+ bundle = getBundleFromToken(intp, token, true);
+ if (bundle == null) {
+ return;
+ }
+ }
+ if (bundle == null) { // must want framework startlevel
+ value = slImpl.getStartLevel();
+ intp.println(
+ ConsoleMsg.formatter.getString(
+ "STARTLEVEL_FRAMEWORK_ACTIVE_STARTLEVEL",
+ value));
+ } else { // must want bundle startlevel
+ value = slImpl.getBundleStartLevel(bundle);
+ intp.println(
+ ConsoleMsg.formatter.getString(
+ "STARTLEVEL_BUNDLE_STARTLEVEL",
+ new Long(bundle.getBundleId()),
+ new Integer(value)));
+ }
+ }
+ }
+
+ /**
+ * Handles the setfwsl (set framework startlevel) command.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _setfwsl(CommandInterpreter intp) throws Exception {
+ if (isStartLevelSvcPresent(intp)) {
+ int value=0;
+ String token = intp.nextArgument();
+ if (token==null) {
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_NO_STARTLEVEL_GIVEN"));
+ value = slImpl.getStartLevel();
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_FRAMEWORK_ACTIVE_STARTLEVEL", value));
+ } else {
+ value = this.getStartLevelFromToken(intp, token);
+ if (value>0) {
+ try {
+ slImpl.setStartLevel(value);
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_FRAMEWORK_ACTIVE_STARTLEVEL", value));
+ } catch (IllegalArgumentException e) {
+ intp.println(e.getMessage());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles the setbsl (set bundle startlevel) command.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _setbsl(CommandInterpreter intp) throws Exception {
+ if (isStartLevelSvcPresent(intp)) {
+ String token;
+ Bundle bundle = null;
+ token = intp.nextArgument();
+ if (token==null) {
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_NO_STARTLEVEL_OR_BUNDLE_GIVEN"));
+ return;
+ }
+
+ int newSL = this.getStartLevelFromToken(intp, token);
+
+ token = intp.nextArgument();
+ if (token==null) {
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_NO_STARTLEVEL_OR_BUNDLE_GIVEN"));
+ return;
+ }
+ while (token!=null) {
+ bundle = getBundleFromToken(intp, token, true);
+ if (bundle!=null) {
+ try {
+ slImpl.setBundleStartLevel(bundle, newSL);
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_BUNDLE_STARTLEVEL", new Long(bundle.getBundleId()), new Integer(newSL)));
+ } catch (IllegalArgumentException e) {
+ intp.println(e.getMessage());
+ }
+ }
+ token = intp.nextArgument();
+ }
+ }
+ }
+
+ /**
+ * Handles the setibsl (set initial bundle startlevel) command.
+ *
+ * @param intp A CommandInterpreter object containing the command and it's arguments.
+ */
+ public void _setibsl(CommandInterpreter intp) throws Exception {
+ if (isStartLevelSvcPresent(intp)) {
+ int value=0;
+ String token = intp.nextArgument();
+ if (token==null) {
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_NO_STARTLEVEL_GIVEN"));
+ value = slImpl.getInitialBundleStartLevel();
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_INITIAL_BUNDLE_STARTLEVEL", value));
+ } else {
+ value = this.getStartLevelFromToken(intp, token);
+ if (value>0) {
+ try {
+ slImpl.setInitialBundleStartLevel(value);
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_INITIAL_BUNDLE_STARTLEVEL", value));
+ } catch (IllegalArgumentException e) {
+ intp.println(e.getMessage());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks for the presence of the StartLevel Service. Outputs a message if it is not present.
+ * @param intp The CommandInterpreter object to be used to write to the console
+ * @return true or false if service is present or not
+ */
+ protected boolean isStartLevelSvcPresent (CommandInterpreter intp) {
+ boolean retval = false;
+ org.osgi.framework.ServiceReference slSvcRef = context.getServiceReference("org.osgi.service.startlevel.StartLevel");
+ if (slSvcRef != null) {
+ org.osgi.service.startlevel.StartLevel slSvc = (org.osgi.service.startlevel.StartLevel) context.getService(slSvcRef);
+ if (slSvc != null) {
+ retval = true;
+ }
+ } else {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_CAN_NOT_USE_STARTLEVEL_NO_STARTLEVEL_SVC_ERROR"));
+ }
+ return retval;
+ }
+
+
+ /**
+ * Given a number, retrieve the Bundle object with that id.
+ *
+ * @param A string containing a potential bundle it
+ * @param A boolean indicating whether or not to output a message
+ * @return The requested Bundle object
+ */
+ protected Bundle getBundleFromToken(CommandInterpreter intp, String token, boolean error)
+ {
+ Bundle bundle;
+ try {
+ long id = Long.parseLong(token);
+ bundle =(Bundle)context.getBundle(id);
+ } catch (NumberFormatException nfe) {
+ bundle = ((BundleContext)context).getBundleByLocation(token);
+ }
+
+ if ((bundle == null) && error) {
+ intp.println(ConsoleMsg.formatter.getString("CONSOLE_CANNOT_FIND_BUNDLE_ERROR",token));
+ }
+
+ return(bundle);
+ }
+
+ /**
+ * Given a string containing a startlevel value, validate it and convert it to an int
+ *
+ * @param A CommandInterpreter object used for printing out error messages
+ * @param A string containing a potential startlevel
+ * @return The start level or an int <0 if it was invalid
+ */
+ protected int getStartLevelFromToken(CommandInterpreter intp, String value) {
+ int retval=-1;
+ try {
+ retval = Integer.parseInt(value);
+ if (Integer.parseInt(value)<=0) {
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_POSITIVE_INTEGER"));
+ }
+ } catch (NumberFormatException nfe) {
+ intp.println(ConsoleMsg.formatter.getString("STARTLEVEL_POSITIVE_INTEGER"));
+ }
+ return retval;
+ }
+
+
+
+ /**
+ * Given a state value, return the string describing that state.
+ *
+ * @param An int containing a state value
+ * @return A String describing the state
+ */
+ protected String getStateName(int state)
+ {
+ switch (state) {
+ case Bundle.UNINSTALLED:
+ return(ConsoleMsg.formatter.getString("CONSOLE_UNINSTALLED_MESSAGE"));
+
+ case Bundle.INSTALLED:
+ return(ConsoleMsg.formatter.getString("CONSOLE_INSTALLED_MESSAGE"));
+
+ case Bundle.RESOLVED:
+ return(ConsoleMsg.formatter.getString("CONSOLE_RESOLVED_MESSAGE"));
+
+ case Bundle.STARTING:
+ return(ConsoleMsg.formatter.getString("CONSOLE_STARTING_MESSAGE"));
+
+ case Bundle.STOPPING:
+ return(ConsoleMsg.formatter.getString("CONSOLE_STOPPING_MESSAGE"));
+
+ case Bundle.ACTIVE:
+ return(ConsoleMsg.formatter.getString("CONSOLE_ACTIVE_MESSAGE"));
+
+ default:
+ return(Integer.toHexString(state));
+ }
+ }
+
+ /**
+ * Answers all thread groups in the system.
+ *
+ * @return An array of all thread groups.
+ */
+ protected ThreadGroup[] getThreadGroups() {
+ ThreadGroup tg = getTopThreadGroup();
+ ThreadGroup[] groups = new ThreadGroup[tg.activeGroupCount()];
+ int count = tg.enumerate(groups, true);
+ if (count == groups.length) {
+ return groups;
+ }
+ // get rid of null entries
+ ThreadGroup[] ngroups = new ThreadGroup[count];
+ System.arraycopy(groups, 0, ngroups, 0, count);
+ return ngroups;
+ }
+
+ /**
+ * Answers the top level group of the current thread.
+ * <p>
+ * It is the 'system' or 'main' thread group under
+ * which all 'user' thread groups are allocated.
+ *
+ * @return The parent of all user thread groups.
+ */
+ protected ThreadGroup getTopThreadGroup() {
+ ThreadGroup topGroup = Thread.currentThread().getThreadGroup();
+ if (topGroup != null) {
+ while (topGroup.getParent() != null) {
+ topGroup = topGroup.getParent();
+ }
+ }
+ return topGroup;
+ }
+
+ /**
+ * Returns the simple class name of an object.
+ *
+ * @param The object for which a class name is requested
+ * @return The simple class name.
+ */
+ public String simpleClassName(Object o) {
+ java.util.StringTokenizer t = new java.util.StringTokenizer(o.getClass().getName(), ".");
+ int ct = t.countTokens();
+ for (int i = 1; i < ct; i++) {
+ t.nextToken();
+ }
+ return t.nextToken();
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkConsole.java b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkConsole.java
new file mode 100644
index 000000000..add42e9bf
--- /dev/null
+++ b/bundles/org.eclipse.osgi/console/src/org/eclipse/osgi/framework/internal/core/FrameworkConsole.java
@@ -0,0 +1,497 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.eclipse.osgi.framework.tracker.ServiceTracker;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * This class starts OSGi with a console for development use.
+ *
+ * FrameworkConsole provides a printStackTrace method to print Exceptions and their
+ * nested Exceptions.
+ */
+public class FrameworkConsole implements Runnable {
+ /** The stream to receive commands on */
+ protected Reader in;
+ /** The stream to write command results to */
+ protected PrintWriter out;
+ /** The current bundle context */
+ protected org.osgi.framework.BundleContext context;
+ /** The current osgi instance */
+ protected OSGi osgi;
+ /** The command line arguments passed at launch time*/
+ protected String[] args;
+ /** The OSGi Command Provider */
+ protected CommandProvider osgicp;
+ /** A tracker containing the service object of all registered command providers */
+ protected CommandProviderTracker cptracker;
+
+ /** Default code page which must be supported by all JVMs */
+ static String defaultEncoding = "iso8859-1";
+ /** The current setting for code page */
+ static String encoding =
+ System.getProperty("file.encoding", defaultEncoding);
+
+ /** set to true if accepting commands from port */
+ protected boolean useSocketStream = false;
+ protected boolean disconnect = false;
+ protected int port = 0;
+ protected ServerSocket ss = null;
+ protected ConsoleSocketGetter scsg = null;
+ protected Socket s;
+
+ /**
+ Constructor for FrameworkConsole.
+ It creates a service tracker to track CommandProvider registrations.
+ The console InputStream is set to System.in and the console PrintStream is set to System.out.
+ @param osgi - an instance of an osgi framework
+ @param args - any arguments passed on the command line when Launcher is started.
+ */
+ public FrameworkConsole(OSGi osgi, String[] args) {
+
+ getDefaultStreams();
+
+ this.args = args;
+ this.osgi = osgi;
+
+ initialize();
+
+ }
+
+ /**
+ Constructor for FrameworkConsole.
+ It creates a service tracker to track CommandProvider registrations.
+ The console InputStream is set to System.in and the console PrintStream is set to System.out.
+ @param osgi - an instance of an osgi framework
+ @param args - any arguments passed on the command line when Launcher is started.
+ */
+ public FrameworkConsole(OSGi osgi, int port, String[] args) {
+
+ getSocketStream(port);
+
+ this.useSocketStream = true;
+ this.port = port;
+ this.args = args;
+ this.osgi = osgi;
+
+ initialize();
+ }
+
+ /**
+ * Open streams for system.in and system.out
+ */
+ private void getDefaultStreams() {
+ in = createBufferedReader(System.in);
+ out = createPrintWriter(System.out);
+ }
+
+ /**
+ * Open a socket and create input and output streams
+ *
+ * @param port number to listen on
+ */
+ private void getSocketStream(int port) {
+ try {
+ System.out.println(
+ ConsoleMsg.formatter.getString(
+ "CONSOLE_LISTENING_ON_PORT",
+ port));
+ if (ss == null) {
+ ss = new ServerSocket(port);
+ scsg = new ConsoleSocketGetter(ss);
+ }
+ scsg.setAcceptConnections(true);
+ s = scsg.getSocket();
+
+ in = createBufferedReader(s.getInputStream());
+ out = createPrintWriter(s.getOutputStream());
+ } catch (UnknownHostException uhe) {
+ uhe.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Return a BufferedReader from an InputStream. Handle encoding.
+ *
+ * @param An InputStream to wrap with a BufferedReader
+ * @return a BufferedReader
+ */
+ private BufferedReader createBufferedReader(InputStream _in) {
+ BufferedReader reader;
+ try {
+ reader = new BufferedReader(new InputStreamReader(_in, encoding));
+ } catch (UnsupportedEncodingException uee) {
+ // if the encoding is not supported by the jvm, punt and use whatever encodiing there is
+ reader = new BufferedReader(new InputStreamReader(_in));
+ }
+ return reader;
+ }
+
+ /**
+ * Return a PrintWriter from an OutputStream. Handle encoding.
+ *
+ * @param An OutputStream to wrap with a PrintWriter
+ * @return a PrintWriter
+ */
+ private PrintWriter createPrintWriter(OutputStream _out) {
+ PrintWriter writer;
+ try {
+ writer =
+ new PrintWriter(
+ new BufferedWriter(new OutputStreamWriter(_out, encoding)),
+ true);
+ } catch (UnsupportedEncodingException uee) {
+ // if the encoding is not supported by the jvm, punt and use whatever encodiing there is
+ writer =
+ new PrintWriter(
+ new BufferedWriter(new OutputStreamWriter(_out)),
+ true);
+ }
+ return writer;
+ }
+
+ /**
+ * Return the current output PrintWriter
+ * @return The currently active PrintWriter
+ */
+ public PrintWriter getWriter() {
+ return out;
+ }
+
+ /**
+ * Return the current input BufferedReader
+ * @return The currently active BufferedReader
+ */
+ public BufferedReader getReader() {
+ return (BufferedReader) in;
+ }
+
+ /**
+ * Return if the SocketSteam (telnet to the console) is being used
+ * @return Return if the SocketSteam is being used
+ */
+ public boolean getUseSocketStream() {
+ return useSocketStream;
+ }
+
+ /**
+ * Initialize common things:
+ * - system bundle context
+ * - ServiceTracker to track CommandProvider registrations
+ * - create OSGi CommandProvider object
+ */
+ private void initialize() {
+
+ context = osgi.getBundleContext();
+
+ // set up a service tracker to track CommandProvider registrations
+ cptracker =
+ new CommandProviderTracker(
+ context,
+ CommandProvider.class.getName(),
+ this);
+ cptracker.open();
+
+ // register the OSGi command provider
+ osgicp = new FrameworkCommandProvider(osgi);
+
+ }
+
+ /**
+ * Begin doing the active part of the class' code. Starts up the console.
+ */
+ public void run() {
+ console(args);
+ if (useSocketStream) {
+ while (true) {
+ getSocketStream(port);
+ console();
+ }
+ }
+ }
+
+ /**
+ * Command Line Interface for OSGi. The method processes the initial commands
+ * and then reads and processes commands from the console InputStream.
+ * Command output is written to the console PrintStream. The method will
+ * loop reading commands from the console InputStream until end-of-file
+ * is reached. This method will then return.
+ *
+ * @param args Initial set of commands to execute.
+ */
+ public void console(String args[]) {
+ // first handle any args passed in from launch
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ docommand(args[i]);
+ }
+ }
+ console();
+ }
+
+ /**
+ * Command Line Interface for OSGi. The method processes the initial commands
+ * and then reads and processes commands from the console InputStream.
+ * Command output is written to the console PrintStream. The method will
+ * loop reading commands from the console InputStream until end-of-file
+ * is reached. This method will then return.
+ */
+ protected void console() {
+ Object lock = new Object();
+ disconnect = false;
+ // wait to receive commands from console and handle them
+ BufferedReader br = (BufferedReader) in;
+ //cache the console prompt String
+ String consolePrompt =
+ "\r\n" + ConsoleMsg.formatter.getString("CONSOLE_PROMPT");
+ while (!disconnect) {
+ out.print(consolePrompt);
+ out.flush();
+
+ // avoid waiting on input stream - apparently generates contention with other native calls
+
+ String cmdline = null;
+ try {
+ synchronized (lock) {
+ while (!br.ready()) {
+ lock.wait(300);
+ }
+ }
+ cmdline = br.readLine();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ continue;
+ }
+
+ if (cmdline == null) {
+ break;
+ }
+
+ docommand(cmdline);
+ }
+ }
+
+ /**
+ * Process the args on the command line.
+ * This method invokes a CommandInterpreter to do the actual work.
+ *
+ * @param a string containing the command line arguments
+ */
+ protected void docommand(String cmdline) {
+ if (cmdline != null && cmdline.length() > 0) {
+ CommandInterpreter intcp =
+ new FrameworkCommandInterpreter(
+ cmdline,
+ cptracker.getServices(),
+ this);
+ String command = intcp.nextArgument();
+ if (command != null) {
+ intcp.execute(command);
+ }
+ }
+ }
+
+ /**
+ * Disconnects from console if useSocketStream is set to true. This
+ * will cause the console to close from a telnet session.
+ */
+
+ void disconnect() throws IOException {
+ disconnect = true;
+ out.close();
+ in.close();
+ s.close();
+ }
+
+ /**
+ * Reads a string from standard input until user hits the Enter key.
+ *
+ * @return The string read from the standard input without the newline character.
+ */
+ public String getInput() {
+ String input;
+ try {
+ /** The buffered input reader on standard in. */
+ input = ((BufferedReader) in).readLine();
+ } catch (IOException e) {
+ input = "";
+ }
+ return input;
+ }
+
+ /**
+ * ConsoleSocketGetter - provides a Thread that listens on the port
+ * for FrameworkConsole. If acceptConnections is set to true then
+ * the thread will notify the getSocket method to return the socket.
+ * If acceptConnections is set to false then the client is notified
+ * that connections are not currently accepted and closes the socket.
+ */
+ class ConsoleSocketGetter implements Runnable {
+
+ /** The ServerSocket to accept connections from */
+ ServerSocket server;
+ /** The current socket to be returned by getSocket */
+ Socket socket;
+ /** if set to true then allow the socket to be returned by getSocket */
+ boolean acceptConnections = true;
+ /** Lock object to synchronize returning of the socket */
+ Object lock = new Object();
+
+ /**
+ * Constructor - sets the server and starts the thread to
+ * listen for connections.
+ *
+ * @param a ServerSocket to accept connections from
+ */
+ ConsoleSocketGetter(ServerSocket server) {
+ this.server = server;
+ Thread t = new Thread(this, "ConsoleSocketGetter");
+ t.start();
+ }
+
+ public void run() {
+ while (true) {
+
+ try {
+ socket = ss.accept();
+ if (!acceptConnections) {
+ PrintWriter o =
+ createPrintWriter(socket.getOutputStream());
+ o.println(
+ ConsoleMsg.formatter.getString(
+ "CONSOLE_TELNET_CONNECTION_REFUSED"));
+ o.println(
+ ConsoleMsg.formatter.getString(
+ "CONSOLE_TELNET_CURRENTLY_USED"));
+ o.println(
+ ConsoleMsg.formatter.getString(
+ "CONSOLE_TELNET_ONE_CLIENT_ONLY"));
+ o.close();
+ socket.close();
+ } else {
+ synchronized (lock) {
+ lock.notify();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+ /**
+ * Method to get a socket connection from a client.
+ *
+ * @return - Socket from a connected client
+ */
+ public Socket getSocket() throws InterruptedException {
+ // wait for a socket to get assigned from the accepter thread
+ synchronized (lock) {
+ lock.wait();
+ }
+ setAcceptConnections(false);
+ return socket;
+ }
+
+ /**
+ * Method to indicate if connections are accepted or not. If set
+ * to false then the clients will be notified that connections
+ * are not accepted.
+ */
+ public void setAcceptConnections(boolean acceptConnections) {
+ this.acceptConnections = acceptConnections;
+ }
+ }
+
+ class CommandProviderTracker extends ServiceTracker {
+
+ FrameworkConsole con;
+ CommandProviderTracker(
+ org.osgi.framework.BundleContext context,
+ String clazz,
+ FrameworkConsole con) {
+ super(context, clazz, null);
+ this.con = con;
+ }
+
+ /**
+ * Default implementation of the <tt>ServiceTrackerCustomizer.addingService</tt> method.
+ *
+ * <p>This method is only called when this <tt>ServiceTracker</tt> object
+ * has been constructed with a <tt>null</tt> <tt>ServiceTrackerCustomizer</tt> argument.
+ *
+ * The default implementation returns the result of
+ * calling <tt>getService</tt>, on the
+ * <tt>BundleContext</tt> object with which this <tt>ServiceTracker</tt> object was created,
+ * passing the specified <tt>ServiceReference</tt> object.
+ *
+ * <p>This method can be overridden to customize
+ * the service object to be tracked for the service
+ * being added.
+ *
+ * @param reference Reference to service being added to this
+ * <tt>ServiceTracker</tt> object.
+ * @return The service object to be tracked for the service
+ * added to this <tt>ServiceTracker</tt> object.
+ */
+ public Object addingService(ServiceReference reference) {
+ CommandProvider cp =
+ (CommandProvider) super.addingService(reference);
+ return cp;
+ }
+
+ /**
+ * Return an array of service objects for all services
+ * being tracked by this <tt>ServiceTracker</tt> object.
+ *
+ * The array is sorted primarily by descending Service Ranking and
+ * secondarily by ascending Service ID.
+ *
+ * @return Array of service objects or <tt>null</tt> if no service
+ * are being tracked.
+ */
+ public Object[] getServices() {
+ ServiceReference[] serviceRefs =
+ (ServiceReference[]) super.getServiceReferences();
+ Util.dsort(serviceRefs, 0, serviceRefs.length);
+
+ Object[] serviceObjects = new Object[serviceRefs.length];
+ for (int i = 0; i < serviceRefs.length; i++) {
+ serviceObjects[i] =
+ FrameworkConsole.this.context.getService(serviceRefs[i]);
+ }
+ return serviceObjects;
+ }
+
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleClassLoader.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleClassLoader.java
new file mode 100644
index 000000000..76c7ffa3f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleClassLoader.java
@@ -0,0 +1,297 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.Enumeration;
+import org.eclipse.osgi.framework.debug.Debug;
+
+/**
+ * The framework needs access to some protected methods in order to
+ * load local resources and classes. The BundleClassLoader simply exposes
+ * some new public methods that call protected methods for the framework.
+ */
+public abstract class BundleClassLoader extends ClassLoader {
+
+ /**
+ * The delegate used to get classes and resources from. The delegate
+ * must always be queried first before the local ClassLoader is searched for
+ * a class or resource.
+ */
+ protected ClassLoaderDelegate delegate;
+
+ /**
+ * The ProtectionDomain to use to define classes.
+ */
+ protected ProtectionDomain domain;
+
+ /**
+ * Indicates this class loader is closed.
+ */
+ protected boolean closed = false;
+
+ /**
+ * The default parent classloader to use when one is not specified.
+ * The behavior of the default parent classloader will be to load classes
+ * from the boot strap classloader.
+ */
+ protected static ParentClassLoader defaultParentClassLoader = new ParentClassLoader();
+
+ /**
+ * The BundleClassLoader parent.
+ */
+ protected ClassLoader parentClassLoader;
+
+ /**
+ * BundleClassLoader constructor.
+ * @param delegate The ClassLoaderDelegate for this bundle.
+ * @param domain The ProtectionDomain for this bundle.
+ */
+ public BundleClassLoader(ClassLoaderDelegate delegate, ProtectionDomain domain, String[] ignored)
+ {
+ this(delegate, domain, ignored, null);
+ }
+
+ /**
+ * BundleClassLoader constructor.
+ * @param delegate The ClassLoaderDelegate for this bundle.
+ * @param domain The ProtectionDomain for this bundle.
+ * @param parent The parent classloader to use.
+ */
+ public BundleClassLoader(ClassLoaderDelegate delegate, ProtectionDomain domain, String[] ignored, ClassLoader parent)
+ {
+ this.delegate = delegate;
+ this.domain = domain;
+ // use the defaultParentClassLoader if a parent is not specified.
+ if (parent == null)
+ parentClassLoader=defaultParentClassLoader;
+ else
+ parentClassLoader = parent;
+ }
+
+ /**
+ * Loads a class for the bundle. First delegate.findClass(name) is called.
+ * The delegate will query the system class loader, bundle imports, bundle
+ * local classes, bundle hosts and fragments. The delegate will call
+ * BundleClassLoader.findLocalClass(name) to find a class local to this
+ * bundle.
+ * @param name the name of the class to load.
+ * @param resolve indicates whether to resolve the loaded class or not.
+ * @return The Class object.
+ * @throws ClassNotFoundException if the class is not found.
+ */
+ public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (closed)
+ throw new ClassNotFoundException(name);
+
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("BundleClassLoader[" + delegate + "].loadClass(" + name + ")");
+
+ try {
+ // First check the parent classloader for system classes.
+ try{
+ return parentClassLoader.loadClass(name);
+ } catch (ClassNotFoundException e){
+ // Do nothing. continue to delegate.
+ }
+
+ // Just ask the delegate. This could result in findLocalClass(name) being called.
+ Class clazz = delegate.findClass(name);
+ // resolve the class if asked to.
+ if (resolve)
+ resolveClass(clazz);
+ return (clazz);
+ } catch (Error e) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER) {
+ Debug.println("BundleClassLoader[" + delegate + "].loadClass(" + name + ") failed.");
+ Debug.printStackTrace(e);
+ }
+ throw e;
+ } catch (ClassNotFoundException e) {
+ // If the class is not found do not try to look for it locally.
+ // The delegate would have already done that for us.
+ if (Debug.DEBUG && Debug.DEBUG_LOADER) {
+ Debug.println("BundleClassLoader[" + delegate + "].loadClass(" + name + ") failed.");
+ Debug.printStackTrace(e);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Finds a class local to this bundle. The bundle class path is used
+ * to search for the class. The delegate must not be used. This method
+ * is abstract to force extending classes to implement this method instead
+ * of using the ClassLoader.findClass(String) method.
+ * @param name The classname of the class to find
+ * @return The Class object.
+ * @throws ClassNotFoundException if the class is not found.
+ */
+ abstract protected Class findClass(String name) throws ClassNotFoundException ;
+
+ /**
+ * Gets a resource for the bundle. First delegate.findResource(name) is
+ * called. The delegate will query the system class loader, bundle imports,
+ * bundle local resources, bundle hosts and fragments. The delegate will
+ * call BundleClassLoader.findLocalResource(name) to find a resource local
+ * to this bundle.
+ * @param name The resource path to get.
+ * @return The URL of the resource or null if it does not exist.
+ */
+ public URL getResource(String name) {
+ if (closed) {
+ return null;
+ }
+ if (Debug.DEBUG && Debug.DEBUG_LOADER) {
+ Debug.println("BundleClassLoader[" + delegate +"].getResource("+name+")");
+ }
+
+ try
+ {
+ // First check the parent classloader for system resources.
+ URL url = parentClassLoader.getResource(name);
+ if (url != null) {
+ return(url);
+ }
+ url = delegate.findResource(name);
+ if (url != null) {
+ return(url);
+ }
+ }
+ catch (ImportResourceNotFoundException e)
+ {
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println("BundleClassLoader[" + delegate +"].getResource("+name+") failed.");
+ }
+
+ return(null);
+ }
+
+ /**
+ * Finds a resource local to this bundle. Simply calls
+ * findResourceImpl(name) to find the resource.
+ * @param name The resource path to find.
+ * @return The URL of the resource or null if it does not exist.
+ */
+ abstract protected URL findResource(String name);
+
+ /**
+ * Finds all resources with the specified name. This method must call
+ * delegate.findResources(name) to find all the resources.
+ * @param name The resource path to find.
+ * @return An Enumeration of all resources found or null if the resource.
+ */
+ protected Enumeration findResources(String name) throws IOException {
+ /* Note: this class cannot be constructed with super(parent).
+ * In order to properly search the imported packages,
+ * ClassLoader.getResources cannot call parent.findResources
+ * which will happen if this class is constructed with super(parent).
+ * This is necessary because ClassLoader.getResources is final. Otherwise
+ * we could override it to better implement the desired behavior.
+ * So instead, we do not have a parent and call delegate.findResources(name).
+ * The delegate may call findLocalResources(name) to find the resources
+ * locally if they are not found outside the bundle.
+ */
+ try {
+ return(delegate.findResources(name));
+ } catch (Exception e){
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println("BundleClassLoader[" + delegate +"].findResources("+name+") failed.");
+ Debug.printStackTrace(e);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Finds a library for this bundle. Simply calls
+ * delegate.findLibrary(libname) to find the library.
+ * @param libname The library to find.
+ * @return The URL of the resource or null if it does not exist.
+ */
+ protected String findLibrary(String libname){
+ return delegate.findLibrary(libname);
+ }
+
+ /**
+ * Finds a local resource in the BundleClassLoader without
+ * consulting the delegate.
+ * @param resource the resource path to find.
+ * @return a URL to the resource or null if the resource does not exist.
+ */
+ public URL findLocalResource(String resource){
+ return this.findResource(resource);
+ }
+
+ /**
+ * Finds all local resources in the BundleClassLoader with the specified
+ * path without consulting the delegate.
+ * @param classname
+ * @return An Enumeration of all resources found or null if the resource.
+ * does not exist.
+ */
+ abstract public Enumeration findLocalResources(String resource);
+
+ /**
+ * Finds a local class in the BundleClassLoader without
+ * consulting the delegate.
+ * @param classname the classname to find.
+ * @return The class object found.
+ * @throws ClassNotFoundException if the classname does not exist locally.
+ */
+ public Class findLocalClass(String classname) throws ClassNotFoundException{
+ return findClass(classname);
+ }
+
+ /**
+ * Closes this class loader. After this method is called
+ * loadClass will always throw ClassNotFoundException,
+ * getResource, getResourceAsStream, and getResources will
+ * return null.
+ *
+ */
+ public void close() {
+ closed = false;
+ }
+
+ /**
+ * Empty parent classloader. This is used by default as our parentClassLoader
+ * The BundleClassLoader constructor may assign a different parentClassLoader
+ * if desired.
+ */
+ protected static class ParentClassLoader extends ClassLoader {
+ protected ParentClassLoader(){
+ super(null);
+ }
+ }
+
+ /**
+ * Attaches the BundleData for a fragment to this BundleClassLoader.
+ * The Fragment BundleData resources must be appended to the end of
+ * this BundleClassLoader's classpath. Fragment BundleData resources
+ * must be searched ordered by Bundle ID's.
+ * @param bundledata The BundleData of the fragment.
+ * @param domain The ProtectionDomain of the resources of the fragment.
+ * Any classes loaded from the fragment's BundleData must belong to this
+ * ProtectionDomain.
+ * @param classpath An array of Bundle-ClassPath entries to
+ * use for loading classes and resources. This is specified by the
+ * Bundle-ClassPath manifest entry of the fragment.
+ */
+ abstract public void attachFragment(BundleData bundledata, ProtectionDomain domain, String[] classpath);
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java
new file mode 100644
index 000000000..ef7a256a6
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+/**
+ * The <code>BundleData</code> represents a single bundle that is persistently
+ * stored by a <code>FrameworkAdaptor</code>. A <code>BundleData</code> creates
+ * the ClassLoader for a bundle, finds native libraries installed in the
+ * FrameworkAdaptor for the bundle, creates data files for the bundle,
+ * used to access bundle entries, manifest information, and getting and saving
+ * metadata.
+ */
+public interface BundleData
+{
+ /**
+ * Creates the ClassLoader for the BundleData. The ClassLoader created
+ * must use the <code>ClassLoaderDelegate</code> to delegate class, resource
+ * and library loading. The delegate is responsible for finding any resource
+ * or classes imported by the bundle or provided by bundle fragments or
+ * bundle hosts. The <code>ProtectionDomain</code> domain must be used
+ * by the Classloader when defining a class.
+ * @param delegate The <code>ClassLoaderDelegate</code> to delegate to.
+ * @param domain The <code>ProtectionDomain</code> to use when defining a class.
+ * @param bundleclasspath An array of bundle classpaths to use to create this
+ * classloader. This is specified by the Bundle-ClassPath manifest entry.
+ * @return The new ClassLoader for the BundleData.
+ */
+ public BundleClassLoader createClassLoader(ClassLoaderDelegate delegate, ProtectionDomain domain, String[] bundleclasspath);
+
+ /**
+ * Gets a <code>URL</code> to the bundle entry specified by path.
+ * This method must not use the BundleClassLoader to find the
+ * bundle entry since the ClassLoader will delegate to find the resource.
+ * @param path The bundle entry path.
+ * @return A URL used to access the entry or null if the entry
+ * does not exist.
+ */
+ public URL getEntry(String path);
+
+ /**
+ * Gets all of the bundle entries that exist under the specified path.
+ * For example: <p>
+ * <code>getEntryPaths("/META-INF")</code> <p>
+ * This will return all entries from the /META-INF directory of the bundle.
+ * @param path The path to a directory in the bundle.
+ * @return An Enumeration of the entry paths or null if the specified path
+ * does not exist.
+ */
+ public Enumeration getEntryPaths(String path);
+
+ /**
+ * Returns the absolute path name of a native library. The BundleData
+ * ClassLoader invokes this method to locate the native libraries that
+ * belong to classes loaded from this BundleData. Returns
+ * null if the library does not exist in this BundleData.
+ * @param libname The name of the library to find the absolute path to.
+ * @return The absolute path name of the native library or null if
+ * the library does not exist.
+ */
+ public String findLibrary(String libname);
+
+ /**
+ * Installs the native code paths for this BundleData. Each
+ * element of nativepaths must be installed for lookup when findLibrary
+ * is called.
+ * @param nativepaths The array of native code paths to install for
+ * the bundle.
+ * @throws BundleException If any error occurs during install.
+ */
+ public void installNativeCode(String[] nativepaths) throws BundleException;
+
+ /**
+ * Return the bundle data directory.
+ * Attempt to create the directory if it does not exist.
+ *
+ * @return Bundle data directory.
+ */
+
+ public File getDataFile(String path);
+
+ /**
+ * Return the BundleManifest for the BundleData.
+ * @return Dictionary for containing the Manifest headers for the BundleData.
+ * @throws BundleException if an error occurred while reading the
+ * bundle manifest data.
+ */
+ public Dictionary getManifest() throws BundleException;
+
+ /**
+ * Get the BundleData bundle ID. This will be used as the bundle
+ * ID by the framework.
+ * @return The BundleData ID.
+ */
+ public long getBundleID();
+
+ /**
+ * Get the BundleData Location. This will be used as the bundle
+ * location by the framework.
+ * @return the BundleData location.
+ */
+ public String getLocation();
+
+ /**
+ * Return a Dictionary of all manifest headers.
+ * @return all manifest headers.
+ */
+ public Dictionary getHeaders();
+
+ /**
+ * Close all resources for this BundleData
+ * @throws IOException If an error occurs closing.
+ */
+ public void close() throws IOException;
+
+ /**
+ * Open the BundleData. This method will reopen the BundleData if it has been
+ * previously closed.
+ * @throws IOException If an error occurs opening.
+ */
+ public void open() throws IOException;
+
+ /**
+ * Sets the Bundle object for this BundleData.
+ * @param bundle The Bundle Object for this BundleData.
+ */
+ public void setBundle(Bundle bundle);
+
+ public int getStartLevel();
+ public int getStatus();
+ public void setStartLevel(int value);
+ public void setStatus(int value);
+ public void save() throws IOException;
+
+ public String getUniqueId();
+ public Version getVersion();
+ public boolean isFragment();
+ public String getClassPath();
+ public String getActivator();
+ public String getExecutionEnvironment();
+ public String getDynamicImports();
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleOperation.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleOperation.java
new file mode 100644
index 000000000..23a08550c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleOperation.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+import org.osgi.framework.*;
+
+/**
+ * Bundle Storage interface for managing a persistent storage life
+ * cycle operation upon a bundle.
+ *
+ * <p>This class is used to provide methods to manage a life cycle
+ * operation on a bundle in persistent storage. BundleOperation objects
+ * are returned by the FrameworkAdaptor object and are called by OSGi
+ * to complete the persistent storage life cycle operation.
+ *
+ * <p>For example
+ * <pre>
+ * Bundle bundle;
+ * BundleOperation storage = adaptor.installBundle(location, source);
+ * try {
+ * bundle = storage.begin();
+ *
+ * // Perform some implementation specific work
+ * // which may fail.
+ *
+ * storage.commit(false);
+ * // bundle has been successfully installed
+ * } catch (BundleException e) {
+ * storage.undo();
+ * throw e; // rethrow the error
+ * }
+ * return bundle;
+ * </pre>
+ *
+ */
+public abstract interface BundleOperation
+{
+
+ /**
+ * Begin the operation on the bundle (install, update, uninstall).
+ *
+ * @return BundleData object for the target bundle.
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+ public abstract BundleData begin() throws BundleException;
+
+ /**
+ * Commit the operation performed.
+ *
+ * @param postpone If true, the bundle's persistent
+ * storage cannot be immediately reclaimed. This may occur if the
+ * bundle is still exporting a package.
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+ public abstract void commit(boolean postpone) throws BundleException;
+
+ /**
+ * Undo the change to persistent storage.
+ * <p>This method can be called before calling commit or if commit
+ * throws an exception to undo any changes in progress.
+ *
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+ public abstract void undo() throws BundleException;
+
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ClassLoaderDelegate.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ClassLoaderDelegate.java
new file mode 100644
index 000000000..6e3cc61f4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ClassLoaderDelegate.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * A ClassLoaderDelegate is used by the BundleClassLoader in a similar
+ * fashion that a parent ClassLoader is used. A ClassLoaderDelegate must
+ * be queried for any resource or class before it is loaded by the
+ * BundleClassLoader.
+ */
+public interface ClassLoaderDelegate {
+ /**
+ * Finds a class for a bundle that may be outside of the actual bundle
+ * (i.e. an imported class or a fragment/host class). The following is a
+ * list of steps that a ClassLoaderDelegate must take when trying to load
+ * a class. <p>
+ * <ul>
+ * <li>Try to load the class using the System ClassLoader.
+ *
+ * <li>If the bundle is not a fragment then continue to the next step
+ * otherwise try to load the class from the host bundle, if the class
+ * is not found in the host then throw a ClassNotFoundException.
+ *
+ * <li>Try to load the class from an imported package. If the class is
+ * not found and it belongs to a package that has been imported then throw
+ * an <code>ImportClassNotFoundException</code>. If the class does not
+ * belong to an imported package then continue to the next step.
+ *
+ * <li>If the bundle is not a host to any fragments then continue to the
+ * next step. Try to load the class as an imported class from all of the
+ * fragment bundles for this host bundle. If the class is not found and
+ * it belongs to a package that has been imported by a fragment then throw
+ * an <code>ImportClassNotFoundException</code>.
+ *
+ * <li>Try to load the class from the actual bundle. This must be done
+ * by calling the findLocalClass(classname) method on the BundleClassLoader.
+ *
+ * <li>If the bundle is a host to any fragments then try to load the class
+ * from the fragment bundles. This must be done by calling the
+ * findLocalClass(classname) method on the fragement BundleClassLoader.
+ *
+ * </ul>
+ * If no class is found then a ClassNotFoundException is thrown.
+ * @param classname the class to find.
+ * @return the Class.
+ * @throws ImportClassNotFoundException if trying to import a class from an
+ * imported package and the class is not found.
+ * @throws ClassNotFoundException if the class is not found.
+ */
+ public Class findClass(String classname) throws ClassNotFoundException;
+
+ /**
+ * Finds a resource for a bundle that may be outside of the actual bundle
+ * (i.e. an imported resource or a fragment/host resource). The following
+ * is a list steps that a ClassLoaderDelegate must take when trying to find
+ * a resource. <p>
+ * <ul>
+ * <li>Try to load the resource using the System ClassLoader.
+ *
+ * <li>If the bundle is not a fragment then continue to the next step
+ * otherwise try to load the resource from the host bundle, if the resource
+ * is not found in the host null is returned.
+ *
+ * <li>Try to load the resource from an imported package. If the resource is
+ * not found and it belongs to a package that has been imported then throw
+ * an <code>ImportResourceNotFoundException</code>. If the resource does not
+ * belong to an imported package then continue to the next step.
+ *
+ * <li>If the bundle is not a host to any fragments then continue to the
+ * next step. Try to load the resource as an imported resource from all
+ * of the fragment bundles for this host bundle. If the resource is not found
+ * and it belongs to a package that has been imported by a fragment then
+ * throw an <code>ImportResourceNotFoundException</code>.
+ *
+ * <li>Try to load the resource from the actual bundle. This must be done
+ * by calling the findLocalResource(name) method on the BundleClassLoader.
+ *
+ * <li>If the bundle is a host to any fragments then try to load the resource
+ * from the fragment bundles. This must be done by calling the
+ * findLocalResource(name) method on the fragement BundleClassLoader.
+ *
+ * </ul>
+ * If no resource is found then return null.
+ * @param resource the resource to load.
+ * @return the resource or null if resource is not found.
+ * @throws ImportResourceNotFoundException if trying to import a resource from an
+ * imported package and the resource is not found.
+ */
+ public URL findResource(String resource) throws ImportResourceNotFoundException;
+
+ /**
+ * Finds a list of resources for a bundle that may be outside of the actual
+ * bundle (i.e. an imported resource or a fragment/host resource). The
+ * following is a list of steps that a ClassLoaderDelegate must take
+ * when trying to find a resource. <p>
+ * <ul>
+ * <li>Try to find the list of resources using the System ClassLoader.
+ *
+ * <li>If the bundle is not a fragment then continue to the next step
+ * otherwise try to find the list of resources from the host bundle, if the
+ * resource is not found in the host then return null.
+ *
+ * <li>Try to find the list of resources from an imported package. If the
+ * resource is not found and it belongs to a package that has been imported
+ * then throw an <code>ImportResourceNotFoundException</code>. If the
+ * resource does not belong to an imported package then continue to the
+ * next step.
+ *
+ * <li>If the bundle is not a host to any fragments then continue to the
+ * next step. Try to find the list of resources as an imported resource
+ * from all of the fragment bundles for this host bundle. If the resource
+ * is not found and it belongs to a package that has been imported by a
+ * fragment then throw an <code>ImportResourceNotFoundException</code>.
+ *
+ * <li>Try to find the list of resources from the actual bundle.
+ *
+ * <li>If the bundle is a host to any fragments then try to find the list of
+ * resources from the fragment bundles.
+ *
+ * </ul>
+ * If no resource is found then return null.
+ * @param resource the resource to find.
+ * @return the enumeration of resource paths found or null if the resource
+ * does not exist.
+ * @throws ImportResourceNotFoundException if trying to import a resource from an
+ * imported package and the resource is not found.
+ */
+ public Enumeration findResources(String resource) throws ImportResourceNotFoundException, IOException;
+
+ /**
+ * Returns the absolute path name of a native library. The following is
+ * a list of steps that a ClassLoaderDelegate must take when trying to
+ * find a library:
+ * <ul>
+ * <li>If the bundle is a fragment then try to find the library in the
+ * host bundle.
+ * <li>if the bundle is a host then try to find the library in the
+ * host bundle and then try to find the library in the fragment
+ * bundles.
+ * </ul>
+ * If no library is found return null.
+ * @param libraryname the library to find the path to.
+ * @return the path to the library or null if not found.
+ */
+ public String findLibrary(String libraryname);
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/EventPublisher.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/EventPublisher.java
new file mode 100644
index 000000000..dcb8f83fa
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/EventPublisher.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * The EventPublisher is used by FrameworkAdaptors to publish events to the
+ * Framework.
+ */
+public interface EventPublisher
+{
+
+ /**
+ * Publish a FrameworkEvent.
+ *
+ * @param type FrameworkEvent type.
+ * @param bundle Bundle related to FrameworkEvent.
+ * @param throwable Related exception or <tt>null</tt>.
+ * @see org.osgi.framework.FrameworkEvent
+ */
+ public abstract void publishFrameworkEvent(int type, Bundle bundle, Throwable throwable);
+
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/FrameworkAdaptor.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/FrameworkAdaptor.java
new file mode 100644
index 000000000..cb0fbeae9
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/FrameworkAdaptor.java
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+import java.io.IOException;
+import java.net.URLConnection;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.State;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+/**
+ * FrameworkAdaptor interface to the osgi framework. This class is used to provide
+ * platform specific support for the osgi framework.
+ *
+ * <p>The OSGi framework will call this class to perform platform specific functions.
+ *
+ * Classes that implement FrameworkAdaptor MUST provide a constructor that takes as a
+ * parameter an array of Strings. This array will contain arguments to be
+ * handled by the FrameworkAdaptor. The FrameworkAdaptor implementation may define the format
+ * and content of its arguments.
+ *
+ * The constructor should parse the arguments passed to it and remember them.
+ * The initialize method should perform the actual processing of the adaptor
+ * arguments.
+ */
+
+public interface FrameworkAdaptor
+{
+
+ /**
+ * Initialize the FrameworkAdaptor object so that it is ready to be
+ * called by the framework. Handle the arguments that were
+ * passed to the constructor.
+ * This method must be called before any other FrameworkAdaptor methods.
+ * @param eventPublisher The EventPublisher used to publish any events to
+ * the framework.
+ */
+ public void initialize(EventPublisher eventPublisher);
+
+ /**
+ * Initialize the persistent storage for the adaptor.
+ *
+ * @throws IOException If the adaptor is unable to
+ * initialize the bundle storage.
+ */
+ public void initializeStorage() throws IOException;
+
+ /**
+ * Compact/cleanup the persistent storage for the adaptor.
+ * @throws ####
+ *
+ */
+ public void compactStorage() throws IOException;
+
+ /**
+ * Return the properties object for the adaptor.
+ * The properties in the returned object supplement
+ * the System properties.
+ * The framework may modify this object.
+ *
+ * @return The properties object for the adaptor.
+ */
+ public Properties getProperties();
+
+ /**
+ * Return a list of the installed bundles. Each element in the
+ * list must be of type <code>BundleData</code>. Each <code>BundleData</code>
+ * corrosponds to one bundle that is persitently stored.
+ * This method must construct <code>BundleData</code> objects for all
+ * installed bundles and return a Vector containing the objects.
+ * The returned Vector becomes the property of the framework.
+ *
+ * @return Vector of installed BundleData objects.
+ */
+ public Vector getInstalledBundles();
+
+ /**
+ * Map a location to a URLConnection.
+ *
+ * @param location of the bundle.
+ * @return URLConnection that represents the location.
+ * @throws BundleException if the mapping fails.
+ */
+ public URLConnection mapLocationToURLConnection(String location)
+ throws BundleException;
+
+ /**
+ * Prepare to install a bundle from a URLConnection.
+ * <p>To complete the install,
+ * begin and then commit
+ * must be called on the returned <code>BundleOperation</code> object.
+ * If either of these methods throw a BundleException
+ * or some other error occurs,
+ * then undo must be called on the <code>BundleOperation</code> object
+ * to undo the change to persistent storage.
+ *
+ * @param location Bundle location.
+ * @param source URLConnection from which the bundle may be read.
+ * Any InputStreams returned from the source
+ * (URLConnections.getInputStream) must be closed by the
+ * <code>BundleOperation</code> object.
+ * @return BundleOperation object to be used to complete the install.
+ * @throws BundleException if the install preparation fails.
+ */
+ public BundleOperation installBundle(String location, URLConnection source);
+
+ /**
+ * Prepare to update a bundle from a URLConnection.
+ * <p>To complete the update
+ * begin and then commit
+ * must be called on the returned <code>BundleOperation</code> object.
+ * If either of these methods throw a BundleException
+ * or some other error occurs,
+ * then undo must be called on the <code>BundleOperation</code> object
+ * to undo the change to persistent storage.
+ *
+ * @param bundledata BundleData to update.
+ * @param source URLConnection from which the updated bundle may be read.
+ * Any InputStreams returned from the source
+ * (URLConnections.getInputStream) must be closed by the
+ * <code>BundleOperation</code> object.
+ * @return BundleOperation object to be used to complete the update.
+ * @throws BundleException if the update preparation fails.
+ */
+ public BundleOperation updateBundle(BundleData bundledata, URLConnection source);
+
+ /**
+ * Prepare to uninstall a bundle.
+ * <p>To complete the uninstall,
+ * begin and then commit
+ * must be called on the returned <code>BundleOperation</code> object.
+ * If either of these methods throw a BundleException
+ * or some other error occurs,
+ * then undo must be called on the <code>BundleOperation</code> object
+ * to undo the change to persistent storage.
+ *
+ * @param bundledata BundleData to uninstall.
+ * @return BundleOperation object to be used to complete the uninstall.
+ */
+ public BundleOperation uninstallBundle(BundleData bundledata);
+
+ /**
+ * Returns the total amount of free space available for bundle storage on the device.
+ *
+ * @return Free space available in bytes or -1 if it does not apply to this adaptor
+ * @exception IOException if an I/O error occurs determining the available space
+ */
+ public long getTotalFreeSpace() throws IOException;
+
+ /**
+ * Returns the PermissionStorage object which will be used to
+ * to manage the permission data.
+ *
+ * @return The PermissionStorage object for the adaptor.
+ * @see "org.osgi.service.permissionadmin.PermissionAdmin"
+ */
+ public PermissionStorage getPermissionStorage() throws IOException;
+
+ /**
+ * Returns the <code>ServiceRegistry</code> object which will be used
+ * to manage ServiceReference bindings.
+ * @return The ServiceRegistry object for the adaptor.
+ */
+ public ServiceRegistry getServiceRegistry();
+
+ /**
+ * The framework uses this value when creating Vectors in data structures
+ * that have long lifespans. <p>
+ * This value specifies amount by which the capacity of a vector is
+ * automatically incremented when its size becomes greater than its
+ * capacity.
+ * @return The Vector capacity increment.
+ */
+ public int getVectorCapacityIncrement();
+
+ /**
+ * The framework uses this value when creating Vectors in data structures
+ * that have long lifespans. <p>
+ * This value specifies the initial capacity of a vector
+ * @return The Vector initial capacity.
+ */
+ public int getVectorInitialCapacity();
+
+ /**
+ * The framework uses this value when creating Hashtables in data structures
+ * that have long lifespans. <p>
+ * This value specifies the initial capacity of a Hashtable
+ * @return The Hashtable initial capacity.
+ */
+ public int getHashtableInitialCapacity();
+
+ /**
+ * The framework uses this value when creating Hashtables in data structures
+ * that have long lifespans. <p>
+ * This value specifies how full the hash table is allowed to get before
+ * its capacity is automatically increased.
+ * @return The Hashtable load factor.
+ */
+ public float getHashtableLoadFactor();
+
+ /**
+ * The framework will call the frameworkStart(BundleContext) method after the
+ * System BundleActivator.start(BundleContext) has been called. The context is
+ * the System Bundle's BundleContext. This method allows FrameworkAdaptors to
+ * have access to the OSGi framework to get services, register services and
+ * perform other OSGi operations.
+ * @param context The System Bundle's BundleContext.
+ * @exception BundleException on any error that may occur.
+ */
+ public void frameworkStart(BundleContext context) throws BundleException;
+
+ /**
+ * The framework will call the frameworkStop(BundleContext) method after the
+ * System BundleActivator.stop(BundleContext) has been called. The context is
+ * the System Bundle's BundleContext. This method allows FrameworkAdaptors to
+ * have access to the OSGi framework to get services, register services and
+ * perform other OSGi operations.
+ * @param context The System Bundle's BundleContext.
+ * @exception BundleException on any error that may occur.
+ */
+ public void frameworkStop(BundleContext context)throws BundleException;
+
+ /**
+ * Gets the value for Export-Package for packages that a FrameworkAdaptor is exporting
+ * to the framework. The String returned will be parsed by the framework
+ * and the packages specified will be exported by the System Bundle.
+ * @return The value for Export-Package that the System Bundle will export or
+ * null if none exist.
+ */
+ public String getExportPackages();
+
+ /**
+ * Gets any Service class names that a FrameworkAdaptor is exporting to the
+ * framework. The class names returned will be exported by the System Bundle.
+ * @return The value of Export-Service that the System Bundle will export or
+ * null if none exist
+ */
+ public String getExportServices();
+
+ /**
+ * Returns the initial bundle start level as maintained by this adaptor
+ * @return the initial bundle start level
+ */
+ public int getInitialBundleStartLevel();
+
+ /**
+ * Sets the initial bundle start level
+ * @param value the initial bundle start level
+ */
+ public void setInitialBundleStartLevel(int value);
+
+ public IBundleStats getBundleStats();
+ public PlatformAdmin getPlatformAdmin();
+ public State getState();
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/IBundleStats.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/IBundleStats.java
new file mode 100644
index 000000000..c7fdcc7be
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/IBundleStats.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.adaptor;
+
+/**
+ * Contains information about activated bundles and acts as the main
+ * entry point for logging plugin activity.
+ */
+
+public interface IBundleStats {
+ public void startActivation(String bundle);
+ public void endActivation(String bundle);
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ImportClassNotFoundException.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ImportClassNotFoundException.java
new file mode 100644
index 000000000..8b479765a
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ImportClassNotFoundException.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+/**
+ * Exception class to denote that the class is in an imported package
+ * but the class was not found! This is thrown by ImportClassLoader
+ * and caught by BundleClassLoader in the loadClass method after
+ * calling parent.loadClass().
+ *
+ */
+public class ImportClassNotFoundException extends ClassNotFoundException
+{
+ /**
+ * Constructor with no detail message
+ */
+ public ImportClassNotFoundException()
+ {
+ super();
+ }
+
+ /**
+ * Constructor with detail message
+ *
+ * @param s the detail message.
+ */
+ public ImportClassNotFoundException(String s)
+ {
+ super(s);
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ImportResourceNotFoundException.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ImportResourceNotFoundException.java
new file mode 100644
index 000000000..82f38ff11
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ImportResourceNotFoundException.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+/**
+ * Exception class to denote that the resource is in an imported package
+ * but the resource was not found! This is thrown by ImportClassLoader
+ * and caught by BundleClassLoader in the getResource* methods after
+ * calling parent.getResource*().
+ *
+ */
+public class ImportResourceNotFoundException extends RuntimeException
+{
+ /**
+ * Constructor with no detail message
+ */
+ public ImportResourceNotFoundException()
+ {
+ super();
+ }
+
+ /**
+ * Constructor with detail message
+ *
+ * @param s the detail message.
+ */
+ public ImportResourceNotFoundException(String s)
+ {
+ super(s);
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/PermissionStorage.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/PermissionStorage.java
new file mode 100644
index 000000000..fd6f5a23a
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/PermissionStorage.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+import java.io.IOException;
+
+/**
+ * Permission Storage interface for managing a persistent storage of
+ * bundle permissions.
+ *
+ * <p>This class is used to provide methods to manage
+ * persistent storage of bundle permissions. The PermissionStorage object
+ * is returned by the FrameworkAdaptor object and is called
+ * to persistently store bundle permissions.
+ *
+ * <p>The permission data will typically take the form of encoded
+ * <tt>PermissionInfo</tt> Strings.
+ * See org.osgi.service.permissionadmin.PermissionInfo.
+ *
+ * <p>For example
+ * <pre>
+ * PermissionStorage storage = adaptor.getPermissionStorage();
+ * try {
+ * storage.setPermissionData(location, permissions);
+ * } catch (IOException e) {
+ * // Take some error action.
+ * }
+ * </pre>
+ *
+ */
+public interface PermissionStorage
+{
+ /**
+ * Returns the locations that have permission data assigned to them,
+ * that is, locations for which permission data
+ * exists in persistent storage.
+ *
+ * @return The locations that have permission data in
+ * persistent storage, or <tt>null</tt> if there is no permission data
+ * in persistent storage.
+ * @throws IOException If a failure occurs accessing peristent storage.
+ */
+ public abstract String[] getLocations() throws IOException;
+
+ /**
+ * Gets the permission data assigned to the specified
+ * location.
+ *
+ * @param location The location whose permission data is to
+ * be returned.
+ * The location can be <tt>null</tt> for the default permission data.
+ *
+ * @return The permission data assigned to the specified
+ * location, or <tt>null</tt> if that location has not been assigned any
+ * permission data.
+ * @throws IOException If a failure occurs accessing peristent storage.
+ */
+ public abstract String[] getPermissionData(String location) throws IOException;
+
+ /**
+ * Assigns the specified permission data to the specified
+ * location.
+ *
+ * @param location The location that will be assigned the
+ * permissions.
+ * The location can be <tt>null</tt> for the default permission data.
+ * @param data The permission data to be assigned, or <tt>null</tt>
+ * if the specified location is to be removed from persistent storaqe.
+ * @throws IOException If a failure occurs modifying peristent storage.
+ */
+ public abstract void setPermissionData(String location, String[] data) throws IOException;
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ServiceRegistry.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ServiceRegistry.java
new file mode 100644
index 000000000..c7b4a8fb2
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/ServiceRegistry.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor;
+
+import java.util.Vector;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * The ServiceRegistry interface is used by the framework to store and
+ * lookup currently registered services.
+ */
+public interface ServiceRegistry {
+
+ /**
+ * Publishes a service to this ServiceRegistry.
+ * @param context the BundleContext that registered the service.
+ * @param serviceReg the ServiceRegistration to register.
+ */
+ public void publishService(BundleContext context, ServiceRegistration serviceReg);
+
+ /**
+ * Unpublishes a service from this ServiceRegistry
+ * @param context the BundleContext that registered the service.
+ * @param serviceReg the ServiceRegistration to unpublish.
+ */
+ public void unpublishService(BundleContext context, ServiceRegistration serviceReg);
+
+ /**
+ * Unpublishes all services from this ServiceRegistry that the
+ * specified BundleContext registered.
+ * @param context the BundleContext to unpublish all services for.
+ */
+ public void unpublishServices(BundleContext context);
+
+ /**
+ * Performs a lookup for ServiceReferences that are bound to this
+ * ServiceRegistry. If both clazz and filter are null then all bound
+ * ServiceReferences are returned.
+ * @param clazz A fully qualified class name. All ServiceReferences that
+ * reference an object that implement this class are returned. May be
+ * null.
+ * @param filter Used to match against published Services. All
+ * ServiceReferences that match the filter are returned. If a clazz is
+ * specified then all ServiceReferences that match the clazz and the
+ * filter parameter are returned. May be null.
+ * @return A Vector of all matching ServiceReferences or null
+ * if none exist.
+ */
+ public Vector lookupServiceReferences(String clazz, Filter filter);
+
+ /**
+ * Performs a lookup for ServiceReferences that are bound to this
+ * ServiceRegistry using the specified BundleContext.
+ * @param context The BundleContext to lookup the ServiceReferences on.
+ * @return A Vector of all matching ServiceReferences or null if none
+ * exist.
+ */
+ public Vector lookupServiceReferences(BundleContext context);
+
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/Version.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/Version.java
new file mode 100644
index 000000000..bd7e4a4f6
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/Version.java
@@ -0,0 +1,521 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.adaptor;
+
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * <p>
+ * Version identifier for a plug-in. In its string representation,
+ * it consists of up to 4 tokens separated by a decimal point.
+ * The first 3 tokens are positive integer numbers, the last token
+ * is an uninterpreted string (no whitespace characters allowed).
+ * For example, the following are valid version identifiers
+ * (as strings):
+ * <ul>
+ * <li><code>0.0.0</code></li>
+ * <li><code>1.0.127564</code></li>
+ * <li><code>3.7.2.build-127J</code></li>
+ * <li><code>1.9</code> (interpreted as <code>1.9.0</code>)</li>
+ * <li><code>3</code> (interpreted as <code>3.0.0</code>)</li>
+ * </ul>
+ * </p>
+ * <p>
+ * The version identifier can be decomposed into a major, minor,
+ * service level component and qualifier components. A difference
+ * in the major component is interpreted as an incompatible version
+ * change. A difference in the minor (and not the major) component
+ * is interpreted as a compatible version change. The service
+ * level component is interpreted as a cumulative and compatible
+ * service update of the minor version component. The qualifier is
+ * not interpreted, other than in version comparisons. The
+ * qualifiers are compared using lexicographical string comparison.
+ * </p>
+ * <p>
+ * Version identifiers can be matched as perfectly equal, equivalent,
+ * compatible or greaterOrEqual.
+ * </p>
+ * <p>
+ * Clients may instantiate; not intended to be subclassed by clients.
+ * </p>
+ * @see IPluginDescriptor#getVersionIdentifier
+ * @see java.lang.String#compareTo
+ */
+public final class Version implements Comparable {
+
+ private int major = 0;
+ private int minor = 0;
+ private int service = 0;
+ private String qualifier = ""; //$NON-NLS-1$
+
+ private static final String SEPARATOR = "."; //$NON-NLS-1$
+
+ public static Version emptyVersion = new Version(0, 0, 0);
+
+ /**
+ * Creates a plug-in version identifier from a given Version.
+ *
+ * @param version
+ */
+ public Version(Version version) {
+ this(version.major, version.minor, version.service, version.qualifier);
+ }
+
+ /**
+ * Creates a plug-in version identifier from its components.
+ *
+ * @param major major component of the version identifier
+ * @param minor minor component of the version identifier
+ * @param service service update component of the version identifier
+ */
+ public Version(int major, int minor, int service) {
+ this(major, minor, service, null);
+ }
+ /**
+ * Creates a plug-in version identifier from its components.
+ *
+ * @param major major component of the version identifier
+ * @param minor minor component of the version identifier
+ * @param service service update component of the version identifier
+ * @param qualifier qualifier component of the version identifier.
+ * Qualifier characters that are not a letter or a digit are replaced.
+ */
+ public Version(int major, int minor, int service, String qualifier)
+ throws IllegalArgumentException {
+
+ // Do the test outside of the assert so that they 'Policy.bind'
+ // will not be evaluated each time (including cases when we would
+ // have passed by the assert).
+
+ if (major < 0)
+ throw new IllegalArgumentException("Negative major");
+ if (minor < 0)
+ throw new IllegalArgumentException("Negative minor");
+ if (service < 0)
+ throw new IllegalArgumentException("Negative service");
+ if (qualifier == null)
+ qualifier = ""; //$NON-NLS-1$
+
+ this.major = major;
+ this.minor = minor;
+ this.service = service;
+ this.qualifier = verifyQualifier(qualifier);
+ }
+ /**
+ * Creates a plug-in version identifier from the given string.
+ * The string represenation consists of up to 4 tokens
+ * separated by decimal point.
+ * For example, the following are valid version identifiers
+ * (as strings):
+ * <ul>
+ * <li><code>0.0.0</code></li>
+ * <li><code>1.0.127564</code></li>
+ * <li><code>3.7.2.build-127J</code></li>
+ * <li><code>1.9</code> (interpreted as <code>1.9.0</code>)</li>
+ * <li><code>3</code> (interpreted as <code>3.0.0</code>)</li>
+ * </ul>
+ * </p>
+ *
+ * @param versionId string representation of the version identifier.
+ * Qualifier characters that are not a letter or a digit are replaced.
+ */
+ public Version(String versionId) {
+ if (versionId==null) versionId = "0.0.0";
+ Object[] parts = parseVersion(versionId);
+ this.major = ((Integer) parts[0]).intValue();
+ this.minor = ((Integer) parts[1]).intValue();
+ this.service = ((Integer) parts[2]).intValue();
+ this.qualifier = (String) parts[3];
+ }
+ /**
+ * Validates the given string as a plug-in version identifier.
+ *
+ * @param version the string to validate
+ * @return a status object with code <code>IStatus.OK</code> if
+ * the given string is valid as a plug-in version identifier, otherwise a status
+ * object indicating what is wrong with the string
+ * @since 2.0
+ */
+ public static RuntimeException validateVersion(String version) {
+ try {
+ parseVersion(version);
+ } catch (RuntimeException e) {
+ return e;
+ // OG...
+ // return new Status(IStatus.ERROR, Platform.PI_RUNTIME, IStatus.ERROR, e.getMessage(), e);
+ }
+ return null;
+ // OG...
+ // return new Status(IStatus.OK, Platform.PI_RUNTIME, IStatus.OK, Policy.bind("ok"), null); //$NON-NLS-1$
+ }
+ private static Object[] parseVersion(String versionId) {
+
+ // Do the test outside of the assert so that they 'Policy.bind'
+ // will not be evaluated each time (including cases when we would
+ // have passed by the assert).
+ if (versionId == null)
+ throw new IllegalArgumentException("Null version string");
+
+ String s = versionId.trim();
+ if (s.equals("")) //$NON-NLS-1$
+ throw new IllegalArgumentException("Empty version string");
+ if (s.startsWith(SEPARATOR))
+ throw new IllegalArgumentException("Invalid version format");
+ if (s.endsWith(SEPARATOR))
+ throw new IllegalArgumentException("Invalid version format");
+ if (s.indexOf(SEPARATOR + SEPARATOR) != -1)
+ throw new IllegalArgumentException("Invalid version format");
+
+ StringTokenizer st = new StringTokenizer(s, SEPARATOR);
+ Vector elements = new Vector(4);
+
+ while (st.hasMoreTokens())
+ elements.addElement(st.nextToken());
+
+ int elementSize = elements.size();
+
+ if (elementSize <= 0)
+ throw new IllegalArgumentException("Invalid version format (no token)");
+ if (elementSize > 4)
+ throw new IllegalArgumentException("Invalid version format (more than 4 tokens)");
+
+ int[] numbers = new int[3];
+ try {
+ numbers[0] = Integer.parseInt((String) elements.elementAt(0));
+ if (numbers[0] < 0)
+ throw new IllegalArgumentException("Negative major");
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Invalid major");
+ }
+
+ try {
+ if (elementSize >= 2) {
+ numbers[1] = Integer.parseInt((String) elements.elementAt(1));
+ if (numbers[1] < 0)
+ throw new IllegalArgumentException("Negative minor");
+ } else
+ numbers[1] = 0;
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Invalid minor");
+ }
+
+ try {
+ if (elementSize >= 3) {
+ numbers[2] = Integer.parseInt((String) elements.elementAt(2));
+ if (numbers[2] < 0)
+ throw new IllegalArgumentException("Invalid service");
+ } else
+ numbers[2] = 0;
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Invalid service");
+ }
+
+ // "result" is a 4-element array with the major, minor, service, and qualifier
+ Object[] result = new Object[4];
+ result[0] = new Integer(numbers[0]);
+ result[1] = new Integer(numbers[1]);
+ result[2] = new Integer(numbers[2]);
+ if (elementSize >= 4)
+ result[3] = verifyQualifier((String) elements.elementAt(3));
+ else
+ result[3] = ""; //$NON-NLS-1$
+ return result;
+ }
+ /**
+ * Compare version identifiers for equality. Identifiers are
+ * equal if all of their components are equal.
+ *
+ * @param object an object to compare
+ * @return whehter or not the two objects are equal
+ */
+ public boolean equals(Object object) {
+ if (!(object instanceof Version))
+ return false;
+ Version v = (Version) object;
+ return v.getMajorComponent() == major
+ && v.getMinorComponent() == minor
+ && v.getServiceComponent() == service
+ && v.getQualifierComponent().equals(qualifier);
+ }
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return an integer which is a hash code value for this object.
+ */
+ public int hashCode() {
+ int code = major + minor + service; // R1.0 result
+ if (qualifier.equals("")) //$NON-NLS-1$
+ return code;
+ else
+ return code + qualifier.hashCode();
+ }
+ /**
+ * Returns the major (incompatible) component of this
+ * version identifier.
+ *
+ * @return the major version
+ */
+ public int getMajorComponent() {
+ return major;
+ }
+ /**
+ * Returns the minor (compatible) component of this
+ * version identifier.
+ *
+ * @return the minor version
+ */
+ public int getMinorComponent() {
+ return minor;
+ }
+ /**
+ * Returns the service level component of this
+ * version identifier.
+ *
+ * @return the service level
+ */
+ public int getServiceComponent() {
+ return service;
+ }
+ /**
+ * Returns the qualifier component of this
+ * version identifier.
+ *
+ * @return the qualifier
+ */
+ public String getQualifierComponent() {
+ return qualifier;
+ }
+ /**
+ * Compares two version identifiers to see if this one is
+ * greater than or equal to the argument.
+ * <p>
+ * A version identifier is considered to be greater than or equal
+ * if its major component is greater than the argument major
+ * component, or the major components are equal and its minor component
+ * is greater than the argument minor component, or the
+ * major and minor components are equal and its service component is
+ * greater than the argument service component, or the major, minor and
+ * service components are equal and the qualifier component is
+ * greated than the argument qualifier component (using lexicographic
+ * string comparison), or all components are equal.
+ * </p>
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is compatible with the given version identifier, and
+ * <code>false</code> otherwise
+ * @since 2.0
+ */
+ public boolean isGreaterOrEqualTo(Version id) {
+ if (id == null)
+ return false;
+ if (major > id.getMajorComponent())
+ return true;
+ if ((major == id.getMajorComponent()) && (minor > id.getMinorComponent()))
+ return true;
+ if ((major == id.getMajorComponent())
+ && (minor == id.getMinorComponent())
+ && (service > id.getServiceComponent()))
+ return true;
+ if ((major == id.getMajorComponent())
+ && (minor == id.getMinorComponent())
+ && (service == id.getServiceComponent())
+ && (qualifier.compareTo(id.getQualifierComponent()) >= 0))
+ return true;
+ else
+ return false;
+ }
+ /**
+ * Compares two version identifiers for compatibility.
+ * <p>
+ * A version identifier is considered to be compatible if its major
+ * component equals to the argument major component, and its minor component
+ * is greater than or equal to the argument minor component.
+ * If the minor components are equal, than the service level of the
+ * version identifier must be greater than or equal to the service level
+ * of the argument identifier. If the service levels are equal, the two
+ * version identifiers are considered to be equivalent if this qualifier is
+ * greated or equal to the qualifier of the argument (using lexicographic
+ * string comparison).
+ * </p>
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is compatible with the given version identifier, and
+ * <code>false</code> otherwise
+ */
+ public boolean isCompatibleWith(Version id) {
+ if (id == null)
+ return false;
+ if (major != id.getMajorComponent())
+ return false;
+ if (minor > id.getMinorComponent())
+ return true;
+ if (minor < id.getMinorComponent())
+ return false;
+ if (service > id.getServiceComponent())
+ return true;
+ if (service < id.getServiceComponent())
+ return false;
+ if (qualifier.compareTo(id.getQualifierComponent()) >= 0)
+ return true;
+ else
+ return false;
+ }
+ /**
+ * Compares two version identifiers for equivalency.
+ * <p>
+ * Two version identifiers are considered to be equivalent if their major
+ * and minor component equal and are at least at the same service level
+ * as the argument. If the service levels are equal, the two version
+ * identifiers are considered to be equivalent if this qualifier is
+ * greated or equal to the qualifier of the argument (using lexicographic
+ * string comparison).
+ *
+ * </p>
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is equivalent to the given version identifier, and
+ * <code>false</code> otherwise
+ */
+ public boolean isEquivalentTo(Version id) {
+ if (id == null)
+ return false;
+ if (major != id.getMajorComponent())
+ return false;
+ if (minor != id.getMinorComponent())
+ return false;
+ if (service > id.getServiceComponent())
+ return true;
+ if (service < id.getServiceComponent())
+ return false;
+ if (qualifier.compareTo(id.getQualifierComponent()) >= 0)
+ return true;
+ else
+ return false;
+ }
+ /**
+ * Compares two version identifiers for perfect equality.
+ * <p>
+ * Two version identifiers are considered to be perfectly equal if their
+ * major, minor, service and qualifier components are equal
+ * </p>
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is perfectly equal to the given version identifier, and
+ * <code>false</code> otherwise
+ * @since 2.0
+ */
+ public boolean isPerfect(Version id) {
+ if (id == null)
+ return false;
+ if ((major != id.getMajorComponent())
+ || (minor != id.getMinorComponent())
+ || (service != id.getServiceComponent())
+ || (!qualifier.equals(id.getQualifierComponent())))
+ return false;
+ else
+ return true;
+ }
+ /**
+ * Compares two version identifiers for order using multi-decimal
+ * comparison.
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is greater than the given version identifier, and
+ * <code>false</code> otherwise
+ */
+ public boolean isGreaterThan(Version id) {
+
+ if (id == null) {
+ if (major == 0 && minor == 0 && service == 0 && qualifier.equals(""))
+ return false; //$NON-NLS-1$
+ else
+ return true;
+ }
+
+ if (major > id.getMajorComponent())
+ return true;
+ if (major < id.getMajorComponent())
+ return false;
+ if (minor > id.getMinorComponent())
+ return true;
+ if (minor < id.getMinorComponent())
+ return false;
+ if (service > id.getServiceComponent())
+ return true;
+ if (service < id.getServiceComponent())
+ return false;
+ if (qualifier.compareTo(id.getQualifierComponent()) > 0)
+ return true;
+ else
+ return false;
+
+ }
+ /**
+ * Returns the string representation of this version identifier.
+ * The result satisfies
+ * <code>vi.equals(new PluginVersionIdentifier(vi.toString()))</code>.
+ *
+ * @return the string representation of this plug-in version identifier
+ */
+ public String toString() {
+ String base = major + SEPARATOR + minor + SEPARATOR + service;
+ // R1.0 result
+ if (qualifier.equals("")) //$NON-NLS-1$
+ return base;
+ else
+ return base + SEPARATOR + qualifier;
+ }
+
+ private static String verifyQualifier(String s) {
+ char[] chars = s.trim().toCharArray();
+ boolean whitespace = false;
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (!(Character.isLetter(c) || Character.isDigit(c))) {
+ chars[i] = '-';
+ whitespace = true;
+ }
+ }
+ return whitespace ? new String(chars) : s;
+ }
+
+ /**
+ * Compares this Version object with the specified Version object for order.
+ * Returns a negative integer, zero, or a positive integer as this object is less
+ * than, equal to, or greater than the specified object.<p>
+ *
+ * @param obj the Version object to be compared.
+ * @return a negative integer, zero, or a positive integer as this object
+ * is less than, equal to, or greater than the specified Version object.
+ *
+ * @throws ClassCastException if the specified object's type
+ * is not Version.
+ */
+ public int compareTo(Object o) {
+ if (!(o instanceof Version))
+ throw new ClassCastException();
+
+ Version version = (Version) o;
+ if (equals(o))
+ return 0;
+
+ if (isGreaterThan((Version) o))
+ return 1;
+
+ return -1;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/AbstractBundleData.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/AbstractBundleData.java
new file mode 100644
index 000000000..0cc98ffab
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/AbstractBundleData.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor.core;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Dictionary;
+
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.Msg;
+import org.eclipse.osgi.framework.util.Headers;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+/**
+ * An abstract BundleData class that has default implementations that most
+ * BundleData implementations can use.
+ */
+public abstract class AbstractBundleData implements BundleData {
+
+ /**
+ * The BundleManfifest for this BundleData.
+ */
+ protected Dictionary manifest = null;
+
+ /**
+ * The Bundle object for this BundleData.
+ */
+ protected Bundle bundle;
+
+ /**
+ * Return the BundleManifest for the BundleData. If the manifest
+ * field is null this method will parse the bundle manifest file and
+ * construct a BundleManifest file to return. If the manifest field is
+ * not null then the manifest object is returned.
+ * @return BundleManifest for the BundleData.
+ * @throws BundleException if an error occurred while reading the
+ * bundle manifest data.
+ */
+ public Dictionary getManifest() throws BundleException {
+ if (manifest == null) {
+ synchronized (this) {
+ // make sure the manifest is still null after we have aquired the lock.
+ if (manifest == null) {
+ URL url = getEntry(Constants.OSGI_BUNDLE_MANIFEST);
+ if (url == null){
+ throw new BundleException(Msg.formatter.getString("MANIFEST_NOT_FOUND_EXCEPTION",Constants.OSGI_BUNDLE_MANIFEST,getLocation()));
+ }
+ try {
+ manifest = Headers.parseManifest(url.openStream());
+ } catch (IOException e) {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_NOT_FOUND_EXCEPTION",Constants.OSGI_BUNDLE_MANIFEST,getLocation()), e);
+ }
+ }
+ }
+ }
+ return manifest;
+ }
+
+ /**
+ * Returns a Dictionary of all manifest headers. Returns the
+ * BundleManifest object of this BundleData. Note that the core
+ * implementation of BundleManifest extends the Dictionary class.
+ * @return A Dictionary of all manifest headers.
+ */
+ public Dictionary getHeaders(){
+ try {
+ return getManifest();
+ } catch (BundleException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the Bundle object for this BundleData.
+ * @param bundle The Bundle Object for this BundleData.
+ */
+ public void setBundle(Bundle bundle){
+ this.bundle = bundle;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/AbstractFrameworkAdaptor.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/AbstractFrameworkAdaptor.java
new file mode 100644
index 000000000..90d7a1928
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/AbstractFrameworkAdaptor.java
@@ -0,0 +1,264 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor.core;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Properties;
+import org.eclipse.osgi.framework.adaptor.EventPublisher;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.Msg;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+/**
+ * An abstract FrameworkAdaptor class that has default implementations that most
+ * FrameworkAdaptor implementations can use.
+ */
+public abstract class AbstractFrameworkAdaptor implements FrameworkAdaptor {
+
+ protected EventPublisher eventPublisher;
+
+ /**
+ * The ServiceRegistry object for this FrameworkAdaptor.
+ */
+ protected ServiceRegistry serviceRegistry;
+
+ /**
+ * The Properties object for this FrameworkAdaptor
+ */
+ protected Properties properties;
+
+ /**
+ * The System Bundle's BundleContext.
+ */
+ protected BundleContext context;
+
+ /**
+ * The Vector Initial Capacity value.
+ */
+ protected int vic;
+
+ /**
+ * The Vector Capacity Increment value.
+ */
+ protected int vci;
+
+ /**
+ * The Hashtable Initial Capacity value.
+ */
+ protected int hic;
+
+ /**
+ * The Hashtable Load Factor value.
+ */
+ protected float hlf;
+
+ /**
+ * The initial bundle start level.
+ */
+ protected int initialBundleStartLevel = 1;
+ /**
+ * Initializes the ServiceRegistry, loads the properties for this
+ * FrameworkAdaptor and initializes all the Vector and Hashtable capacity,
+ * increment and factor values.
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#initialize()
+ */
+ public void initialize(EventPublisher eventPublisher) {
+ this.eventPublisher = eventPublisher;
+ serviceRegistry = new ServiceRegistry();
+ serviceRegistry.initialize();
+ loadProperties();
+ vic = 10;
+ vci = 10;
+ hic = 10;
+ hlf = 0.75f;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getProperties()
+ */
+ public Properties getProperties() {
+ return properties;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#mapLocationToURLConnection(String)
+ */
+ public URLConnection mapLocationToURLConnection(String location) throws BundleException {
+ try {
+ return (new URL(location).openConnection());
+ } catch (IOException e) {
+ throw new BundleException(Msg.formatter.getString("ADAPTOR_URL_CREATE_EXCEPTION", location), e);
+ }
+ }
+
+ /**
+ * Always returns -1 to indicate that this operation is not supported by this
+ * FrameworkAdaptor. Extending classes should override this method if
+ * they support this operation.
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getTotalFreeSpace()
+ */
+ public long getTotalFreeSpace() throws IOException {
+ return -1;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getServiceRegistry()
+ */
+ public org.eclipse.osgi.framework.adaptor.ServiceRegistry getServiceRegistry() {
+ return serviceRegistry;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getVectorInitialCapacity()
+ */
+ public int getVectorInitialCapacity() {
+ return vic;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getVectorCapacityIncrement()
+ */
+ public int getVectorCapacityIncrement(){
+ return vci;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getHashtableInitialCapacity()
+ */
+ public int getHashtableInitialCapacity() {
+ return hic;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getHashtableLoadFactor()
+ */
+ public float getHashtableLoadFactor() {
+ return hlf;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#frameworkStart(org.osgi.framework.BundleContext)
+ */
+ public void frameworkStart(BundleContext context) throws BundleException
+ {
+ this.context = context;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#frameworkStop(org.osgi.framework.BundleContext)
+ */
+ public void frameworkStop(BundleContext context) throws BundleException
+ {
+ this.context = null;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getExportPackages()
+ */
+ public String getExportPackages()
+ {
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getExportServices()
+ */
+ public String getExportServices()
+ {
+ return null;
+ }
+
+ /**
+ * Returns the EventPublisher for this FrameworkAdaptor.
+ * @return The EventPublisher.
+ */
+ public EventPublisher getEventPublisher(){
+ return eventPublisher;
+ }
+
+ /**
+ * This method locates and reads the osgi.properties file.
+ * If the system property <i>org.eclipse.osgi.framework.internal.core.properties</i> is specifed, its value
+ * will be used as the name of the file instead of
+ * <tt>osgi.properties</tt>. There are 3 places to look for these properties. These
+ * 3 places are searched in the following order, stopping when the properties are found.
+ *
+ * <ol>
+ * <li>Look for a file in the file system
+ * <li>Look for a resource in the FrameworkAdaptor's package
+ * </ol>
+ */
+ protected void loadProperties() {
+ properties = new Properties();
+
+ String resource = System.getProperty(Constants.KEY_OSGI_PROPERTIES, Constants.DEFAULT_OSGI_PROPERTIES);
+
+ try
+ {
+ InputStream in = null;
+ File file = new File(resource);
+ if (file.exists())
+ {
+ in = new FileInputStream(file);
+ }
+
+ if (in == null)
+ {
+ in = getClass().getResourceAsStream(resource);
+ }
+
+
+ if (in != null)
+ {
+ try
+ {
+ properties.load(new BufferedInputStream(in));
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+ }
+ else
+ {
+ Debug.println("Unable to find osgi.properties");
+ }
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to load osgi.properties: " + e.getMessage());
+ }
+ }
+ }
+
+ public int getInitialBundleStartLevel() {
+ return initialBundleStartLevel;
+ }
+
+ public void setInitialBundleStartLevel(int value) {
+ initialBundleStartLevel = value;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/ServiceRegistry.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/ServiceRegistry.java
new file mode 100644
index 000000000..ce3d79066
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/core/ServiceRegistry.java
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.adaptor.core;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * A default implementation of the ServiceRegistry.
+ */
+public class ServiceRegistry implements org.eclipse.osgi.framework.adaptor.ServiceRegistry {
+
+ /** Published services by class name. Key is a String class name; Value is a Vector of ServiceRegistrations */
+ protected Hashtable publishedServicesByClass;
+ /** All published services. Value is ServiceRegistrations */
+ protected Vector allPublishedServices;
+ /** Published services by BundleContext. Key is a BundleContext; Value is a Vector of ServiceRegistrations*/
+ protected Hashtable publishedServicesByContext;
+
+ /**
+ * Initializes the internal data structures of this ServiceRegistry.
+ *
+ */
+ public void initialize(){
+ publishedServicesByClass = new Hashtable(53);
+ publishedServicesByContext = new Hashtable(53);
+ allPublishedServices = new Vector(50, 20);
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.ServiceRegistry#publishService(BundleContext, ServiceRegistration)
+ */
+ public void publishService(BundleContext context, ServiceRegistration serviceReg) {
+
+ // Add the ServiceRegistration to the list of Services published by BundleContext.
+ Vector contextServices = (Vector) publishedServicesByContext.get(context);
+ if (contextServices == null) {
+ contextServices = new Vector(10, 10);
+ publishedServicesByContext.put(context, contextServices);
+ }
+ contextServices.addElement(serviceReg);
+
+ // Add the ServiceRegistration to the list of Services published by Class Name.
+ String[] clazzes = (String[]) serviceReg.getReference().getProperty(Constants.OBJECTCLASS);
+ int size = clazzes.length;
+
+ for (int i = 0; i < size; i++) {
+ String clazz = clazzes[i];
+
+ Vector services = (Vector) publishedServicesByClass.get(clazz);
+
+ if (services == null) {
+ services = new Vector(10, 10);
+ publishedServicesByClass.put(clazz, services);
+ }
+
+ services.addElement(serviceReg);
+ }
+
+ // Add the ServiceRegistration to the list of all published Services.
+ allPublishedServices.addElement(serviceReg);
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.ServiceRegistry#unpublishService(BundleContext, ServiceRegistration)
+ */
+ public void unpublishService(BundleContext context, ServiceRegistration serviceReg) {
+
+ // Remove the ServiceRegistration from the list of Services published by BundleContext.
+ Vector contextServices = (Vector) publishedServicesByContext.get(context);
+ if (contextServices != null) {
+ contextServices.removeElement(serviceReg);
+ }
+
+ // Remove the ServiceRegistration from the list of Services published by Class Name.
+ String[] clazzes = (String[]) serviceReg.getReference().getProperty(Constants.OBJECTCLASS);
+ int size = clazzes.length;
+
+ for (int i = 0; i < size; i++) {
+ String clazz = clazzes[i];
+ Vector services = (Vector) publishedServicesByClass.get(clazz);
+ services.removeElement(serviceReg);
+ }
+
+ // Remove the ServiceRegistration from the list of all published Services.
+ allPublishedServices.removeElement(serviceReg);
+
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.ServiceRegistry#unpublishServices(BundleContext)
+ */
+ public void unpublishServices(BundleContext context) {
+ // Get all the Services published by the BundleContext.
+ Vector serviceRegs = (Vector) publishedServicesByContext.get(context);
+ if (serviceRegs != null) {
+ // Remove this list for the BundleContext
+ publishedServicesByContext.remove(context);
+ int size = serviceRegs.size();
+ for (int i = 0; i < size; i++) {
+ ServiceRegistration serviceReg = (ServiceRegistration) serviceRegs.elementAt(i);
+ // Remove each service from the list of all published Services
+ allPublishedServices.removeElement(serviceReg);
+
+ // Remove each service from the list of Services published by Class Name.
+ String[] clazzes = (String[]) serviceReg.getReference().getProperty(Constants.OBJECTCLASS);
+ int numclazzes = clazzes.length;
+
+ for (int j = 0; j < numclazzes; j++) {
+ String clazz = clazzes[j];
+ Vector services = (Vector) publishedServicesByClass.get(clazz);
+ services.removeElement(serviceReg);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.ServiceRegistry#lookupServiceReferences(String, Filter)
+ */
+ public Vector lookupServiceReferences(String clazz, Filter filter) {
+ int size;
+ Vector references = new Vector();
+
+ if (clazz == null) /* all services */ {
+ Vector serviceRegs = allPublishedServices;
+
+ if (serviceRegs == null) {
+ return (null);
+ }
+
+ size = serviceRegs.size();
+
+ if (size == 0) {
+ return (null);
+ }
+
+ for (int i = 0; i < size; i++) {
+ ServiceRegistration registration = (ServiceRegistration) serviceRegs.elementAt(i);
+
+ ServiceReference reference = registration.getReference();
+ if ((filter == null) || filter.match(reference)) {
+ references.addElement(reference);
+ }
+ }
+ } else /* services registered under the class name */ {
+ Vector serviceRegs = (Vector) publishedServicesByClass.get(clazz);
+
+ if (serviceRegs == null) {
+ return (null);
+ }
+
+ size = serviceRegs.size();
+
+ if (size == 0) {
+ return (null);
+ }
+
+ for (int i = 0; i < size; i++) {
+ ServiceRegistration registration = (ServiceRegistration) serviceRegs.elementAt(i);
+
+ ServiceReference reference = registration.getReference();
+ if ((filter == null) || filter.match(reference)) {
+ references.addElement(reference);
+ }
+ }
+ }
+
+ if (references.size() == 0) {
+ return null;
+ }
+
+ return (references);
+
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.ServiceRegistry#lookupServiceReferences(BundleContext)
+ */
+ public Vector lookupServiceReferences(BundleContext context){
+ int size;
+ Vector references = new Vector();
+ Vector serviceRegs = (Vector) publishedServicesByContext.get(context);
+
+ if (serviceRegs == null) {
+ return (null);
+ }
+
+ size = serviceRegs.size();
+
+ if (size == 0) {
+ return (null);
+ }
+
+ for (int i = 0; i < size; i++) {
+ ServiceRegistration registration = (ServiceRegistration) serviceRegs.elementAt(i);
+
+ ServiceReference reference = registration.getReference();
+ references.addElement(reference);
+ }
+
+ if (references.size() == 0) {
+ return null;
+ }
+
+ return (references);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/Headers.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/Headers.java
new file mode 100644
index 000000000..1b57cb243
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/Headers.java
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.eclipse.osgi.framework.internal.core.Msg;
+import org.osgi.framework.BundleException;
+
+/**
+ * Headers classes. This class implements a Dictionary that has
+ * the following behaviour:
+ * <ul>
+ * <li>put and remove clear throw UnsupportedOperationException.
+ * The Dictionary is thus read-only to others.
+ * <li>The String keys in the Dictionary are case-preserved,
+ * but the get operation is case-insensitive.
+ * </ul>
+ */
+public class Headers extends Dictionary
+{
+ /**
+ * Dictionary of keys: Lower case key => Case preserved key.
+ */
+ protected Dictionary headers;
+
+ /**
+ * Dictionary of values: Case preserved key => value.
+ */
+ protected Dictionary values;
+
+ /**
+ * Create an empty Headers dictionary.
+ *
+ * @param initialCapacity The initial capacity of this Headers object.
+ */
+ public Headers(int initialCapacity)
+ {
+ super();
+
+ headers = new Hashtable(initialCapacity);
+ values = new Hashtable(initialCapacity);
+ }
+
+ /**
+ * Create a Headers dictionary from a Dictionary.
+ *
+ * @param values The initial dictionary for this Headers object.
+ * @exception IllegalArgumentException If a case-variant of the key is
+ * in the dictionary parameter.
+ */
+ public Headers(Dictionary values)
+ {
+ super();
+
+ headers = new Hashtable(values.size());
+ this.values = values;
+
+ /* initialize headers dictionary */
+ Enumeration enum = values.keys();
+
+ while (enum.hasMoreElements())
+ {
+ Object key = enum.nextElement();
+
+ if (key instanceof String)
+ {
+ String header = ((String)key).toLowerCase();
+
+ if (headers.put(header, key) != null) /* if case-variant already present */
+ {
+ throw new IllegalArgumentException(Msg.formatter.getString("HEADER_DUPLICATE_KEY_EXCEPTION", header));
+ }
+ }
+ }
+ }
+
+ /**
+ * Case-preserved keys.
+ */
+ public synchronized Enumeration keys()
+ {
+ return(values.keys());
+ }
+
+ /**
+ * Values.
+ */
+ public synchronized Enumeration elements()
+ {
+ return(values.elements());
+ }
+
+ /**
+ * Support case-insensitivity for keys.
+ *
+ * @param key name.
+ */
+ public synchronized Object get(Object key)
+ {
+ Object value = values.get(key);
+
+ if ((value == null) && (key instanceof String))
+ {
+ key = headers.get(((String)key).toLowerCase());
+
+ if (key != null)
+ {
+ value = values.get(key);
+ }
+ }
+
+ return(value);
+ }
+
+ /**
+ * Set a header value.
+ *
+ * @param key Key name.
+ * @param value Value of the key or null to remove key.
+ * @return the previous value to which the key was mapped,
+ * or null if the key did not have a previous mapping.
+ *
+ * @exception IllegalArgumentException If a case-variant of the key is
+ * already present.
+ */
+ public synchronized Object set(Object key, Object value)
+ {
+ String header = (key instanceof String) ? ((String)key).toLowerCase() : null;
+
+ if (value == null) /* remove */
+ {
+ if (header != null) /* String key */
+ {
+ key = headers.remove(header);
+
+ if (key != null) /* is String key in hashtable? */
+ {
+ value = values.remove(key);
+ }
+ }
+ else /* non-String key */
+ {
+ value = values.remove(key);
+ }
+
+ return(value);
+ }
+ else /* put */
+ {
+ if (header != null) /* String key */
+ {
+ Object oldKey = headers.put(header, key);
+
+ if ((oldKey != null) && !header.equals(oldKey)) /* if case-variant already present */
+ {
+ headers.put(header, oldKey); /* put old case-variant back */
+
+ throw new IllegalArgumentException(Msg.formatter.getString("HEADER_DUPLICATE_KEY_EXCEPTION", header));
+ }
+
+ }
+
+ return(values.put(key, value));
+ }
+
+ }
+
+ /**
+ * Returns the number of entries (distinct keys) in this dictionary.
+ *
+ * @return the number of keys in this dictionary.
+ */
+ public synchronized int size()
+ {
+ return(values.size());
+ }
+
+ /**
+ * Tests if this dictionary maps no keys to value. The general contract
+ * for the <tt>isEmpty</tt> method is that the result is true if and only
+ * if this dictionary contains no entries.
+ *
+ * @return <code>true</code> if this dictionary maps no keys to values;
+ * <code>false</code> otherwise.
+ */
+ public synchronized boolean isEmpty()
+ {
+ return(values.isEmpty());
+ }
+
+ /**
+ * Always throws UnsupportedOperationException.
+ *
+ * @param key header name.
+ * @param value header value.
+ * @throws UnsupportedOperationException.
+ */
+ public Object put(Object key, Object value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Always throws UnsupportedOperationException.
+ *
+ * @param key header name.
+ * @throws UnsupportedOperationException.
+ */
+ public Object remove(Object key)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String toString()
+ {
+ return(values.toString());
+ }
+
+ public static Headers parseManifest(InputStream in) throws BundleException {
+ try
+ {
+ Headers headers = new Headers(10);
+ BufferedReader br;
+ try
+ {
+ br = new BufferedReader(new InputStreamReader(in, "UTF8"));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ br = new BufferedReader(new InputStreamReader(in));
+ }
+
+ String header = null;
+ StringBuffer value = new StringBuffer(256);
+ boolean firstLine = true;
+
+ while (true)
+ {
+ String line = br.readLine();
+ /* The java.util.jar classes in JDK 1.3 use the value of the last
+ * encountered manifest header. So we do the same to emulate
+ * this behavior. We no longer throw a BundleException
+ * for duplicate manifest headers.
+ */
+
+ if ((line == null) || (line.length() == 0)) /* EOF or empty line */
+ {
+ if (!firstLine) /* flush last line */
+ {
+ headers.set(header, null); /* remove old attribute,if present */
+ headers.set(header, value.toString().trim());
+ }
+ break; /* done processing main attributes */
+ }
+
+ if (line.charAt(0) == ' ') /* continuation */
+ {
+ if (firstLine) /* if no previous line */
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_LINE_EXCEPTION", line));
+ }
+ value.append(line.substring(1));
+ continue;
+ }
+
+ if (!firstLine)
+ {
+ headers.set(header, null); /* remove old attribute,if present */
+ headers.set(header, value.toString().trim());
+ value.setLength(0); /* clear StringBuffer */
+ }
+
+ int colon = line.indexOf(':');
+ if (colon == -1) /* no colon */
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_LINE_EXCEPTION", line));
+ }
+ header = line.substring(0, colon).trim();
+ value.append(line.substring(colon+1).trim());
+ firstLine = false;
+ }
+ return headers;
+ } catch (IOException e) {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_LINE_EXCEPTION", ""), e);
+ } finally {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/ManifestElement.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/ManifestElement.java
new file mode 100644
index 000000000..d10b38cdd
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/ManifestElement.java
@@ -0,0 +1,592 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.util;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.core.Msg;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+
+
+
+/**
+ * This class represents a manifest element.
+ */
+public class ManifestElement
+{
+
+ protected String value;
+ protected Hashtable attributes;
+
+ public ManifestElement(){
+ this(null);
+ }
+
+ public ManifestElement(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getAttribute(String key) {
+ if (attributes == null) {
+ return null;
+ }
+ return (String) attributes.get(key);
+ }
+
+ public Enumeration getKeys(){
+ if (attributes == null) {
+ return null;
+ }
+ return attributes.keys();
+ }
+
+ protected void setValue(String value) {
+ this.value = value;
+ }
+
+ protected void addAttribute(String key, String value) {
+ if (attributes == null) {
+ attributes = new Hashtable(7);
+ }
+ String curValue = (String) attributes.get(key);
+ if (curValue != null) {
+ value = curValue + ";" + value;
+ }
+ attributes.put(key,value);
+ }
+
+
+ public static ManifestElement[] parseClassPath(String value) throws BundleException
+ {
+ if (value == null)
+ {
+ return (null);
+ }
+ Vector classpaths = new Vector(10);
+
+ Tokenizer tokenizer = new Tokenizer(value);
+
+ parseloop : while (true)
+ {
+ String path = tokenizer.getToken(",");
+ if (path == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_CLASSPATH, value));
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.println("Classpath entry: " + path);
+ }
+ ManifestElement classpath = new ManifestElement(path);
+
+ int index = path.indexOf(";");
+ if (index != -1)
+ {
+ if ((index + 1) >= path.length())
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_CLASSPATH, value));
+ }
+ String filterString = path.substring(index + 1);
+
+ if (!filterString.endsWith(")"))
+ {
+ // we have a space in the filter or an invalid Bundle-ClassPath header
+ StringBuffer buf = new StringBuffer(filterString);
+ buf.append(' ');
+ while (true)
+ {
+ char nextChar = tokenizer.getChar();
+ if (nextChar == -1)
+ {
+ throw new BundleException(
+ Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_CLASSPATH, value));
+ }
+ buf.append(nextChar);
+ if (nextChar == ')')
+ {
+ break;
+ }
+ }
+ filterString = buf.toString();
+ }
+ classpath.addAttribute("filter", filterString);
+ }
+ classpaths.addElement(classpath);
+
+ char c = tokenizer.getChar();
+
+ if (c == ',') /* another path */
+ {
+ continue parseloop;
+ }
+
+ if (c == '\0') /* end of value */
+ {
+ break parseloop;
+ }
+
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_CLASSPATH, value));
+ }
+
+ int size = classpaths.size();
+
+ if (size == 0)
+ {
+ return (null);
+ }
+
+ ManifestElement[] result = new ManifestElement[size];
+ classpaths.copyInto(result);
+
+ return (result);
+ }
+
+ /**
+ * @param value The key to query the manifest for exported packages
+ * @return The Array of all ManifestElements that describe import or export package statements.
+ * @throws BundleException
+ */
+ public static ManifestElement[] parsePackageDescription(String value)
+ throws BundleException
+ {
+ if (value == null)
+ {
+ return(null);
+ }
+
+ Vector pkgvec = new Vector(10, 10);
+
+ Tokenizer tokenizer = new Tokenizer(value);
+
+ parseloop:
+ while (true)
+ {
+ String pkgname = tokenizer.getToken(";,");
+ if (pkgname == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ ManifestElement pkgdes = new ManifestElement(pkgname);
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print("PackageDescription: "+pkgname);
+ }
+
+ char c = tokenizer.getChar();
+
+ while (c == ';') /* attributes */
+ {
+ String key = tokenizer.getToken(";,=");
+ if (key == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ c = tokenizer.getChar();
+
+ if (c == '=') /* must be an attribute */
+ {
+ String val = tokenizer.getString(";,");
+ if (val == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print(";"+key+"="+val);
+ }
+ try
+ {
+ pkgdes.addAttribute(key, val);
+ }
+ catch (Exception e)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value), e);
+ }
+
+ c = tokenizer.getChar();
+ }
+ else /* error */
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+ }
+
+ pkgvec.addElement(pkgdes);
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.println("");
+ }
+
+ if (c == ',') /* another description */
+ {
+ continue parseloop;
+ }
+
+ if (c == '\0') /* end of value */
+ {
+ break parseloop;
+ }
+
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ int size = pkgvec.size();
+
+ if (size == 0)
+ {
+ return (null);
+ }
+
+ ManifestElement[] result = new ManifestElement[size];
+ pkgvec.copyInto(result);
+
+ return(result);
+ }
+
+ public static ManifestElement[] parseBundleDescriptions(String value) throws BundleException
+ {
+ if (value == null)
+ {
+ return(null);
+ }
+
+ Vector bundlevec = new Vector(10, 10);
+
+ Tokenizer tokenizer = new Tokenizer(value);
+
+ parseloop:
+ while (true)
+ {
+ String bundleUID = tokenizer.getToken(";,");
+ if (bundleUID == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ ManifestElement bundledes = new ManifestElement(bundleUID);
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print("BundleDescription: "+bundleUID);
+ }
+
+ char c = tokenizer.getChar();
+
+ while (c == ';') /* attributes */
+ {
+ String key = tokenizer.getToken(";,=");
+ if (key == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ c = tokenizer.getChar();
+
+ if (c == '=') /* must be an attribute */
+ {
+ String val = tokenizer.getString(";,");
+ if (val == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print(";"+key+"="+val);
+ }
+ try
+ {
+ bundledes.addAttribute(key, val);
+ }
+ catch (Exception e)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value), e);
+ }
+
+ c = tokenizer.getChar();
+ }
+ else /* error */
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+ }
+
+ bundlevec.addElement(bundledes);
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.println("");
+ }
+
+ if (c == ',') /* another description */
+ {
+ continue parseloop;
+ }
+
+ if (c == '\0') /* end of value */
+ {
+ break parseloop;
+ }
+
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ int size = bundlevec.size();
+
+ if (size == 0)
+ {
+ return (null);
+ }
+
+ ManifestElement[] result = new ManifestElement[size];
+ bundlevec.copyInto(result);
+
+ return(result);
+ }
+
+ public static ManifestElement[] parseNativeCodeDescription(String value) throws BundleException
+ {
+ if (value == null)
+ {
+ return(null);
+ }
+
+ Vector nativevec = new Vector(10, 10);
+
+ Tokenizer tokenizer = new Tokenizer(value);
+
+ parseloop:
+ while (true)
+ {
+ String next = tokenizer.getToken(";,");
+ if (next == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_NATIVECODE, value));
+ }
+
+ StringBuffer codepaths = new StringBuffer(next);
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print("NativeCodeDescription: "+next);
+ }
+
+ char c = tokenizer.getChar();
+
+ while (c == ';')
+ {
+ next = tokenizer.getToken(";,=");
+ if (next == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_NATIVECODE, value));
+ }
+
+ c = tokenizer.getChar();
+
+ if (c == ';') /* more */
+ {
+ codepaths.append(";").append(next);
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print(";"+next);
+ }
+ }
+ }
+
+ ManifestElement nativedes = new ManifestElement(codepaths.toString());
+
+ while (c == '=')
+ {
+ String val = tokenizer.getString(";,");
+ if (val == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_NATIVECODE, value));
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print(";"+next+"="+val);
+ }
+ try
+ {
+ nativedes.addAttribute(next, val);
+ }
+ catch (Exception e)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_NATIVECODE, value), e);
+ }
+
+ c = tokenizer.getChar();
+
+ if (c == ';') /* more */
+ {
+ next = tokenizer.getToken("=");
+
+ if (next == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_NATIVECODE, value));
+ }
+
+ c = tokenizer.getChar();
+ }
+ }
+
+ nativevec.addElement(nativedes);
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.println("");
+ }
+
+ if (c == ',') /* another description */
+ {
+ continue parseloop;
+ }
+
+ if (c == '\0') /* end of value */
+ {
+ break parseloop;
+ }
+
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", Constants.BUNDLE_NATIVECODE, value));
+ }
+
+ int size = nativevec.size();
+
+ if (size == 0)
+ {
+ return (null);
+ }
+
+ ManifestElement[] result = new ManifestElement[size];
+ nativevec.copyInto(result);
+
+ return(result);
+ }
+
+
+ public static ManifestElement[] parseBasicCommaSeparation(String header, String value) throws BundleException
+ {
+ if (value == null)
+ {
+ return(null);
+ }
+
+ Vector bundlevec = new Vector(10, 10);
+
+ Tokenizer tokenizer = new Tokenizer(value);
+
+ parseloop:
+ while (true)
+ {
+ String elementname = tokenizer.getToken(";,");
+ if (elementname == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ ManifestElement element = new ManifestElement(elementname);
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print("ManifestElement: "+elementname);
+ }
+
+ char c = tokenizer.getChar();
+
+ while (c == ';') /* attributes */
+ {
+ String key = tokenizer.getToken(";,=");
+ if (key == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ c = tokenizer.getChar();
+
+ if (c == '=') /* must be an attribute */
+ {
+ String val = tokenizer.getString(";,");
+ if (val == null)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value));
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.print(";"+key+"="+val);
+ }
+ try
+ {
+ element.addAttribute(key, val);
+ }
+ catch (Exception e)
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_PACKAGE_EXCEPTION", value), e);
+ }
+
+ c = tokenizer.getChar();
+ }
+ else /* error */
+ {
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", value));
+ }
+ }
+
+ bundlevec.addElement(element);
+
+ if (Debug.DEBUG && Debug.DEBUG_MANIFEST)
+ {
+ Debug.println("");
+ }
+
+ if (c == ',') /* another description */
+ {
+ continue parseloop;
+ }
+
+ if (c == '\0') /* end of value */
+ {
+ break parseloop;
+ }
+
+ throw new BundleException(Msg.formatter.getString("MANIFEST_INVALID_HEADER_EXCEPTION", value));
+ }
+
+ int size = bundlevec.size();
+
+ if (size == 0)
+ {
+ return (null);
+ }
+
+ ManifestElement[] result = new ManifestElement[size];
+ bundlevec.copyInto(result);
+
+ return(result);
+
+ }
+
+}
+
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/Tokenizer.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/Tokenizer.java
new file mode 100644
index 000000000..b8cad4fef
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/util/Tokenizer.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.util;
+
+/**
+ * Simple tokenizer class. Used to parse data.
+ */
+public class Tokenizer
+{
+ protected char value[];
+ protected int max;
+ protected int cursor;
+
+ public Tokenizer(String value)
+ {
+ this.value = value.toCharArray();
+ max = this.value.length;
+ cursor = 0;
+ }
+
+ private void skipWhiteSpace()
+ {
+ char[] val = value;
+ int cur = cursor;
+
+ for ( ;cur < max; cur++)
+ {
+ char c = val[cur];
+ if ((c == ' ') || (c == '\t'))
+ {
+ continue;
+ }
+ break;
+ }
+ cursor = cur;
+ }
+
+ public String getToken(String terminals)
+ {
+ skipWhiteSpace();
+ char[] val = value;
+ int cur = cursor;
+
+ int begin = cur;
+ for ( ;cur < max; cur++)
+ {
+ char c = val[cur];
+ if ((c == ' ') || (c == '\t') || (terminals.indexOf(c) != -1))
+ {
+ break;
+ }
+ }
+ cursor = cur;
+ int count = cur-begin;
+ if (count > 0)
+ {
+ skipWhiteSpace();
+ return(new String(val, begin, count));
+ }
+ return(null);
+ }
+
+ public String getString(String terminals)
+ {
+ skipWhiteSpace();
+ char[] val = value;
+ int cur = cursor;
+
+ if (cur < max)
+ {
+ if (val[cur] == '\"') /* if a quoted string */
+ {
+ cur++; /* skip quote */
+ char c = '\0';
+ int begin = cur;
+ for ( ;cur < max; cur++)
+ {
+ c = val[cur];
+ if (c == '\"')
+ {
+ break;
+ }
+ }
+ int count = cur-begin;
+ if (c == '\"')
+ {
+ cur++;
+ }
+ cursor = cur;
+ if (count > 0)
+ {
+ skipWhiteSpace();
+ return(new String(val, begin, count));
+ }
+ }
+ else /* not a quoted string; same as token */
+ {
+ int begin = cur;
+ for ( ;cur < max; cur++)
+ {
+ char c = val[cur];
+ if (c=='\"') {
+ // but there could be a quoted string in the middle of the string
+ cur = cur+skipQuotedString(val,cur);
+ } else if ((c == ' ') || (c == '\t') || (terminals.indexOf(c) != -1)) {
+ break;
+ }
+ }
+ cursor = cur;
+ int count = cur-begin;
+ if (count > 0)
+ {
+ skipWhiteSpace();
+ return(new String(val, begin, count));
+ }
+ }
+ }
+ return(null);
+ }
+
+ private int skipQuotedString(char[] val, int cur) {
+ cur++; /* skip quote */
+ char c = '\0';
+ int begin = cur;
+ for ( ;cur < max; cur++)
+ {
+ c = val[cur];
+ if (c == '\"')
+ {
+ break;
+ }
+ }
+ int count = cur-begin;
+ if (c == '\"')
+ {
+ cur++;
+ }
+ cursor = cur;
+ if (count > 0)
+ {
+ skipWhiteSpace();
+ }
+ return count;
+ }
+
+ public char getChar()
+ {
+ int cur = cursor;
+ if (cur < max)
+ {
+ cursor = cur+1;
+ return(value[cur]);
+ }
+ return('\0'); /* end of value */
+ }
+
+ public boolean hasMoreTokens() {
+ if (cursor<max) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/META-INF/SYSTEMBUNDLE.MF b/bundles/org.eclipse.osgi/core/framework/META-INF/SYSTEMBUNDLE.MF
new file mode 100644
index 000000000..cf8585285
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/META-INF/SYSTEMBUNDLE.MF
@@ -0,0 +1,30 @@
+Export-Package: org.osgi.framework; specification-version=1.2,
+ org.osgi.service.packageadmin; specification-version=1.1,
+ org.osgi.service.permissionadmin; specification-version=1.1,
+ org.osgi.service.url; specification-version=1.0,
+ org.osgi.service.startlevel; specification-version=1.0,
+ org.eclipse.osgi.framework.eventmgr,
+ org.eclipse.osgi.framework.msg; specification-version=1.0,
+ org.eclipse.osgi.service.environment
+Import-Package: org.osgi.framework; specification-version=1.2,
+ org.osgi.service.packageadmin; specification-version=1.1,
+ org.osgi.service.permissionadmin; specification-version=1.1,
+ org.osgi.service.url; specification-version=1.0,
+ org.osgi.service.startlevel; specification-version=1.0,
+ org.eclipse.osgi.framework.eventmgr,
+ org.eclipse.osgi.framework.msg; specification-version=1.0,
+ org.eclipse.osgi.service.environment
+Export-Service: org.osgi.service.packageadmin.PackageAdmin,
+ org.osgi.service.permissionadmin.PermissionAdmin,
+ org.osgi.service.startlevel.StartLevel,
+ org.eclipse.osgi.service.environment.DebugOptions
+Bundle-Name: OSGi System Bundle
+Bundle-GlobalName: org.osgi.framework
+Bundle-Activator: org.eclipse.osgi.framework.internal.core.SystemBundleActivator
+Bundle-Description: OSGi System Bundle
+Bundle-Copyright: Copyright (c) 2003 IBM Corporation and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html
+Bundle-Vendor: Eclipse
+Bundle-Version: 3.6.0
+Bundle-DocUrl: http://www.eclipse.org
+Bundle-ContactAddress: pervasive@us.ibm.com
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/console/CommandInterpreter.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/console/CommandInterpreter.java
new file mode 100644
index 000000000..485e4b58f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/console/CommandInterpreter.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.console;
+
+import java.util.Dictionary;
+
+import org.eclipse.osgi.framework.internal.core.Bundle;
+
+/** A command interpreter is a shell that can interpret command
+ * lines. This object is passed as parameter when a CommandProvider
+ * is invoked.
+*/
+public interface CommandInterpreter {
+ public final static String NAME = "org.eclipse.osgi.framework.internal.core.command.CommandInterpreter";
+
+ /**
+ * Get the next argument in the input.
+ *
+ * E.g. if the commandline is hello world, the _hello method
+ * will get "world" as the first argument.
+ */
+ public String nextArgument();
+
+ /**
+ * Execute a command line as if it came from the end user
+ * and return the result.
+ *
+ * Throws any exceptions generated by the command that executed.
+ */
+ public Object execute( String cmd );
+
+ /**
+ * Prints an object to the outputstream
+ *
+ * @param o the object to be printed
+ */
+ public void print(Object o);
+
+ /**
+ * Prints an empty line to the outputstream
+ */
+ public void println();
+
+ /**
+ * Prints an object to the output medium (appended with newline character).
+ * <p>
+ * If running on the target environment the user is prompted with '--more'
+ * if more than the configured number of lines have been printed without user prompt.
+ * That way the user of the program has control over the scrolling.
+ * <p>
+ * For this to work properly you should not embedded "\n" etc. into the string.
+ *
+ * @param o the object to be printed
+ */
+ public void println(Object o);
+
+ /**
+ * Print a stack trace including nested exceptions.
+ * @param The offending exception
+ */
+ public void printStackTrace(Throwable t);
+
+
+ /**
+ * Prints the given dictionary sorted by keys.
+ *
+ * @param dic the dictionary to print
+ * @param title the header to print above the key/value pairs
+ */
+ public void printDictionary(Dictionary dic, String title);
+
+ /**
+ * Prints the given bundle resource if it exists
+ *
+ * @param bundle the bundle containing the resource
+ * @param resource the resource to print
+ */
+ public void printBundleResource(Bundle bundle, String resource);
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/console/CommandProvider.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/console/CommandProvider.java
new file mode 100644
index 000000000..faeda3677
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/console/CommandProvider.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.console;
+
+/**
+ When an object wants to provide a number of commands
+ to the console, it should register an object with this
+ interface. Some console can then pick this up and execute
+ command lines.
+ The SERVICE_RANKING registration property can be used to influence the
+ order that a CommandProvider gets called. Specify a value less than
+ Integer.MAXVALUE, where higher is more significant. The default value
+ if SERVICE_RANKING is not set is 0.
+ <p>
+ The interface contains only methods for the help.
+ The console should use inspection
+ to find the commands. All public commands, starting with
+ a '_' and taking a CommandInterpreter as parameter
+ will be found. E.g.
+ <pre>
+ public Object _hello( CommandInterpreter intp ) {
+ return "hello " + intp.nextArgument();
+ }
+ </pre>
+*/
+public interface CommandProvider {
+ public final static String NAME = "org.eclipse.osgi.framework.internal.core.command.CommandProvider";
+
+ /**
+ Answer a string (may be as many lines as you like) with help
+ texts that explain the command.
+ */
+ public String getHelp();
+
+}
+
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/debug/Debug.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/debug/Debug.java
new file mode 100644
index 000000000..ece10fbc0
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/debug/Debug.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.debug;
+
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public class Debug
+{
+ public static final boolean DEBUG = true;
+ public static boolean DEBUG_GENERAL = false; // "debug"
+ public static boolean DEBUG_BUNDLE_TIME = false; //"debug.bundleTime"
+ public static boolean DEBUG_LOADER = false; // "debug.loader"
+ public static boolean DEBUG_EVENTS = false; // "debug.events"
+ public static boolean DEBUG_SERVICES = false; // "debug.services"
+ public static boolean DEBUG_PACKAGES = false; // "debug.packages"
+ public static boolean DEBUG_MANIFEST = false; // "debug.manifest"
+ public static boolean DEBUG_FILTER = false; // "debug.filter"
+ public static boolean DEBUG_SECURITY = false; // "debug.security"
+ public static boolean DEBUG_STARTLEVEL = false; // "debug.startlevel"
+ public static boolean DEBUG_PACKAGEADMIN = false; // "debug.packageadmin"
+ public static boolean DEBUG_PACKAGEADMIN_TIMING = false; //"debug.packageadmin/timing"
+ static {
+ DebugOptions dbgOptions = DebugOptions.getDefault();
+ if (dbgOptions != null) {
+ DEBUG_GENERAL = dbgOptions.getBooleanOption("debug", false);
+ DEBUG_BUNDLE_TIME = dbgOptions.getBooleanOption("debug.bundleTime", false) || dbgOptions.getBooleanOption("org.eclipse.core.runtime/timing/startup", false);
+ DEBUG_LOADER = dbgOptions.getBooleanOption("debug.loader",false);
+ DEBUG_EVENTS = dbgOptions.getBooleanOption("debug.events",false);
+ DEBUG_SERVICES = dbgOptions.getBooleanOption("debug.services",false);
+ DEBUG_PACKAGES = dbgOptions.getBooleanOption("debug.packages",false);
+ DEBUG_MANIFEST = dbgOptions.getBooleanOption("debug.manifest",false);
+ DEBUG_FILTER = dbgOptions.getBooleanOption("debug.filter",false);
+ DEBUG_SECURITY = dbgOptions.getBooleanOption("debug.security",false);
+ DEBUG_STARTLEVEL = dbgOptions.getBooleanOption("debug.startlevel",false);
+ DEBUG_PACKAGEADMIN = dbgOptions.getBooleanOption("debug.packageadmin",false) ;
+ DEBUG_PACKAGEADMIN_TIMING = dbgOptions.getBooleanOption("debug.packageadmin/timing", false) || dbgOptions.getBooleanOption("org.eclipse.core.runtime/debug", false);
+ }
+ }
+ public static PrintStream out = System.out;
+
+ public static void print(boolean x)
+ {
+ out.print(x);
+ }
+ public static void print(char x)
+ {
+ out.print(x);
+ }
+ public static void print(int x)
+ {
+ out.print(x);
+ }
+ public static void print(long x)
+ {
+ out.print(x);
+ }
+ public static void print(float x)
+ {
+ out.print(x);
+ }
+ public static void print(double x)
+ {
+ out.print(x);
+ }
+ public static void print(char x[])
+ {
+ out.print(x);
+ }
+ public static void print(String x)
+ {
+ out.print(x);
+ }
+ public static void print(Object x)
+ {
+ out.print(x);
+ }
+ public static void println(boolean x)
+ {
+ out.println(x);
+ }
+ public static void println(char x)
+ {
+ out.println(x);
+ }
+ public static void println(int x)
+ {
+ out.println(x);
+ }
+ public static void println(long x)
+ {
+ out.println(x);
+ }
+ public static void println(float x)
+ {
+ out.println(x);
+ }
+ public static void println(double x)
+ {
+ out.println(x);
+ }
+ public static void println(char x[])
+ {
+ out.println(x);
+ }
+ public static void println(String x)
+ {
+ out.println(x);
+ }
+ public static void println(Object x)
+ {
+ out.println(x);
+ }
+ public static void printStackTrace(Throwable x)
+ {
+ printStackTrace(x,out);
+ }
+ private static void printStackTrace(Throwable t, PrintStream out)
+ {
+ t.printStackTrace(out);
+
+ Method[] methods = t.getClass().getMethods();
+
+ int size = methods.length;
+ Class throwable = Throwable.class;
+
+ for (int i = 0; i < size; i++)
+ {
+ Method method = methods[i];
+
+ if (Modifier.isPublic(method.getModifiers()) &&
+ method.getName().startsWith("get") &&
+ throwable.isAssignableFrom(method.getReturnType()) &&
+ (method.getParameterTypes().length == 0))
+ {
+ try
+ {
+ Throwable nested = (Throwable) method.invoke(t, null);
+
+ if ((nested != null) && (nested != t))
+ {
+ out.println("Nested Exception:");
+ printStackTrace(nested, out);
+ }
+ }
+ catch (IllegalAccessException e)
+ {
+ }
+ catch (InvocationTargetException e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/debug/DebugOptions.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/debug/DebugOptions.java
new file mode 100644
index 000000000..2e6f7e736
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/debug/DebugOptions.java
@@ -0,0 +1,123 @@
+package org.eclipse.osgi.framework.debug;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+
+public class DebugOptions implements org.eclipse.osgi.service.environment.DebugOptions {
+ Properties options = null;
+
+ private static DebugOptions singleton = null;
+ private static boolean debugEnabled = true;
+ private static final String OPTIONS = ".options"; //$NON-NLS-1$
+
+ public static DebugOptions getDefault() {
+ if (singleton == null && debugEnabled) {
+ DebugOptions result = new DebugOptions();
+ debugEnabled = result.isDebugEnabled();
+ if (debugEnabled)
+ singleton = result;
+ }
+ return singleton;
+ }
+
+ private DebugOptions() {
+ super();
+ loadOptions();
+ }
+
+ public boolean getBooleanOption(String option, boolean defaultValue) {
+ String optionValue = getOption(option);
+ return (optionValue != null && optionValue.equalsIgnoreCase("true")) || defaultValue; //$NON-NLS-1$
+ }
+
+ public String getOption(String option) {
+ return options != null ? options.getProperty(option) : null;
+ }
+
+ public int getIntegerOption(String option, int defaultValue) {
+ String value = getOption(option);
+ try {
+ return value == null ? defaultValue : Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ public void setOption(String option, String value) {
+ if (options != null)
+ options.put(option, value.trim());
+ }
+
+ public boolean isDebugEnabled() {
+ return options != null;
+ }
+
+ private void loadOptions() {
+ // if no debug option was specified, don't even bother to try.
+ // Must ensure that the options slot is null as this is the signal to the
+ // platform that debugging is not enabled.
+ String debugOptionsFilename = System.getProperty("osgi.debug");
+ if (debugOptionsFilename == null)
+ return;
+ options = new Properties();
+ URL optionsFile;
+ if (debugOptionsFilename.length() == 0) {
+ // default options location is user.dir (install location may be r/o so
+ // is not a good candidate for a trace options that need to be updatable by
+ // by the user)
+ String userDir = System.getProperty("user.dir").replace(File.separatorChar,'/'); //$NON-NLS-1$
+ if (!userDir.endsWith("/")) //$NON-NLS-1$
+ userDir += "/"; //$NON-NLS-1$
+ debugOptionsFilename = "file:" + userDir + OPTIONS; //$NON-NLS-1$
+ }
+ try {
+ optionsFile = getURL(debugOptionsFilename);
+ } catch (MalformedURLException e) {
+ System.out.println("Unable to construct URL for options file: " + debugOptionsFilename); //$NON-NLS-1$
+ e.printStackTrace(System.out);
+ return;
+ }
+ System.out.println("Debug-Options:\n " + debugOptionsFilename); //$NON-NLS-1$
+ try {
+ InputStream input = optionsFile.openStream();
+ try {
+ options.load(input);
+ } finally {
+ input.close();
+ }
+ } catch (FileNotFoundException e) {
+ // Its not an error to not find the options file
+ } catch (IOException e) {
+ System.out.println("Could not parse the options file: " + optionsFile); //$NON-NLS-1$
+ e.printStackTrace(System.out);
+ }
+ // trim off all the blanks since properties files don't do that.
+ for (Iterator i = options.keySet().iterator(); i.hasNext();) {
+ Object key = i.next();
+ options.put(key, ((String) options.get(key)).trim());
+ }
+ if (options.size() == 0)
+ options = null;
+ }
+ /**
+ * Helper method that creates an URL object from the given string
+ * representation. The string must correspond to a valid URL or file system
+ * path.
+ */
+ private URL getURL(String urlString) throws MalformedURLException {
+ try {
+ return new URL(urlString);
+ } catch (MalformedURLException e) {
+ // if it is not a well formed URL, tries to create a "file:" URL
+ try {
+ return new File(urlString).toURL();
+ } catch (MalformedURLException ex) {
+ // re-throw the original exception if nothing works
+ throw e;
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventListeners.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventListeners.java
new file mode 100644
index 000000000..2dc9c5a58
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventListeners.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+/**
+ * This class manages the list of listeners.
+ * Listeners may be added or removed as necessary.
+ */
+
+public class EventListeners
+{
+ /**
+ * This field contains the listener list.
+ */
+ ListenerList list;
+
+ /**
+ * Constructor for EventListeners. An empty list of listeners is created.
+ *
+ */
+ public EventListeners()
+ {
+ list = null;
+ }
+
+ /**
+ * This method is called to add a listener to the list.
+ *
+ * @param listener This is the listener object to be added to the list.
+ * @param listenerObject This is an optional listener-specific object.
+ * This object will be passed to the EventSource along with the listener
+ * when the listener is to be called.
+ */
+ public synchronized void addListener(Object listener, Object listenerObject)
+ {
+ if (listener != null)
+ {
+ list = ListenerList.addListener(list, listener, listenerObject);
+ }
+ }
+
+ /**
+ * This method is called to remove a listener from the list.
+ *
+ * @param listener This is the listener object to be removed from the list.
+ */
+ public synchronized void removeListener(Object listener)
+ {
+ if (listener != null)
+ {
+ list = ListenerList.removeListener(list, listener);
+ }
+ }
+
+ /**
+ * This method is called to remove all listeners from the list.
+ */
+ public synchronized void removeAllListeners()
+ {
+ list = null;
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventManager.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventManager.java
new file mode 100644
index 000000000..b1b85f6ea
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventManager.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+
+
+/**
+ * This class is the central class for the event manager. Each
+ * program that wishes to use the event manager should construct
+ * an EventManager object and use that object to construct
+ * EventQueue for dispatching events.
+ */
+
+public class EventManager
+{
+ /**
+ * EventThread for asynchronous dispatch of events.
+ */
+ protected EventThread thread;
+
+ /**
+ * EventThread Name
+ */
+ protected String threadName;
+
+ /**
+ * EventManager constructor. An EventManager object is responsible for
+ * the delivery of events to listeners via an EventSource.
+ *
+ */
+ public EventManager()
+ {
+ thread = null;
+ }
+
+ public EventManager(String threadName){
+ this();
+ this.threadName = threadName;
+ }
+
+ /**
+ * This method can be called to release any resources associated with this
+ * EventManager.
+ *
+ */
+ public synchronized void close()
+ {
+ if (thread != null)
+ {
+ thread.close();
+ thread = null;
+ }
+ }
+
+ /**
+ * Asynchronously dispatch an event to the set of listeners. An event dispatch thread
+ * maintained by the associated EventManager is used to deliver the events.
+ * This method may return immediately to the caller.
+ *
+ * @param ll The set of listeners to which the event will be dispatched.
+ * @param eventAction This value is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ * @param eventObject This object is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ */
+ void dispatchEventAsynchronous(ListenerList ll, int eventAction, Object eventObject)
+ {
+ EventThread thread = getEventThread();
+
+ for (; ll != null; ll = ll.list)
+ {
+ thread.postEvent((ListenerList) ll.listener, (EventSource) ll.object, eventAction, eventObject);
+ }
+ }
+
+ /**
+ * Synchronously dispatch an event to the set of listeners. The event may
+ * be dispatch on the current thread or an event dispatch thread
+ * maintained by the associated EventManager.
+ * This method will not return to the caller until an EventSource
+ * has been called (and returned) for each listener.
+ *
+ * @param ll The set of listeners to which the event will be dispatched.
+ * @param eventAction This value is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ * @param eventObject This object is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ */
+ void dispatchEventSynchronous(ListenerList ll, int eventAction, Object eventObject)
+ {
+ for (; ll != null; ll = ll.list)
+ {
+ ((ListenerList) ll.listener).dispatchEvent((EventSource) ll.object, eventAction, eventObject);
+ }
+ }
+
+ /**
+ * Returns an EventThread to use for dispatching events asynchronously.
+ *
+ * @return EventThread.
+ */
+ private synchronized EventThread getEventThread()
+ {
+ if (thread == null)
+ {
+ if (threadName == null)
+ {
+ thread = new EventThread();
+ }
+ else
+ {
+ thread = new EventThread(threadName);
+ }
+ }
+
+ return(thread);
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventQueue.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventQueue.java
new file mode 100644
index 000000000..e3f767348
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventQueue.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+
+
+/**
+ * The EventQueue is used to build the set of listeners and then dispatch
+ * events to those listeners. An EventQueue object is associated with a
+ * specific EventManager object.
+ *
+ * <p>EventQueue objects are created on demand to build a set of listeners
+ * that should receive a specific event. Once the set is created, the event
+ * can then be synchronously or asynchronously delivered to the set of
+ * listeners. After the event has been dispatched for delivery, the
+ * EventQueue object should be discarded. A new EventQueue object should be
+ * created for the delivery of another specific event.
+ */
+
+public class EventQueue
+{
+ /**
+ * EventManager with which this queue is associated.
+ */
+ protected EventManager manager;
+ /**
+ * Set of listeners (list of listener lists).
+ */
+ protected ListenerList queue;
+
+ /**
+ * EventQueue constructor. This method creates an empty event queue.
+ *
+ * @param manager The EventManager this queue is associated with.
+ * @exception java.lang.NullPointerException if manager is null.
+ */
+ public EventQueue(EventManager manager)
+ {
+ this.manager = manager;
+ if (manager == null)
+ {
+ throw new NullPointerException();
+ }
+
+ queue = null;
+ }
+
+ /**
+ * Build the set of listeners. This method can be called multiple times, prior to
+ * calling one of the dispatchEvent methods, to build the set of listeners for the
+ * delivery of a specific event. The current list of listeners in the given ListenerList,
+ * at the time this method is called, is added to the set.
+ *
+ * @param listeners An EventListeners object to add to the queue. All listeners
+ * previously added to
+ * the EventListeners object will be called when an event is dispatched.
+ * @param source An EventSource object to use when dispatching an event
+ * to the listeners on this ListenerList.
+ */
+ public synchronized void queueListeners(EventListeners listeners, EventSource source)
+ {
+ if (listeners != null)
+ {
+ ListenerList list = listeners.list;
+
+ if (list != null)
+ {
+ queue = ListenerList.addListener(queue, list, source);
+ }
+ }
+ }
+
+ /**
+ * Asynchronously dispatch an event to the set of listeners. An event dispatch thread
+ * maintained by the associated EventManager is used to deliver the events.
+ * This method may return immediately to the caller.
+ *
+ * @param eventAction This value is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ * @param eventObject This object is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ */
+ public void dispatchEventAsynchronous(int eventAction, Object eventObject)
+ {
+ manager.dispatchEventAsynchronous(queue, eventAction, eventObject);
+ }
+
+ /**
+ * Synchronously dispatch an event to the set of listeners. The event may
+ * be dispatched on the current thread or an event dispatch thread
+ * maintained by the associated EventManager.
+ * This method will not return to the caller until an EventSource
+ * has been called (and returned) for each listener.
+ *
+ * @param eventAction This value is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ * @param eventObject This object is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ */
+ public void dispatchEventSynchronous(int eventAction, Object eventObject)
+ {
+ manager.dispatchEventSynchronous(queue, eventAction, eventObject);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventSource.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventSource.java
new file mode 100644
index 000000000..fc4e51d8b
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventSource.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+
+
+/**
+ * The EventSource interface contains the method that is called by the
+ * Event Manager to complete the event delivery to the event listener.
+ */
+
+public abstract interface EventSource
+{
+ /**
+ * This method is the call back that is called once for each listener.
+ * This method must cast the EventListener object to the appropriate listener
+ * class for the event type and call the appropriate listener method.
+ *
+ * @param listener This listener must be cast to the appropriate listener
+ * class for the events created by this source and the appropriate listener method
+ * must then be called.
+ * @param listenerObject This is the optional object that was passed to
+ * ListenerList.addListener when the listener was added to the ListenerList.
+ * @param eventAction This value was passed to the EventQueue object via one of its
+ * dispatchEvent* method calls. It can provide information (such
+ * as which listener method to call) so that this method
+ * can complete the delivery of the event to the listener.
+ * @param eventObject This object was passed to the EventQueue object via one of its
+ * dispatchEvent* method calls. This object was created by the event source and
+ * is passed to this method. It should contain all the necessary information (such
+ * as what event object to pass) so that this method
+ * can complete the delivery of the event to the listener.
+ */
+ public abstract void dispatchEvent(Object listener, Object listenerObject, int eventAction, Object eventObject);
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventThread.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventThread.java
new file mode 100644
index 000000000..1eb7e31da
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/EventThread.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+
+
+/**
+ * This class is used for asynchronously dispatching events.
+ */
+
+class EventThread extends Thread
+{
+ /**
+ * EventThreadItem is a nested top-level (non-member) class. This class
+ * represents the items which are placed on the queue.
+ */
+ static class EventThreadItem
+ {
+ /** listener list for this event */
+ private final ListenerList listeners;
+ /** source of this event */
+ private final EventSource source;
+ /** action for this event */
+ private final int action;
+ /** object for this event */
+ private final Object object;
+ /** next item in event queue */
+ EventThreadItem next;
+
+ /**
+ * Constructor for event queue item
+ *
+ * @param l Listener list for this event
+ * @param s Source for this event
+ * @param a Action for this event
+ * @param o Object for this event
+ */
+ EventThreadItem(ListenerList l, EventSource s, int a, Object o)
+ {
+ listeners = l;
+ source = s;
+ action = a;
+ object = o;
+ next = null;
+ }
+
+ /**
+ * This method will dispatch this event queue item to its listeners
+ */
+ void dispatchEvent()
+ {
+ listeners.dispatchEvent(source, action, object);
+ }
+ }
+
+ /** item at the head of the event queue */
+ private EventThreadItem head;
+ /** item at the tail of the event queue */
+ private EventThreadItem tail;
+ /** if false the thread must terminate */
+ private volatile boolean running;
+
+ /**
+ * Constructor for the event queue. The queue is created empty and the
+ * queue dispatcher thread is started.
+ */
+ EventThread(String threadName)
+ {
+ super(threadName);
+ init();
+ }
+
+ /**
+ * Constructor for the event queue. The queue is created empty and the
+ * queue dispatcher thread is started.
+ */
+ EventThread()
+ {
+ super();
+ init();
+ }
+
+ void init(){
+ running = true;
+ head = null;
+ tail = null;
+
+ setDaemon(true); /* Mark thread as daemon thread */
+ start(); /* Start thread */
+ }
+
+ /**
+ * Stop thread.
+ */
+ void close()
+ {
+ running = false;
+ interrupt();
+ }
+
+ /**
+ * This method is the event queue dispatcher thread. It pulls events from
+ * the queue and dispatches them.
+ */
+ public void run()
+ {
+ while (running)
+ {
+ try
+ {
+ getNextEvent().dispatchEvent();
+ }
+ catch (Throwable t)
+ {
+ }
+ }
+ }
+
+ /**
+ * This methods takes the input parameters and creates an EventThreadItem
+ * and queues it.
+ * The thread is notified.
+ *
+ * @param l Listener list for this event
+ * @param s Source for this event
+ * @param a Action for this event
+ * @param o Object for this event
+ */
+ synchronized void postEvent(ListenerList l, EventSource s, int a, Object o)
+ {
+ EventThreadItem item = new EventThreadItem(l, s, a, o);
+
+ if (head == null) /* if the queue was empty */
+ {
+ head = item;
+ tail = item;
+ }
+ else /* else add to end of queue */
+ {
+ tail.next = item;
+ tail = item;
+ }
+
+ notify();
+ }
+
+ /**
+ * This method is called by the thread to remove
+ * items from the queue so that they can be dispatched to their listeners.
+ * If the queue is empty, the thread waits.
+ *
+ * @return The EventThreadItem removed from the top of the queue.
+ */
+ private synchronized EventThreadItem getNextEvent() throws InterruptedException
+ {
+ while (running && (head == null))
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ if (!running) /* if we are stopping */
+ {
+ throw new InterruptedException(); /* throw an exception */
+ }
+
+ EventThreadItem item = head;
+ head = item.next;
+ if (head == null)
+ {
+ tail = null;
+ }
+
+ return(item);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/ListenerList.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/ListenerList.java
new file mode 100644
index 000000000..cdaa49347
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/eventmgr/ListenerList.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+/**
+ * This internal class manages the list of listeners. Each object is
+ * immutable, once created it cannot be modified. The most expensive
+ * operation on the list is removeListener. This operation must descend
+ * the list until the listener is found, then as it returns up the call
+ * chain, new ListenerList objects are created and returned to rebuild the list.
+ */
+
+class ListenerList
+{
+ /**
+ * Next item in list.
+ */
+ final ListenerList list;
+
+ /**
+ * Listener referenced by this item.
+ */
+ final Object listener;
+
+ /**
+ * Listener object referenced by this item.
+ */
+ final Object object;
+
+ static final boolean DEBUG = true;
+
+ /**
+ * Private constructor used by addListener and removeListener.
+ *
+ * @param oldlist Existing list or null if no list.
+ * @param l Listener to be added to list.
+ * @return List object.
+ */
+ private ListenerList(ListenerList oldlist, Object l, Object o)
+ {
+ list = oldlist;
+ listener = l;
+ object = o;
+ }
+
+ /**
+ * Static method to add a listener to a list.
+ *
+ * @param oldlist Existing list to which the listener is to be added.
+ * @param l Listener to be added to list.
+ * @param o Listener specific object to be added to list.
+ * @return New list with listener added.
+ */
+ static ListenerList addListener(ListenerList oldlist, Object l, Object o)
+ {
+ /* Create the linked list element in the listener's memory space */
+ // resman begin
+ return (new ListenerList(oldlist, l, o));
+ // resman end
+ }
+
+ /**
+ * Static method to remove a listener from the list.
+ *
+ * @param oldlist Existing list from which the listener is to be removed
+ * @param l Listener to be removed from list
+ * @return New list with listener removed.
+ */
+ static ListenerList removeListener(ListenerList oldlist, Object l)
+ {
+ if (oldlist != null) /* list is not empty */
+ {
+ try
+ {
+ return (oldlist.removeListener(l));
+ }
+ catch (IllegalArgumentException e)
+ {
+ /* Listener to be removed was not found */
+ }
+ }
+ return (oldlist);
+ }
+
+ /**
+ * Private method to recurse down the list looking for the listener to remove.
+ * When the listener is found, the call chain returns creating new List
+ * objects rebuilding the list.
+ *
+ * @param l Listener to be removed from list
+ * @return New sublist with listener removed.
+ * @exception java.lang.IllegalArgumentException If listener to be removed
+ * is not found
+ */
+ private ListenerList removeListener(Object l)
+ throws IllegalArgumentException
+ {
+ if (listener == l) /* Check if this the guy to remove */
+ {
+ return (list); /* return the next on the list */
+ }
+
+ if (list != null) /* we've not reached the end of the list */
+ {
+ ListenerList ll = list.removeListener(l);
+
+ /* Create the linked list element in the listener's memory space */
+ // resman begin
+ return (new ListenerList(ll, listener, object));
+ // resman end
+ }
+
+ /* We have reached the end of the list and have not found the listener */
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * This method calls the EventSource object to complete the dispatch of
+ * the event. If there are more listeners in the list, call dispatchEvent
+ * on the next item on the list.
+ *
+ * @param source Call back object which is called to complete the delivery of
+ * the event.
+ * @param action This value was passed by the event source and
+ * is passed to this method. This is passed on to the call back object.
+ * @param object This object was created by the event source and
+ * is passed to this method. This is passed on to the call back object.
+ */
+ void dispatchEvent(EventSource source, int action, Object object)
+ {
+ /* Remember the current thread's active memory space */
+ // resman begin
+ for (ListenerList ll = this; ll != null; ll = ll.list)
+ {
+ /* Dispatch the event in the listener's memory space */
+ // resman select memoryspace
+
+ try
+ {
+ /* Call the call back method with the listener */
+ source.dispatchEvent(ll.listener, ll.object, action, object);
+ }
+ catch (Throwable t)
+ {
+ /* Consume and ignore any exceptions thrown by the listener */
+
+ if (DEBUG) {
+ System.out.println("Exception in " + ll.listener);
+ t.printStackTrace();
+ }
+ }
+ }
+ // resman end
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AliasMapper.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AliasMapper.java
new file mode 100644
index 000000000..0b603570f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AliasMapper.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.Hashtable;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.util.*;
+
+/**
+ * This class maps aliases.
+ */
+public class AliasMapper
+{
+ private static Hashtable processorAliasTable;
+ private static Hashtable osnameAliasTable;
+
+ /**
+ * Constructor.
+ *
+ */
+ public AliasMapper()
+ {
+ }
+
+ /**
+ * Return the master alias for the processor.
+ *
+ * @param processor Input name
+ * @return aliased name (if any)
+ */
+ public String aliasProcessor(String processor)
+ {
+ processor = processor.toLowerCase();
+
+ if (processorAliasTable == null)
+ {
+ InputStream in = getClass().getResourceAsStream(Constants.OSGI_PROCESSOR_ALIASES);
+ if (in != null)
+ {
+ try
+ {
+ processorAliasTable = initAliases(in);
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+ }
+
+ }
+
+ if (processorAliasTable != null)
+ {
+ String alias = (String)processorAliasTable.get(processor);
+
+ if (alias != null)
+ {
+ processor = alias;
+ }
+ }
+
+ return(processor);
+ }
+
+ /**
+ * Return the master alias for the osname.
+ *
+ * @param osname Input name
+ * @return aliased name (if any)
+ */
+ public String aliasOSName(String osname)
+ {
+ osname = osname.toLowerCase();
+
+ if (osnameAliasTable == null)
+ {
+ InputStream in = getClass().getResourceAsStream(Constants.OSGI_OSNAME_ALIASES);
+ if (in != null)
+ {
+ try
+ {
+ osnameAliasTable = initAliases(in);
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+ }
+ }
+
+ if (osnameAliasTable != null)
+ {
+ String alias = (String)osnameAliasTable.get(osname);
+
+ if (alias != null)
+ {
+ osname = alias;
+ }
+ }
+
+ return(osname);
+ }
+
+ /**
+ * Read alias data and populate a Hashtable.
+ *
+ * @param in InputStream from which to read alias data.
+ * @return Hashtable of aliases.
+ */
+ protected static Hashtable initAliases(InputStream in)
+ {
+ Hashtable aliases = new Hashtable(37);
+
+ try
+ {
+ BufferedReader br;
+ try
+ {
+ br = new BufferedReader(new InputStreamReader(in, "UTF8"));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ br = new BufferedReader(new InputStreamReader(in));
+ }
+
+ while (true)
+ {
+ String line = br.readLine();
+
+ if (line == null) /* EOF */
+ {
+ break; /* done */
+ }
+
+ Tokenizer tokenizer = new Tokenizer(line);
+
+ String master = tokenizer.getString("#");
+
+ if (master != null)
+ {
+ aliases.put(master.toLowerCase(), master);
+
+ parseloop:
+ while (true)
+ {
+ String alias = tokenizer.getString("#");
+
+ if (alias == null)
+ {
+ break parseloop;
+ }
+
+ aliases.put(alias.toLowerCase(), master);
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.printStackTrace(e);
+ }
+ }
+
+ return(aliases);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Bundle.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Bundle.java
new file mode 100644
index 000000000..e17acbcf4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Bundle.java
@@ -0,0 +1,1591 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.*;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.adaptor.Version;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.*;
+
+/**
+ * This object is given out to bundles and wraps the internal
+ * Bundle object. It is destroyed when a bundle is uninstalled
+ * and reused if a bundle is updated. This class is abstract and
+ * is extended by BundleHost and BundleFragment.
+ */
+
+public abstract class Bundle implements org.osgi.framework.Bundle, Comparable, KeyedElement {
+
+ /** The Framework this bundle is part of */
+ protected Framework framework;
+
+ /** The state of the bundle. */
+ protected volatile int state;
+
+ /** A flag to denote whether a bundle state change is in progress */
+ protected volatile Thread stateChanging;
+
+ /** Bundle's BundleData object */
+ protected BundleData bundledata;
+
+ /** The unique identifier */
+ protected long id;
+
+ /** The identity string for the bundle */
+ protected String location;
+
+ /** Internal object used for state change synchronization */
+ protected Object statechangeLock = new Object();
+
+ /** ProtectionDomain for the bundle */
+ protected ProtectionDomain domain;
+
+ /** Bundle assigned startlevel */
+ protected int startLevel;
+
+ /**
+ * This exception captures the package names that could not be resolved.
+ * This information is collected by importPackages, but the
+ * exception cannot be thrown during the resolve phase. It is saved
+ * here to be thrown later (by Bundle.start for example).
+ */
+ protected BundleException resolveException;
+
+ public static boolean MONITOR_BUNDLES = false;
+
+ /**
+ * Bundle object constructor.
+ * This constructor should not perform any real work.
+ *
+ * @param id Unique context id assigned to bundle
+ * @param bundleFile the bundle's file
+ * @param localStore adaptor specific object for the bundle's local storage
+ * @param manifest Bundle's manifest
+ * @param location identity string for the bundle
+ * @param framework Framework this bundle is running in
+ */
+ protected static Bundle createBundle(BundleData bundledata, String location, Framework framework, int startLevel) throws BundleException
+ {
+ if (bundledata.isFragment())
+ return new BundleFragment(bundledata,location,framework,startLevel);
+ else
+ return new BundleHost(bundledata,location,framework,startLevel);
+ }
+ /**
+ * Bundle object constructor.
+ * This constructor should not perform any real work.
+ *
+ * @param id Unique context id assigned to bundle
+ * @param bundleFile the bundle's file
+ * @param localStore adaptor specific object for the bundle's local storage
+ * @param manifest Bundle's manifest
+ * @param location identity string for the bundle
+ * @param framework Framework this bundle is running in
+ */
+ protected Bundle(BundleData bundledata, String location, Framework framework, int startLevel) throws BundleException
+ {
+ state = INSTALLED;
+ stateChanging = null;
+
+ this.id = bundledata.getBundleID();
+ this.bundledata = bundledata;
+ this.location = location;
+ this.framework = framework;
+ this.startLevel = startLevel;
+ bundledata.setBundle(this);
+ }
+
+ /**
+ * Load the bundle.
+ * @exception org.osgi.framework.BundleException
+ */
+ protected abstract void load() throws BundleException;
+
+ /**
+ * Reload from a new bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @param newBundle Dummy Bundle which contains new data.
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ * @exception org.osgi.framework.BundleException
+ */
+ protected abstract boolean reload(Bundle newBundle) throws BundleException;
+
+ /**
+ * Refresh the bundle. This is called by Framework.refreshPackages.
+ * This method must be called while holding the bundles lock.
+ * this.loader.unimportPackages must have already been called before calling
+ * this method!
+ *
+ * @exception org.osgi.framework.BundleException if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected abstract void refresh() throws BundleException;
+
+ /**
+ * Unload the bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected abstract boolean unload();
+
+ /**
+ * Close the the Bundle's file.
+ *
+ */
+ protected void close()
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (INSTALLED)) == 0)
+ {
+ Debug.println("Bundle.close called when state != INSTALLED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ state = UNINSTALLED;
+ }
+
+ /**
+ * Load and instantiate bundle's BundleActivator class
+ */
+ protected BundleActivator loadBundleActivator() throws BundleException
+ {
+ /* load Bundle's BundleActivator if it has one */
+ String activatorClassName = (String)bundledata.getActivator();
+ if (activatorClassName != null)
+ {
+ try
+ {
+ Class activatorClass = loadClass(activatorClassName,false);
+
+ /* Create the activator for the bundle*/
+ return (BundleActivator)(activatorClass.newInstance());
+
+ }
+ catch (Throwable t)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.printStackTrace(t);
+ }
+ throw new BundleException(Msg.formatter.getString("BUNDLE_INVALID_ACTIVATOR_EXCEPTION", activatorClassName), t);
+ }
+ }
+
+ return(null);
+ }
+
+ /**
+ * This method loads a class from the bundle.
+ *
+ * @param name the name of the desired Class.
+ * @param checkPermission indicates whether a permission check should be done.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException if the class definition was not found.
+ */
+ protected abstract Class loadClass(String name, boolean checkPermission) throws ClassNotFoundException;
+
+ /**
+ * Find the specified resource in this bundle.
+ *
+ * This bundle's class loader is called to search for the named resource.
+ * If this bundle's state is <tt>INSTALLED</tt>, then only this bundle will
+ * be searched for the specified resource. Imported packages cannot be searched
+ * when a bundle has not been resolved.
+ *
+ * @param name The name of the resource.
+ * See <tt>java.lang.ClassLoader.getResource</tt> for a description of
+ * the format of a resource name.
+ * @return a URL to the named resource, or <tt>null</tt> if the resource could
+ * not be found or if the caller does not have
+ * the <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ *
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ */
+ public abstract URL getResource(String name);
+
+ /**
+ * Returns the current state of the bundle.
+ *
+ * A bundle can only be in one state at any time.
+ *
+ * @return bundle's state.
+ */
+ public int getState()
+ {
+ return(state);
+ }
+
+ /**
+ * Return true if the bundle is starting or active.
+ *
+ */
+ protected boolean isActive()
+ {
+ return((state & (ACTIVE | STARTING)) != 0);
+ }
+
+ /**
+ * Return true if the bundle is resolved.
+ *
+ */
+ protected boolean isResolved()
+ {
+ return (state & (INSTALLED | UNINSTALLED)) == 0;
+ }
+
+ /**
+ * Start this bundle.
+ *
+ * If the current start level is less than this bundle's start level, then the
+ * Framework must persistently mark this bundle as started and delay the
+ * starting of this bundle until the Framework's current start level becomes
+ * equal or more than the bundle's start level.
+ * <p>Otherwise, the following steps are required to start a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED} then
+ * an <code>IllegalStateException</code> is thrown.
+ * <li>If the bundle is {@link #ACTIVE} or {@link #STARTING}
+ * then this method returns immediately.
+ * <li>If the bundle is {@link #STOPPING}
+ * then this method may wait for the bundle to return
+ * to the {@link #RESOLVED} state before continuing. If this does not occur
+ * in a reasonable time, a {@link BundleException} is thrown to indicate
+ * the bundle was unable to be started.
+ * <li>If the bundle is not {@link #RESOLVED},
+ * an attempt is made to resolve the bundle. If the bundle cannot be
+ * resolved, a {@link BundleException} is thrown.
+ * <li>The state of the bundle is set to {@link #STARTING}.
+ * <li>The {@link BundleActivator#start start} method of the bundle's
+ * {@link BundleActivator}, if one is specified, is called.
+ * If the {@link BundleActivator}
+ * is invalid or throws an exception, the state of the bundle
+ * is set back to {@link #RESOLVED},
+ * the bundle's listeners, if any, are removed,
+ * service's registered by the bundle, if any, are unregistered,
+ * and service's used by the bundle, if any, are released.
+ * A {@link BundleException} is then thrown.
+ * <li>It is recorded that this bundle has been started, so that when
+ * the framework is restarted, this bundle will be automatically started.
+ * <li>The state of the bundle is set to {@link #ACTIVE}.
+ * <li>A {@link BundleEvent} of type {@link BundleEvent#STARTED} is broadcast.
+ * </ol>
+ *
+ * <h5>Preconditons</h5>
+ * <ul>
+ * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() in {{@link #ACTIVE}}.
+ * <li>{@link BundleActivator#start BundleActivator.start} has been called and did not
+ * throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #STARTING}, {@link #ACTIVE}}.
+ * </ul>
+ *
+ * @exception BundleException If the bundle couldn't be started.
+ * This could be because a code dependency could not be resolved or
+ * the specified BundleActivator could not be loaded or threw an exception.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled or the
+ * bundle tries to change its own state.
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link AdminPermission} permission and the Java runtime environment
+ * supports permissions.
+ */
+
+ public void start() throws BundleException
+ {
+ long start = 0;
+ if (Debug.DEBUG && Debug.DEBUG_BUNDLE_TIME) {
+ start = System.currentTimeMillis();
+ System.out.println("Starting " + getGlobalName());
+ }
+ framework.checkAdminPermission();
+ checkValid();
+ beginStateChange();
+ try
+ {
+ startWorker(true);
+ }
+ finally
+ {
+ completeStateChange();
+ }
+ if (Debug.DEBUG && Debug.DEBUG_BUNDLE_TIME)
+ System.out.println("End starting " + getGlobalName() + " " + (System.currentTimeMillis() - start));
+ }
+
+ /**
+ * Internal worker to start a bundle.
+ *
+ * @param persistent if true persistently record the bundle was started.
+ */
+ protected abstract void startWorker(boolean persistent) throws BundleException;
+
+ /**
+ * Start this bundle w/o marking is persistently started.
+ *
+ * <p>The following steps are followed to start a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED} then
+ * an <code>IllegalStateException</code> is thrown.
+ * <li>If the bundle is {@link #ACTIVE} or {@link #STARTING}
+ * then this method returns immediately.
+ * <li>If the bundle is {@link #STOPPING}
+ * then this method may wait for the bundle to return
+ * to the {@link #RESOLVED} state before continuing. If this does not occur
+ * in a reasonable time, a {@link BundleException} is thrown to indicate
+ * the bundle was unable to be started.
+ * <li>If the bundle is not {@link #RESOLVED},
+ * an attempt is made to resolve the bundle. If the bundle cannot be
+ * resolved, a {@link BundleException} is thrown.
+ * <li>The state of the bundle is set to {@link #STARTING}.
+ * <li>The {@link BundleActivator#start start} method of the bundle's
+ * {@link BundleActivator}, if one is specified, is called.
+ * If the {@link BundleActivator}
+ * is invalid or throws an exception, the state of the bundle
+ * is set back to {@link #RESOLVED},
+ * the bundle's listeners, if any, are removed,
+ * service's registered by the bundle, if any, are unregistered,
+ * and service's used by the bundle, if any, are released.
+ * A {@link BundleException} is then thrown.
+ * <li>The state of the bundle is set to {@link #ACTIVE}.
+ * <li>A {@link BundleEvent} of type {@link BundleEvent#STARTED} is broadcast.
+ * </ol>
+ *
+ * <h5>Preconditons</h5>
+ * <ul>
+ * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() in {{@link #ACTIVE}}.
+ * <li>{@link BundleActivator#start BundleActivator.start} has been called and did not
+ * throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #STARTING}, {@link #ACTIVE}}.
+ * </ul>
+ *
+ * @exception BundleException If the bundle couldn't be started.
+ * This could be because a code dependency could not be resolved or
+ * the specified BundleActivator could not be loaded or threw an exception.
+ * @exception java.lang.IllegalStateException If the
+ * bundle tries to change its own state.
+ */
+ protected void resume() throws BundleException
+ {
+ if (state == UNINSTALLED)
+ {
+ return;
+ }
+
+ beginStateChange();
+ try
+ {
+ startWorker(false);
+ }
+ finally
+ {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Stop this bundle.
+ *
+ * Any services registered by this bundle will be unregistered.
+ * Any services used by this bundle will be released.
+ * Any listeners registered by this bundle will be removed.
+ *
+ * <p>The following steps are followed to stop a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED} then
+ * an <code>IllegalStateException</code> is thrown.
+ * <li>If the bundle is {@link #STOPPING}, {@link #RESOLVED},
+ * or {@link #INSTALLED}
+ * then this method returns immediately.
+ * <li>If the bundle is {@link #STARTING}
+ * then this method may wait for the bundle to reach
+ * the {@link #ACTIVE} state before continuing. If this does not occur
+ * in a reasonable time, a {@link BundleException} is thrown to indicate
+ * the bundle was unable to be stopped.
+ * <li>The state of the bundle is set to {@link #STOPPING}.
+ * <li>It is recorded that this bundle has been stopped, so that when
+ * the framework is restarted, this bundle will not be automatically started.
+ * <li>The {@link BundleActivator#stop stop} method of the bundle's
+ * {@link BundleActivator}, if one is specified, is called.
+ * If the {@link BundleActivator}
+ * throws an exception, this method will continue to stop the bundle.
+ * A {@link BundleException} will be thrown after completion of the remaining steps.
+ * <li>The bundle's listeners, if any, are removed,
+ * service's registered by the bundle, if any, are unregistered, and
+ * service's used by the bundle, if any, are released.
+ * <li>The state of the bundle is set to {@link #RESOLVED}.
+ * <li>A {@link BundleEvent} of type {@link BundleEvent#STOPPED} is broadcast.
+ * </ol>
+ *
+ * <h5>Preconditons</h5>
+ * <ul>
+ * <li>getState() in {{@link #ACTIVE}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #ACTIVE}, {@link #STOPPING}}.
+ * <li>{@link BundleActivator#stop BundleActivator.stop} has been called and did not
+ * throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>None.
+ * </ul>
+ *
+ * @exception BundleException If the bundle's
+ * BundleActivator could not be loaded or threw an exception.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled or the
+ * bundle tries to change its own state.
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link AdminPermission} permission and the Java runtime environment
+ * supports permissions.
+ */
+ public void stop() throws BundleException
+ {
+ framework.checkAdminPermission();
+
+ checkValid();
+
+ beginStateChange();
+ try
+ {
+ stopWorker(true);
+ }
+ finally
+ {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Internal worker to stop a bundle.
+ *
+ * @param persistent if true persistently record the bundle was stopped.
+ */
+ protected abstract void stopWorker(boolean persistent) throws BundleException;
+
+ /**
+ * Set the persistent status bit for the bundle.
+ *
+ * @param mask Mask for bit to set/clear
+ * @param state true to set bit, false to clear bit
+ */
+ protected void setStatus(final int mask, final boolean state)
+
+ {
+ try
+ {
+ AccessController.doPrivileged(new PrivilegedExceptionAction()
+ {
+ public Object run() throws BundleException, IOException
+ {
+ int status = bundledata.getStatus();
+ boolean test = ((status & mask) != 0);
+ if (test != state)
+ {
+ bundledata.setStatus(state ? (status | mask) : (status & ~mask));
+ bundledata.save();
+ }
+
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException pae)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, pae.getException());
+ }
+ }
+
+ /**
+ * Stop this bundle w/o marking is persistently stopped.
+ *
+ * Any services registered by this bundle will be unregistered.
+ * Any services used by this bundle will be released.
+ * Any listeners registered by this bundle will be removed.
+ *
+ * <p>The following steps are followed to stop a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED} then
+ * an <code>IllegalStateException</code> is thrown.
+ * <li>If the bundle is {@link #STOPPING}, {@link #RESOLVED},
+ * or {@link #INSTALLED}
+ * then this method returns immediately.
+ * <li>If the bundle is {@link #STARTING}
+ * then this method may wait for the bundle to reach
+ * the {@link #ACTIVE} state before continuing. If this does not occur
+ * in a reasonable time, a {@link BundleException} is thrown to indicate
+ * the bundle was unable to be stopped.
+ * <li>The state of the bundle is set to {@link #STOPPING}.
+ * <li>The {@link BundleActivator#stop stop} method of the bundle's
+ * {@link BundleActivator}, if one is specified, is called.
+ * If the {@link BundleActivator}
+ * throws an exception, this method will continue to stop the bundle.
+ * A {@link BundleException} will be thrown after completion of the remaining steps.
+ * <li>The bundle's listeners, if any, are removed,
+ * service's registered by the bundle, if any, are unregistered, and
+ * service's used by the bundle, if any, are released.
+ * <li>The state of the bundle is set to {@link #RESOLVED}.
+ * <li>A {@link BundleEvent} of type {@link BundleEvent#STOPPED} is broadcast.
+ * </ol>
+ *
+ * <h5>Preconditons</h5>
+ * <ul>
+ * <li>getState() in {{@link #ACTIVE}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #ACTIVE}, {@link #STOPPING}}.
+ * <li>{@link BundleActivator#stop BundleActivator.stop} has been called and did not
+ * throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>None.
+ * </ul>
+ *
+ * @param lock true if state change lock should be held
+ * when returning from this method.
+ * @exception BundleException If the bundle's
+ * BundleActivator could not be loaded or threw an exception.
+ * @exception java.lang.IllegalStateException If the
+ * bundle tries to change its own state.
+ */
+ protected void suspend(boolean lock) throws BundleException
+ {
+ if (state == UNINSTALLED)
+ {
+ return;
+ }
+
+ beginStateChange();
+ try
+ {
+ stopWorker(false);
+ }
+ finally
+ {
+ if (!lock)
+ {
+ completeStateChange();
+ }
+ }
+ }
+
+ /**
+ * Update this bundle. If the bundle is {@link #ACTIVE}, the bundle will be stopped
+ * before the update and started after the update successfully completes.
+ *
+ * <p>The following steps are followed to update a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED} then
+ * an <code>IllegalStateException</code> is thrown.
+ * <li>If the bundle is {@link #ACTIVE} or {@link #STARTING},
+ * the bundle is stopped as described in the {@link #stop} method.
+ * If {@link #stop} throws an exception, the exception is rethrown
+ * terminating the update.
+ * <li>The location for the new version of the bundle is determined from either the
+ * manifest header <code>Bundle-UpdateLocation</code> if available or the original
+ * location.
+ * <li>The location is interpreted in an implementation dependent way
+ * (typically as a URL) and the new version of the bundle is obtained from the location.
+ * <li>The new version of the bundle is installed. If the framework is unable
+ * to install the new version of the bundle, the original version of the bundle will
+ * be restored and a {@link BundleException} will be thrown
+ * after completion of the remaining steps.
+ * <li>The state of the bundle is set to {@link #INSTALLED}.
+ * <li>If the new version of the bundle was successfully installed,
+ * a {@link BundleEvent} of type {@link BundleEvent#UPDATED} is broadcast.
+ * <li>If the bundle was originally {@link #ACTIVE},
+ * the updated bundle is started as described in the {@link #start} method.
+ * If {@link #start} throws an exception,
+ * a {@link FrameworkEvent} of type {@link FrameworkEvent#ERROR} is broadcast
+ * containing the exception.
+ * </ol>
+ *
+ * <h5>Preconditions</h5>
+ * <ul>
+ * <li>getState() not in {{@link #UNINSTALLED}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED},{@link #ACTIVE}}.
+ * <li>The bundle has been updated.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED},{@link #ACTIVE}}.
+ * <li>Original bundle is still used, no update took place.
+ * </ul>
+ *
+ * @exception BundleException If the update fails.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled or the
+ * bundle tries to change its own state.
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link AdminPermission} permission and the Java runtime environment
+ * supports permissions.
+ * @see #stop()
+ * @see #start()
+ */
+ public void update() throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("update location "+location);
+ }
+
+ framework.checkAdminPermission();
+
+ checkValid();
+
+ beginStateChange();
+ try
+ {
+ updateWorker(new PrivilegedExceptionAction()
+ {
+ public Object run() throws BundleException
+ {
+ /* compute the update location */
+ String updateLocation = location;
+ if (bundledata.getManifest().get(Constants.BUNDLE_UPDATELOCATION) != null)
+ {
+ updateLocation = (String)bundledata.getManifest().get(Constants.BUNDLE_UPDATELOCATION);
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println(" from location: " + updateLocation);
+ }
+ }
+
+ /* Map the identity to a URLConnection */
+ URLConnection source = framework.adaptor.mapLocationToURLConnection(updateLocation);
+
+ /* call the worker */
+ updateWorkerPrivileged(source);
+
+ return null;
+ }
+ });
+ }
+ catch (BundleException e)
+ {
+ if (!(e.getNestedException() instanceof NothingToUpdateException))
+ {
+ throw e;
+ }
+
+ //There is no work to be done with this update.
+ //Quietly return with no error.
+ }
+ finally
+ {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Update this bundle from an InputStream.
+ *
+ * <p>This method performs all the steps listed in
+ * {@link #update}, except the bundle
+ * will be read in through the supplied <code>InputStream</code>, rather
+ * than a <code>URL</code>.
+ *
+ * @param in The InputStream from which to read the new bundle.
+ * @see #update()
+ */
+ public void update(final InputStream in) throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("update location "+location);
+ Debug.println(" from: "+in);
+ }
+
+ framework.checkAdminPermission();
+
+ checkValid();
+
+ beginStateChange();
+ try
+ {
+ updateWorker(new PrivilegedExceptionAction()
+ {
+ public Object run() throws BundleException
+ {
+ /* Map the InputStream to a URLConnection */
+ URLConnection source = new BundleSource(in);
+
+ /* call the worker */
+ updateWorkerPrivileged(source);
+
+ return null;
+ }
+ });
+ }
+ finally
+ {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Update worker. Assumes the caller has the state change lock.
+ */
+ protected void updateWorker(PrivilegedExceptionAction action) throws BundleException
+ {
+ boolean bundleActive = false;
+ Bundle host=null;
+ if (isFragment()) {
+ host = (Bundle) getHost();
+ bundleActive = (host == null ? false : (host.state == ACTIVE));
+ }
+ else {
+ bundleActive = (state == ACTIVE);
+ }
+
+ if (bundleActive)
+ {
+ try
+ {
+ if (isFragment()){
+ host.stopWorker(false);
+ }
+ else {
+ stopWorker(false);
+ }
+ }
+ catch (BundleException e)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+
+ if (state == ACTIVE) /* if the bundle is still active */
+ {
+ throw e;
+ }
+ }
+ }
+
+ try
+ {
+ AccessController.doPrivileged(action);
+
+ framework.publishBundleEvent(BundleEvent.UPDATED, this);
+ }
+ catch (PrivilegedActionException pae)
+ {
+ throw (BundleException)pae.getException();
+ }
+ finally
+ {
+ if (bundleActive)
+ {
+ try
+ {
+ if (isFragment()){
+ host.startWorker(false);
+ }
+ else {
+ startWorker(false);
+ }
+ }
+ catch (BundleException e)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update worker. Assumes the caller has the state change lock.
+ */
+ protected void updateWorkerPrivileged(URLConnection source) throws BundleException
+ {
+ Bundle oldBundle = Bundle.createBundle(this.bundledata, this.location, framework, this.startLevel);
+ boolean reloaded = false;
+
+ BundleOperation storage = framework.adaptor.updateBundle(this.bundledata, source);
+ BundleRepository bundles = framework.getBundles();
+
+ try
+ {
+ BundleData newBundleData = storage.begin();
+ // Must call framework createBundle to check execution environment.
+ Bundle newBundle = framework.createBundle(newBundleData,this.location,this.startLevel);
+
+ // Check for a bundle already installed with the same UniqueId
+ // and version.
+ Bundle installedBundle =
+ framework.getBundleByUniqueId(newBundle.getGlobalName(), newBundle.getVersion().toString());
+ if (installedBundle != null && installedBundle != this) {
+ throw new BundleException(
+ Msg.formatter.getString(
+ "BUNDLE_INSTALL_SAME_UNIQUEID",
+ newBundle.getGlobalName(), newBundle.getVersion().toString()));
+ }
+
+ String[] nativepaths = framework.selectNativeCode(newBundle);
+ if (nativepaths != null) {
+ bundledata.installNativeCode(nativepaths);
+ }
+
+ boolean exporting;
+
+ synchronized (bundles)
+ {
+ bundles.markDependancies();
+ exporting = reload(newBundle);
+ }
+ reloaded = true; /* indicate we have loaded from the new version of the bundle */
+
+ storage.commit(exporting);
+ }
+ catch (BundleException e)
+ {
+ try
+ {
+ storage.undo();
+
+ if (reloaded) /* if we loaded from the new version of the bundle */
+ {
+ synchronized (bundles)
+ {
+ reload(oldBundle); /* revert to old version */
+ }
+ }
+ }
+ catch (BundleException ee)
+ {
+ /* if we fail to revert then we are in big trouble */
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee);
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ * Uninstall this bundle.
+ * <p>This method removes all traces of the bundle, including any data in the
+ * persistent storage area provided for the bundle by the framework.
+ *
+ * <p>The following steps are followed to uninstall a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED} then
+ * an <code>IllegalStateException</code> is thrown.
+ * <li>If the bundle is {@link #ACTIVE} or {@link #STARTING},
+ * the bundle is stopped as described in the {@link #stop} method.
+ * If {@link #stop} throws an exception,
+ * a {@link FrameworkEvent} of type {@link FrameworkEvent#ERROR} is broadcast
+ * containing the exception.
+ * <li>A {@link BundleEvent} of type {@link BundleEvent#UNINSTALLED} is broadcast.
+ * <li>The state of the bundle is set to {@link #UNINSTALLED}.
+ * <li>The bundle and the
+ * persistent storage area provided for the bundle by the framework,
+ * if any, is removed.
+ * </ol>
+ *
+ * <h5>Preconditions</h5>
+ * <ul>
+ * <li>getState() not in {{@link #UNINSTALLED}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() in {{@link #UNINSTALLED}}.
+ * <li>The bundle has been uninstalled.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #UNINSTALLED}}.
+ * <li>The Bundle has not been uninstalled.
+ * </ul>
+ *
+ * @exception BundleException If the uninstall failed.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled or the
+ * bundle tries to change its own state.
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link AdminPermission} permission and the Java runtime environment
+ * supports permissions.
+ * @see #stop()
+ */
+ public void uninstall() throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("uninstall location: " + location);
+ }
+
+ framework.checkAdminPermission();
+
+ checkValid();
+
+ beginStateChange();
+ try
+ {
+ uninstallWorker(new PrivilegedExceptionAction()
+ {
+ public Object run() throws BundleException
+ {
+ uninstallWorkerPrivileged();
+
+ return null;
+ }
+ });
+ } finally
+ {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Uninstall worker. Assumes the caller has the state change lock.
+ */
+ protected void uninstallWorker(PrivilegedExceptionAction action) throws BundleException
+ {
+ boolean bundleActive = false;
+ Bundle host=null;
+ if (isFragment()) {
+ host = (Bundle) getHost();
+ bundleActive = (host == null ? false : (host.state == ACTIVE));
+ }
+ else {
+ bundleActive = (state == ACTIVE);
+ }
+
+ if (bundleActive)
+ {
+ try
+ {
+ if (isFragment()){
+ host.stopWorker(true);
+ }
+ else {
+ stopWorker(true);
+ }
+ }
+ catch (BundleException e)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ }
+ }
+
+ try
+ {
+ AccessController.doPrivileged(action);
+ }
+ catch (PrivilegedActionException pae)
+ {
+ if (bundleActive) /* if we stopped the bundle */
+ {
+ try
+ {
+ if (isFragment()){
+ host.startWorker(false);
+ }
+ else {
+ startWorker(false);
+ }
+ }
+ catch (BundleException e)
+ {
+ /* if we fail to start the original bundle then we are in big trouble */
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ }
+ // set the bundleActive to false so that the finally will not try
+ // to start the bundle again.
+ bundleActive = false;
+ }
+
+ throw (BundleException)pae.getException();
+ }
+ finally {
+ if (isFragment() && bundleActive) {
+ try {
+ host.startWorker(false);
+ }
+ catch (BundleException e)
+ {
+ /* if we fail to start the original host bundle then we are in big trouble */
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ }
+ }
+ }
+
+ framework.publishBundleEvent(BundleEvent.UNINSTALLED, this);
+ }
+
+ /**
+ * Uninstall worker. Assumes the caller has the state change lock.
+ */
+ protected void uninstallWorkerPrivileged() throws BundleException
+ {
+ boolean unloaded = false;
+
+ BundleOperation storage = framework.adaptor.uninstallBundle(this.bundledata);
+
+ BundleRepository bundles = framework.getBundles();
+
+ try
+ {
+ storage.begin();
+
+ boolean exporting;
+
+ synchronized (bundles)
+ {
+ bundles.markDependancies();
+ bundles.remove(this); /* remove before calling unload */
+ exporting = unload();
+ }
+ unloaded = true;
+
+ storage.commit(exporting);
+
+ close();
+ }
+ catch (BundleException e)
+ {
+ try
+ {
+ storage.undo();
+
+ if (unloaded) /* if we unloaded the bundle */
+ {
+ synchronized (bundles) {
+ load(); /* reload the bundle */
+
+ bundles.add(this);
+ }
+ }
+ }
+ catch (BundleException ee)
+ {
+ /* if we fail to load the original bundle then we are in big trouble */
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee);
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ * Return the bundle's manifest headers and values from the manifest's
+ * preliminary section. That is all the manifest's headers and values
+ * prior to the first blank line.
+ *
+ * <p>Manifest header names are case-insensitive. The methods of the
+ * returned <code>Dictionary</code> object will operate on header names
+ * in a case-insensitive manner.
+ *
+ * <p>For example, the following manifest headers and values are included
+ * if they are present in the manifest:
+ * <pre>
+ * Bundle-Name
+ * Bundle-Vendor
+ * Bundle-Version
+ * Bundle-Description
+ * Bundle-DocURL
+ * Bundle-ContactAddress
+ * </pre>
+ *
+ * <p>This method will continue to return this information
+ * when the bundle is in the {@link #UNINSTALLED} state.
+ *
+ * @return A <code>Dictionary</code> object containing
+ * the bundle's manifest headers and values.
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link AdminPermission} permission and the Java runtime environment
+ * supports permissions.
+ */
+ public Dictionary getHeaders()
+ {
+ framework.checkAdminPermission();
+ return bundledata.getHeaders();
+ }
+
+ /**
+ * Retrieve the bundle's unique identifier, which the framework
+ * assigned to this bundle when it was installed.
+ *
+ * <p>The unique identifier has the following attributes:
+ * <ul>
+ * <li>It is unique and persistent.
+ * <li>The identifier is a long.
+ * <li>Once its value is assigned to a bundle, that value is
+ * not reused for another bundle, even after the bundle
+ * is uninstalled.
+ * <li>Its value does not change as long as the bundle remains installed.
+ * <li>Its value does not change when the bundle is updated
+ * </ul>
+ *
+ * <p>This method will continue to return the bundle's unique identifier
+ * when the bundle is in the {@link #UNINSTALLED} state.
+ *
+ * @return This bundle's unique identifier.
+ */
+ public long getBundleId()
+ {
+ return(id);
+ }
+
+
+ /**
+ * Retrieve the location identifier of the bundle.
+ * This is typically the location passed to
+ * {@link BundleContext#installBundle BundleContext.installBundle}
+ * when the bundle was installed. The location identifier of the
+ * bundle may change during bundle update.
+ * Calling this method while framework is updating
+ * the bundle results in undefined behavior.
+ *
+ * <p>This method will continue to return the bundle's location
+ * identifier when the bundle is in the {@link #UNINSTALLED} state.
+ *
+ * @return A string that is the location identifier of the bundle.
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link AdminPermission} permission and the Java runtime environment
+ * supports permissions.
+ */
+ public String getLocation()
+ {
+ framework.checkAdminPermission();
+
+ return(location);
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the services
+ * registered by this bundle
+ * or <code>null</code> if the bundle has no registered
+ * services.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceRegistration
+ * @see ServiceReference
+ */
+ public abstract org.osgi.framework.ServiceReference[] getRegisteredServices();
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the
+ * services this bundle is using,
+ * or <code>null</code> if the bundle is not using any services.
+ * A bundle is considered to be using a service if the bundle's
+ * use count for the service is greater than zero.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceReference
+ */
+ public abstract org.osgi.framework.ServiceReference[] getServicesInUse();
+
+ /**
+ * Determine whether the bundle has the requested
+ * permission.
+ *
+ * <p>If the Java runtime environment does not supports permissions
+ * this method always returns <code>true</code>.
+ * The permission parameter is of type <code>Object</code> to
+ * avoid referencing the <code>java.security.Permission</code> class
+ * directly. This is to allow the framework to be implemented in Java
+ * environments which do not support permissions.
+ *
+ * @param permission The requested permission.
+ * @return <code>true</code> if the bundle has the requested permission or
+ * <code>false</code> if the bundle does not have the permission
+ * or the permission parameter is not an
+ * <code>instanceof java.security.Permission</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ */
+ public boolean hasPermission(Object permission)
+ {
+ checkValid();
+
+ if (domain != null)
+ {
+ if (permission instanceof Permission)
+ {
+ return domain.implies((Permission)permission);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * This method marks the bundle's state as changing so that other calls to
+ * start/stop/suspend/update/uninstall can wait until the state change is complete.
+ * If stateChanging is non-null when this method is called, we will wait for the
+ * state change to complete. If the timeout expires without changing state (this may
+ * happen if the state change is back up our call stack), a BundleException is thrown
+ * so that we don't wait forever.
+ *
+ * A call to this method should be immediately followed by a try block whose
+ * finally block calls completeStateChange().
+ *
+ * beginStateChange();
+ * try
+ * {
+ * // change the bundle's state here...
+ * }
+ * finally
+ * {
+ * completeStateChange();
+ * }
+ *
+ * @exception org.osgi.framework.BundleException if the bundles state
+ * is still changing after waiting for the timeout.
+ */
+ protected void beginStateChange() throws BundleException
+ {
+ synchronized (statechangeLock)
+ {
+ boolean doubleFault = false;
+ while (true)
+ {
+ if (stateChanging == null)
+ {
+ stateChanging = Thread.currentThread();
+ return;
+ }
+
+ if (doubleFault || (stateChanging == Thread.currentThread()))
+ {
+ throw new BundleException(Msg.formatter.getString("BUNDLE_STATE_CHANGE_EXCEPTION"));
+ }
+
+ try
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println(" Waiting for state to change in bundle "+this);
+ }
+ long start = 0;
+ if (Debug.DEBUG)
+ start = System.currentTimeMillis();
+ statechangeLock.wait(5000); /* wait for other thread to finish changing state */
+ if (Debug.DEBUG) {
+ long end = System.currentTimeMillis();
+ if (end - start > 0) {
+ System.out.println("Waiting... : " + getGlobalName() + " " + (end -start) );
+ }
+ }
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ doubleFault = true;
+ }
+ }
+ }
+
+ /**
+ * This method completes the bundle state change by setting stateChanging
+ * to null and notifying one waiter that the state change has completed.
+ */
+ protected void completeStateChange()
+ {
+ synchronized (statechangeLock)
+ {
+ if (stateChanging != null)
+ {
+ stateChanging = null;
+ statechangeLock.notify(); /* notify one waiting thread that the state change is complete */
+ }
+ }
+ }
+
+ /**
+ * Return a string representation of this bundle.
+ *
+ * @return String
+ */
+ public String toString()
+ {
+ return(location+" ["+id+"]");
+ }
+
+ /**
+ * Answers an integer indicating the relative positions
+ * of the receiver and the argument in the natural order
+ * of elements of the receiver's class.
+ *
+ * @return int
+ * which should be <0 if the receiver
+ * should sort before the argument, 0
+ * if the receiver should sort in the
+ * same position as the argument, and
+ * >0 if the receiver should sort after
+ * the argument.
+ * @param obj another Bundle
+ * an object to compare the receiver to
+ * @exception ClassCastException
+ * if the argument can not be converted
+ * into something comparable with the
+ * receiver.
+ */
+ public int compareTo(Object obj)
+ {
+ int slcomp = startLevel - ((Bundle)obj).startLevel;
+ if (slcomp != 0)
+ {
+ return slcomp;
+ }
+
+ long idcomp = id - ((Bundle)obj).id;
+ return (idcomp < 0L) ? -1 : ((idcomp > 0L) ? 1 : 0);
+ }
+
+ /**
+ * This method checks that the bundle is not uninstalled. If the bundle is
+ * uninstalled, an IllegalStateException is thrown.
+ *
+ * @exception java.lang.IllegalStateException
+ * If the bundle is uninstalled.
+ */
+ protected void checkValid()
+ {
+ if (state == UNINSTALLED)
+ {
+ throw new IllegalStateException(Msg.formatter.getString("BUNDLE_UNINSTALLED_EXCEPTION"));
+ }
+ }
+
+ /**
+ * Get the bundle's ProtectionDomain.
+ *
+ * @return bundle's ProtectionDomain.
+ */
+ protected ProtectionDomain getProtectionDomain()
+ {
+ return domain;
+ }
+
+ /**
+ * The bundle must unresolve the permissions in these packages.
+ *
+ * @param unresolvedPackages A list of the package which have been unresolved
+ * as a result of a packageRefresh
+ */
+ protected void unresolvePermissions(Hashtable unresolvedPackages)
+ {
+ if (domain != null)
+ {
+ BundlePermissionCollection collection = (BundlePermissionCollection) domain.getPermissions();
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unresolving permissions in bundle "+this);
+ }
+
+ collection.unresolvePermissions(unresolvedPackages);
+ }
+ }
+
+
+
+ public org.osgi.framework.Bundle[] getFragments() {
+ checkValid();
+ return null;
+ }
+
+ public boolean isFragment() {
+ return false;
+ }
+
+ public org.osgi.framework.Bundle getHost() {
+ checkValid();
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.framework.Bundle#findClass(java.lang.String)
+ */
+ public Class loadClass(String classname) throws ClassNotFoundException {
+ return loadClass(classname, true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.framework.Bundle#getResourcePaths(java.lang.String)
+ */
+ public Enumeration getEntryPaths(final String path) {
+ try {
+ framework.checkAdminPermission();
+ }
+ catch (SecurityException e) {
+ return null;
+ }
+ checkValid();
+
+ if (bundledata == null) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Bundle.getResourcePaths("+path+") called when bundledata == null: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+
+ return(null);
+ }
+ return (Enumeration) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return bundledata.getEntryPaths(path);
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.framework.Bundle#getFile(java.lang.String)
+ */
+ public URL getEntry(String fileName) throws IOException {
+ try {
+ framework.checkAdminPermission();
+ }
+ catch (SecurityException e) {
+ return null;
+ }
+ checkValid();
+ if (bundledata == null) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Bundle.getFile("+fileName+") called when bundledata == null: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+
+ return(null);
+ }
+
+ if (System.getSecurityManager() == null)
+ return bundledata.getEntry(fileName);
+
+ final String ffileName = fileName;
+ return (URL) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return bundledata.getEntry(ffileName);
+ }
+ });
+
+ }
+
+ public String getGlobalName() {
+ return bundledata.getUniqueId();
+ }
+
+ public BundleData getBundleData() {
+ return bundledata;
+ }
+
+ public Version getVersion() {
+ return bundledata.getVersion();
+ }
+ protected BundleDescription getBundleDescription() {
+ return framework.adaptor.getState().getBundle(getBundleId());
+ }
+
+ public abstract BundleLoader getBundleLoader();
+
+ protected abstract void resolve();
+ protected abstract boolean unresolve() throws BundleException;
+
+ /**
+ * Return the current context for this bundle.
+ *
+ * @return BundleContext for this bundle.
+ */
+ abstract protected BundleContext getContext();
+
+ protected String getResolutionFailureMessage() {
+ String defaultMessage = Msg.formatter.getString("BUNDLE_UNRESOLVED_EXCEPTION");
+ // don't spend time if debug info is not needed
+ if (!Debug.DEBUG) {
+ return defaultMessage;
+ }
+ BundleDescription bundleDescription = getBundleDescription();
+ if (bundleDescription == null) {
+ return defaultMessage;
+ }
+ // just a sanity check - this would be an inconsistency between the framework and the state
+ if (bundleDescription.isResolved()) {
+ throw new IllegalStateException("bundle *is* resolved");
+ }
+ VersionConstraint[] unsatisfied = bundleDescription.getUnsatisfiedConstraints();
+ if (unsatisfied.length == 0) {
+ return Msg.formatter.getString("BUNDLE_UNRESOLVED_NOT_CHOSEN_EXCEPTION");
+ }
+ StringBuffer missing = new StringBuffer();
+ for (int i = 0; i < unsatisfied.length; i++) {
+ if (unsatisfied[i] instanceof PackageSpecification) {
+ missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_PACKAGE", toString(unsatisfied[i])));
+ } else if (unsatisfied[i] instanceof BundleSpecification) {
+ missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_BUNDLE", toString(unsatisfied[i])));
+ } else {
+ missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_HOST", toString(unsatisfied[i])));
+ }
+ missing.append(',');
+ }
+ missing.deleteCharAt(missing.length()-1);
+ return Msg.formatter.getString("BUNDLE_UNRESOLVED_UNSATISFIED_CONSTRAINT_EXCEPTION", missing.toString());
+ }
+ private String toString(VersionConstraint constraint) {
+ org.eclipse.osgi.service.resolver.Version versionSpec = constraint.getVersionSpecification();
+ if (versionSpec == null)
+ return constraint.getName();
+ return constraint.getName() + '_' + versionSpec;
+ }
+ public int getKeyHashCode() {
+ return (int)id;
+ }
+ public boolean compare(KeyedElement other) {
+ return id == ((Bundle)other).getBundleId();
+ }
+ public Object getKey() {
+ return new Long(id);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleCombinedPermissions.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleCombinedPermissions.java
new file mode 100644
index 000000000..08eb14403
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleCombinedPermissions.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.security.Permission;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.NoSuchElementException;
+
+/**
+ * A combination of two BundlePermissionCollection classes.
+ *
+ */
+final class BundleCombinedPermissions extends BundlePermissionCollection
+{
+ private BundlePermissionCollection assigned;
+ private BundlePermissionCollection implied;
+
+ /**
+ * Create a permission combiner class.
+ *
+ * @param implied The permissions a bundle always has.
+ */
+ BundleCombinedPermissions(BundlePermissionCollection implied)
+ {
+ this.implied = implied;
+
+ setReadOnly(); /* this doesn't really mean anything */
+ }
+
+ /**
+ * Assign the administrator defined permissions.
+ *
+ * @param assigned The permissions assigned by the administrator.
+ */
+ void setAssignedPermissions(BundlePermissionCollection assigned)
+ {
+ this.assigned = assigned;
+ }
+
+ /**
+ * The Permission collection will unresolve the permissions in these packages.
+ *
+ * @param unresolvedPackages A list of the package which have been unresolved
+ * as a result of a packageRefresh
+ */
+ void unresolvePermissions(Hashtable unresolvedPackages)
+ {
+ if (assigned != null)
+ {
+ assigned.unresolvePermissions(unresolvedPackages);
+ }
+
+ if (implied != null)
+ {
+ implied.unresolvePermissions(unresolvedPackages);
+ }
+ }
+
+ /**
+ * Adds the argument to the collection.
+ *
+ * @param permission java.security.Permission
+ * the permission to add to the collection.
+ * @exception SecurtityException
+ * if the collection is read only.
+ */
+ public void add(Permission permission)
+ {
+ throw new SecurityException();
+ }
+ /**
+ * Answers an enumeration of the permissions
+ * in the receiver.
+ *
+ * @return Enumeration
+ * the permissions in the receiver.
+ */
+ public Enumeration elements()
+ {
+ return new Enumeration() {
+ private int i = 0;
+ private Enumeration[] enums;
+
+ {
+ enums = new Enumeration[]
+ {
+ (assigned == null) ? null : assigned.elements(),
+ (implied == null) ? null : implied.elements()
+ };
+ }
+
+ /**
+ * Answers if this Enumeration has more elements.
+ *
+ * @return true if there are more elements, false otherwise
+ *
+ * @see #nextElement
+ */
+ public boolean hasMoreElements()
+ {
+ while (i < enums.length)
+ {
+ Enumeration enum = enums[i];
+ if ((enum != null) && enum.hasMoreElements())
+ {
+ return true;
+ }
+
+ i++;
+ }
+
+ return false;
+ }
+ /**
+ * Answers the next element in this Enumeration.
+ *
+ * @return the next element in this Enumeration
+ *
+ * @exception NoSuchElementException when there are no more elements
+ *
+ * @see #hasMoreElements
+ */
+ public Object nextElement()
+ {
+ while (i < enums.length)
+ {
+ try
+ {
+ Enumeration enum = enums[i];
+ if (enum != null)
+ {
+ return enum.nextElement();
+ }
+ }
+ catch (NoSuchElementException e)
+ {
+ }
+ i++;
+ }
+
+ throw new NoSuchElementException();
+ }
+ };
+ }
+
+ /**
+ * Indicates whether the argument permission is implied
+ * by the permissions contained in the receiver.
+ *
+ * @return boolean
+ * <code>true</code> if the argument permission
+ * is implied by the permissions in the receiver,
+ * and <code>false</code> if it is not.
+ * @param permission java.security.Permission
+ * the permission to check
+ */
+ public boolean implies(Permission permission)
+ {
+ return ((assigned != null) && assigned.implies(permission)) ||
+ ((implied != null) && implied.implies(permission));
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleContext.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleContext.java
new file mode 100644
index 000000000..78cedd6f4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleContext.java
@@ -0,0 +1,1629 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.File;
+import java.io.InputStream;
+import java.security.*;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.eventmgr.EventListeners;
+import org.eclipse.osgi.framework.eventmgr.EventSource;
+import org.osgi.framework.*;
+
+/**
+ * Bundle's execution context.
+ *
+ * This object is given out to bundles and wraps the internal
+ * BundleContext object. It is destroyed when a bundle is stopped.
+ */
+
+public class BundleContext implements org.osgi.framework.BundleContext, EventSource {
+ /** Bundle object this context is associated with. */
+ protected BundleHost bundle;
+
+ /** Internal framework object. */
+ protected Framework framework;
+
+ /** Services that bundle has used. Key is ServiceReference,
+ Value is ServiceUse */
+ protected Hashtable servicesInUse;
+
+ /** Listener list for bundle's BundleListeners */
+ protected EventListeners bundleEvent;
+
+ /** Listener list for bundle's SynchronousBundleListeners */
+ protected EventListeners bundleEventSync;
+
+ /** Listener list for bundle's ServiceListeners */
+ protected EventListeners serviceEvent;
+
+ /** Listener list for bundle's FrameworkListeners */
+ protected EventListeners frameworkEvent;
+
+ /** The current instantiation of the activator. */
+ protected BundleActivator activator;
+
+ /** The current instantiation of fragment activators */
+ protected BundleActivator[] fragmentActivators;
+
+ /** private object for locking */
+ protected Object contextLock = new Object();
+
+ /**
+ * Construct a BundleContext which wrappers the framework for a
+ * bundle
+ *
+ * @param bundle The bundle we are wrapping.
+ */
+ protected BundleContext(BundleHost bundle)
+ {
+ this.bundle = bundle;
+ framework = bundle.framework;
+ bundleEvent = null;
+ bundleEventSync = null;
+ serviceEvent = null;
+ frameworkEvent = null;
+ servicesInUse = null;
+ activator = null;
+ fragmentActivators = null;
+ }
+
+ /**
+ * Destroy the wrapper. This is called when the bundle is stopped.
+ *
+ */
+ protected void close()
+ {
+ bundle = null; /* invalidate context */
+
+ if (serviceEvent != null)
+ {
+ framework.serviceEvent.removeListener(this);
+ serviceEvent = null;
+ }
+ if (frameworkEvent != null)
+ {
+ framework.frameworkEvent.removeListener(this);
+ frameworkEvent = null;
+ }
+ if (bundleEvent != null)
+ {
+ framework.bundleEvent.removeListener(this);
+ bundleEvent = null;
+ }
+ if (bundleEventSync != null) {
+ framework.bundleEventSync.removeListener(this);
+ bundleEventSync = null;
+ }
+
+ /* service's registered by the bundle, if any, are unregistered. */
+
+ Vector registeredServices = null;
+ ServiceReference[] publishedReferences = null;
+ int regSize=0;
+ synchronized (framework.serviceRegistry) {
+ registeredServices = framework.serviceRegistry.lookupServiceReferences(this);
+ if (registeredServices != null){
+ regSize = registeredServices.size();
+ }
+
+ if (regSize > 0) {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
+ Debug.println("Unregistering services");
+ }
+
+ publishedReferences = new ServiceReference[regSize];
+ registeredServices.copyInto(publishedReferences);
+ }
+ }
+
+ for (int i = 0; i < regSize; i++) {
+ try {
+ publishedReferences[i].registration.unregister();
+ } catch (IllegalStateException e) {
+ /* already unregistered */
+ }
+ }
+
+ registeredServices = null;
+
+ /* service's used by the bundle, if any, are released. */
+ if (servicesInUse != null)
+ {
+ int usedSize;
+ ServiceReference[] usedRefs = null;
+
+ synchronized (servicesInUse)
+ {
+ usedSize = servicesInUse.size();
+
+ if (usedSize > 0)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("Releasing services");
+ }
+
+ usedRefs = new ServiceReference[usedSize];
+
+ Enumeration enum = servicesInUse.keys();
+ for (int i = 0; i < usedSize; i++)
+ {
+ usedRefs[i] = (ServiceReference)enum.nextElement();
+ }
+ }
+ }
+
+ for (int i = 0; i < usedSize; i++)
+ {
+ usedRefs[i].registration.releaseService(this);
+ }
+
+ servicesInUse = null;
+ }
+ }
+
+ /**
+ * Retrieve the value of the named environment property.
+ *
+ * @param key The name of the requested property.
+ * @return The value of the requested property, or <code>null</code> if
+ * the property is undefined.
+ */
+ public String getProperty(String key)
+ {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null)
+ {
+ sm.checkPropertyAccess(key);
+ }
+
+ return(framework.getProperty(key));
+ }
+
+ /**
+ * Retrieve the Bundle object for the context bundle.
+ *
+ * @return The context bundle's Bundle object.
+ */
+ public org.osgi.framework.Bundle getBundle()
+ {
+ checkValid();
+
+ return(bundle);
+ }
+
+ /**
+ * Install a bundle from a location.
+ *
+ * The bundle is obtained from the location
+ * parameter as interpreted by the framework
+ * in an implementation dependent way. Typically, location
+ * will most likely be a URL.
+ *
+ * @param location The location identifier of the bundle to install.
+ * @return The Bundle object of the installed bundle.
+ */
+ public org.osgi.framework.Bundle installBundle(String location) throws BundleException
+ {
+
+ framework.checkAdminPermission();
+
+ checkValid();
+ // TODO: check if we only want to use only one type of protocal to install. Maybe inquire the FrameworkAdaptor?
+
+ return framework.installBundle(location);
+
+ }
+
+ /**
+ * Install a bundle from an InputStream.
+ *
+ * <p>This method performs all the steps listed in
+ * {@link #installBundle(java.lang.String)}, except the
+ * bundle's content will be read from the InputStream.
+ * The location identifier specified will be used
+ * as the identity of the bundle.
+ *
+ * @param location The location identifier of the bundle to install.
+ * @param in The InputStream from which the bundle will be read.
+ * @return The Bundle of the installed bundle.
+ */
+ public org.osgi.framework.Bundle installBundle(String location, InputStream in) throws BundleException
+ {
+ framework.checkAdminPermission();
+
+ checkValid();
+
+ return framework.installBundle(location, in);
+ }
+
+ /**
+ * Retrieve the bundle that has the given unique identifier.
+ *
+ * @param id The identifier of the bundle to retrieve.
+ * @return A Bundle object, or <code>null</code>
+ * if the identifier doesn't match any installed bundle.
+ */
+ public org.osgi.framework.Bundle getBundle(long id)
+ {
+ return(framework.getBundle(id));
+ }
+
+ /**
+ * Retrieve the bundle that has the given location.
+ *
+ * @param location The location string of the bundle to retrieve.
+ * @return A Bundle object, or <code>null</code>
+ * if the location doesn't match any installed bundle.
+ */
+ public Bundle getBundleByLocation(String location)
+ {
+ return(framework.getBundleByLocation(location));
+ }
+
+ /**
+ * Retrieve a list of all installed bundles.
+ * The list is valid at the time
+ * of the call to getBundles, but the framework is a very dynamic
+ * environment and bundles can be installed or uninstalled at anytime.
+ *
+ * @return An array of {@link Bundle} objects, one
+ * object per installed bundle.
+ */
+ public org.osgi.framework.Bundle[] getBundles()
+ {
+ BundleRepository bundles = framework.getBundles();
+
+ synchronized (bundles)
+ {
+ List allBundles = bundles.getBundles();
+ int size = allBundles.size();
+
+ if (size == 0)
+ {
+ return(null);
+ }
+
+ Bundle[] bundlelist = new Bundle[size];
+
+ allBundles.toArray(bundlelist);
+
+ return(bundlelist);
+ }
+ }
+
+ /**
+ * Add a service listener with a filter.
+ * {@link ServiceListener}s are notified when a service has a lifecycle
+ * state change.
+ * See {@link #getServiceReferences getServiceReferences}
+ * for a description of the filter syntax.
+ * The listener is added to the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * <p>The listener is called if the filter criteria is met.
+ * To filter based upon the class of the service, the filter
+ * should reference the "objectClass" property.
+ * If the filter paramater is <code>null</code>, all services
+ * are considered to match the filter.
+ * <p>If the Java runtime environment supports permissions, then additional
+ * filtering is done.
+ * {@link Bundle#hasPermission Bundle.hasPermission} is called for the
+ * bundle which defines the listener to validate that the listener has the
+ * {@link ServicePermission} permission to <code>"get"</code> the service
+ * using at least one of the named classes the service was registered under.
+ *
+ * @param listener The service listener to add.
+ * @param filter The filter criteria.
+ * @exception InvalidSyntaxException If the filter parameter contains
+ * an invalid filter string which cannot be parsed.
+ * @see ServiceEvent
+ * @see ServiceListener
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ */
+ public void addServiceListener(ServiceListener listener, String filter)
+ throws InvalidSyntaxException
+ {
+ checkValid();
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("addServiceListener["+bundle+"]("+listenerName+", \""+filter+"\")");
+ }
+
+ ServiceListener filteredListener = (filter == null) ? listener :
+ new FilteredServiceListener(filter, listener);
+
+ synchronized (framework.serviceEvent)
+ {
+ if (serviceEvent == null)
+ {
+ serviceEvent = new EventListeners();
+ framework.serviceEvent.addListener(this, this);
+ }
+ else
+ {
+ serviceEvent.removeListener(listener);
+ }
+
+ serviceEvent.addListener(listener, filteredListener);
+ }
+ }
+
+ /**
+ * Add a service listener.
+ *
+ * <p>This method is the same as calling
+ * {@link #addServiceListener(ServiceListener, String)}
+ * with filter set to <code>null</code>.
+ *
+ * @see #addServiceListener(ServiceListener, String)
+ */
+ public void addServiceListener(ServiceListener listener)
+ {
+ try
+ {
+ addServiceListener(listener, null);
+ }
+ catch (InvalidSyntaxException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("InvalidSyntaxException w/ null filter" + e.getMessage());
+ Debug.printStackTrace(e);
+ }
+ }
+ }
+
+ /**
+ * Remove a service listener.
+ * The listener is removed from the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * <p>If this method is called with a listener which is not registered,
+ * then this method does nothing.
+ *
+ * @param listener The service listener to remove.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ */
+ public void removeServiceListener(ServiceListener listener)
+ {
+ checkValid();
+
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("removeServiceListener["+bundle+"]("+listenerName+")");
+ }
+
+ if (serviceEvent != null)
+ {
+ synchronized (framework.serviceEvent)
+ {
+ serviceEvent.removeListener(listener);
+ }
+ }
+ }
+
+ /**
+ * Add a bundle listener.
+ * {@link BundleListener}s are notified when a bundle has a lifecycle
+ * state change.
+ * The listener is added to the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * @param listener The bundle listener to add.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ * @see BundleEvent
+ * @see BundleListener
+ */
+ public void addBundleListener(BundleListener listener)
+ {
+ checkValid();
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("addBundleListener["+bundle+"]("+listenerName+")");
+ }
+
+ if (listener instanceof SynchronousBundleListener)
+ {
+ framework.checkAdminPermission();
+
+ synchronized (framework.bundleEventSync)
+ {
+ if (bundleEventSync == null)
+ {
+ bundleEventSync = new EventListeners();
+ framework.bundleEventSync.addListener(this, this);
+ }
+ else
+ {
+ bundleEventSync.removeListener(listener);
+ }
+
+ bundleEventSync.addListener(listener, listener);
+ }
+ }
+ else
+ {
+ synchronized (framework.bundleEvent)
+ {
+ if (bundleEvent == null)
+ {
+ bundleEvent = new EventListeners();
+ framework.bundleEvent.addListener(this, this);
+ }
+ else
+ {
+ bundleEvent.removeListener(listener);
+ }
+
+ bundleEvent.addListener(listener, listener);
+ }
+ }
+ }
+
+ /**
+ * Remove a bundle listener.
+ * The listener is removed from the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * <p>If this method is called with a listener which is not registered,
+ * then this method does nothing.
+ *
+ * @param listener The bundle listener to remove.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ */
+ public void removeBundleListener(BundleListener listener)
+ {
+ checkValid();
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("removeBundleListener["+bundle+"]("+listenerName+")");
+ }
+
+ if (listener instanceof SynchronousBundleListener)
+ {
+ framework.checkAdminPermission();
+
+ if (bundleEventSync != null)
+ {
+ synchronized (framework.bundleEventSync)
+ {
+ bundleEventSync.removeListener(listener);
+ }
+ }
+ }
+ else
+ {
+ if (bundleEvent != null)
+ {
+ synchronized (framework.bundleEvent)
+ {
+ bundleEvent.removeListener(listener);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a general framework listener.
+ * {@link FrameworkListener}s are notified of general framework events.
+ * The listener is added to the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * @param listener The framework listener to add.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ * @see FrameworkEvent
+ * @see FrameworkListener
+ */
+ public void addFrameworkListener(FrameworkListener listener)
+ {
+ checkValid();
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("addFrameworkListener["+bundle+"]("+listenerName+")");
+ }
+
+ synchronized (framework.frameworkEvent)
+ {
+ if (frameworkEvent == null)
+ {
+ frameworkEvent = new EventListeners();
+ framework.frameworkEvent.addListener(this, this);
+ }
+ else
+ {
+ frameworkEvent.removeListener(listener);
+ }
+
+ frameworkEvent.addListener(listener, listener);
+ }
+ }
+
+ /**
+ * Remove a framework listener.
+ * The listener is removed from the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * <p>If this method is called with a listener which is not registered,
+ * then this method does nothing.
+ *
+ * @param listener The framework listener to remove.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ */
+ public void removeFrameworkListener(FrameworkListener listener)
+ {
+ checkValid();
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("removeFrameworkListener["+bundle+"]("+listenerName+")");
+ }
+
+ if (frameworkEvent != null)
+ {
+ synchronized (framework.frameworkEvent)
+ {
+ frameworkEvent.removeListener(listener);
+ }
+ }
+ }
+
+ /**
+ * Register a service with multiple names.
+ * This method registers the given service object with the given properties
+ * under the given class names.
+ * A {@link ServiceRegistration} object is returned.
+ * The {@link ServiceRegistration} object is for the private use of the bundle
+ * registering the service and should not be shared with other bundles.
+ * The registering bundle is defined to be the context bundle.
+ * See {@link #getBundle()} for a definition of context bundle.
+ * Other bundles can locate the service by using either the
+ * {@link #getServiceReferences getServiceReferences} or
+ * {@link #getServiceReference getServiceReference} method.
+ *
+ * <p>A bundle can register a service object that implements the
+ * {@link ServiceFactory} interface to
+ * have more flexiblity in providing service objects to different
+ * bundles.
+ *
+ * <p>The following steps are followed to register a service:
+ * <ol>
+ * <li>If the service parameter is not a {@link ServiceFactory},
+ * an <code>IllegalArgumentException</code> is thrown if the
+ * service parameter is not an <code>instanceof</code>
+ * all the classes named.
+ * <li>The service is added to the framework's service registry
+ * and may now be used by other bundles.
+ * <li>A {@link ServiceEvent} of type {@link ServiceEvent#REGISTERED}
+ * is synchronously sent.
+ * <li>A {@link ServiceRegistration} object for this registration
+ * is returned.
+ * </ol>
+ *
+ * @param clazzes The class names under which the service can be located.
+ * The class names in this array will be stored in the service's
+ * properties under the key "objectClass".
+ * @param service The service object or a {@link ServiceFactory} object.
+ * @param properties The properties for this service.
+ * The keys in the properties object must all be Strings.
+ * Changes should not be made to this object after calling this method.
+ * To update the service's properties call the
+ * {@link ServiceRegistration#setProperties ServiceRegistration.setProperties}
+ * method.
+ * This parameter may be <code>null</code> if the service has no properties.
+ * @return A {@link ServiceRegistration} object for use by the bundle
+ * registering the service to update the
+ * service's properties or to unregister the service.
+ * @exception java.lang.IllegalArgumentException If one of the following is true:
+ * <ul>
+ * <li>The service parameter is null.
+ * <li>The service parameter is not a {@link ServiceFactory} and is not an
+ * <code>instanceof</code> all the named classes in the clazzes parameter.
+ * </ul>
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link ServicePermission} permission to "register" the service for
+ * all the named classes
+ * and the Java runtime environment supports permissions.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ * @see ServiceRegistration
+ * @see ServiceFactory
+ */
+ public org.osgi.framework.ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties)
+ {
+ checkValid();
+
+ if (service == null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("Service object is null");
+ }
+
+ throw new NullPointerException(Msg.formatter.getString("SERVICE_ARGUMENT_NULL_EXCEPTION"));
+ }
+
+ int size = clazzes.length;
+
+ if (size == 0)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("Classes array is empty");
+ }
+
+ throw new IllegalArgumentException(Msg.formatter.getString("SERVICE_EMPTY_CLASS_LIST_EXCEPTION"));
+ }
+
+ /* copy the array so that changes to the original will not affect us. */
+ String[] copy = new String[clazzes.length];
+ for (int i = 0; i < clazzes.length; i++)
+ {
+ copy[i] = new String(clazzes[i].getBytes());
+ }
+ clazzes = copy;
+
+
+ /* check for ServicePermissions. */
+ framework.checkRegisterServicePermission(clazzes);
+
+ if (!(service instanceof ServiceFactory))
+ {
+ PackageAdmin packageAdmin = framework.packageAdmin;
+
+ for (int i = 0; i < size; i++)
+ {
+ Class clazz;
+
+ try
+ {
+ clazz = bundle.loadClass(clazzes[i],false);
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ // TODO this code really never should be run. If the factory's classloader cannot
+ // see the required class (above) then it is not available. Looking in a global list of
+ // package exporters etc is not a good plan in the face of modules and multiple
+ // versions. Actually, it is unclear that we need to verify the pedigree of the service object
+ // at registration time.
+ // For now just put in a dummy classload call to reduce code impact.
+ clazz = Class.forName(clazzes[i]);
+// clazz = packageAdmin.loadClass(clazzes[i]);
+ }
+ catch (ClassNotFoundException ee)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println(clazzes[i] + " class not found");
+ }
+
+ throw new IllegalArgumentException(Msg.formatter.getString("SERVICE_CLASS_NOT_FOUND_EXCEPTION", clazzes[i]));
+ }
+ }
+
+ if (!clazz.isInstance(service))
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("Service object is not an instanceof "+clazzes[i]);
+ }
+
+ throw new IllegalArgumentException(Msg.formatter.getString("SERVICE_NOT_INSTANCEOF_CLASS_EXCEPTION", clazzes[i]));
+ }
+ }
+ }
+
+ return(createServiceRegistration(clazzes, service, properties));
+ }
+
+ /**
+ * Create a new ServiceRegistration object. This method is used so that it may be overridden
+ * by a secure implementation.
+ *
+ * @param clazzes The class names under which the service can be located.
+ * @param service The service object or a {@link ServiceFactory} object.
+ * @param properties The properties for this service.
+ * @return A {@link ServiceRegistration} object for use by the bundle.
+ */
+ protected ServiceRegistration createServiceRegistration(String[] clazzes, Object service, Dictionary properties)
+ {
+ return(new ServiceRegistration(this, clazzes, service, properties));
+ }
+
+ /**
+ * Register a service with a single name.
+ * This method registers the given service object with the given properties
+ * under the given class name.
+ *
+ * <p>This method is otherwise identical to
+ * {@link #registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)}
+ * and is provided as a convenience when the service parameter will only be registered
+ * under a single class name.
+ *
+ * @see #registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+ */
+ public org.osgi.framework.ServiceRegistration registerService(String clazz, Object service, Dictionary properties)
+ {
+ String[] clazzes = new String[] { clazz };
+
+ return(registerService(clazzes, service, properties));
+ }
+
+ /**
+ * Returns a list of <tt>ServiceReference</tt> objects. This method returns a list of
+ * <tt>ServiceReference</tt> objects for services which implement and were registered under
+ * the specified class and match the specified filter criteria.
+ *
+ * <p>The list is valid at the time of the call to this method, however as the Framework is
+ * a very dynamic environment, services can be modified or unregistered at anytime.
+ *
+ * <p><tt>filter</tt> is used to select the registered service whose
+ * properties objects contain keys and values which satisfy the filter.
+ * See {@link Filter}for a description of the filter string syntax.
+ *
+ * <p>If <tt>filter</tt> is <tt>null</tt>, all registered services
+ * are considered to match the filter.
+ * <p>If <tt>filter</tt> cannot be parsed, an {@link InvalidSyntaxException}will
+ * be thrown with a human readable message where the filter became unparsable.
+ *
+ * <p>The following steps are required to select a service:
+ * <ol>
+ * <li>If the Java Runtime Environment supports permissions, the caller is checked for the
+ * <tt>ServicePermission</tt> to get the service with the specified class.
+ * If the caller does not have the correct permission, <tt>null</tt> is returned.
+ * <li>If the filter string is not <tt>null</tt>, the filter string is
+ * parsed and the set of registered services which satisfy the filter is
+ * produced.
+ * If the filter string is <tt>null</tt>, then all registered services
+ * are considered to satisfy the filter.
+ * <li>If <code>clazz</code> is not <tt>null</tt>, the set is further reduced to
+ * those services which are an <tt>instanceof</tt> and were registered under the specified class.
+ * The complete list of classes of which a service is an instance and which
+ * were specified when the service was registered is available from the
+ * service's {@link Constants#OBJECTCLASS}property.
+ * <li>An array of <tt>ServiceReference</tt> to the selected services is returned.
+ * </ol>
+ *
+ * @param clazz The class name with which the service was registered, or
+ * <tt>null</tt> for all services.
+ * @param filter The filter criteria.
+ * @return An array of <tt>ServiceReference</tt> objects, or
+ * <tt>null</tt> if no services are registered which satisfy the search.
+ * @exception InvalidSyntaxException If <tt>filter</tt> contains
+ * an invalid filter string which cannot be parsed.
+ */
+ public org.osgi.framework.ServiceReference[] getServiceReferences(String clazz, String filter)
+ throws InvalidSyntaxException
+ {
+ checkValid();
+
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("getServiceReferences("+clazz+", \""+filter+"\")");
+ }
+
+ return(framework.getServiceReferences(clazz, filter));
+ }
+
+ /**
+ * Get a service reference.
+ * Retrieves a {@link ServiceReference} for a service
+ * which implements the named class.
+ *
+ * <p>This reference is valid at the time
+ * of the call to this method, but since the framework is a very dynamic
+ * environment, services can be modified or unregistered at anytime.
+ *
+ * <p>This method is provided as a convenience for when the caller is
+ * interested in any service which implements a named class. This method is
+ * the same as calling {@link #getServiceReferences getServiceReferences}
+ * with a <code>null</code> filter string but only a single {@link ServiceReference}
+ * is returned.
+ *
+ * @param clazz The class name which the service must implement.
+ * @return A {@link ServiceReference} object, or <code>null</code>
+ * if no services are registered which implement the named class.
+ * @see #getServiceReferences
+ */
+ public org.osgi.framework.ServiceReference getServiceReference(String clazz)
+ {
+ checkValid();
+
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("getServiceReference("+clazz+")");
+ }
+
+ try
+ {
+ ServiceReference[] references = framework.getServiceReferences(clazz, null);
+
+ if (references != null)
+ {
+ int index = 0;
+
+ int length = references.length;
+
+ if (length > 1) /* if more than one service, select highest ranking */
+ {
+ int rankings[] = new int[length];
+ int count = 0;
+ int maxRanking = Integer.MIN_VALUE;
+
+ for (int i = 0 ; i < length; i++)
+ {
+ int ranking = references[i].getRanking();
+
+ rankings[i] = ranking;
+
+ if (ranking > maxRanking)
+ {
+ index = i;
+ maxRanking = ranking;
+ count = 1;
+ }
+ else
+ {
+ if (ranking == maxRanking)
+ {
+ count++;
+ }
+ }
+ }
+
+ if (count > 1) /* if still more than one service, select lowest id */
+ {
+ long minId = Long.MAX_VALUE;
+
+ for (int i = 0 ; i < length; i++)
+ {
+ if (rankings[i] == maxRanking)
+ {
+ long id = references[i].getId();
+
+ if (id < minId)
+ {
+ index = i;
+ minId = id;
+ }
+ }
+ }
+ }
+ }
+
+ return(references[index]);
+ }
+ }
+ catch (InvalidSyntaxException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("InvalidSyntaxException w/ null filter" + e.getMessage());
+ Debug.printStackTrace(e);
+ }
+ }
+
+ return(null);
+ }
+
+ /**
+ * Get a service's service object.
+ * Retrieves the service object for a service.
+ * A bundle's use of a service is tracked by a
+ * use count. Each time a service's service object is returned by
+ * {@link #getService}, the context bundle's use count for the service
+ * is incremented by one. Each time the service is release by
+ * {@link #ungetService}, the context bundle's use count
+ * for the service is decremented by one.
+ * When a bundle's use count for a service
+ * drops to zero, the bundle should no longer use the service.
+ * See {@link #getBundle()} for a definition of context bundle.
+ *
+ * <p>This method will always return <code>null</code> when the
+ * service associated with this reference has been unregistered.
+ *
+ * <p>The following steps are followed to get the service object:
+ * <ol>
+ * <li>If the service has been unregistered,
+ * <code>null</code> is returned.
+ * <li>The context bundle's use count for this service is incremented by one.
+ * <li>If the context bundle's use count for the service is now one and
+ * the service was registered with a {@link ServiceFactory},
+ * the {@link ServiceFactory#getService ServiceFactory.getService} method
+ * is called to create a service object for the context bundle.
+ * This service object is cached by the framework.
+ * While the context bundle's use count for the service is greater than zero,
+ * subsequent calls to get the services's service object for the context bundle
+ * will return the cached service object.
+ * <br>If the service object returned by the {@link ServiceFactory}
+ * is not an <code>instanceof</code>
+ * all the classes named when the service was registered or
+ * the {@link ServiceFactory} throws an exception,
+ * <code>null</code> is returned and a
+ * {@link FrameworkEvent} of type {@link FrameworkEvent#ERROR} is broadcast.
+ * <li>The service object for the service is returned.
+ * </ol>
+ *
+ * @param reference A reference to the service whose service object is desired.
+ * @return A service object for the service associated with this
+ * reference, or <code>null</code> if the service is not registered.
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link ServicePermission} permission to "get" the service
+ * using at least one of the named classes the service was registered under
+ * and the Java runtime environment supports permissions.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ * @see #ungetService
+ * @see ServiceFactory
+ */
+ public Object getService(org.osgi.framework.ServiceReference reference)
+ {
+ checkValid();
+
+ if (servicesInUse == null)
+ {
+ synchronized (contextLock)
+ {
+ if (servicesInUse == null)
+ {
+ servicesInUse = new Hashtable(17);
+ }
+ }
+ }
+
+ ServiceRegistration registration = ((ServiceReference)reference).registration;
+
+ framework.checkGetServicePermission(registration.clazzes);
+
+ return registration.getService(BundleContext.this);
+ }
+
+ /**
+ * Unget a service's service object.
+ * Releases the service object for a service.
+ * If the context bundle's use count for the service is zero, this method
+ * returns <code>false</code>. Otherwise, the context bundle's use count for the
+ * service is decremented by one.
+ * See {@link #getBundle()} for a definition of context bundle.
+ *
+ * <p>The service's service object
+ * should no longer be used and all references to it should be destroyed
+ * when a bundle's use count for the service
+ * drops to zero.
+ *
+ * <p>The following steps are followed to unget the service object:
+ * <ol>
+ * <li>If the context bundle's use count for the service is zero or
+ * the service has been unregistered,
+ * <code>false</code> is returned.
+ * <li>The context bundle's use count for this service is decremented by one.
+ * <li>If the context bundle's use count for the service is now zero and
+ * the service was registered with a {@link ServiceFactory},
+ * the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
+ * is called to release the service object for the context bundle.
+ * <li><code>true</code> is returned.
+ * </ol>
+ *
+ * @param reference A reference to the service to be released.
+ * @return <code>false</code> if the context bundle's use count for the service
+ * is zero or if the service has been unregistered,
+ * otherwise <code>true</code>.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ * @see #getService
+ * @see ServiceFactory
+ */
+ public boolean ungetService(org.osgi.framework.ServiceReference reference)
+ {
+ checkValid();
+
+ ServiceRegistration registration = ((ServiceReference)reference).registration;
+
+ return registration.ungetService(BundleContext.this);
+ }
+
+ /**
+ * Creates a <code>File</code> object for a file in the
+ * persistent storage area provided for the bundle by the framework.
+ * If the adaptor does not have file system support, this method will
+ * return <code>null</code>.
+ *
+ * <p>A <code>File</code> object for the base directory of the
+ * persistent storage area provided for the context bundle by the framework
+ * can be obtained by calling this method with the empty string ("")
+ * as the parameter.
+ * See {@link #getBundle()} for a definition of context bundle.
+ *
+ * <p>If the Java runtime environment supports permissions,
+ * the framework the will ensure that the bundle has
+ * <code>java.io.FilePermission</code> with actions
+ * "read","write","execute","delete" for all files (recursively) in the
+ * persistent storage area provided for the context bundle by the framework.
+ *
+ * @param filename A relative name to the file to be accessed.
+ * @return A <code>File</code> object that represents the requested file or
+ * <code>null</code> if the adaptor does not have file system support.
+ * @exception java.lang.IllegalStateException
+ * If the context bundle {@link <a href="#context_valid">has stopped</a>}.
+ */
+ public File getDataFile(String filename)
+ {
+ checkValid();
+
+ return(framework.getDataFile(bundle, filename));
+ }
+
+
+ /**
+ * Call bundle's BundleActivator.start()
+ * This method is called by Bundle.startWorker to start the bundle.
+ *
+ * @exception org.osgi.framework.BundleException if
+ * the bundle has a class that implements the BundleActivator interface,
+ * but Framework couldn't instantiate it, or the BundleActivator.start()
+ * method failed
+ */
+ protected void start() throws BundleException
+ {
+ activator = bundle.loadBundleActivator();
+
+ if (activator != null)
+ {
+ try {
+ startActivator(activator);
+ }
+ catch (BundleException be) {
+ activator = null;
+ throw be;
+ }
+ }
+
+ // now start all attached fragments.
+ if (bundle.fragments != null) {
+ synchronized (bundle.fragments)
+ {
+ fragmentActivators = new BundleActivator[bundle.fragments.size()];
+ for (int i = 0; i < fragmentActivators.length; i++)
+ {
+ BundleFragment fragment = (BundleFragment) bundle.fragments.elementAt(i);
+ try {
+ fragmentActivators[i] = fragment.loadBundleActivator();
+ }
+ catch (Throwable t) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR,fragment,t);
+ }
+ }
+ for (int i = 0; i < fragmentActivators.length; i++)
+ {
+ Bundle fragment = (Bundle) bundle.fragments.elementAt(i);
+ if (fragmentActivators[i] != null)
+ {
+ try {
+ // set the fragment state to STARTING
+ fragment.state = Bundle.STARTING;
+
+ // call the start() method on the fragment activator
+ startActivator(fragmentActivators[i]);
+
+ // set the fragment state to ACTIVE
+ fragment.state = Bundle.ACTIVE;
+ framework.publishBundleEvent(BundleEvent.STARTED, fragment);
+ }
+ catch (Throwable t) {
+ // set the fragment activator to null so that it will not get called during
+ // host stop.
+ fragmentActivators[i] = null;
+
+ // set the fragment state to resolved.
+ fragment.state = Bundle.RESOLVED;
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR,fragment,t);
+ }
+ }
+ else {
+ fragment.state = Bundle.ACTIVE;
+ framework.publishBundleEvent(BundleEvent.STARTED, fragment);
+ }
+ }
+ }
+ }
+
+ /* activator completed successfully. We must use this
+ same activator object when we stop this bundle. */
+ }
+
+ /**
+ * Calls the start method of a BundleActivator.
+ * @param bundleActivator that activator to start
+ */
+ protected void startActivator(final BundleActivator bundleActivator) throws BundleException {
+ try
+ {
+ AccessController.doPrivileged(new PrivilegedExceptionAction()
+ {
+ public Object run() throws Exception
+ {
+ if (bundleActivator != null)
+ {
+ /* Start the bundle synchronously */
+ bundleActivator.start(BundleContext.this);
+ }
+ return null;
+ }
+ });
+ }
+ catch (Throwable t)
+ {
+ if (t instanceof PrivilegedActionException)
+ {
+ t = ((PrivilegedActionException)t).getException();
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.printStackTrace(t);
+ }
+
+ String clazz = null;
+ clazz = bundleActivator.getClass().getName();
+
+ throw new BundleException(Msg.formatter.getString("BUNDLE_ACTIVATOR_EXCEPTION", clazz, "start"), t);
+ }
+
+ }
+
+ /**
+ * This method is called when a fragment attaches to an already active
+ * host.
+ * @param fragment The fragment bundle to start.
+ */
+ protected void startFragment(BundleFragment fragment) {
+ BundleActivator fragmentActivator=null;
+
+ try {
+ fragmentActivator = fragment.loadBundleActivator();
+ }
+ catch (Throwable t) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR,fragment,t);
+ }
+
+ if (fragmentActivator != null) {
+ try
+ {
+ // set the fragment state to STARTING
+ fragment.state = Bundle.STARTING;
+ startActivator(fragmentActivator);
+
+ if (fragmentActivators != null)
+ {
+ BundleActivator[] newFragmentActivators = new BundleActivator[fragmentActivators.length + 1];
+ System.arraycopy(fragmentActivators, 0, newFragmentActivators, 0, fragmentActivators.length);
+ newFragmentActivators[newFragmentActivators.length - 1] = fragmentActivator;
+ fragmentActivators = newFragmentActivators;
+ }
+ else
+ {
+ fragmentActivators = new BundleActivator[] { fragmentActivator };
+ }
+
+ // set the fragment state to ACTIVE
+ fragment.state = Bundle.ACTIVE;
+ framework.publishBundleEvent(BundleEvent.STARTED, fragment);
+ } catch (Throwable t)
+ {
+ // set the fragment state to resolved.
+ fragment.state = Bundle.RESOLVED;
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, fragment, t);
+ }
+ }
+ else{
+ fragment.state = Bundle.ACTIVE;
+ framework.publishBundleEvent(BundleEvent.STARTED, fragment);
+ }
+ }
+
+ /**
+ * Call bundle's BundleActivator.stop()
+ * This method is called by Bundle.stopWorker to stop the bundle.
+ *
+ * @exception org.osgi.framework.BundleException if
+ * the bundle has a class that implements the BundleActivator interface,
+ * and the BundleActivator.stop() method failed
+ */
+ protected void stop() throws BundleException
+ {
+ try
+ {
+ AccessController.doPrivileged(new PrivilegedExceptionAction()
+ {
+ public Object run() throws Exception
+ {
+ // first suspend all attached fragments;
+ if (fragmentActivators != null) {
+ for (int i=fragmentActivators.length-1; i>=0; i--) {
+ if (fragmentActivators[i] != null) {
+ Bundle fragment = (Bundle) bundle.fragments.elementAt(i);
+
+ fragment.state = Bundle.STOPPING;
+
+ try {
+ fragmentActivators[i].stop(BundleContext.this);
+ }
+ catch (Throwable t) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR,fragment,t);
+ }
+ finally {
+ fragment.state = Bundle.RESOLVED;
+ framework.publishBundleEvent(BundleEvent.STOPPED, fragment);
+ }
+
+ }
+ }
+ }
+ if (activator != null)
+ {
+ /* Stop the bundle synchronously */
+ activator.stop(BundleContext.this);
+ }
+ return null;
+ }
+ });
+ }
+ catch (Throwable t)
+ {
+ if (t instanceof PrivilegedActionException)
+ {
+ t = ((PrivilegedActionException)t).getException();
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.printStackTrace(t);
+ }
+
+ String clazz = (activator == null) ? "" : activator.getClass().getName();
+
+ throw new BundleException(Msg.formatter.getString("BUNDLE_ACTIVATOR_EXCEPTION", clazz, "stop"), t);
+ }
+ finally
+ {
+ activator = null;
+ fragmentActivators = null;
+ }
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the services
+ * registered by this bundle
+ * or <code>null</code> if the bundle has no registered
+ * services.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceRegistration
+ * @see ServiceReference
+ */
+ protected ServiceReference[] getRegisteredServices() {
+ ServiceReference[] references = null;
+
+ synchronized (framework.serviceRegistry) {
+ Vector services = framework.serviceRegistry.lookupServiceReferences(this);
+ if (services == null) {
+ return null;
+ }
+
+ for (int i = services.size()-1; i >= 0; i--) {
+ ServiceReference ref = (ServiceReference) services.elementAt(i);
+ String[] classes = ref.getClasses();
+ try { /* test for permission to the classes */
+ framework.checkGetServicePermission(classes);
+ } catch (SecurityException se) {
+ services.removeElementAt(i);
+ }
+ }
+
+ if (services.size() > 0) {
+ references = new ServiceReference[services.size()];
+ services.toArray(references);
+ }
+ }
+ return (references);
+
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the
+ * services this bundle is using,
+ * or <code>null</code> if the bundle is not using any services.
+ * A bundle is considered to be using a service if the bundle's
+ * use count for the service is greater than zero.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceReference
+ */
+ protected ServiceReference[] getServicesInUse()
+ {
+ if (servicesInUse == null)
+ {
+ return(null);
+ }
+
+ synchronized (servicesInUse)
+ {
+ int size = servicesInUse.size();
+
+ if (size == 0)
+ {
+ return(null);
+ }
+
+ ServiceReference[] references = new ServiceReference[size];
+ int refcount = 0;
+
+ Enumeration enum = servicesInUse.keys();
+
+ for (int i = 0; i < size; i++)
+ {
+ ServiceReference reference = (ServiceReference)enum.nextElement();
+
+ try
+ {
+ framework.checkGetServicePermission(reference.registration.clazzes);
+ }
+ catch (SecurityException se)
+ {
+ continue;
+ }
+
+ references[refcount] = reference;
+ refcount++;
+ }
+
+ if (refcount < size)
+ {
+ if (refcount == 0)
+ {
+ return(null);
+ }
+
+ ServiceReference[] refs = references;
+ references = new ServiceReference[refcount];
+
+ System.arraycopy(refs, 0, references, 0, refcount);
+ }
+
+ return(references);
+ }
+ }
+
+ /**
+ * Bottom level event dispatcher for the BundleContext.
+ *
+ * @param originalListener listener object registered under.
+ * @param l listener to call (may be filtered).
+ * @param action Event class type
+ * @param object Event object
+ */
+ public void dispatchEvent(Object originalListener, Object l, int action, Object object)
+ {
+ // save the bundle ref to a local variable
+ // to avoid interference from another thread closing this context
+ Bundle tmpBundle = bundle;
+ try
+ {
+ if (tmpBundle != null) /* if context still valid */
+ {
+ switch (action)
+ {
+ case Framework.BUNDLEEVENT:
+ case Framework.BUNDLEEVENTSYNC:
+ {
+ BundleListener listener = (BundleListener) l;
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("dispatchBundleEvent["+tmpBundle+"]("+listenerName+")");
+ }
+
+ listener.bundleChanged((BundleEvent)object);
+ break;
+ }
+
+ case Framework.SERVICEEVENT:
+ {
+ ServiceEvent event = (ServiceEvent) object;
+
+ if (hasListenServicePermission(event))
+ {
+ ServiceListener listener = (ServiceListener) l;
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("dispatchServiceEvent["+tmpBundle+"]("+listenerName+")");
+ }
+
+ listener.serviceChanged(event);
+ }
+
+ break;
+ }
+
+ case Framework.FRAMEWORKEVENT:
+ {
+ FrameworkListener listener = (FrameworkListener) l;
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("dispatchFrameworkEvent["+tmpBundle+"]("+listenerName+")");
+ }
+
+ listener.frameworkEvent((FrameworkEvent)object);
+ break;
+ }
+ }
+ }
+ }
+ catch (Throwable t)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Exception in bottom level event dispatcher: " + t.getMessage());
+ Debug.printStackTrace(t);
+ }
+
+ publisherror:
+ {
+ if (action == Framework.FRAMEWORKEVENT)
+ {
+ FrameworkEvent event = (FrameworkEvent)object;
+ if (event.getType() == FrameworkEvent.ERROR)
+ {
+ break publisherror; // avoid infinite loop
+ }
+ }
+
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, tmpBundle, t);
+ }
+ }
+ }
+
+ /**
+ * Check for permission to listen to a service.
+ */
+ protected boolean hasListenServicePermission(ServiceEvent event)
+ {
+ ProtectionDomain domain = bundle.getProtectionDomain();
+
+ if (domain != null)
+ {
+ ServiceReference reference = (ServiceReference)event.getServiceReference();
+
+ String[] names = reference.registration.clazzes;
+
+ int len = names.length;
+
+ for (int i = 0; i < len; i++)
+ {
+ if (domain.implies(new ServicePermission(names[i], ServicePermission.GET)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return(true);
+ }
+
+ /**
+ * Construct a Filter object. This filter object may be used
+ * to match a ServiceReference or a Dictionary.
+ * See Filter
+ * for a description of the filter string syntax.
+ *
+ * @param filter The filter string.
+ * @return A Filter object encapsulating the filter string.
+ * @exception InvalidSyntaxException If the filter parameter contains
+ * an invalid filter string which cannot be parsed.
+ */
+ public org.osgi.framework.Filter createFilter(String filter) throws InvalidSyntaxException
+ {
+ checkValid();
+
+ return(new Filter(filter));
+ }
+
+ /**
+ * This method checks that the context is still valid. If the context is
+ * no longer valid, an IllegalStateException is thrown.
+ *
+ * @exception java.lang.IllegalStateException
+ * If the context bundle has stopped.
+ */
+ protected void checkValid()
+ {
+ if (bundle == null)
+ {
+ throw new IllegalStateException(Msg.formatter.getString("BUNDLE_CONTEXT_INVALID_EXCEPTION"));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.framework.BundleContext#getBundle(java.lang.String)
+ */
+ public org.osgi.framework.Bundle[] getBundles(String uniqueId) {
+ return framework.getBundleByUniqueId(uniqueId);
+ }
+
+ public org.osgi.framework.Bundle getBundle(String uniqueId) {
+ org.osgi.framework.Bundle[] bundles = getBundles(uniqueId);
+ return bundles == null ? null : bundles[0];
+ }
+
+ /* (non-Javadoc)
+ * @see org.osgi.framework.BundleContext#getBundleFor(java.lang.Object)
+ */
+ public org.osgi.framework.Bundle getBundleFor(Object object) {
+ ClassLoader cl = object.getClass().getClassLoader();
+ if (cl == null) {
+ return null;
+ }
+
+ if (cl instanceof BundleClassLoader) {
+ return framework.getBundleByClassLoader((BundleClassLoader) cl);
+ }
+ return null;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleFragment.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleFragment.java
new file mode 100644
index 000000000..7ba5b88d3
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleFragment.java
@@ -0,0 +1,474 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.*;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.osgi.framework.*;
+import org.osgi.framework.ServiceReference;
+
+public class BundleFragment extends Bundle
+{
+
+ /** The resolved host that this fragment is attached to */
+ protected BundleHost host;
+ /** The current instantiation of the activator. */
+ protected BundleActivator activator;
+
+ /**
+ * @param bundledata
+ * @param location
+ * @param framework
+ * @param startLevel
+ * @throws BundleException
+ */
+ public BundleFragment(BundleData bundledata, String location, Framework framework, int startLevel) throws BundleException
+ {
+ super(bundledata, location, framework, startLevel);
+ host = null;
+ }
+
+
+ /**
+ * Load the bundle.
+ * @exception org.osgi.framework.BundleException
+ */
+ protected void load() throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (INSTALLED)) == 0)
+ {
+ Debug.println("Bundle.load called when state != INSTALLED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ if (framework.isActive())
+ {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null)
+ {
+ PermissionCollection collection = framework.permissionAdmin.createPermissionCollection(this);
+
+ domain = new ProtectionDomain(null, collection);
+ }
+
+ try
+ {
+ bundledata.open(); /* make sure the BundleData is open */
+ } catch (IOException e)
+ {
+ throw new BundleException(Msg.formatter.getString("BUNDLE_READ_EXCEPTION"), e);
+ }
+ }
+ }
+
+ /**
+ * Changes the state from ACTIVE | RESOVED to INSTALLED. This is called when a
+ * host gets reloaded or unloaded.
+ * This method must be called while holding the bundles lock.
+ *
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ * @exception org.osgi.framework.BundleException
+ */
+ protected boolean unresolve()
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (INSTALLED | RESOLVED)) == 0)
+ {
+ Debug.println("Bundle.reload called when state != INSTALLED | RESOLVED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ if (framework.isActive())
+ {
+ if (host != null)
+ {
+ if (state == RESOLVED)
+ {
+ state = INSTALLED;
+ host = null;
+ }
+ }
+ }
+ else
+ {
+ /* close the outgoing jarfile */
+ try
+ {
+ this.bundledata.close();
+ }
+ catch (IOException e)
+ {
+ // Do Nothing
+ }
+ }
+
+ return(false);
+ }
+
+
+ /**
+ * Reload from a new bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @param newBundle Dummy Bundle which contains new data.
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ * @exception org.osgi.framework.BundleException
+ */
+ protected boolean reload(Bundle newBundle) throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (INSTALLED | RESOLVED)) == 0)
+ {
+ Debug.println("Bundle.reload called when state != INSTALLED | RESOLVED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ boolean exporting = false;
+
+ if (framework.isActive())
+ {
+ if (host != null)
+ {
+ if (state == RESOLVED)
+ {
+ // Unresolving the host will cause the fragment to unresolve
+ exporting = host.unresolve();
+ }
+ }
+
+ }
+
+ if (!exporting)
+ {
+ /* close the outgoing jarfile */
+ try
+ {
+ this.bundledata.close();
+ }
+ catch (IOException e)
+ {
+ // Do Nothing
+ }
+ }
+
+ this.bundledata = newBundle.bundledata;
+ this.bundledata.setBundle(this);
+ return(exporting);
+ }
+
+
+ /**
+ * Refresh the bundle. This is called by Framework.refreshPackages.
+ * This method must be called while holding the bundles lock.
+ * this.loader.unimportPackages must have already been called before calling
+ * this method!
+ *
+ * @exception org.osgi.framework.BundleException if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected void refresh() throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (UNINSTALLED | INSTALLED | RESOLVED)) == 0)
+ {
+ Debug.println("Bundle.refresh called when state != UNINSTALLED | INSTALLED | RESOLVED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ if (state == RESOLVED) {
+ host=null;
+ state = INSTALLED;
+ }
+ }
+
+ /**
+ * Unload the bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected boolean unload()
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (UNINSTALLED | INSTALLED | RESOLVED)) == 0)
+ {
+ Debug.println("Bundle.unload called when state != UNINSTALLED | INSTALLED | RESOLVED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ boolean exporting = false;
+
+ if (framework.isActive())
+ {
+ if (host != null)
+ {
+ BundleHost resumeHost = host;
+ if (state == RESOLVED)
+ {
+ // Unresolving the host will cause the fragment to unresolve
+ try {
+ exporting = host.unresolve();
+ }
+ catch (BundleException be) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR,this,be);
+ }
+ }
+ if (!exporting) {
+ domain = null;
+ try
+ {
+ this.bundledata.close();
+ }
+ catch (IOException e)
+ { // Do Nothing.
+ }
+ }
+ // We must resume the host now that we are unloaded.
+ framework.resumeBundle(resumeHost);
+ }
+ }
+ else
+ {
+ try
+ {
+ this.bundledata.close();
+ }
+ catch (IOException e)
+ { // Do Nothing.
+ }
+ }
+
+ return(exporting);
+ }
+
+ /**
+ * This method loads a class from the bundle.
+ *
+ * @param name the name of the desired Class.
+ * @param checkPermission indicates whether a permission check should be done.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException if the class definition was not found.
+ */
+ protected Class loadClass(String name, boolean checkPermission) throws ClassNotFoundException
+ {
+ if (checkPermission) {
+ framework.checkAdminPermission();
+ checkValid();
+ }
+ if (host == null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Bundle.loadClass("+name+") called when host == null: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+
+ throw new ClassNotFoundException(name);
+ }
+
+ return(host.loadClass(name,checkPermission));
+ }
+
+ /**
+ * Find the specified resource in this bundle.
+ *
+ * This bundle's class loader is called to search for the named resource.
+ * If this bundle's state is <tt>INSTALLED</tt>, then only this bundle will
+ * be searched for the specified resource. Imported packages cannot be searched
+ * when a bundle has not been resolved.
+ *
+ * @param name The name of the resource.
+ * See <tt>java.lang.ClassLoader.getResource</tt> for a description of
+ * the format of a resource name.
+ * @return a URL to the named resource, or <tt>null</tt> if the resource could
+ * not be found or if the caller does not have
+ * the <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ *
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ */
+ public URL getResource(String name)
+ {
+ checkValid();
+
+ if (host == null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Bundle.getResource("+name+") called when host == null: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+
+ return(null);
+ }
+
+ return(host.getResource(name));
+ }
+
+ /**
+ * Internal worker to start a bundle.
+ *
+ * @param persistent if true persistently record the bundle was started.
+ */
+ protected void startWorker(boolean persistent) throws BundleException
+ {
+ throw new BundleException(Msg.formatter.getString("FRAGMENT_CANNOT_START"));
+ }
+
+ /**
+ * Internal worker to stop a bundle.
+ *
+ * @param persistent if true persistently record the bundle was stopped.
+ */
+ protected void stopWorker(boolean persistent) throws BundleException
+ {
+ throw new BundleException(Msg.formatter.getString("FRAGMENT_CANNOT_STOP"));
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the services
+ * registered by this bundle
+ * or <code>null</code> if the bundle has no registered
+ * services.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceRegistration
+ * @see ServiceReference
+ */
+ public ServiceReference[] getRegisteredServices()
+ {
+ checkValid();
+ if (host == null) {
+ return null;
+ }
+ return host.getRegisteredServices();
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the
+ * services this bundle is using,
+ * or <code>null</code> if the bundle is not using any services.
+ * A bundle is considered to be using a service if the bundle's
+ * use count for the service is greater than zero.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceReference
+ */
+ public ServiceReference[] getServicesInUse()
+ {
+ checkValid();
+ if (host == null) {
+ return null;
+ }
+ return host.getServicesInUse();
+ }
+
+ public org.osgi.framework.Bundle getHost() {
+ return host;
+ }
+
+ public boolean isFragment() {
+ return true;
+ }
+
+ /**
+ * Sets the host for this fragment from the list of available
+ * BundleExporters. If a matching host cannot be found then a
+ * resolve Exception is logged.
+ * @param exporters The available BundleExporters to resolve the host from.
+ */
+ protected boolean setHost(BundleHost value) {
+ host = value;
+ if (host != null){
+ try {
+ host.attachFragment(this);
+ }
+ catch (BundleException be) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR,host,be);
+ return false;
+ }
+ }
+ return true;
+ // TODO detach the fragment if the host is null???
+ }
+
+ /**
+ * Check for BundlePermission to Host.
+ *
+ * @return true if bundle has the require permission.
+ */
+ protected boolean hasHostBundlePermission(String uniqueId)
+ {
+ if (domain != null)
+ {
+ return domain.implies(new BundlePermission(uniqueId, BundlePermission.HOST));
+ }
+
+ return true;
+ }
+
+ public BundleLoader getBundleLoader() {
+ return host == null ? null : host.getBundleLoader();
+ }
+
+ /**
+ * Mark this bundle as resolved
+ */
+ protected void resolve() {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ if ((state & (INSTALLED)) == 0) {
+ Debug.println("Bundle.resolve called when state != INSTALLED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ if (host == null) {
+ Debug.println("Bundle.resolve called when host == null: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+ state = RESOLVED;
+ }
+
+ /**
+ * Return the current context for this bundle.
+ *
+ * @return BundleContext for this bundle.
+ */
+ protected BundleContext getContext() {
+ return host == null ? null : host.getContext();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java
new file mode 100644
index 000000000..fc2775f82
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java
@@ -0,0 +1,728 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.PermissionCollection;
+import java.security.ProtectionDomain;
+import java.util.Vector;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+
+public class BundleHost extends Bundle {
+
+ /** Loaded state object. */
+ protected BundleLoader loader;
+
+ private BundleLoaderProxy proxy;
+
+ /** The BundleContext that represents this Bundle and all of its fragments */
+ protected BundleContext context;
+
+ /** The List of BundleFragments */
+ protected Vector fragments;
+
+ public BundleHost(BundleData bundledata, String location, Framework framework, int startLevel) throws BundleException {
+ super(bundledata, location, framework, startLevel);
+ context = null;
+ loader = null;
+ fragments = null;
+ }
+
+ protected BundleLoader basicGetBundleLoader() {
+ return loader;
+ }
+
+ /**
+ * Load the bundle.
+ * @exception org.osgi.framework.BundleException
+ */
+ protected void load() throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (INSTALLED)) == 0)
+ {
+ Debug.println("Bundle.load called when state != INSTALLED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ if (loader != null)
+ {
+ Debug.println("Bundle.load called when loader != null: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ if (framework.isActive())
+ {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null)
+ {
+ PermissionCollection collection = framework.permissionAdmin.createPermissionCollection(this);
+
+ domain = new ProtectionDomain(null, collection);
+ }
+
+ }
+ loader = null;
+
+ }
+
+ /**
+ * Changes the state from ACTIVE | RESOVED to INSTALLED. This is called when a
+ * fragment gets reloaded or unloaded.
+ * This method must be called while holding the bundles lock.
+ *
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ * @exception org.osgi.framework.BundleException
+ */
+ protected boolean unresolve() throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (INSTALLED | RESOLVED)) == 0)
+ {
+ Debug.println("Bundle.reload called when state != INSTALLED | RESOLVED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ boolean exporting = false;
+
+ if (framework.isActive())
+ {
+
+ if (state == RESOLVED)
+ {
+ // suspend and acquire the state change lock
+ if (isActive()) {
+ boolean suspended = framework.suspendBundle(this,true);
+ if (!suspended) {
+ throw new BundleException(Msg.formatter.getString("BUNDLE_STATE_CHANGE_EXCEPTION"));
+ }
+ }
+ else {
+ beginStateChange();
+ }
+
+ BundleLoaderProxy curProxy = getLoaderProxy();
+ exporting = curProxy.inUse();
+ if (exporting){
+ // make sure the BundleLoader is created so it can
+ // be added to the removalPending list.
+ curProxy.getBundleLoader();
+ framework.packageAdmin.addRemovalPending(curProxy);
+ }
+ else {
+ framework.packageAdmin.unexportResources(curProxy);
+ if (loader != null) {
+ loader.clear();
+ loader.close();
+ }
+ framework.bundles.unMarkDependancies(curProxy);
+ }
+
+ unresolveFragments();
+ loader = null;
+ fragments = null;
+ proxy = null;
+ state = INSTALLED;
+ completeStateChange();
+ }
+ }
+ else
+ {
+ /* close the outgoing jarfile */
+ try
+ {
+ this.bundledata.close();
+ }
+ catch (IOException e)
+ {
+ // Do Nothing
+ }
+ }
+
+ return(exporting);
+ }
+
+ /**
+ * Reload from a new bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @param newBundle Dummy Bundle which contains new data.
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ * @exception org.osgi.framework.BundleException
+ */
+ protected boolean reload(Bundle newBundle) throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (INSTALLED | RESOLVED)) == 0)
+ {
+ Debug.println("Bundle.reload called when state != INSTALLED | RESOLVED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ boolean exporting = false;
+
+ if (framework.isActive())
+ {
+ if (state == RESOLVED)
+ {
+ BundleLoaderProxy curProxy = getLoaderProxy();
+ exporting = curProxy.inUse();
+ if (exporting) {
+ // make sure the BundleLoader is created so it can
+ // be added to the removalPending list.
+ curProxy.getBundleLoader();
+ framework.packageAdmin.addRemovalPending(curProxy);
+ }
+ else {
+ framework.packageAdmin.unexportResources(curProxy);
+ if (loader != null)
+ {
+ loader.clear();
+ loader.close();
+ }
+ framework.bundles.unMarkDependancies(curProxy);
+ }
+ state = INSTALLED;
+ loader = null;
+ proxy = null;
+ unresolveFragments();
+ fragments = null;
+ }
+
+
+ }
+ else
+ {
+ /* close the outgoing jarfile */
+ try
+ {
+ this.bundledata.close();
+ }
+ catch (IOException e)
+ {
+ // Do Nothing
+ }
+ }
+ this.bundledata = newBundle.bundledata;
+ this.bundledata.setBundle(this);
+ return(exporting);
+ }
+
+ /**
+ * Unresolves all attached fragments. This is called any time the host
+ * bundle is reloaded, unloaded or is unresovled.
+ *
+ */
+ protected void unresolveFragments() {
+ if (fragments != null) {
+// TODO is this sync really needed? unresolve should only ever be done in with the state change lock aquired no?
+ synchronized (fragments) {
+ int size = fragments.size();
+ for (int i=0; i<size; i++) {
+ BundleFragment fragment = (BundleFragment) fragments.elementAt(i);
+ fragment.unresolve();
+ }
+ }
+ }
+ }
+
+ /**
+ * Refresh the bundle. This is called by Framework.refreshPackages.
+ * This method must be called while holding the bundles lock.
+ *
+ * @exception org.osgi.framework.BundleException if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected void refresh() throws BundleException
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (UNINSTALLED | INSTALLED | RESOLVED)) == 0)
+ {
+ Debug.println("Bundle.reload called when state != UNINSTALLED | INSTALLED | RESOLVED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+ if (state == RESOLVED) {
+ BundleLoaderProxy curProxy = getLoaderProxy();
+ framework.packageAdmin.unexportResources(curProxy);
+ if (loader != null) {
+ loader.clear();
+ loader.close();
+ }
+ framework.bundles.unMarkDependancies(curProxy);
+ loader = null;
+ proxy = null;
+ fragments = null;
+ state = INSTALLED;
+ }
+
+ }
+
+ /**
+ * Unload the bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected boolean unload()
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (UNINSTALLED | INSTALLED | RESOLVED)) == 0)
+ {
+ Debug.println("Bundle.unload called when state != UNINSTALLED | INSTALLED | RESOLVED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ boolean exporting = false;
+
+ if (framework.isActive())
+ {
+ if (state == RESOLVED)
+ {
+ BundleLoaderProxy curProxy = getLoaderProxy();
+ exporting = curProxy.inUse();
+ if (exporting) {
+ // make sure the BundleLoader is created so it can
+ // be added to the removalPending list.
+ curProxy.getBundleLoader();
+ framework.packageAdmin.addRemovalPending(curProxy);
+ }
+ else {
+ framework.packageAdmin.unexportResources(curProxy);
+ if (loader != null)
+ {
+ loader.clear();
+ loader.close();
+ }
+ framework.bundles.unMarkDependancies(curProxy);
+ }
+
+ state = INSTALLED;
+ loader = null;
+ proxy = null;
+ unresolveFragments();
+ fragments = null;
+ domain = null;
+ }
+
+ //TODO Do we need to fix something here?
+ //BUGBUG ? search for any detached loaders for this bundle and
+ // close them if they are not exporting.
+ }
+ else
+ {
+ try
+ {
+ this.bundledata.close();
+ }
+ catch (IOException e)
+ { // Do Nothing.
+ }
+ }
+
+ return(exporting);
+ }
+
+ /**
+ * This method loads a class from the bundle.
+ *
+ * @param name the name of the desired Class.
+ * @param checkPermission indicates whether a permission check should be done.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException if the class definition was not found.
+ */
+ protected Class loadClass(String name, boolean checkPermission) throws ClassNotFoundException
+ {
+ if (checkPermission) {
+ framework.checkAdminPermission();
+ checkValid();
+ }
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if ((state & (STARTING|ACTIVE|STOPPING)) == 0)
+ {
+ Debug.println("Bundle.loadClass("+name+") called when state != STARTING | ACTIVE | STOPPING: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ getBundleLoader();
+ if (loader == null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Bundle.loadClass("+name+") called when loader == null: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ throw new ClassNotFoundException(name);
+ }
+
+ return(loader.loadClass(name));
+ }
+
+ /**
+ * Find the specified resource in this bundle.
+ *
+ * This bundle's class loader is called to search for the named resource.
+ * If this bundle's state is <tt>INSTALLED</tt>, then only this bundle will
+ * be searched for the specified resource. Imported packages cannot be searched
+ * when a bundle has not been resolved.
+ *
+ * @param name The name of the resource.
+ * See <tt>java.lang.ClassLoader.getResource</tt> for a description of
+ * the format of a resource name.
+ * @return a URL to the named resource, or <tt>null</tt> if the resource could
+ * not be found or if the caller does not have
+ * the <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ *
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ */
+ public URL getResource(String name)
+ {
+ checkValid();
+
+ getBundleLoader();
+ if (loader == null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Bundle.getResource("+name+") called when loader == null: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+
+ return(null);
+ }
+
+ return(loader.findResource(name));
+ }
+
+ /**
+ * Internal worker to start a bundle.
+ *
+ * @param persistent if true persistently record the bundle was started.
+ */
+ protected void startWorker(boolean persistent) throws BundleException
+ {
+ if (framework.active) {
+ if ((state & (STARTING | ACTIVE)) != 0) {
+ return;
+ }
+
+ //STARTUP TIMING Start here
+ if (Debug.DEBUG && MONITOR_BUNDLES)
+ framework.adaptor.getBundleStats().startActivation(getGlobalName());
+
+ try {
+ if (state == INSTALLED) {
+ framework.packageAdmin.resolveBundles();
+
+ if (state != RESOLVED) {
+ throw new BundleException(getResolutionFailureMessage());
+ }
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Bundle: Active sl = " + framework.startLevelImpl.getStartLevel() + "; Bundle " + id + " sl = " + this.startLevel);
+ }
+
+ if (this.startLevel <= framework.startLevelImpl.getStartLevel()) {
+ state = STARTING;
+
+ context = createContext();
+ try {
+ context.start();
+
+ if (framework.active) {
+ state = ACTIVE;
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("->started " + this);
+ }
+
+ framework.publishBundleEvent(BundleEvent.STARTED, this);
+ }
+
+ } catch (BundleException e) {
+ context.close();
+ context = null;
+
+ state = RESOLVED;
+
+ throw e;
+ }
+
+ if (state == UNINSTALLED) {
+ context.close();
+ context = null;
+
+ throw new BundleException(Msg.formatter.getString("BUNDLE_UNINSTALLED_EXCEPTION"));
+ }
+ }
+ } finally {
+ if (Debug.DEBUG && MONITOR_BUNDLES)
+ framework.adaptor.getBundleStats().endActivation(getGlobalName());
+ }
+ }
+
+ if (persistent)
+ {
+ setStatus(Constants.BUNDLE_STARTED, true);
+ }
+ }
+
+ /**
+ * Create a BundleContext for this bundle.
+ *
+ * @return BundleContext for this bundle.
+ */
+ protected BundleContext createContext()
+ {
+ return(new BundleContext(this));
+ }
+
+ /**
+ * Return the current context for this bundle.
+ *
+ * @return BundleContext for this bundle.
+ */
+ protected BundleContext getContext()
+ {
+ return(context);
+ }
+
+ /**
+ * Internal worker to stop a bundle.
+ *
+ * @param persistent if true persistently record the bundle was stopped.
+ */
+ protected void stopWorker(boolean persistent) throws BundleException
+ {
+ if (persistent)
+ {
+ setStatus(Constants.BUNDLE_STARTED, false);
+ }
+
+ if (framework.active)
+ {
+ if ((state & (STOPPING | RESOLVED | INSTALLED)) != 0)
+ {
+ return;
+ }
+
+ state = STOPPING;
+
+ try
+ {
+ context.stop();
+ }
+ finally
+ {
+ context.close();
+ context = null;
+
+ checkValid();
+
+ state = RESOLVED;
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("->stopped "+ this);
+ }
+
+ framework.publishBundleEvent(BundleEvent.STOPPED, this);
+ }
+ }
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the services
+ * registered by this bundle
+ * or <code>null</code> if the bundle has no registered
+ * services.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceRegistration
+ * @see ServiceReference
+ */
+ public org.osgi.framework.ServiceReference[] getRegisteredServices()
+ {
+ checkValid();
+
+ if (context == null)
+ {
+ return(null);
+ }
+
+ return(context.getRegisteredServices());
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the
+ * services this bundle is using,
+ * or <code>null</code> if the bundle is not using any services.
+ * A bundle is considered to be using a service if the bundle's
+ * use count for the service is greater than zero.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceReference
+ */
+ public org.osgi.framework.ServiceReference[] getServicesInUse()
+ {
+ checkValid();
+
+ if (context == null)
+ {
+ return(null);
+ }
+
+ return(context.getServicesInUse());
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.framework.Bundle#getFragments()
+ */
+ public org.osgi.framework.Bundle[] getFragments() {
+
+ synchronized (this) {
+ if (fragments == null)
+ return null;
+ org.osgi.framework.Bundle[] result = new org.osgi.framework.Bundle[fragments.size()];
+ fragments.toArray(result);
+ return result;
+ }
+ }
+
+ /**
+ * Attaches a fragment to this BundleHost. Fragments must be attached to
+ * the host by ID order. If the ClassLoader of the host is already created
+ * then the fragment must be attached to the host ClassLoader
+ * @param fragment The fragment bundle to attach
+ * return true if the fragment successfully attached; false if the fragment
+ * could not be logically inserted at the end of the fragment chain.
+ */
+ public void attachFragment(BundleFragment fragment) throws BundleException
+ {
+ if (fragments == null)
+ {
+ synchronized (this)
+ {
+ if (fragments == null)
+ {
+ fragments = new Vector(10);
+ fragments.addElement(fragment);
+ }
+ }
+ }
+ else {
+ synchronized (fragments) {
+ int size = fragments.size();
+ boolean inserted = false;
+ // We must keep our fragments ordered by bundle ID; or
+ // install order.
+ for (int i = 0; i < size; i++) {
+ BundleFragment frag = (BundleFragment) fragments.elementAt(i);
+ if (fragment.id < frag.id) {
+ // if the loader has already been created
+ // then we cannot attach a fragment into the middle
+ // of the fragment chain.
+ if (loader != null) {
+ throw new BundleException(Msg.formatter.getString("FRAGMENT_ATTACHMENT ERROR"));
+ }
+ fragments.insertElementAt(fragment, i);
+ inserted = true;
+ }
+ }
+ if (!inserted) {
+ fragments.addElement(fragment);
+ }
+ }
+ }
+
+ // If the Host ClassLoader has exists then we must attach
+ // the fragment to the ClassLoader.
+ if (loader != null) {
+ loader.attachFragment(fragment,System.getProperties());
+ }
+
+ if (state == ACTIVE && context != null) {
+ context.startFragment(fragment);
+ }
+ }
+
+ public BundleLoader getBundleLoader() {
+ if (loader == null) {
+ synchronized(this) {
+ if (loader == null)
+ try {
+ loader = new BundleLoader(this, getBundleDescription());
+ getLoaderProxy().setBundleLoader(loader);
+ } catch (BundleException e) {
+ // TODO log something here
+ e.printStackTrace();
+ return null;
+ }
+ }
+ }
+ return loader;
+ }
+
+ /**
+ * Mark this bundle as resolved.
+ */
+ protected void resolve() {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ if ((state & (INSTALLED)) == 0) {
+ Debug.println("Bundle.resolve called when state != INSTALLED: "+this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+ state = RESOLVED;
+ }
+
+ protected BundleLoaderProxy getLoaderProxy() {
+ if (proxy == null){
+ synchronized(this){
+ if (proxy == null){
+ proxy = new BundleLoaderProxy(this);
+ }
+ }
+ }
+ return proxy;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleLoader.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleLoader.java
new file mode 100644
index 000000000..0bdb053a6
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleLoader.java
@@ -0,0 +1,1045 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.*;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.util.ManifestElement;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.*;
+
+/**
+ * This object is created when the bundle
+ * loaded at framework launch or bundle install or update.
+ *
+ * It represents the loaded state of the bundle.
+ *
+ */
+public class BundleLoader implements ClassLoaderDelegate
+{
+ /** Bundle object */
+ protected BundleHost bundle;
+
+ /** The is the BundleClassLoader for the bundle */
+ protected BundleClassLoader classloader;
+
+ /** Single object for permission checks */
+ protected BundleResourcePermission resourcePermission;
+
+ /**
+ * Hashtable of imported packages. Key is packagename, Value is BundleLoader
+ */
+ protected KeyedHashSet importedPackages;
+
+ protected boolean hasImportedPackages = false;
+
+ protected boolean hasDynamicImports = false;
+ /**
+ * If true, import all packages dynamically.
+ */
+ protected boolean dynamicImportPackageAll;
+ /**
+ * If not null, list of package stems to import dynamically.
+ */
+ protected String[] dynamicImportPackageStems;
+ /**
+ * If not null, list of package names to import dynamically.
+ */
+ protected String[] dynamicImportPackages;
+
+ protected KeyedHashSet providedPackages;
+ protected KeyedHashSet requiredPackagesCache;
+ protected BundleLoaderProxy[] requiredBundles;
+ protected int[] reexportTable;
+
+ /**
+ * Returns the package name from the specified class name.
+ * The returned package is dot seperated.
+ *
+ * @param name Name of a class.
+ * @return Dot separated package name or null if the class
+ * has no package name.
+ */
+ protected static String getPackageName(String name) {
+ if (name != null) {
+ int index = name.lastIndexOf('.'); /* find last period in class name */
+ if (index > 0)
+ return name.substring(0, index);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the package name from the specified resource name.
+ * The returned package is dot seperated.
+ *
+ * @param name Name of a resource.
+ * @return Dot separated package name or null if the resource
+ * has no package name.
+ */
+ protected static String getResourcePackageName(String name) {
+ if (name != null) {
+ /* check for leading slash*/
+ int begin = ((name.length() > 1) && (name.charAt(0) == '/')) ? 1 : 0;
+ int end = name.lastIndexOf('/'); /* index of last slash */
+ if (end > begin)
+ return name.substring(begin, end).replace('/', '.');
+ }
+ return null;
+ }
+
+ /**
+ * Bundle runtime constructor. This object is created when the bundle is
+ * loaded at framework launch or bundle install or update.
+ *
+ * @param bundle Bundle object for this loader.
+ * @param file BundleFile for this object
+ * @param manifest Bundle's manifest
+ * @exception org.osgi.framework.BundleException
+ */
+ protected BundleLoader(BundleHost bundle, org.eclipse.osgi.service.resolver.BundleDescription description) throws BundleException {
+ this.bundle = bundle;
+ try {
+ bundle.getBundleData().open(); /* make sure the BundleData is open */
+ } catch (IOException e) {
+ throw new BundleException(Msg.formatter.getString("BUNDLE_READ_EXCEPTION"), e);
+ }
+ initialize(description);
+ }
+
+ protected void initialize(BundleDescription description) {
+ hasImportedPackages = hasDynamicImports = SystemBundleLoader.getSystemPackages() != null;
+
+ //This is the fastest way to access to the description for fragments since the hostdescription.getFragments() is slow
+ org.osgi.framework.Bundle[] fragmentObjects = bundle.getFragments();
+ BundleDescription[] fragments = new BundleDescription[fragmentObjects==null ? 0 : fragmentObjects.length];
+ for (int i = 0; i < fragments.length; i++) {
+ fragments[i] = ((Bundle) fragmentObjects[i]).getBundleDescription();
+ }
+
+ // init the imported packages list taking the bundle...
+ addImportedPackages(description.getPackages());
+ // ...and its fragments
+ for (int i = 0; i < fragments.length; i++)
+ if (fragments[i].isResolved())
+ addImportedPackages(fragments[i].getPackages());
+
+ // init the require bundles list. Need to account for optional bundles so bundles with
+ // no supplier should be skipped.
+ BundleSpecification[] required = description.getRequiredBundles();
+ ArrayList bundles = new ArrayList(Arrays.asList(required == null ? new BundleSpecification[0] : required));
+ for (int i = 0; i < fragments.length; i++)
+ if (fragments[i].isResolved()) {
+ BundleSpecification[] fragmentRequires = fragments[i].getRequiredBundles();
+ if (fragmentRequires != null)
+ bundles.addAll(Arrays.asList(fragmentRequires));
+ }
+ if (bundles.size() > 0) {
+ ArrayList bound = new ArrayList(bundles.size());
+ int[] reexported = new int[bundles.size()];
+ int reexportIndex = 0;
+ for (int i = 0; i < bundles.size(); i++) {
+ BundleSpecification spec = (BundleSpecification)bundles.get(i);
+ if (spec.isResolved()) {
+ String bundleKey = new StringBuffer(spec.getName()).append("_").append(spec.getActualVersion().toString()).toString();
+
+ BundleLoaderProxy loaderProxy =
+ (BundleLoaderProxy) bundle.framework.packageAdmin.exportedBundles.getByKey(bundleKey);
+ if (loaderProxy != null) {
+ bound.add(loaderProxy);
+ if (spec.isExported())
+ reexported[reexportIndex++] = i;
+ }
+ else {
+ // TODO log error
+ System.out.println("Could not find loaderProxy: " + bundleKey);
+ }
+ }
+ }
+ requiredBundles = (BundleLoaderProxy[])bound.toArray(new BundleLoaderProxy[bound.size()]);
+ if (reexportIndex > 0) {
+ reexportTable = new int[reexportIndex];
+ System.arraycopy(reexported, 0, reexportTable, 0, reexportIndex);
+ }
+ }
+
+ // init the provided packages
+ String[] provides = description.getProvidedPackages();
+ ArrayList packages = new ArrayList(Arrays.asList(required == null ? new String[0] : provides));
+ for (int i = 0; i < fragments.length; i++)
+ if (fragments[i].isResolved()) {
+ String[] fragmentProvides = fragments[i].getProvidedPackages();
+ if (fragmentProvides != null)
+ packages.addAll(Arrays.asList(fragmentProvides));
+ }
+ if (packages.size() > 0) {
+ providedPackages = new KeyedHashSet(packages.size());
+ for (int i = 0; i < packages.size(); i++)
+ providedPackages.add(new SingleSourcePackage((String)packages.get(i), bundle.getLoaderProxy()));
+ }
+
+ // init the dynamic imports tables
+ try {
+ String spec = bundle.getBundleData().getDynamicImports();
+ ManifestElement[] imports = ManifestElement.parsePackageDescription(spec);
+ initDynamicImportPackage(imports);
+ } catch (BundleException e) {
+ // TODO log an error
+ }
+ }
+
+ private void addImportedPackages(PackageSpecification[] packages) {
+ if (packages != null && packages.length > 0) {
+ if (!hasImportedPackages || importedPackages == null) {
+ hasImportedPackages = true;
+ importedPackages = new KeyedHashSet();
+ }
+ for (int i = 0; i < packages.length; i++) {
+ SingleSourcePackage packagesource =
+ (SingleSourcePackage) bundle.framework.packageAdmin.exportedPackages.getByKey(packages[i].getName());
+ if (packagesource != null) {
+ importedPackages.add(packagesource);
+ }
+ }
+ }
+ }
+
+ protected BundleHost getSupplier(VersionConstraint spec) {
+ BundleDescription supplier = spec.getSupplier();
+ if (supplier == null)
+ return null;
+ return (BundleHost)bundle.framework.getBundle(supplier.getBundleId());
+ }
+
+ /**
+ * Close the the Bundle's file.
+ *
+ */
+ protected void close() {
+ if (bundle == null)
+ return;
+ BundleData bundleData = bundle.getBundleData();
+ importedPackages = null;
+ if (bundleData != null) {
+ if (classloader != null)
+ classloader.close();
+ classloader = null;
+ /* close the jar file */
+ try {
+ bundleData.close();
+ } catch (IOException e) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("IOException on close :" + e.getMessage());
+ }
+ bundle = null; /* This indicates the BundleLoader is destroyed */
+ }
+ }
+
+ /**
+ * This method loads a class from the bundle. The class is searched for in the
+ * same manner as it would if it was being loaded from a bundle (i.e. all
+ * hosts, fragments, import, required bundles and local resources are searched.
+ *
+ * @param name the name of the desired Class.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException if the class definition was not found.
+ */
+ protected Class loadClass(String name) throws ClassNotFoundException {
+ return createClassLoader().loadClass(name, false);
+ }
+
+ /**
+ * Gets a ResourceBundle using the bundle's classloader.
+ * @param name The name of the ResourceBundle to get.
+ * @param locale The locale to use to get the ResourceBundle.
+ * @return The ResourceBundle.
+ * @throws java.util.MissingResourceException if the resource is not found.
+ */
+ protected ResourceBundle getResourceBundle(String name, Locale locale){
+ return ResourceBundle.getBundle(name, locale, createClassLoader());
+ }
+
+ /**
+ * Imports a class from this Bundle Loader. The local
+ * classloader is searched then the fragments.
+ * @param name The name of the class to import.
+ * @return The loaded Class or null if the class is not found.
+ */
+ protected Class importClass(String name) {
+ Class result = findLocalClass(name);
+ return result;
+ }
+
+ protected BundleClassLoader createClassLoader() {
+ if (classloader != null)
+ return classloader;
+ synchronized(this) {
+ if (classloader != null)
+ return classloader;
+
+ try {
+ String[] classpath = getClassPath(bundle, System.getProperties());
+ if (classpath != null){
+ classloader = createBCLPrevileged(bundle.getProtectionDomain(), classpath);
+ org.osgi.framework.Bundle[] fragments = bundle.getFragments();
+ if (fragments != null)
+ for (int i = 0; i < fragments.length; i++) {
+ Bundle fragment = (Bundle)fragments[i];
+ classloader.attachFragment(fragment.getBundleData(), fragment.domain, getClassPath(fragment, System.getProperties()));
+ }
+ }
+ else {
+ bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, new BundleException(Msg.formatter.getString("BUNDLE_NO_CLASSPATH_MATCH")));
+ }
+ } catch (BundleException e) {
+ bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle,e);
+ }
+
+ }
+ return classloader;
+ }
+
+ /**
+ * Finds a class local to this bundle. Only the classloader for this bundle is searched.
+ * @param name The name of the class to find.
+ * @return The loaded Class or null if the class is not found.
+ */
+ protected Class findLocalClass(String name) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("BundleLoader["+this+"].findLocalClass("+name+")");
+ try {
+ Class clazz = createClassLoader().findLocalClass(name);
+ if (Debug.DEBUG && Debug.DEBUG_LOADER && clazz != null)
+ Debug.println("BundleLoader[" + this + "] found local class " + name);
+ return clazz;
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Finds the class for a bundle. This method is used for delegation by the bundle's classloader.
+ */
+ public Class findClass(String name) throws ClassNotFoundException {
+ if (isClosed())
+ throw new ClassNotFoundException(name);
+
+ if (Debug.DEBUG && Debug.DEBUG_LOADER) {
+ Debug.println("BundleLoader["+this+"].loadBundleClass("+name+")");
+ }
+
+ String packageName = getPackageName(name);
+
+ Class result = null;
+ if (packageName != null) {
+ result = findImportedClass(name, packageName);
+ if (result == null) {
+ result = findRequiredClass(name, packageName);
+ }
+ }
+
+ if (result==null) {
+ result = findLocalClass(name);
+ if (result==null) {
+ throw new ClassNotFoundException(name);
+ }
+ }
+
+ return result;
+ }
+
+ boolean isClosed() {
+ return bundle == null;
+ }
+ /**
+ * Finds the resource for a bundle. This method is used for delegation by the bundle's classloader.
+ */
+ public URL findResource(String name) {
+ if (isClosed())
+ return null;
+ if ((name.length() > 1) && (name.charAt(0) == '/')) /* if name has a leading slash */
+ name = name.substring(1); /* remove leading slash before search */
+
+ try {
+ checkResourcePermission();
+ } catch (SecurityException e) {
+ try {
+ bundle.framework.checkAdminPermission();
+ } catch (SecurityException ee) {
+ return null;
+ }
+ }
+ String packageName = getResourcePackageName(name);
+
+ URL resource = null;
+ if (packageName != null) {
+ resource = findImportedResource(name, packageName);
+ if (resource == null) {
+ resource = findRequiredResource(name, packageName);
+ }
+ }
+
+ if (resource == null) {
+ resource = findLocalResource(name);
+ }
+
+ return resource;
+ }
+
+ /**
+ * Finds the resources for a bundle. This method is used for delegation by the bundle's classloader.
+ */
+ public Enumeration findResources(String name) throws IOException {
+ if (isClosed())
+ return null;
+ if ((name.length() > 1) && (name.charAt(0) == '/')) /* if name has a leading slash */
+ name = name.substring(1); /* remove leading slash before search */
+
+ try {
+ checkResourcePermission();
+ } catch (SecurityException e) {
+ try {
+ bundle.framework.checkAdminPermission();
+ } catch (SecurityException ee) {
+ return null;
+ }
+ }
+ String packageName = getResourcePackageName(name);
+
+ Enumeration result = null;
+ if (packageName != null) {
+ result = findImportedResources(name, packageName);
+ if (result == null) {
+ result = findRequiredResources(name, packageName);
+ }
+ }
+
+ if (result == null) {
+ result = findLocalResources(name);
+ }
+
+ return result;
+ }
+
+ /**
+ * Imports a resource from this Bundle Loader. The local
+ * classloader is searched then the fragments.
+ * @param name The name of the resource to import.
+ * @return The URL to the resource or null if the resource is not found.
+ */
+ protected URL importResource(String name) {
+ URL result = findLocalResource(name);
+ return result;
+ }
+
+ /**
+ * Finds a resource local to this bundle. Only the classloader for this bundle is searched.
+ * @param name The name of the resource to find.
+ * @return The URL to the resource or null if the resource is not found.
+ */
+ protected URL findLocalResource(final String name) {
+ if (System.getSecurityManager() == null)
+ return createClassLoader().findLocalResource(name);
+ return (URL) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return createClassLoader().findLocalResource(name);
+ }
+ });
+ }
+
+ /**
+ * Imports the resources from this Bundle Loader. The local
+ * classloader is searched then the fragments.
+ * @param name The name of the resource to import.
+ * @return an Enumeration of URLs for the resources or null if the resource is not found.
+ */
+ protected Enumeration importResources(String name) {
+ Enumeration result = findLocalResources(name);
+ return result;
+ }
+
+ /**
+ * Returns an Enumeration of URLs representing all the resources with
+ * the given name. Only the classloader for this bundle is searched.
+ *
+ * @param name the resource name
+ * @return an Enumeration of URLs for the resources
+ * @throws IOException if I/O errors occur
+ */
+ protected Enumeration findLocalResources(String name) {
+ if ((name.length() > 1) && (name.charAt(0) == '/')) /* if name has a leading slash */
+ name = name.substring(1);
+ try {
+ checkResourcePermission();
+ } catch (SecurityException e) {
+ return null;
+ }
+ return createClassLoader().findLocalResources(name);
+ }
+
+ /**
+ * Returns the absolute path name of a native library.
+ *
+ * @param name the library name
+ * @return the absolute path of the native library or null if not found
+ */
+ public String findLibrary(final String name) {
+ if (isClosed())
+ return null;
+ if (System.getSecurityManager() == null)
+ return findLocalLibrary(name);
+ return (String) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return findLocalLibrary(name);
+ }
+ });
+ }
+
+ protected String findLocalLibrary(final String name) {
+ String result = bundle.getBundleData().findLibrary(name);
+ if (result != null)
+ return result;
+
+ org.osgi.framework.Bundle[] fragments = bundle.getFragments();
+ if (fragments == null || fragments.length == 0)
+ return null;
+
+ // look in fragments imports ...
+ for (int i=0; i<fragments.length; i++) {
+ result = ((Bundle)fragments[i]).getBundleData().findLibrary(name);
+ if (result !=null)
+ return result;
+ }
+ return result;
+ }
+
+ /**
+ * Return the bundle we are associated with.
+ *
+ */
+ protected Bundle getBundle() {
+ return bundle;
+ }
+
+ private BundleClassLoader createBCLPrevileged(final ProtectionDomain pd, final String[] cp){
+ if (System.getSecurityManager() == null)
+ return bundle.getBundleData().createClassLoader(BundleLoader.this,pd,cp);
+ return (BundleClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return bundle.getBundleData().createClassLoader(BundleLoader.this,pd,cp);
+ }
+ });
+ }
+
+ /**
+ * Check for PackagePermission to Export.
+ *
+ * @return true if bundle has the package permission.
+ */
+ protected boolean hasExportPackagePermission(String name) {
+ ProtectionDomain domain = bundle.getProtectionDomain();
+ if (domain != null)
+ return domain.implies(new PackagePermission(name, PackagePermission.EXPORT));
+ return true;
+ }
+
+ /**
+ * Check for BundlePermission to Provide.
+ *
+ * @return true if bundle has the permission to export the bundle.
+ */
+ protected boolean hasProvideBundlePermission(String uniqueId) {
+ ProtectionDomain domain = bundle.getProtectionDomain();
+ if (domain != null)
+ return domain.implies(new BundlePermission(uniqueId, BundlePermission.PROVIDE));
+ return true;
+ }
+
+ /**
+ * Check for PackagePermission to Import.
+ *
+ * @return true if bundle has the package permission.
+ */
+ protected boolean hasImportPackagePermission(String name) {
+ ProtectionDomain domain = bundle.getProtectionDomain();
+ if (domain != null)
+ return domain.implies(new PackagePermission(name, PackagePermission.IMPORT));
+ return true;
+ }
+
+ /**
+ * Check for BundlePermission to Require.
+ *
+ * @return true if bundle has the require permission.
+ */
+ protected boolean hasRequireBundlePermission(String uniqueId) {
+ ProtectionDomain domain = bundle.getProtectionDomain();
+ if (domain != null)
+ return domain.implies(new BundlePermission(uniqueId, BundlePermission.REQUIRE));
+ return true;
+ }
+
+ /**
+ * Check for BundlePermission to Host.
+ *
+ * @return true if bundle has the require permission.
+ */
+ protected boolean hasHostBundlePermission(String uniqueId) {
+ ProtectionDomain domain = bundle.getProtectionDomain();
+ if (domain != null)
+ return domain.implies(new BundlePermission(uniqueId, BundlePermission.HOST));
+ return true;
+ }
+
+ /**
+ * Return a string representation of this loader.
+ *
+ * @return String
+ */
+ public String toString() {
+ BundleData result = bundle.getBundleData();
+ return result == null ? "BundleLoader.bundledata == null!" : result.toString();
+ }
+
+ protected void checkResourcePermission() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (resourcePermission == null)
+ resourcePermission = new BundleResourcePermission(bundle.getBundleId());
+ sm.checkPermission(resourcePermission);
+ }
+ }
+
+ /**
+ * Get the BundleLoader for the package if it is imported.
+ *
+ * @param pkgname The name of the package to import.
+ * @return BundleLoader to load from or null if the package is not imported.
+ */
+ protected BundleLoader getPackageExporter(String pkgname) {
+ if (importedPackages != null) {
+ PackageSource exporter = (PackageSource)importedPackages.getByKey(pkgname);
+ if (exporter != null)
+ return exporter.getSupplier().getBundleLoader();
+ }
+
+ if (isDynamicallyImported(pkgname)) {
+ PackageSource exporter =
+ (PackageSource) bundle.framework.packageAdmin.exportedPackages.getByKey(pkgname);
+ if (exporter != null) {
+ exporter.getSupplier().markUsed(bundle.getLoaderProxy());
+ importedPackages.add(exporter);
+ return exporter.getSupplier().getBundleLoader();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return true if the target package name matches
+ * a name in the DynamicImport-Package manifest header.
+ *
+ * @param pkgname The name of the requested class' package.
+ * @return true if the package should be imported.
+ */
+ protected boolean isDynamicallyImported(String pkgname)
+ {
+ /* quick shortcut check */
+ if (!hasDynamicImports) {
+ return false;
+ }
+
+ /* "*" shortcut */
+ if (dynamicImportPackageAll)
+ return true;
+
+ /*
+ * If including the system bundle packages by default, dynamically import them.
+ * Most OSGi framework implementations assume the system bundle packages
+ * are on the VM classpath. As a result some bundles neglect to import
+ * framework packages (e.g. org.osgi.framework).
+ */
+ String[] systemPackages = SystemBundleLoader.getSystemPackages();
+ if (systemPackages != null) {
+ for (int i = 0; i < systemPackages.length; i++)
+ if (pkgname.equals(systemPackages[i]))
+ return true;
+ }
+
+ /* match against specific names */
+ if (dynamicImportPackages != null)
+ for (int i = 0; i < dynamicImportPackages.length; i++)
+ if (pkgname.equals(dynamicImportPackages[i]))
+ return true;
+
+ /* match against names with trailing wildcards */
+ if (dynamicImportPackageStems != null)
+ for (int i = 0; i < dynamicImportPackageStems.length; i++)
+ if (pkgname.startsWith(dynamicImportPackageStems[i]))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Find a class using the imported packages for this bundle. Only the
+ * ImportClassLoader is used for the search.
+ * @param name The name of the class to find.
+ * @return The loaded class or null if the class does not belong to a package
+ * that is imported by the bundle.
+ * @throws ImportClassNotFoundException If the class does belong to a package
+ * that is imported by the bundle but the class is not found.
+ */
+ protected Class findImportedClass(String name, String packageName) throws ImportClassNotFoundException {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("ImportClassLoader["+this+"].findImportedClass("+name+")");
+ if (!hasImportedPackages)
+ return null;
+ Class result = null;
+
+ try {
+ BundleLoader exporter = getPackageExporter(packageName);
+ if (exporter != null) {
+ result = exporter.importClass(name);
+ if (result == null)
+ throw new ImportClassNotFoundException(name);
+ }
+ } finally {
+ if (result == null) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("ImportClassLoader[" + this + "] class "+name+" not found in imported package " + packageName);
+ } else {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("BundleLoader["+this+"] found imported class "+name);
+ }
+ }
+ return result;
+ }
+
+ protected void addExportedProvidersFor(String packageName, ArrayList result, KeyedHashSet visited) {
+ // TODO is it ok to use bundle as the visit token or should it be the loader?
+ if (!visited.add(bundle))
+ return;
+ PackageSource local = getProvidedPackage(packageName);
+ if (local != null)
+ result.add(local.getSupplier());
+ if (requiredBundles == null)
+ return;
+ int size = reexportTable == null ? 0 : reexportTable.length;
+ int reexportIndex = 0;
+ for (int i = 0; i < requiredBundles.length; i++) {
+ if (reexportIndex < size && reexportTable[reexportIndex] == i) {
+ reexportIndex++;
+ requiredBundles[i].getBundleLoader().addExportedProvidersFor(packageName, result, visited);
+ }
+ }
+ }
+
+ /**
+ * Find a class using the required bundles for this bundle. Only the
+ * required bundles are used to search for the class.
+ * @param name The name of the class to find.
+ * @return The loaded class or null if the class is not found.
+ */
+ protected PackageSource getProvidersFor(String packageName) {
+ // first look in the required packages cache
+ if (requiredPackagesCache != null) {
+ PackageSource result = (PackageSource)requiredPackagesCache.getByKey(packageName);
+
+ if (result != null){
+ if (result.isNullSource()) {
+ return null;
+ }
+ else {
+ return result;
+ }
+ }
+ }
+
+ // didn't find it in the cache search the actual required bundles
+ if (requiredBundles == null)
+ return null;
+ KeyedHashSet visited = new KeyedHashSet(false);
+ ArrayList result = new ArrayList(3);
+ for (int i = 0; i < requiredBundles.length; i++) {
+ BundleLoader requiredLoader = requiredBundles[i].getBundleLoader();
+ requiredLoader.addExportedProvidersFor(packageName, result, visited);
+ }
+
+ // found some so cache the result for next time and return
+ if (requiredPackagesCache == null)
+ requiredPackagesCache = new KeyedHashSet();
+ if (result.size() == 0){
+ // did not find it in our required bundles lets record the failure
+ // so we do not have to do the search again for this package.
+ requiredPackagesCache.add(new NullPackageSource(packageName));
+ return null;
+ }
+ else if (result.size() == 1) {
+ // if there is just one source, remember just the single source
+ BundleLoaderProxy bundle = (BundleLoaderProxy)result.get(0);
+ PackageSource source = new SingleSourcePackage(packageName, bundle);
+ requiredPackagesCache.add(source);
+ return source;
+ } else {
+ // if there was more than one source, build a multisource and cache that.
+ BundleLoaderProxy[] bundles = (BundleLoaderProxy[])result.toArray(new BundleLoaderProxy[result.size()]);
+ MultiSourcePackage source = new MultiSourcePackage(packageName, bundles);
+ requiredPackagesCache.add(source);
+ return source;
+ }
+ }
+
+
+ /**
+ * Find a class using the required bundles for this bundle. Only the
+ * required bundles are used to search for the class.
+ * @param name The name of the class to find.
+ * @return The loaded class or null if the class is not found.
+ */
+ protected Class findRequiredClass(String name, String packageName) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("ImportClassLoader["+this+"].findRequiredClass("+name+")");
+ PackageSource source = getProvidersFor(packageName);
+ if (source == null)
+ return null;
+ if (source.isMultivalued()) {
+ BundleLoaderProxy[] bundles = source.getSuppliers();
+ for (int i = 0; i < bundles.length; i++) {
+ Class result = bundles[i].getBundleLoader().importClass(name);
+ if (result != null)
+ return result;
+ }
+ } else
+ return source.getSupplier().getBundleLoader().importClass(name);
+ return null;
+ }
+
+ protected PackageSource getProvidedPackage(String name) {
+ return providedPackages == null ? null : (PackageSource)providedPackages.getByKey(name);
+ }
+
+ /**
+ * Find a resource using the imported packages for this bundle. Only the
+ * ImportClassLoader is used for the search.
+ * @param name The name of the resource to find.
+ * @return The URL of the resource or null if the resource does not belong to a package
+ * that is imported by the bundle.
+ * @throws ImportResourceNotFoundException If the resource does belong to a package
+ * that is imported by the bundle but the resource is not found.
+ */
+ protected URL findImportedResource(String name, String packageName) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("ImportClassLoader["+this+"].findImportedResource("+name+")");
+ if (!hasImportedPackages)
+ return null;
+
+ BundleLoader exporter = getPackageExporter(packageName);
+ if (exporter != null) {
+ URL url = exporter.importResource(name);
+ if (url != null)
+ return url;
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("ImportClassLoader["+this+"] resource "+name+" not found in imported package "+ packageName);
+ throw new ImportResourceNotFoundException(name);
+ }
+ return null;
+ }
+
+ /**
+ * Find a resource using the required bundles for this bundle. Only the
+ * required bundles are used to search.
+ * @param name The name of the resource to find.
+ * @return The URL for the resource or null if the resource is not found.
+ */
+ protected URL findRequiredResource(String name, String packageName) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("ImportClassLoader["+ this +"].findRequiredResource("+name+")");
+ PackageSource source = getProvidersFor(packageName);
+ if (source == null)
+ return null;
+ if (source.isMultivalued()) {
+ BundleLoaderProxy[] bundles = source.getSuppliers();
+ for (int i = 0; i < bundles.length; i++) {
+ URL result = bundles[i].getBundleLoader().importResource(name);
+ if (result != null)
+ return result;
+ }
+ } else
+ return source.getSupplier().getBundleLoader().importResource(name);
+ return null;
+ }
+
+ /**
+ * Returns an Enumeration of URLs representing all the resources with
+ * the given name.
+ *
+ * If the resource is in a package that is imported, call the exporting
+ * bundle. Otherwise return null.
+ *
+ * @param name the resource name
+ * @return an Enumeration of URLs for the resources if the package is
+ * imported, null otherwise.
+ * @throws IOException if I/O errors occur
+ */
+ protected Enumeration findImportedResources(String name, String packageName) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("ImportClassLoader["+this+"].findImportedResources("+name+")");
+ if (!hasImportedPackages)
+ return null;
+ BundleLoader exporter = getPackageExporter(packageName);
+ if (exporter != null)
+ return exporter.importResources(name);
+ return null;
+ }
+
+ /**
+ * Returns an Enumeration of URLs representing all the resources with
+ * the given name.
+ * Find the resources using the required bundles for this bundle. Only the
+ * required bundles are used to search.
+ *
+ * If the resource is in a package that is imported, call the exporting
+ * bundle. Otherwise return null.
+ *
+ * @param name the resource name
+ * @return an Enumeration of URLs for the resources if the package is
+ * imported, null otherwise.
+ * @throws IOException if I/O errors occur
+ */
+ protected Enumeration findRequiredResources(String name, String packageName) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println("ImportClassLoader["+this+"].findRequiredResources("+name+")");
+ PackageSource source = getProvidersFor(packageName);
+ if (source == null)
+ return null;
+ if (source.isMultivalued()) {
+ BundleLoaderProxy[] bundles = source.getSuppliers();
+ for (int i = 0; i < bundles.length; i++) {
+ Enumeration result = bundles[i].getBundleLoader().importResources(name);
+ if (result != null)
+ return result;
+ }
+ } else
+ return source.getSupplier().getBundleLoader().importResources(name);
+ return null;
+ }
+
+ protected void initDynamicImportPackage(ManifestElement[] packages) {
+ if (packages == null && SystemBundleLoader.getSystemPackages() == null)
+ return;
+
+ hasDynamicImports = hasImportedPackages = true;
+ // make sure importedPackages is not null;
+ if (importedPackages == null) {
+ importedPackages = new KeyedHashSet();
+ }
+
+ if (packages == null)
+ return;
+
+ int size = packages.length;
+ ArrayList stems = new ArrayList(size);
+ ArrayList names = new ArrayList(size);
+ for (int i = 0; i < size; i++) {
+ String name = packages[i].getValue();
+ if (name.equals("*")) { /* shortcut */
+ dynamicImportPackageAll = true;
+ return;
+ }
+
+ if (name.endsWith(".*"))
+ stems.add(name.substring(1, name.length()-1));
+ else
+ names.add(name);
+ }
+
+ size = stems.size();
+ if (size > 0)
+ dynamicImportPackageStems = (String[]) stems.toArray(new String[size]);
+
+ size = names.size();
+ if (size > 0)
+ dynamicImportPackages = (String[]) names.toArray(new String[size]);
+ }
+
+ protected void clear() {
+ providedPackages = null;
+ requiredBundles = null;
+ importedPackages = null;
+ dynamicImportPackages = null;
+ dynamicImportPackageStems = null;
+ }
+
+ protected ClassLoader getClassLoader() {
+ return classloader;
+ }
+
+ protected void attachFragment(BundleFragment fragment, Properties props){
+ initialize(bundle.getBundleDescription());
+ if (classloader == null)
+ return;
+
+ try {
+ String[] classpath = getClassPath(fragment, props);
+ if (classpath != null)
+ classloader.attachFragment(fragment.getBundleData(), fragment.domain, classpath);
+ else
+ bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, new BundleException(Msg.formatter.getString("BUNDLE_NO_CLASSPATH_MATCH")));
+ } catch (BundleException e) {
+ bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle,e);
+ }
+
+ }
+
+ protected String[] getClassPath(Bundle bundle, Properties props) throws BundleException {
+ String spec = bundle.getBundleData().getClassPath();
+ ManifestElement[] classpathElements = ManifestElement.parseClassPath(spec);
+ return matchClassPath(classpathElements, props);
+ }
+
+ protected String[] matchClassPath(ManifestElement[] classpath, Properties props) {
+ if (classpath == null) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println(" no classpath");
+ /* create default BundleClassPath */
+ return new String[] { "." };
+ }
+
+ ArrayList result = new ArrayList(10);
+ for (int i = 0; i < classpath.length; i++) {
+ Filter filter = createFilter(classpath[i].getAttribute("filter"));
+ if (filter == null || filter.match(props)) {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ Debug.println(" found match for classpath entry " + classpath[i].getValue());
+ result.add(classpath[i].getValue());
+ }
+ }
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ protected Filter createFilter(String filter) {
+ if (filter == null)
+ return null;
+ try {
+ return new Filter(filter);
+ } catch (InvalidSyntaxException e) {
+ bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR,bundle,e);
+ return null;
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleLoaderProxy.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleLoaderProxy.java
new file mode 100644
index 000000000..ead61a922
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleLoaderProxy.java
@@ -0,0 +1,155 @@
+package org.eclipse.osgi.framework.internal.core;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.PackageSpecification;
+
+public class BundleLoaderProxy implements KeyedElement {
+ private BundleLoader loader;
+ private BundleHost bundle;
+ private String uniqueId;
+ private String key;
+ private boolean stale = false;
+ private KeyedHashSet users;
+ protected boolean markedUsedDependencies = false;
+
+ public BundleLoaderProxy(BundleHost bundle) {
+ this.bundle = bundle;
+ this.uniqueId = bundle.getGlobalName();
+ if (this.uniqueId == null) {
+ this.uniqueId = new StringBuffer().append(bundle.id).append("NOUNIQUEID").toString();
+ }
+ this.key = new StringBuffer(uniqueId).append("_").append(bundle.getVersion().toString()).toString();
+ this.users = new KeyedHashSet(false);
+ }
+ public BundleLoader getBundleLoader() {
+ if (loader == null)
+ loader = bundle.getBundleLoader();
+ return loader;
+ }
+ public Bundle getBundle() {
+ return bundle;
+ }
+
+ public void setBundleLoader(BundleLoader value) {
+ loader = value;
+ }
+
+ public void markUsed(BundleLoaderProxy user){
+ // only mark as used if the user is not our own bundle.
+ if (user.getBundle() != bundle) {
+ users.add(user);
+ }
+ }
+
+ public void unMarkUsed(BundleLoaderProxy user){
+ users.removeByKey(user.getKey());
+ }
+
+ public int getKeyHashCode() {
+ return key.hashCode();
+ }
+
+ public boolean compare(KeyedElement other) {
+ if (!(other instanceof BundleLoaderProxy))
+ return false;
+ BundleLoaderProxy otherLoaderProxy = (BundleLoaderProxy) other;
+ return (uniqueId.equals(otherLoaderProxy.uniqueId) &&
+ bundle.getVersion().isPerfect(otherLoaderProxy.bundle.getVersion()));
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public void setStale() {
+ stale = true;
+ }
+ public boolean isStale() {
+ return stale;
+ }
+
+ public boolean inUse(){
+ return (users.size() > 0);
+ }
+
+ public Bundle[] getDependentBundles() {
+ KeyedElement[] proxyLoaders = users.elements();
+ KeyedHashSet bundles = new KeyedHashSet(proxyLoaders.length,false);
+ for (int i=0; i<proxyLoaders.length; i++) {
+ BundleLoaderProxy loaderProxy = (BundleLoaderProxy) proxyLoaders[i];
+ bundles.add(loaderProxy.getBundle());
+ }
+
+ KeyedElement[] elements = bundles.elements();
+ Bundle[] result = new Bundle[elements.length];
+ System.arraycopy(elements,0,result,0,elements.length);
+
+ return result;
+ }
+
+ public String toString() {
+ return bundle.getLocation();
+ }
+
+ protected void markDependencies(){
+ if (markedUsedDependencies || !bundle.isResolved()) {
+ return;
+ }
+ markedUsedDependencies = true;
+
+ BundleDescription bundleDes = bundle.getBundleDescription();
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ if (bundleDes == null) {
+ Debug.println("Bundle.resolved called and getBundleDescription returned null: " + this);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+
+ PackageSpecification[] packages = bundleDes.getPackages();
+ BundleSpecification[] requiredBundles = bundleDes.getRequiredBundles();
+ BundleDescription[] fragDescriptions = bundleDes.getFragments();
+
+ markUsedPackages(packages);
+ markUsedBundles(requiredBundles);
+
+ for (int i=0; i<fragDescriptions.length; i++) {
+ if (fragDescriptions[i].isResolved()) {
+ markUsedPackages(fragDescriptions[i].getPackages());
+ markUsedBundles(fragDescriptions[i].getRequiredBundles());
+ }
+ }
+
+ // besure to create the BundleLoader;
+ getBundleLoader();
+ }
+
+ private void markUsedPackages(PackageSpecification[] packages) {
+ if (packages!=null) {
+ for (int i=0; i<packages.length; i++) {
+ SingleSourcePackage packagesource =
+ (SingleSourcePackage) bundle.framework.packageAdmin.exportedPackages.getByKey(packages[i].getName());
+ if (packagesource != null) {
+ packagesource.getSupplier().markUsed(this);
+ }
+ }
+ }
+ }
+
+ private void markUsedBundles(BundleSpecification[] requiredBundles) {
+ if (requiredBundles != null) {
+ for (int i=0; i<requiredBundles.length; i++) {
+ if (requiredBundles[i].isResolved()) {
+ String bundleKey = new StringBuffer(requiredBundles[i].getName()).append("_").append(requiredBundles[i].getActualVersion().toString()).toString();
+
+ BundleLoaderProxy loaderProxy =
+ (BundleLoaderProxy) bundle.framework.packageAdmin.exportedBundles.getByKey(bundleKey);
+ if (loaderProxy != null) {
+ loaderProxy.markUsed(this);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleNativeCode.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleNativeCode.java
new file mode 100644
index 000000000..6f3cbdb80
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleNativeCode.java
@@ -0,0 +1,530 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.adaptor.Version;
+import org.eclipse.osgi.framework.util.ManifestElement;
+import org.osgi.framework.Constants;
+
+/**
+ * This class represents a description of native code.
+ *
+ * Native Code dependencies
+ *
+ * The Bundle-NativeCode header allows a bundle to carry the native code it
+ * needs, and make use of it when it is installed. The bundle must have
+ * RuntimePermission in order to run native code in the Framework. The value
+ * of the header must conform to the following syntax:
+ *
+ *
+ * Bundle-NativeCode: nativecode-clause ( , nativecode-clause)*
+ * nativecode-clause: nativepaths ( ; env-parameter )*
+ * nativepaths: nativepath ( ; nativepath )*
+ * env-parameter: ( processordef | osnamedef | osversiondef | languagedef )
+ * processordef: <I>processor=</I>token
+ * osnamedef: <I>osname=</I>token
+ * osversiondef: <I>osversion=</I>token
+ * languagedef: <I>language=</I>token
+ *
+ * For example:
+ *
+ * Bundle-NativeCode: http.dll ; osname=Win95; processor=x86; language=en,
+ * libhttp.so; osname=Solaris; processor=sparc
+ *
+ * The Bundle-NativeCode header allows a bundle programmer to specify an
+ * environment, and to declare what native code libraries it carries for that
+ * specific environment. The environment is characterized by the following
+ * properties:
+ *
+ * <UL>
+ * <LI><CODE>processor</CODE> The processor on which the hosting the Framework is running.
+ * It is compared against <CODE>org.osgi.framework.processor</CODE>.
+ * <LI><CODE>osname</CODE> The operating system name.
+ * It is compared against <CODE>org.osgi.framework.os.name</CODE>.
+ * <LI><CODE>osversion</CODE> The version of the operating system.
+ * It is compared against <CODE>org.osgi.framework.os.version</CODE>.
+ * <LI><CODE>language</CODE> The language.
+ * It is compared against <CODE>org.osgi.framework.language</CODE>.
+ * </UL>
+ *
+ * These properties should follow the conventions and values defined in the
+ * "Open Software Description" specification.
+ *
+ * The Framework uses the following algorithm to find the "best" matching
+ * native code clause:
+ *
+ * <ol>
+ * <li>
+ * Pick the clauses with a matching processor and operating system with the
+ * one the Framework runs on. If
+ * no clause matches both the required processor and operating system, the
+ * bundle installation/activation fails. If only one clause matches, it can
+ * be used, otherwise, remaining steps are executed.
+ * </li>
+ * <li>
+ * Pick the clauses that best match the operating system version.
+ * If they match each
+ * other exactly, that clause is considered the best match. If there is only
+ * one clause with an exact match, it can be used. If there are more than
+ * one clause that matches the property, these clauses will be picked to
+ * perform the next step. Operating system versions are taken to be backward
+ * compatible. If there is no exact match in the clauses, clauses with
+ * operating system versions lower than the value specified in
+ * org.osgi.framework.osversion will be picked. If there is only one
+ * clause which has a compatible operating system version, it can be used.
+ * Otherwise, all clauses with compatible operating system versions will go
+ * through the next step. If no clause has a matching or compatible
+ * operating system version, pick the clause that does not have operating
+ * system version specified. If that is not possible, the bundle
+ * installation fails.
+ * </li>
+ * <li>
+ * Pick the clause that best matches the language. If
+ * more than one clause remains at that point, then the Framework is free to
+ * pick amongst them randomly. If no clauses have the exact match with the
+ * value of the property, pick the clause that does not have language
+ * specified. If that is not possible, the bundle installation fails.
+ * </li>
+ * </ol>
+ *
+ */
+public class BundleNativeCode
+{
+ /**
+ * The Native Code paths for the Native Code entry
+ */
+ private Attribute nativepaths;
+
+ /**
+ * The processor attribute for this Native Code entry
+ */
+ private Attribute processor;
+
+ /**
+ * The osname attribute for this Native Code entry
+ */
+ private Attribute osname;
+
+ /**
+ * The language attribute for this Native Code entry
+ */
+ private Attribute language;
+
+ /**
+ * The osversion attribute for this Native Code entry
+ */
+ private Attribute osversion;
+
+ /**
+ * The windowingsystem attribute for this Native Code entry
+ */
+ private Attribute windowingsystem;
+
+ /**
+ * The AliasMapper used to alias OS Names.
+ */
+ private static AliasMapper aliasMapper = new AliasMapper();
+
+ /**
+ * Constructor.
+ *
+ */
+ protected BundleNativeCode(ManifestElement element)
+ {
+ StringTokenizer st = new StringTokenizer(element.getValue(),";");
+ while (st.hasMoreTokens()) {
+ addPath(st.nextToken());
+ }
+
+ String attrValue = element.getAttribute(Constants.BUNDLE_NATIVECODE_OSNAME);
+ if (attrValue!=null){
+ StringTokenizer attrSt = new StringTokenizer(attrValue,",");
+ while(attrSt.hasMoreTokens()) {
+ addAttribute(Constants.BUNDLE_NATIVECODE_OSNAME,attrSt.nextToken());
+ }
+ }
+
+ attrValue = element.getAttribute(Constants.BUNDLE_NATIVECODE_PROCESSOR);
+ if (attrValue!=null){
+ StringTokenizer attrSt = new StringTokenizer(attrValue,",");
+ while(attrSt.hasMoreTokens()) {
+ addAttribute(Constants.BUNDLE_NATIVECODE_PROCESSOR,attrSt.nextToken());
+ }
+ }
+
+ attrValue = element.getAttribute(Constants.BUNDLE_NATIVECODE_OSVERSION);
+ if (attrValue!=null){
+ StringTokenizer attrSt = new StringTokenizer(attrValue,",");
+ while(attrSt.hasMoreTokens()) {
+ addAttribute(Constants.BUNDLE_NATIVECODE_OSVERSION,attrSt.nextToken());
+ }
+ }
+
+ attrValue = element.getAttribute(Constants.BUNDLE_NATIVECODE_LANGUAGE);
+ if (attrValue!=null){
+ StringTokenizer attrSt = new StringTokenizer(attrValue,",");
+ while(attrSt.hasMoreTokens()) {
+ addAttribute(Constants.BUNDLE_NATIVECODE_LANGUAGE,attrSt.nextToken());
+ }
+ }
+
+ attrValue = element.getAttribute(Constants.BUNDLE_NATIVECODE_WINDOWING_SYSTEM);
+ if (attrValue!=null){
+ StringTokenizer attrSt = new StringTokenizer(attrValue,",");
+ while(attrSt.hasMoreTokens()) {
+ addAttribute(Constants.BUNDLE_NATIVECODE_WINDOWING_SYSTEM,attrSt.nextToken());
+ }
+ }
+ }
+
+ /**
+ * Returns the native code paths.
+ *
+ * @return Vector of String code paths.
+ */
+ public String[] getPaths()
+ {
+ if(nativepaths == null){
+ return null;
+ }
+ String[] paths = new String[nativepaths.size()];
+ nativepaths.toArray(paths);
+
+ return(paths);
+ }
+
+ /**
+ * addPath is used to add a new element to the list of native
+ * files.
+ *
+ * @param nativepath new native file
+ */
+ protected void addPath(String nativepath)
+ {
+ if (nativepaths == null)
+ {
+ nativepaths = new Attribute();
+ }
+
+ nativepaths.addElement(nativepath);
+ }
+
+ /**
+ * addAttribute is used to add the specification-version string to the package
+ * description. It is the only key supported at this time.
+ *
+ * @param key attribute key name
+ * @param value attribute value name
+ */
+ protected synchronized void addAttribute(String key, String value)
+ {
+ if (key.equals(Constants.BUNDLE_NATIVECODE_PROCESSOR))
+ {
+ if (processor == null)
+ {
+ processor = new Attribute();
+ }
+
+ processor.addElement(aliasMapper.aliasProcessor(value));
+ return;
+ }
+ if (key.equals(Constants.BUNDLE_NATIVECODE_OSNAME))
+ {
+ if (osname == null)
+ {
+ osname = new Attribute();
+ }
+
+ osname.addElement(aliasMapper.aliasOSName(value));
+ return;
+ }
+ if (key.equals(Constants.BUNDLE_NATIVECODE_OSVERSION))
+ {
+ if (osversion == null)
+ {
+ osversion = new Attribute();
+ }
+
+ osversion.addElement(new Version(value));
+ return;
+ }
+ if (key.equals(Constants.BUNDLE_NATIVECODE_LANGUAGE))
+ {
+ if (language == null)
+ {
+ language = new Attribute();
+ }
+
+ language.addElement(value.toLowerCase());
+ return;
+ }
+ if (key.equals(Constants.BUNDLE_NATIVECODE_WINDOWING_SYSTEM))
+ {
+ if (windowingsystem == null)
+ {
+ windowingsystem = new Attribute();
+ }
+
+ windowingsystem.addElement(value.toLowerCase());
+ return;
+ }
+ }
+
+ /**
+ * Override toString. Return a String representation of this object
+ *
+ * @return String representation of the object
+ */
+ public String toString()
+ {
+ Vector nativepaths = this.nativepaths;
+ int size = nativepaths.size();
+ StringBuffer sb = new StringBuffer(50 * size);
+
+ for (int i = 0; i < size; i++)
+ {
+ if (i > 0)
+ {
+ sb.append(';');
+ }
+ sb.append(nativepaths.elementAt(i).toString());
+ }
+
+ if (processor != null)
+ {
+ size = processor.size();
+ for (int i = 0; i < size; i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_PROCESSOR);
+ sb.append('=');
+ sb.append(processor.elementAt(i).toString());
+ }
+ }
+
+ if (osname != null)
+ {
+ size = osname.size();
+ for (int i = 0; i < size; i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_OSNAME);
+ sb.append('=');
+ sb.append(osname.elementAt(i).toString());
+ }
+ }
+
+ if (osversion != null)
+ {
+ size = osversion.size();
+ for (int i = 0; i < size; i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_OSVERSION);
+ sb.append('=');
+ sb.append(osversion.elementAt(i).toString());
+ }
+ }
+
+ if (language != null)
+ {
+ size = language.size();
+ for (int i = 0; i < size; i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_LANGUAGE);
+ sb.append('=');
+ sb.append(language.elementAt(i).toString());
+ }
+ }
+
+ if (windowingsystem != null)
+ {
+ size = windowingsystem.size();
+ for (int i = 0; i < size; i++)
+ {
+ sb.append(';');
+ sb.append(Constants.BUNDLE_NATIVECODE_WINDOWING_SYSTEM);
+ sb.append('=');
+ sb.append(windowingsystem.elementAt(i).toString());
+ }
+ }
+
+ return(sb.toString());
+ }
+
+ /**
+ * Return the match value for the given processor and os name.
+ * A higher value indicates a better match.
+ *
+ * @param processor processor name to match against.
+ * @param osname os name to match against.
+ * @return match value
+ */
+ public int matchProcessorOSName(String processor, String osname)
+ {
+ if ((this.processor == null) || (this.osname == null))
+ {
+ return(0);
+ }
+
+ String otherProcessor = aliasMapper.aliasProcessor(processor);
+ String otherOSName = aliasMapper.aliasOSName(osname);
+
+ if (this.processor.equals(otherProcessor) &&
+ this.osname.equals(otherOSName))
+ {
+ return(1);
+ }
+
+ return(0);
+ }
+
+
+ /**
+ * Return the higest matching value for the given os version
+ * that is less than or equal to the given os version.
+ *
+ * @param version os version to match against.
+ * @return version or null if no match.
+ */
+ public org.eclipse.osgi.framework.adaptor.Version matchOSVersion(org.eclipse.osgi.framework.adaptor.Version version)
+ {
+ if (this.osversion == null)
+ {
+ return Version.emptyVersion;
+ }
+
+ Version result = null;
+ int size = this.osversion.size();
+
+ for (int i = 0; i < size; i++)
+ {
+ Version ver = (Version)this.osversion.elementAt(i);
+ int compare = ver.compareTo(version);
+
+ if (compare == 0) /* versions are equal; best possible match */
+ {
+ return ver;
+ }
+
+ if (compare < 0) /* requested version < current OS version */
+ {
+ if ((result == null) || (ver.compareTo(result) > 0))
+ {
+ result = ver; /* remember the highest version less than osversion */
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the match value for the given language.
+ * A higher value indicates a better match.
+ *
+ * @param language language name to match against.
+ * @return match value
+ */
+ public int matchLanguage(String language)
+ {
+ if (this.language == null)
+ {
+ return(1);
+ }
+
+ if (this.language.equals(language.toLowerCase()))
+ {
+ return(2);
+ }
+
+ return(0);
+ }
+
+ /**
+ * Return the match value for the given windowing system.
+ * A higher value indicates a better match.
+ *
+ * @param ws windowing system to match against.
+ * @return match value
+ */
+ public int matchWindowingSystem(String runtimeWS)
+ {
+ if(runtimeWS == null)
+ {
+ if(this.windowingsystem == null)
+ {
+ return(1);
+ }
+ return (0);
+ }
+ else
+ {
+ if (this.windowingsystem.equals(runtimeWS.toLowerCase()))
+ {
+ return(2);
+ }
+ return(0);
+ }
+ }
+
+ /**
+ * Extension of Vector for attributes.
+ */
+ static class Attribute extends Vector
+ {
+ /**
+ * Attribute constructor.
+ *
+ */
+ Attribute()
+ {
+ super(10, 10);
+ }
+
+ /**
+ * Perform an "OR" operation on equals.
+ *
+ * @param obj Object to test against.
+ * @return true if at least one attribute is equal; false otherwise.
+ */
+ public synchronized boolean equals(Object obj)
+ {
+ for (int i = 0; i < elementCount; i++)
+ {
+ if (elementData[i].equals(obj))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ /**
+ * Add the object if it is not already in the vector.
+ *
+ * @param obj Object to add to the vector.
+ */
+ public synchronized void addElement(Object obj)
+ {
+ if (!contains(obj))
+ {
+ super.addElement(obj);
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundlePermissionCollection.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundlePermissionCollection.java
new file mode 100644
index 000000000..1c386d335
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundlePermissionCollection.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.security.PermissionCollection;
+import java.util.Hashtable;
+
+/**
+ * An abstract subclass of PermissionCollection.
+ *
+ */
+abstract class BundlePermissionCollection extends PermissionCollection
+{
+ /**
+ * The Permission collection will unresolve the permissions in these packages.
+ *
+ * @param unresolvedPackages A list of the package which have been unresolved
+ * as a result of a packageRefresh
+ */
+ abstract void unresolvePermissions(Hashtable unresolvedPackages);
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundlePermissions.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundlePermissions.java
new file mode 100644
index 000000000..74f149e4f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundlePermissions.java
@@ -0,0 +1,344 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.security.AllPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.debug.Debug;
+
+/**
+ * A heterogeneous collection of permissions for a bundle.
+ *
+ */
+final class BundlePermissions extends BundlePermissionCollection
+{
+ /**
+ * Maps a Permission's class to an appropriate PermissionCollection.
+ * Class => PermissionCollection
+ */
+ private Hashtable collections = new Hashtable(8);
+
+ /**
+ * Set to an AllPermissionCollection if this Permissions contains AllPermission.
+ */
+ private PermissionCollection allPermission;
+
+ /** Used to load classes for unresolved permissions */
+ private PackageAdmin packageAdmin;
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ */
+ BundlePermissions(PackageAdmin packageAdmin)
+ {
+ super();
+
+ this.packageAdmin = packageAdmin;
+ }
+
+
+ /**
+ * Adds the argument to the collection.
+ *
+ * @param permission java.security.Permission
+ * the permission to add to the collection
+ */
+ public void add(Permission permission)
+ {
+ if (isReadOnly())
+ {
+ throw new SecurityException();
+ }
+
+ PermissionCollection collection;
+
+ synchronized (collections)
+ {
+ collection = findCollection(permission);
+
+ if (collection == null)
+ {
+ collection = newPermissionCollection(permission);
+ }
+ }
+
+ if (permission instanceof AllPermission)
+ {
+ allPermission = collection;
+ }
+
+ collection.add(permission);
+ }
+
+ /**
+ * Answers an enumeration of the permissions
+ * in the receiver.
+ *
+ * @return Enumeration
+ * the permissions in the receiver.
+ */
+ public Enumeration elements()
+ {
+ return new Enumeration() {
+ Enumeration enumMap = collections.elements();
+ PermissionCollection c;
+ Enumeration enumC;
+ Permission next = findNextPermission();
+
+ public boolean hasMoreElements()
+ {
+ return(next != null);
+ }
+
+ public Object nextElement()
+ {
+ if (next == null)
+ {
+ throw new NoSuchElementException();
+ }
+ else
+ {
+ Object answer = next;
+ next = findNextPermission();
+ return(answer);
+ }
+ }
+
+ // This method is the important one. It looks for and
+ // answers the next available permission. If there are
+ // no permissions left to return, it answers null.
+ private Permission findNextPermission()
+ {
+ // Loop until we get a collection with at least one element.
+ while (c == null && enumMap.hasMoreElements())
+ {
+ c = (PermissionCollection) enumMap.nextElement();
+ enumC = c.elements();
+ if (!enumC.hasMoreElements())
+ c = null;
+ }
+ // At this point, c == null if there are no more elements,
+ // and otherwise is the first collection with a free element
+ // (with enumC set up to return that element).
+ if (c == null)
+ {
+ // no more elements, so return null;
+ return(null);
+ }
+ else
+ {
+ Permission answer = (Permission) enumC.nextElement();
+ if (!enumC.hasMoreElements())
+ c = null;
+ return(answer);
+ }
+ }
+ };
+ }
+
+ /**
+ * Find the appropriate permission collection to use for
+ * the given permission.
+ *
+ * @param permission Permission
+ * the permission to find a collection for
+ * @return PermissionCollection
+ * the collection to use with the permission.
+ */
+ private PermissionCollection findCollection(Permission permission)
+ {
+ Class clazz = permission.getClass();
+
+ PermissionCollection collection = (PermissionCollection) collections.get(clazz);
+
+ if (collection == null)
+ {
+ synchronized (collections)
+ {
+ collection = (PermissionCollection) collections.get(clazz);
+
+ if (collection == null)
+ {
+ collection = resolvePermissions(permission);
+ }
+ }
+ }
+
+ return collection;
+ }
+
+ /**
+ * This method will attempt to resolve unresolved permissions of the
+ * type of the specified permission.
+ *
+ * This method should only be called while holding the collections lock.
+ *
+ * @param permission Permission whose type we shall attempt to resolve.
+ * @return A PermissionCollection for the resolved permissions or
+ * <tt>null</tt> if the permissions cannot currently be resolved.
+ */
+ private PermissionCollection resolvePermissions(Permission permission)
+ {
+ UnresolvedPermissionCollection unresolvedCollection = (UnresolvedPermissionCollection)collections.get(UnresolvedPermission.class);
+
+ if (unresolvedCollection != null)
+ {
+ String name = permission.getClass().getName();
+
+ Vector permissions = unresolvedCollection.getPermissions(name);
+
+ if (permissions != null)
+ {
+ PermissionCollection collection = null;
+ Class clazz;
+
+ try
+ {
+ // We really need to resolve the permission
+ // by loading it only from the proper classloader,
+ // i.e. the system classloader or and exporting bundle's
+ // classloader. Otherwise there is a security hole.
+
+ // TODO It is unclear how this works in the world of modules and multiple
+ // versions. Just loading up any old class with the right name does not seem
+ // appropriate. For now just put in a dummy classload call to reduce code impact.
+ clazz = Class.forName(name);
+// clazz = packageAdmin.loadClass(name);
+ }
+ catch (ClassNotFoundException e)
+ {
+ return null;
+ }
+
+ Enumeration enum = permissions.elements();
+
+ while (enum.hasMoreElements())
+ {
+ Permission resolved = ((UnresolvedPermission)enum.nextElement()).resolve(clazz);
+
+ if (resolved != null)
+ {
+ if (collection == null)
+ {
+ collection = newPermissionCollection(resolved);
+ }
+
+ collection.add(resolved);
+ }
+ }
+
+ return collection;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a PermissionCollection suitable to hold the specified permission.
+ * The created collection is added to the collections Hashtable.
+ *
+ * This method should only be called while holding the collections lock.
+ */
+ private PermissionCollection newPermissionCollection(Permission permission)
+ {
+ PermissionCollection collection = permission.newPermissionCollection();
+
+ if (collection == null)
+ {
+ collection = new PermissionsHash();
+ }
+
+ collections.put(permission.getClass(), collection);
+
+ return collection;
+ }
+
+ /**
+ * Indicates whether the argument permission is implied
+ * by the permissions contained in the receiver.
+ *
+ * @return boolean
+ * <code>true</code> if the argument permission
+ * is implied by the permissions in the receiver,
+ * and <code>false</code> if it is not.
+ * @param perm java.security.Permission
+ * the permission to check
+ */
+ public boolean implies(Permission perm)
+ {
+ if ((allPermission != null) && allPermission.implies(perm))
+ {
+ return true;
+ }
+
+ PermissionCollection collection = findCollection(perm);
+
+ if (collection == null)
+ {
+ return false;
+ }
+
+ return collection.implies(perm);
+ }
+
+ /**
+ * The Permission collection will unresolve the permissions in these packages.
+ *
+ * @param unresolvedPackages A list of the package which have been unresolved
+ * as a result of a packageRefresh
+ */
+ void unresolvePermissions(Hashtable unresolvedPackages)
+ {
+ synchronized (collections)
+ {
+ int size = collections.size();
+
+ Class[] clazzes = new Class[size];
+ Enumeration enum = collections.keys();
+
+ for (int i = 0; i < size; i++)
+ {
+ clazzes[i] = (Class)enum.nextElement();
+ }
+
+ for (int i = 0; i < size; i++)
+ {
+ Class clazz = clazzes[i];
+
+ String name = clazz.getName();
+
+ int index = name.lastIndexOf('.'); /* find last period in class name */
+
+ if (index > 0)
+ {
+ if (unresolvedPackages.get(name.substring(0, index)) != null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println(" Unresolving permission class "+name);
+ }
+
+ collections.remove(clazz);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleRepository.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleRepository.java
new file mode 100644
index 000000000..6fb69fe6d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleRepository.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.eclipse.osgi.framework.adaptor.Version;
+
+public class BundleRepository {
+ /** bundles by install order */
+ private ArrayList bundlesByInstallOrder;
+
+ /** bundles keyed by bundle Id */
+ private KeyedHashSet bundlesById;
+
+ /** bundles keyed by GlobalName */
+ private Hashtable bundlesByGlobalName;
+
+ /** PackageAdmin */
+ private PackageAdmin packageAdmin;
+
+ public BundleRepository(int initialCapacity, PackageAdmin packageAdmin) {
+ bundlesByInstallOrder = new ArrayList(initialCapacity);
+ bundlesById = new KeyedHashSet(initialCapacity,true);
+ bundlesByGlobalName = new Hashtable(initialCapacity);
+ this.packageAdmin = packageAdmin;
+ }
+
+ /**
+ * Gets a list of bundles ordered by install order.
+ * @return List of bundles by install order.
+ */
+ public List getBundles() {
+ return bundlesByInstallOrder;
+ }
+
+ /**
+ * Gets a bundle by its bundle Id.
+ * @param bundleId
+ * @return
+ */
+ public Bundle getBundle(long bundleId) {
+ Long key = new Long(bundleId);
+ return (Bundle)bundlesById.getByKey(key);
+ }
+
+ public Bundle[] getBundles(String globalName) {
+ return (Bundle[]) bundlesByGlobalName.get(globalName);
+ }
+
+ public Bundle getBundle(String globalName, String version){
+ ArrayList list = (ArrayList) bundlesByGlobalName.get(globalName);
+ if (list != null) {
+ Version ver = new Version(version);
+ int size = list.size();
+ if (size>0) {
+ for(int i=0; i<size; i++) {
+ Bundle bundle = (Bundle) list.get(i);
+ if (bundle.getVersion().isPerfect(ver)) {
+ return bundle;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public void add(Bundle bundle) {
+ bundlesByInstallOrder.add(bundle);
+ bundlesById.add(bundle);
+ String globalName = bundle.getGlobalName();
+ if (globalName != null) {
+ Bundle[] bundles = (Bundle[]) bundlesByGlobalName.get(globalName);
+ if (bundles == null) {
+ // making the initial capacity on this 1 since it
+ // should be rare that multiple version exist
+ bundles = new Bundle[1];
+ bundles[0] = bundle;
+ bundlesByGlobalName.put(globalName,bundles);
+ return;
+ }
+ ArrayList list = new ArrayList(bundles.length+1);
+ // find place to insert the bundle
+ Version newVersion = bundle.getVersion();
+ boolean added = false;
+ for (int i=0; i<bundles.length; i++) {
+ Bundle oldBundle = bundles[i];
+ Version oldVersion = oldBundle.getVersion();
+ if (!added && newVersion.isGreaterOrEqualTo(oldVersion)){
+ added = true;
+ list.add(bundle);
+ }
+ list.add(oldBundle);
+ }
+ if (!added) {
+ list.add(bundle);
+ }
+
+ list.toArray(bundles);
+ bundlesByGlobalName.put(globalName,bundles);
+ }
+ }
+
+ public boolean remove(Bundle bundle) {
+ boolean removed = bundlesById.remove(bundle);
+ if (removed) {
+ bundlesByInstallOrder.remove(bundle);
+ String globalName = bundle.getGlobalName();
+ if (globalName != null) {
+ ArrayList list = (ArrayList) bundlesByGlobalName.get(globalName);
+ if (list != null) {
+ list.remove(bundle);
+ }
+ }
+ }
+ return removed;
+ }
+
+ public void removeAllBundles() {
+ bundlesByInstallOrder.clear();
+ bundlesById = new KeyedHashSet();
+ bundlesByGlobalName.clear();
+ }
+
+ public synchronized void markDependancies() {
+ KeyedElement[] elements = bundlesById.elements();
+ for(int i=0; i<elements.length; i++) {
+ if (elements[i] instanceof BundleHost) {
+ ((BundleHost)elements[i]).getLoaderProxy().markDependencies();
+ }
+ }
+ }
+
+ public synchronized void unMarkDependancies(BundleLoaderProxy user) {
+ KeyedElement[] elements = bundlesById.elements();
+ for(int i=0; i<elements.length; i++) {
+ if (elements[i] instanceof BundleHost) {
+ BundleLoaderProxy loaderProxy = ((BundleHost)elements[i]).getLoaderProxy();
+ loaderProxy.unMarkUsed(user);
+ }
+ }
+
+ // look in removal pending
+ int size = packageAdmin.removalPending.size();
+ for (int i=0; i<size; i++) {
+ BundleLoaderProxy loaderProxy = (BundleLoaderProxy) packageAdmin.removalPending.elementAt(i);
+ loaderProxy.unMarkUsed(user);
+ }
+ user.markedUsedDependencies = false;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleResourcePermission.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleResourcePermission.java
new file mode 100644
index 000000000..6875a17b4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleResourcePermission.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.security.Permission;
+
+/**
+ * Implementation specific permission to read a bundle's resources.
+ *
+ */
+
+final class BundleResourcePermission extends Permission
+{
+ private long id;
+
+ BundleResourcePermission(long id)
+ {
+ super(String.valueOf(id));
+
+ this.id = id;
+ }
+
+ BundleResourcePermission(String id)
+ {
+ super(id);
+
+ this.id = Long.parseLong(id);
+ }
+
+ /**
+ * Determines if the specified permission is implied by this object.
+ *
+ * @param p The target permission to interrogate.
+ * @return <tt>true</tt> if the specified permission is implied by
+ * this object; <tt>false</tt> otherwise.
+ */
+
+ public boolean implies(Permission p)
+ {
+ if (p instanceof BundleResourcePermission)
+ {
+ BundleResourcePermission target = (BundleResourcePermission) p;
+
+ return this.id == target.id;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the empty String.
+ */
+
+ public String getActions()
+ {
+ return "";
+ }
+
+ /**
+ * Determines the equality of two <tt>BundleResourcePermission</tt> objects.
+ *
+ * @param obj The object to test for equality with this object.
+ * @return <tt>true</tt> if <tt><i>obj</i></tt> is a <tt>BundleResourcePermission</tt>, and has the
+ * same bundle id this object; <tt>false</tt> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return(true);
+ }
+
+ if (!(obj instanceof BundleResourcePermission))
+ {
+ return(false);
+ }
+
+ BundleResourcePermission target = (BundleResourcePermission) obj;
+
+ return this.id == target.id;
+ }
+
+ /**
+ * Returns the hash code value for this object.
+ *
+ * @return A hash code value for this object.
+ */
+
+ public int hashCode()
+ {
+ return getName().hashCode();
+ }
+}
+
+
+
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleSource.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleSource.java
new file mode 100644
index 000000000..436f09f04
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleSource.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLConnection;
+
+/**
+ * BundleSource class to wrap in InputStream.
+ *
+ * <p>This class implements a URLConnection which
+ * wraps an InputStream.
+ */
+public class BundleSource extends URLConnection
+{
+ private InputStream in;
+
+ protected BundleSource(InputStream in)
+ {
+ super(null);
+ this.in = in;
+ }
+
+ public void connect() throws IOException
+ {
+ connected = true;
+ }
+
+ public InputStream getInputStream() throws IOException
+ {
+ return(in);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Constants.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Constants.java
new file mode 100644
index 000000000..129918fe8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Constants.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+/**
+ * This interface contains the constants used by the eclipse
+ * OSGi implementation.
+ */
+
+public interface Constants
+extends org.osgi.framework.Constants /* since Framework 1.1 */
+{
+ /** OSGI implementation version - make sure it is 3 digits for ServerConnection.java */
+ public static final String OSGI_IMPL_VERSION = "3.6.0";
+
+ /** OSGI implementation version properties key */
+ public static final String OSGI_IMPL_VERSION_KEY = "org.eclipse.osgi.framework.internal.core.version";
+
+ /** Default framework version */
+ public static final String OSGI_FRAMEWORK_VERSION = "1.2";
+
+ /** Framework vendor */
+ public static final String OSGI_FRAMEWORK_VENDOR = "Eclipse";
+
+ /** SystemBundle manifest name */
+ public static final String OSGI_SYSTEMBUNDLE_MANIFEST = "/META-INF/SYSTEMBUNDLE.MF";
+
+ /** Bundle manifest name */
+ public static final String OSGI_BUNDLE_MANIFEST = "META-INF/MANIFEST.MF";
+
+ /** OSGi framework package name. */
+ public static final String OSGI_FRAMEWORK_PACKAGE = "org.osgi.framework";
+
+ /** Bundle resource URL protocol */
+ public static final String OSGI_URL_PROTOCOL = "bundle";
+
+ /** Processor aliases resource */
+ public static final String OSGI_PROCESSOR_ALIASES = "processor.aliases";
+
+ /** OS name aliases resource */
+ public static final String OSGI_OSNAME_ALIASES = "osname.aliases";
+
+ /** Default permissions for bundles with no permission set
+ * and there are no default permissions set.
+ */
+ public static final String OSGI_DEFAULT_DEFAULT_PERMISSIONS = "default.permissions";
+
+ /** Base implied permissions for all bundles */
+ public static final String OSGI_BASE_IMPLIED_PERMISSIONS = "implied.permissions";
+
+ /** Name of OSGi LogService */
+ public static final String OSGI_LOGSERVICE_NAME = "org.osgi.service.log.LogService";
+
+ /** Name of OSGi PackageAdmin */
+ public static final String OSGI_PACKAGEADMIN_NAME = "org.osgi.service.packageadmin.PackageAdmin";
+
+ /** Name of OSGi PermissionAdmin */
+ public static final String OSGI_PERMISSIONADMIN_NAME = "org.osgi.service.permissionadmin.PermissionAdmin";
+
+ /** Name of OSGi StartLevel */
+ public static final String OSGI_STARTLEVEL_NAME = "org.osgi.service.startlevel.StartLevel";
+
+ /** OSGI system package property */
+ public static final String OSGI_SYSTEMPACKAGES_PROPERTY = "org.eclipse.osgi.framework.internal.core.systempackages";
+
+ /** Bundle loader buffer size property */
+ public static final String OSGI_LOADERBUFFERSIZE_PROPERTY = "org.eclipse.osgi.framework.internal.core.loader.buffersize";
+
+ /** JVM java.vm.name property name */
+ public static final String JVM_VM_NAME = "java.vm.name";
+
+ /** J9 configuration (for example: core, gw, gwp, rm, foun, max) */
+ public static final String JVM_CONFIGURATION = "com.ibm.oti.configuration";
+
+ /** JVM os.arch property name */
+ public static final String JVM_OS_ARCH = "os.arch";
+
+ /** JVM os.name property name */
+ public static final String JVM_OS_NAME = "os.name";
+
+ /** JVM os.version property name */
+ public static final String JVM_OS_VERSION = "os.version";
+
+ /** JVM user.language property name */
+ public static final String JVM_USER_LANGUAGE = "user.language";
+
+ /** JVM user.region property name */
+ public static final String JVM_USER_REGION = "user.region";
+
+ /** J2ME configuration property name */
+ public static final String J2ME_MICROEDITION_CONFIGURATION = "microedition.configuration";
+
+ /** J2ME profile property name */
+ public static final String J2ME_MICROEDITION_PROFILES = "microedition.profiles";
+
+ /** Persistent bundle status */
+ public static final int BUNDLE_STARTED = 0x00000001;
+
+ /** Property file locations and default names. */
+ public static final String KEY_OSGI_PROPERTIES = "org.eclipse.osgi.framework.internal.core.properties";
+ public static final String DEFAULT_OSGI_PROPERTIES = "osgi.properties";
+
+ /** Properties set by the framework */
+ public static final String KEY_ENDIAN = "org.eclipse.osgi.framework.internal.core.endian";
+ public static final String KEY_ADDRESSLENGTH = "org.eclipse.osgi.framework.internal.core.addresslength";
+ public static final String KEY_VM = "org.eclipse.osgi.framework.internal.core.vm";
+ public static final String KEY_IMPLTYPE = "org.eclipse.osgi.framework.internal.core.impltype";
+ public static final String KEY_COUNTRY = "org.eclipse.osgi.framework.internal.core.country";
+ public static final String KEY_FRAMEWORKBEGINNINGSTARTLEVEL = "org.eclipse.osgi.framework.internal.core.FrameworkBeginningStartLevel";
+
+ /** Properties defaults */
+ public static final String DEFAULT_ADDRESSLENGTH = "32";
+ public static final String DEFAULT_ENDIAN = "le";
+ public static final String DEFAULT_STARTLEVEL = "1";
+
+ /** ImplTypes (Execution environments) properties */
+ public static final String IMPLTYPE_FOUNDATION = "JCL_Foundation_1_3";
+ public static final String IMPLTYPE_GWP = "JCL_GatewayPlus_1_3";
+ public static final String IMPLTYPE_MAX = "JCL_Max_1_3";
+ public static final String IMPLTYPE_UNDEFINED = "";
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ExportedPackageImpl.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ExportedPackageImpl.java
new file mode 100644
index 000000000..c2e0b347d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ExportedPackageImpl.java
@@ -0,0 +1,83 @@
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.ArrayList;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.PackageSpecification;
+import org.eclipse.osgi.service.resolver.Version;
+import org.osgi.framework.Constants;
+
+
+public class ExportedPackageImpl extends SingleSourcePackage implements org.osgi.service.packageadmin.ExportedPackage {
+
+ String specVersion;
+ PackageSpecification packageSpec;
+
+ public ExportedPackageImpl(PackageSpecification packageSpec, BundleLoaderProxy supplier) {
+ super(packageSpec.getName(), supplier);
+ this.packageSpec = packageSpec;
+ Version version = this.packageSpec.getActualVersion();
+ if (version != null) {
+ this.specVersion = version.toString();
+ }
+ }
+
+ public String getName() {
+ return getId();
+ }
+
+ public org.osgi.framework.Bundle getExportingBundle() {
+ if (supplier.isStale()) {
+ return null;
+ }
+ return supplier.getBundle();
+ }
+
+ public org.osgi.framework.Bundle[] getImportingBundles() {
+ if (supplier.isStale()) {
+ return null;
+ }
+
+ Bundle[] dependentBundles = supplier.getDependentBundles();
+ ArrayList importingBundles = new ArrayList();
+
+ // always add self
+ importingBundles.add(supplier.getBundle());
+
+ for(int i=0; i<dependentBundles.length; i++) {
+ Bundle bundle = dependentBundles[i];
+ BundleLoader bundleLoader = bundle.getBundleLoader();
+ /* check to make sure this package is really imported;
+ * do not call bundleLoader.getPackageExporter() here because we do
+ * not want to cause the bundle to dynamically import any packages
+ * that may not have been referenced yet.
+ */
+ if (bundleLoader.importedPackages != null &&
+ bundleLoader.importedPackages.getByKey(getId()) != null){
+ importingBundles.add(bundle);
+ }
+ }
+
+ Bundle[] result = new Bundle[importingBundles.size()];
+ importingBundles.toArray(result);
+ return result;
+ }
+
+ public String getSpecificationVersion() {
+ return specVersion;
+ }
+
+ public boolean isRemovalPending() {
+ Bundle bundle = supplier.getBundle();
+ return bundle.framework.packageAdmin.removalPending.contains(supplier);
+ }
+
+ public String toString(){
+ StringBuffer result = new StringBuffer(getId());
+ if (specVersion != null) {
+ result.append("; ").append(Constants.PACKAGE_SPECIFICATION_VERSION);
+ result.append("=\"").append(specVersion).append("\"");
+ }
+ return result.toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ExternalMessages.properties b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ExternalMessages.properties
new file mode 100644
index 000000000..bf8be01a8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ExternalMessages.properties
@@ -0,0 +1,78 @@
+######################################################################
+# Copyright (c) 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+######################################################################
+
+#External Messages for EN locale
+ECLIPSE_OSGI_NAME=Eclipse OSGi Framework
+ECLIPSE_OSGI_VERSION=3.6.0
+OSGI_VERSION=OSGi Service Platform Release 3
+ECLIPSE_COPYRIGHT=Copyright (c) 2003 IBM Corporation and others. \nAll rights reserved. This program and the accompanying materials \nare made available under the terms of the Common Public License v1.0 \nwhich accompanies this distribution, and is available at \nhttp://www.eclipse.org/legal/cpl-v10.html \nContributors: \n IBM Corporation - initial API and implementation
+OSGI_SYSTEMBUNDLE_CREATE_EXCEPTION=Exception while creating System Bundle: {0}
+OSGI_INTERNAL_ERROR=Internal Error in the OSGi framework. Please report this problem.
+BUNDLE_INSTALL_RECURSION_EXCEPTION=Bundle is already being installed
+BUNDLE_INSTALL_REQUIRED_EE_EXCEPTION=No match is available for the required execution environment: {0}
+BUNDLE_INSTALL_SAME_UNIQUEID=A bundle with the unique ID and version is already installed: {0} {1}
+BUNDLE_UNINSTALLED_EXCEPTION=Bundle has been uninstalled
+BUNDLE_CONTEXT_INVALID_EXCEPTION=BundleContext is no longer valid
+BUNDLE_NATIVECODE_EXCEPTION=The Bundle-NativeCode file {0} could not be not found
+BUNDLE_NATIVECODE_MATCH_EXCEPTION=No Bundle-NativeCode match
+BUNDLE_JXE_UNSUPPORTED_EXCEPTION=JXE Bundles are not supported in this environment
+BUNDLE_STATE_CHANGE_EXCEPTION=Bundle state change in progress
+BUNDLE_REFRESH_FAILURE=PackageAdmin.refreshPackages failed to complete
+BUNDLE_ACTIVATOR_EXCEPTION=Exception in {0}.{1}()
+BUNDLE_INVALID_ACTIVATOR_EXCEPTION=The BundleActivator {0} is invalid
+BUNDLE_INVALID_CLASSPATH_EXCEPTION=The BundleActivator {0} was not loaded by the bundle's ClassLoader; This may indicate the bundle is on the system classpath
+BUNDLE_UNRESOLVED_EXCEPTION=The bundle could not be resolved
+BUNDLE_UNRESOLVED_PACKAGE=imported package {0}
+BUNDLE_UNRESOLVED_BUNDLE=required bundle {0}
+BUNDLE_UNRESOLVED_HOST=host {0}
+BUNDLE_UNRESOLVED_NOT_CHOSEN_EXCEPTION=The bundle could not be resolved. Reason: probably another version has been chosen
+BUNDLE_UNRESOLVED_UNSATISFIED_CONSTRAINT_EXCEPTION=The bundle could not be resolved. Reason: missing {0}
+BUNDLE_JXE_LOAD_EXCEPTION=The file rom.classes in the bundle could not be loaded
+BUNDLE_JXE_UNLOAD_EXCEPTION=Unable to unload {0}/{1}
+BUNDLE_LIBRARY_NOT_FOUND_EXCEPTION=The native library {0} was not found in the bundle
+BUNDLE_SYSTEMBUNDLE_UNINSTALL_EXCEPTION=The System Bundle cannot be uninstalled
+BUNDLE_READ_EXCEPTION=An error occured trying to read the bundle
+BUNDLE_CLASSPATH_ENTRY_NOT_FOUND_EXCEPTION=The Bundle-ClassPath entry {0} was not found in the bundle
+BUNDLE_NO_CLASSPATH_MATCH=The Bundle-ClassPath does not have any matching entries
+BUNDLE_NOT_IN_STATE=The bundle does not exist in the state: {0}
+STATE_UNRESOLVED_WRONG_BUNDLE=The state unresolved a bundle that was not in the list of bundles to resolve: {0}
+FRAGMENT_CANNOT_START=A fragment bundle cannot be started.
+FRAGMENT_CANNOT_STOP=A fragment bundle cannot be stopped.
+ADAPTOR_STORAGE_EXCEPTION=The FrameworkAdaptor object could not perform the operation
+ADAPTOR_DIRECTORY_EXCEPTION=\"{0}\" is not a directory
+ADAPTOR_URL_CREATE_EXCEPTION=\"{0}\" is an invalid URL
+ADAPTOR_DIRECTORY_CREATE_EXCEPTION=The directory \"{0}\" could not be created
+RESOURCE_NOT_FOUND_EXCEPTION=The resource {0} could not be found
+MANIFEST_NOT_FOUND_EXCEPTION=The manifest file {0} could not be found in the bundle {1}
+MANIFEST_INVALID_LINE_EXCEPTION=The manifest line \"{0}\" is invalid
+MANIFEST_INVALID_HEADER_EXCEPTION=The {0} \"{1}\" is invalid
+MANIFEST_INVALID_PACKAGE_EXCEPTION=The package description {0} in invalid
+REGISTRY_UPDATE_EXCEPTION=A registry update failed
+SERVICE_FACTORY_EXCEPTION=Exception in {0}.{1}()
+SERVICE_CLASS_NOT_FOUND_EXCEPTION=The service class {0} could not be loaded from an exported package
+SERVICE_NOT_INSTANCEOF_CLASS_EXCEPTION={0}.getService() returned a service object that is not an instanceof the service class {1}
+SERVICE_OBJECT_NULL_EXCEPTION={0}.getService() returned a null service object
+SERVICE_ARGUMENT_NULL_EXCEPTION=The service parameter is null
+SERVICE_ALREADY_UNREGISTERED_EXCEPTION=The service has been unregistered
+SERVICE_EMPTY_CLASS_LIST_EXCEPTION=The array of service names is empty
+HEADER_DUPLICATE_KEY_EXCEPTION "The key \"{0}\" already exists in another case variation
+FILTER_MISSING_LEFTPAREN=Missing \'(\' at character {0}
+FILTER_MISSING_RIGHTPAREN=Missing \')\' at character {0}
+FILTER_TRAILING_CHARACTERS=Extraneous trailing characters at character {0}
+FILTER_TERMINATED_ABRUBTLY=Filter ended abruptly
+FILTER_INVALID_OPERATOR=Invalid operator at character {0}
+FILTER_MISSING_ATTR=Missing attr at character {0}
+FILTER_MISSING_VALUE=Missing value at character {0}
+FILTER_INVALID_VALUE=Invalid value at character {0}
+PROPERTIES_INVALID_ADDRESSLENGTH=Invalid osgi.properties value \"org.eclipse.osgi.framework.internal.core.addresslength={0}\"
+PROPERTIES_INVALID_ENDIAN=Invalid osgi.properties value \"org.eclipse.osgi.framework.internal.core.endian={0}\"
+PROPERTIES_INVALID_FW_STARTLEVEL=Invalid initial framework start level. Value must be a positive integer. Using Default={0}
+STARTLEVEL_EXCEPTION_INVALID_REQUESTED_STARTLEVEL=The requested start level of {0} is invalid. The value must be a positive integer.
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Filter.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Filter.java
new file mode 100644
index 000000000..e73fcda6c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Filter.java
@@ -0,0 +1,1837 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.Dictionary;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.util.*;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * RFC 1960-based Filter. Filter objects can be created by calling
+ * the constructor with the desired filter string.
+ * A Filter object can be called numerous times to determine if the
+ * match argument matches the filter string that was used to create the Filter
+ * object.
+ *
+ * <p>The syntax of a filter string is the string representation
+ * of LDAP search filters as defined in RFC 1960:
+ * <i>A String Representation of LDAP Search Filters</i> (available at
+ * http://www.ietf.org/rfc/rfc1960.txt).
+ * It should be noted that RFC 2254:
+ * <i>A String Representation of LDAP Search Filters</i>
+ * (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
+ * RFC 1960 but only adds extensible matching and is not applicable for this
+ * API.
+ *
+ * <p>The string representation of an LDAP search filter is defined by the
+ * following grammar. It uses a prefix format.
+ * <pre>
+ * &lt;filter&gt; ::= '(' &lt;filtercomp&gt; ')'
+ * &lt;filtercomp&gt; ::= &lt;and&gt; | &lt;or&gt; | &lt;not&gt; | &lt;item&gt;
+ * &lt;and&gt; ::= '&' &lt;filterlist&gt;
+ * &lt;or&gt; ::= '|' &lt;filterlist&gt;
+ * &lt;not&gt; ::= '!' &lt;filter&gt;
+ * &lt;filterlist&gt; ::= &lt;filter&gt; | &lt;filter&gt; &lt;filterlist&gt;
+ * &lt;item&gt; ::= &lt;simple&gt; | &lt;present&gt; | &lt;substring&gt;
+ * &lt;simple&gt; ::= &lt;attr&gt; &lt;filtertype&gt; &lt;value&gt;
+ * &lt;filtertype&gt; ::= &lt;equal&gt; | &lt;approx&gt; | &lt;greater&gt; | &lt;less&gt;
+ * &lt;equal&gt; ::= '='
+ * &lt;approx&gt; ::= '~='
+ * &lt;greater&gt; ::= '&gt;='
+ * &lt;less&gt; ::= '&lt;='
+ * &lt;present&gt; ::= &lt;attr&gt; '=*'
+ * &lt;substring&gt; ::= &lt;attr&gt; '=' &lt;initial&gt; &lt;any&gt; &lt;final&gt;
+ * &lt;initial&gt; ::= NULL | &lt;value&gt;
+ * &lt;any&gt; ::= '*' &lt;starval&gt;
+ * &lt;starval&gt; ::= NULL | &lt;value&gt; '*' &lt;starval&gt;
+ * &lt;final&gt; ::= NULL | &lt;value&gt;
+ * </pre>
+ *
+ * <code>&lt;attr&gt;</code> is a string representing an attribute, or
+ * key, in the properties objects of the registered services.
+ * Attribute names are not case sensitive;
+ * that is cn and CN both refer to the same attribute.
+ * <code>&lt;value&gt;</code> is a string representing the value, or part of
+ * one, of a key in the properties objects of the registered services.
+ * If a <code>&lt;value&gt;</code> must
+ * contain one of the characters '<code>*</code>' or '<code>(</code>'
+ * or '<code>)</code>', these characters
+ * should be escaped by preceding them with the backslash '<code>\</code>'
+ * character.
+ * Note that although both the <code>&lt;substring&gt;</code> and
+ * <code>&lt;present&gt;</code> productions can
+ * produce the <code>'attr=*'</code> construct, this construct is used only to
+ * denote a presence filter.
+ *
+ * <p>Examples of LDAP filters are:
+ *
+ * <pre>
+ * &quot;(cn=Babs Jensen)&quot;
+ * &quot;(!(cn=Tim Howes))&quot;
+ * &quot;(&(&quot; + Constants.OBJECTCLASS + &quot;=Person)(|(sn=Jensen)(cn=Babs J*)))&quot;
+ * &quot;(o=univ*of*mich*)&quot;
+ * </pre>
+ *
+ * <p>The approximate match (<code>~=</code>) is implementation specific but
+ * should at least ignore case and white space differences. Optional are
+ * codes like soundex or other smart "closeness" comparisons.
+ *
+ * <p>Comparison of values is not straightforward. Strings
+ * are compared differently than numbers and it is
+ * possible for a key to have multiple values. Note that
+ * that keys in the match argument must always be strings.
+ * The comparison is defined by the object type of the key's
+ * value. The following rules apply for comparison:
+ *
+ * <blockquote>
+ * <TABLE BORDER=0>
+ * <TR><TD><b>Property Value Type </b></TD><TD><b>Comparison Type</b></TD></TR>
+ * <TR><TD>String </TD><TD>String comparison</TD></TR>
+ * <TR valign=top><TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal </TD><TD>numerical comparison</TD></TR>
+ * <TR><TD>Character </TD><TD>character comparison</TD></TR>
+ * <TR><TD>Boolean </TD><TD>equality comparisons only</TD></TR>
+ * <TR><TD>[] (array)</TD><TD>recursively applied to values </TD></TR>
+ * <TR><TD>Vector</TD><TD>recursively applied to elements </TD></TR>
+ * </TABLE>
+ * Note: arrays of primitives are also supported.
+ * </blockquote>
+ *
+ * A filter matches a key that has multiple values if it
+ * matches at least one of those values. For example,
+ * <pre>
+ * Dictionary d = new Hashtable();
+ * d.put( "cn", new String[] { "a", "b", "c" } );
+ * </pre>
+ * d will match <code>(cn=a)</code> and also <code>(cn=b)</code>
+ *
+ * <p>A filter component that references a key having an unrecognizable
+ * data type will evaluate to <code>false</code> .
+ */
+
+public class Filter
+ implements org.osgi.framework.Filter /* since Framework 1.1 */
+{
+ /* public methods in org.osgi.framework.Filter */
+
+ /**
+ * Constructs a {@link Filter} object. This filter object may be used
+ * to match a {@link ServiceReference} or a Dictionary.
+ *
+ * <p> If the filter cannot be parsed, an {@link InvalidSyntaxException}
+ * will be thrown with a human readable message where the
+ * filter became unparsable.
+ *
+ * @param filter the filter string.
+ * @exception InvalidSyntaxException If the filter parameter contains
+ * an invalid filter string that cannot be parsed.
+ */
+ public Filter(String filter) throws InvalidSyntaxException
+ {
+ topLevel = true;
+ new Parser(filter).parse(this);
+ }
+
+ /**
+ * Filter using a service's properties.
+ * The Filter is executed using the referenced service's
+ * properties.
+ *
+ * @param reference the reference to the service whose
+ * properties are used in the match.
+ * @return <code>true</code> if the service's properties match this filter;
+ * <code>false</code> otherwise.
+ */
+ public boolean match(org.osgi.framework.ServiceReference reference)
+ {
+ return(match0(((ServiceReference)reference).registration.properties));
+ }
+
+ /**
+ * Filter using a Dictionary.
+ * The Filter is executed using the Dictionary's keys.
+ *
+ * @param dictionary the dictionary whose keys are used in the match.
+ * @return <code>true</code> if the Dictionary's keys match this filter;
+ * <code>false</code> otherwise.
+ */
+ public boolean match(Dictionary dictionary)
+ {
+ if (dictionary != null)
+ {
+ dictionary = new Headers(dictionary);
+ }
+
+ return(match0(dictionary));
+ }
+
+ /**
+ * Returns this Filter object's filter string.
+ * The filter string is normalized by removing
+ * whitespace which does not affect the meaning of the filter.
+ *
+ * @return filter string.
+ */
+ public String toString()
+ {
+ if (this.filter == null)
+ {
+ StringBuffer filter = new StringBuffer();
+ filter.append('(');
+
+ switch (operation)
+ {
+ case AND:
+ {
+ filter.append('&');
+
+ Filter[] filters = (Filter[])value;
+ int size = filters.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ filter.append(filters[i].toString());
+ }
+
+ break;
+ }
+
+ case OR:
+ {
+ filter.append('|');
+
+ Filter[] filters = (Filter[])value;
+ int size = filters.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ filter.append(filters[i].toString());
+ }
+
+ break;
+ }
+
+ case NOT:
+ {
+ filter.append('!');
+ filter.append(value.toString());
+
+ break;
+ }
+
+ case SUBSTRING:
+ {
+ filter.append(attr);
+ filter.append('=');
+
+ String[] substrings = (String[])value;
+
+ int size = substrings.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ String substr = substrings[i];
+
+ if (substr == null) /* * */
+ {
+ filter.append('*');
+ }
+ else /* xxx */
+ {
+ filter.append(encodeValue(substr));
+ }
+ }
+
+ break;
+ }
+ case EQUAL:
+ {
+ filter.append(attr);
+ filter.append('=');
+ filter.append(encodeValue(value.toString()));
+
+ break;
+ }
+ case GREATER:
+ {
+ filter.append(attr);
+ filter.append(">=");
+ filter.append(encodeValue(value.toString()));
+
+ break;
+ }
+ case LESS:
+ {
+ filter.append(attr);
+ filter.append("<=");
+ filter.append(encodeValue(value.toString()));
+
+ break;
+ }
+ case APPROX:
+ {
+ filter.append(attr);
+ filter.append("~=");
+ filter.append(encodeValue(approxString(value.toString())));
+
+ break;
+ }
+
+ case PRESENT:
+ {
+ filter.append(attr);
+ filter.append("=*");
+
+ break;
+ }
+ }
+
+ filter.append(')');
+
+ if (topLevel) /* only hold onto String object at toplevel */
+ {
+ this.filter = filter.toString();
+ }
+ else
+ {
+ return(filter.toString());
+ }
+ }
+
+ return(this.filter);
+ }
+
+ /**
+ * Compares this Filter object to another object.
+ *
+ * @param obj the object to compare.
+ * @return If the other object is a Filter object, then
+ * returns <code>this.toString().equals(obj.toString())</code>,
+ * otherwise <code>false</code>.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return (true);
+ }
+
+ if (!(obj instanceof Filter))
+ {
+ return (false);
+ }
+
+ return(this.toString().equals(obj.toString()));
+ }
+
+ /**
+ * Returns the hashCode for this Filter object.
+ *
+ * @return The hashCode of the filter string, <i>i.e.</i>
+ * <code>this.toString().hashCode()</code>.
+ */
+ public int hashCode()
+ {
+ return(this.toString().hashCode());
+ }
+
+
+ /* Protected fields and methods for the Filter implementation */
+
+ /** filter operation */
+ protected int operation;
+ protected static final int EQUAL = 1;
+ protected static final int APPROX = 2;
+ protected static final int GREATER = 3;
+ protected static final int LESS = 4;
+ protected static final int PRESENT = 5;
+ protected static final int SUBSTRING = 6;
+ protected static final int AND = 7;
+ protected static final int OR = 8;
+ protected static final int NOT = 9;
+
+ /** filter attribute or null if operation AND, OR or NOT */
+ protected String attr;
+ /** filter operands */
+ protected Object value;
+
+ /* normalized filter string for topLevel Filter object */
+ protected String filter;
+
+ /* true if root Filter object */
+ protected boolean topLevel;
+
+ protected Filter()
+ {
+ topLevel = false;
+ }
+
+ protected void setFilter(int operation, String attr, Object value)
+ {
+ this.operation = operation;
+ this.attr = attr;
+ this.value = value;
+ }
+
+ /**
+ * Filter using a service's properties.
+ * The Filter is executed using the referenced service's
+ * properties.
+ *
+ * @param reference A reference to the service whose
+ * properties are used in the match.
+ * @return If the service's properties match the filter,
+ * return <code>true</code>. Otherwise, return <code>false</code>.
+ */
+ protected boolean match(ServiceReference reference)
+ {
+ return(match0(reference.registration.properties));
+ }
+
+ /**
+ * Internal match routine.
+ * Dictionary parameter must support case-insensitive get.
+ *
+ * @param dictionary A dictionary whose
+ * keys are used in the match.
+ * @return If the Dictionary's keys match the filter,
+ * return <code>true</code>. Otherwise, return <code>false</code>.
+ */
+ protected boolean match0(Dictionary properties)
+ {
+ switch (operation)
+ {
+ case AND:
+ {
+ Filter[] filters = (Filter[])value;
+ int size = filters.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (!filters[i].match0(properties))
+ {
+ return(false);
+ }
+ }
+
+ return(true);
+ }
+
+ case OR:
+ {
+ Filter[] filters = (Filter[])value;
+ int size = filters.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (filters[i].match0(properties))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ case NOT:
+ {
+ Filter filter = (Filter)value;
+
+ return(!filter.match0(properties));
+ }
+
+ case SUBSTRING:
+ case EQUAL:
+ case GREATER:
+ case LESS:
+ case APPROX:
+ {
+ Object prop = (properties == null) ? null : properties.get(attr);
+
+ return(compare(operation, prop, value));
+ }
+
+ case PRESENT:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("PRESENT("+attr+")");
+ }
+
+ Object prop = (properties == null) ? null : properties.get(attr);
+
+ return(prop != null);
+ }
+ }
+
+ return(false);
+ }
+
+ /**
+ * Encode the value string such that '(', '*', ')'
+ * and '\' are escaped.
+ *
+ * @param value unencoded value string.
+ * @return encoded value string.
+ */
+ protected static String encodeValue(String value)
+ {
+ boolean encoded = false;
+ int inlen = value.length();
+ int outlen = inlen << 1; /* inlen * 2 */
+
+ char[] output = new char[outlen];
+ value.getChars(0, inlen, output, inlen);
+
+ int cursor = 0;
+ for (int i = inlen; i < outlen; i++)
+ {
+ char c = output[i];
+
+ switch (c)
+ {
+ case '(':
+ case '*':
+ case ')':
+ case '\\':
+ {
+ output[cursor] = '\\';
+ cursor++;
+ encoded = true;
+
+ break;
+ }
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return(encoded ? new String(output, 0, cursor) : value);
+ }
+
+ protected boolean compare(int operation, Object value1, Object value2)
+ {
+ if (value1 == null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("compare("+value1+","+value2+")");
+ }
+
+ return(false);
+ }
+
+ try
+ {
+ if (value1 instanceof String)
+ {
+ return(compare_String(operation, (String)value1, value2));
+ }
+
+ Class clazz = value1.getClass();
+
+ if (clazz.isArray())
+ {
+ Class type = clazz.getComponentType();
+
+ if (type.isPrimitive())
+ {
+ return(compare_PrimitiveArray(operation, type, value1, value2));
+ }
+ else
+ {
+ return(compare_ObjectArray(operation, (Object[])value1, value2));
+ }
+
+ }
+
+ if (value1 instanceof Vector)
+ {
+ return(compare_Vector(operation, (Vector)value1, value2));
+ }
+
+ if (value1 instanceof Integer)
+ {
+ return(compare_Integer(operation, ((Integer)value1).intValue(), value2));
+ }
+
+ if (value1 instanceof Long)
+ {
+ return(compare_Long(operation, ((Long)value1).longValue(), value2));
+ }
+
+ if (value1 instanceof Byte)
+ {
+ return(compare_Byte(operation, ((Byte)value1).byteValue(), value2));
+ }
+
+ if (value1 instanceof Short)
+ {
+ return(compare_Short(operation, ((Short)value1).shortValue(), value2));
+ }
+
+ if (value1 instanceof Character)
+ {
+ return(compare_Character(operation, ((Character)value1).charValue(), value2));
+ }
+
+ if (value1 instanceof Float)
+ {
+ return(compare_Float(operation, ((Float)value1).floatValue(), value2));
+ }
+
+ if (value1 instanceof Double)
+ {
+ return(compare_Double(operation, ((Double)value1).doubleValue(), value2));
+ }
+
+ if (value1 instanceof Boolean)
+ {
+ return(compare_Boolean(operation, ((Boolean)value1).booleanValue(), value2));
+ }
+
+ try
+ {
+ if (value1 instanceof Comparable)
+ {
+ return(compare_Comparable(operation, (Comparable)value1, value2));
+ }
+ }
+ catch (NoClassDefFoundError notype)
+ {
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("Type not supported");
+ }
+ }
+ catch (Throwable t)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.printStackTrace(t);
+ }
+ }
+
+ return(false);
+ }
+
+
+ protected boolean compare_Vector(int operation, Vector vector, Object value2)
+ {
+ int size = vector.size();
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare(operation, vector.elementAt(i), value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_ObjectArray(int operation, Object[] array, Object value2)
+ {
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_PrimitiveArray(int operation, Class type, Object primarray, Object value2)
+ {
+ if (Integer.TYPE.isAssignableFrom(type))
+ {
+ int[] array = (int[])primarray;
+
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare_Integer(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ if (Long.TYPE.isAssignableFrom(type))
+ {
+ long[] array = (long[])primarray;
+
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare_Long(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ if (Byte.TYPE.isAssignableFrom(type))
+ {
+ byte[] array = (byte[])primarray;
+
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare_Byte(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ if (Short.TYPE.isAssignableFrom(type))
+ {
+ short[] array = (short[])primarray;
+
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare_Short(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ if (Character.TYPE.isAssignableFrom(type))
+ {
+ char[] array = (char[])primarray;
+
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare_Character(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ if (Float.TYPE.isAssignableFrom(type))
+ {
+ float[] array = (float[])primarray;
+
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare_Float(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ if (Double.TYPE.isAssignableFrom(type))
+ {
+ double[] array = (double[])primarray;
+
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare_Double(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ if (Boolean.TYPE.isAssignableFrom(type))
+ {
+ boolean[] array = (boolean[])primarray;
+
+ int size = array.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (compare_Boolean(operation, array[i], value2))
+ {
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_String(int operation, String string, Object value2)
+ {
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+string+","+value2+")");
+ }
+
+ String[] substrings = (String[])value2;
+ int pos = 0;
+
+ int size = substrings.length;
+
+ for (int i = 0; i < size; i++)
+ {
+ String substr = substrings[i];
+
+ if (i+1 < size) /* if this is not that last substr */
+ {
+ if (substr == null) /* * */
+ {
+ String substr2 = substrings[i+1];
+
+ if (substr2 == null) /* ** */
+ {
+ continue; /* ignore first star */
+ }
+ else /* *xxx */
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("indexOf(\""+substr2+"\","+pos+")");
+ }
+ int index = string.indexOf(substr2, pos);
+ if (index == -1)
+ {
+ return(false);
+ }
+
+ i++;
+ pos = index + substr2.length();
+ }
+ }
+ else /* xxx */
+ {
+ int len = substr.length();
+
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("regionMatches("+pos+",\""+substr+"\")");
+ }
+ if (string.regionMatches(pos, substr, 0, len))
+ {
+ pos += len;
+ }
+ else
+ {
+ return(false);
+ }
+ }
+ }
+ else /* last substr */
+ {
+ if (substr == null) /* * */
+ {
+ return(true);
+ }
+ else /* xxx */
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("regionMatches("+pos+","+substr+")");
+ }
+ return(string.regionMatches(pos, substr, 0, substr.length()));
+ }
+ }
+ }
+
+ return(true);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+string+","+value2+")");
+ }
+ return(string.equals(value2));
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+string+","+value2+")");
+ }
+
+ string = approxString(string);
+ String string2 = approxString((String)value2);
+
+ return(string.equalsIgnoreCase(string2));
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+string+","+value2+")");
+ }
+ return(string.compareTo((String)value2) >= 0);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+string+","+value2+")");
+ }
+ return(string.compareTo((String)value2) <= 0);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Integer(int operation, int intval, Object value2)
+ {
+ int intval2 = Integer.parseInt(((String)value2).trim());
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+intval+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+intval+","+value2+")");
+ }
+ return(intval == intval2);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+intval+","+value2+")");
+ }
+ return(intval == intval2);
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+intval+","+value2+")");
+ }
+ return(intval >= intval2);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+intval+","+value2+")");
+ }
+ return(intval <= intval2);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Long(int operation, long longval, Object value2)
+ {
+ long longval2 = Long.parseLong(((String)value2).trim());
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+longval+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+longval+","+value2+")");
+ }
+ return(longval == longval2);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+longval+","+value2+")");
+ }
+ return(longval == longval2);
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+longval+","+value2+")");
+ }
+ return(longval >= longval2);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+longval+","+value2+")");
+ }
+ return(longval <= longval2);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Byte(int operation, byte byteval, Object value2)
+ {
+ byte byteval2 = Byte.parseByte(((String)value2).trim());
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+byteval+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+byteval+","+value2+")");
+ }
+ return(byteval == byteval2);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+byteval+","+value2+")");
+ }
+ return(byteval == byteval2);
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+byteval+","+value2+")");
+ }
+ return(byteval >= byteval2);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+byteval+","+value2+")");
+ }
+ return(byteval <= byteval2);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Short(int operation, short shortval, Object value2)
+ {
+ short shortval2 = Short.parseShort(((String)value2).trim());
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+shortval+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+shortval+","+value2+")");
+ }
+ return(shortval == shortval2);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+shortval+","+value2+")");
+ }
+ return(shortval == shortval2);
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+shortval+","+value2+")");
+ }
+ return(shortval >= shortval2);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+shortval+","+value2+")");
+ }
+ return(shortval <= shortval2);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Character(int operation, char charval, Object value2)
+ {
+ char charval2 = (((String)value2).trim()).charAt(0);
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+charval+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+charval+","+value2+")");
+ }
+ return(charval == charval2);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+charval+","+value2+")");
+ }
+ return(Character.toLowerCase(charval) == Character.toLowerCase(charval2));
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+charval+","+value2+")");
+ }
+ return(charval >= charval2);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+charval+","+value2+")");
+ }
+ return(charval <= charval2);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Boolean(int operation, boolean boolval, Object value2)
+ {
+ boolean boolval2 = new Boolean(((String)value2).trim()).booleanValue();
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+boolval+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+boolval+","+value2+")");
+ }
+ return(boolval == boolval2);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+boolval+","+value2+")");
+ }
+ return(boolval == boolval2);
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+boolval+","+value2+")");
+ }
+ return(boolval == boolval2);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+boolval+","+value2+")");
+ }
+ return(boolval == boolval2);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Float(int operation, float floatval, Object value2)
+ {
+ float floatval2 = Float.parseFloat(((String)value2).trim());
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+floatval+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+floatval+","+value2+")");
+ }
+ return(floatval == floatval2);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+floatval+","+value2+")");
+ }
+ return(floatval == floatval2);
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+floatval+","+value2+")");
+ }
+ return(floatval >= floatval2);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+floatval+","+value2+")");
+ }
+ return(floatval <= floatval2);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Double(int operation, double doubleval, Object value2)
+ {
+ double doubleval2 = Double.parseDouble(((String)value2).trim());
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+doubleval+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+doubleval+","+value2+")");
+ }
+ return(doubleval == doubleval2);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+doubleval+","+value2+")");
+ }
+ return(doubleval == doubleval2);
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+doubleval+","+value2+")");
+ }
+ return(doubleval >= doubleval2);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+doubleval+","+value2+")");
+ }
+ return(doubleval <= doubleval2);
+ }
+ }
+
+ return(false);
+ }
+
+ protected boolean compare_Comparable(int operation, Comparable value1, Object value2)
+ {
+
+ try
+ {
+ java.lang.reflect.Constructor constructor =
+ value1.getClass().getConstructor(new Class[] {value2.getClass()});
+ value2 = constructor.newInstance(new Object[] {((String)value2).trim()});
+ }
+ catch (Throwable t)
+ {
+ return(false);
+ }
+
+ switch (operation)
+ {
+ case SUBSTRING:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("SUBSTRING("+value1+","+value2+")");
+ }
+ return(false);
+ }
+ case EQUAL:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("EQUAL("+value1+","+value2+")");
+ }
+ return(value1.compareTo(value2) == 0);
+ }
+ case APPROX:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("APPROX("+value1+","+value2+")");
+ }
+ return(value1.compareTo(value2) == 0);
+ }
+ case GREATER:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("GREATER("+value1+","+value2+")");
+ }
+ return(value1.compareTo(value2) >= 0);
+ }
+ case LESS:
+ {
+ if (Debug.DEBUG && Debug.DEBUG_FILTER)
+ {
+ Debug.println("LESS("+value1+","+value2+")");
+ }
+ return(value1.compareTo(value2) <= 0);
+ }
+ }
+
+ return(false);
+ }
+
+ /**
+ * Map a string for an APPROX (~=) comparison.
+ *
+ * This implementation removes white spaces.
+ * This is the minimum implementation allowed by
+ * the OSGi spec.
+ *
+ * @param input Input string.
+ * @return String ready for APPROX comparison.
+ */
+ protected static String approxString(String input)
+ {
+ boolean changed = false;
+ char[] output = input.toCharArray();
+
+ int length = output.length;
+
+ int cursor = 0;
+ for (int i = 0; i < length; i++)
+ {
+ char c = output[i];
+
+ if (Character.isWhitespace(c))
+ {
+ changed = true;
+ continue;
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return(changed ? new String(output, 0, cursor) : input);
+ }
+
+
+ /**
+ * Parser class for OSGi filter strings. This class parses
+ * the complete filter string and builds a tree of Filter
+ * objects rooted at the parent.
+ */
+ static class Parser
+ {
+ protected String filterstring;
+ protected char[] filter;
+ protected int pos;
+
+ protected Parser(String filterstring)
+ {
+ this.filterstring = filterstring;
+ filter = filterstring.toCharArray();
+ pos = 0;
+ }
+
+ protected void parse(Filter parent) throws InvalidSyntaxException
+ {
+ try
+ {
+ parse_filter(parent);
+ }
+ catch (ArrayIndexOutOfBoundsException e)
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_TERMINATED_ABRUBTLY"), filterstring);
+ }
+
+ if (pos != filter.length)
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_TRAILING_CHARACTERS", pos), filterstring);
+ }
+ }
+
+ protected void parse_filter(Filter parent) throws InvalidSyntaxException
+ {
+ skipWhiteSpace();
+
+ if (filter[pos] != '(')
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_MISSING_LEFTPAREN", pos), filterstring);
+ }
+
+ pos++;
+
+ parse_filtercomp(parent);
+
+ skipWhiteSpace();
+
+ if (filter[pos] != ')')
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_MISSING_RIGHTPAREN", pos), filterstring);
+ }
+
+ pos++;
+
+ skipWhiteSpace();
+ }
+
+ protected void parse_filtercomp(Filter parent) throws InvalidSyntaxException
+ {
+ skipWhiteSpace();
+
+ char c = filter[pos];
+
+ switch (c)
+ {
+ case '&':
+ {
+ pos++;
+ parse_and(parent);
+ break;
+ }
+ case '|':
+ {
+ pos++;
+ parse_or(parent);
+ break;
+ }
+ case '!':
+ {
+ pos++;
+ parse_not(parent);
+ break;
+ }
+ default:
+ {
+ parse_item(parent);
+ break;
+ }
+ }
+ }
+
+ protected void parse_and(Filter parent) throws InvalidSyntaxException
+ {
+ skipWhiteSpace();
+
+ if (filter[pos] != '(')
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_MISSING_LEFTPAREN", pos), filterstring);
+ }
+
+ Vector operands = new Vector(10, 10);
+
+ while (filter[pos] == '(')
+ {
+ Filter child = new Filter();
+ parse_filter(child);
+ operands.addElement(child);
+ }
+
+ int size = operands.size();
+
+ Filter[] children = new Filter[size];
+
+ operands.copyInto(children);
+
+ parent.setFilter(Filter.AND, null, children);
+ }
+
+ protected void parse_or(Filter parent) throws InvalidSyntaxException
+ {
+ skipWhiteSpace();
+
+ if (filter[pos] != '(')
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_MISSING_LEFTPAREN", pos), filterstring);
+ }
+
+ Vector operands = new Vector(10, 10);
+
+ while (filter[pos] == '(')
+ {
+ Filter child = new Filter();
+ parse_filter(child);
+ operands.addElement(child);
+ }
+
+ int size = operands.size();
+
+ Filter[] children = new Filter[size];
+
+ operands.copyInto(children);
+
+ parent.setFilter(Filter.OR, null, children);
+ }
+
+ protected void parse_not(Filter parent) throws InvalidSyntaxException
+ {
+ skipWhiteSpace();
+
+ if (filter[pos] != '(')
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_MISSING_LEFTPAREN", pos), filterstring);
+ }
+
+ Filter child = new Filter();
+ parse_filter(child);
+
+ parent.setFilter(Filter.NOT, null, child);
+ }
+
+ protected void parse_item(Filter parent) throws InvalidSyntaxException
+ {
+ String attr = parse_attr();
+
+ skipWhiteSpace();
+
+ switch (filter[pos])
+ {
+ case '~':
+ {
+ if (filter[pos+1] == '=')
+ {
+ pos += 2;
+ parent.setFilter(Filter.APPROX, attr, parse_value());
+ return;
+ }
+ break;
+ }
+ case '>':
+ {
+ if (filter[pos+1] == '=')
+ {
+ pos += 2;
+ parent.setFilter(Filter.GREATER, attr, parse_value());
+ return;
+ }
+ break;
+ }
+ case '<':
+ {
+ if (filter[pos+1] == '=')
+ {
+ pos += 2;
+ parent.setFilter(Filter.LESS, attr, parse_value());
+ return;
+ }
+ break;
+ }
+ case '=':
+ {
+ if (filter[pos+1] == '*')
+ {
+ int oldpos = pos;
+ pos += 2;
+ skipWhiteSpace();
+ if (filter[pos] == ')')
+ {
+ parent.setFilter(Filter.PRESENT, attr, null);
+ return; /* present */
+ }
+ pos = oldpos;
+ }
+
+ pos++;
+ Object string = parse_substring();
+
+ if (string instanceof String)
+ {
+ parent.setFilter(Filter.EQUAL, attr, string);
+ }
+ else
+ {
+ parent.setFilter(Filter.SUBSTRING, attr, string);
+ }
+
+ return;
+ }
+ }
+
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_INVALID_OPERATOR", pos), filterstring);
+ }
+
+ protected String parse_attr() throws InvalidSyntaxException
+ {
+ skipWhiteSpace();
+
+ int begin = pos;
+ int end = pos;
+
+ char c = filter[pos];
+
+ while ("~<>=()".indexOf(c) == -1)
+ {
+ pos++;
+
+ if (!Character.isWhitespace(c))
+ {
+ end = pos;
+ }
+
+ c = filter[pos];
+ }
+
+ int length = end - begin;
+
+ if (length == 0)
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString(".FILTER_MISSING_ATTR", pos), filterstring);
+ }
+
+ return(new String(filter, begin, length));
+ }
+
+ protected String parse_value() throws InvalidSyntaxException
+ {
+ StringBuffer sb = new StringBuffer(filter.length - pos);
+
+ parseloop:
+ while (true)
+ {
+ char c = filter[pos];
+
+ switch (c)
+ {
+ case ')':
+ {
+ break parseloop;
+ }
+
+ case '(':
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_INVALID_VALUE", pos), filterstring);
+ }
+
+ case '\\':
+ {
+ pos++;
+ c = filter[pos];
+ /* fall through into default */
+ }
+
+ default:
+ {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ if (sb.length() == 0)
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_MISSING_VALUE", pos), filterstring);
+ }
+
+ return(sb.toString());
+ }
+
+ protected Object parse_substring() throws InvalidSyntaxException
+ {
+ StringBuffer sb = new StringBuffer(filter.length - pos);
+
+ Vector operands = new Vector(10, 10);
+
+ parseloop:
+ while (true)
+ {
+ char c = filter[pos];
+
+ switch (c)
+ {
+ case ')':
+ {
+ if (sb.length() > 0)
+ {
+ operands.addElement(sb.toString());
+ }
+
+ break parseloop;
+ }
+
+ case '(':
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_INVALID_VALUE", pos), filterstring);
+ }
+
+ case '*':
+ {
+ if (sb.length() > 0)
+ {
+ operands.addElement(sb.toString());
+ }
+
+ sb.setLength(0);
+
+ operands.addElement(null);
+ pos++;
+
+ break;
+ }
+
+ case '\\':
+ {
+ pos++;
+ c = filter[pos];
+ /* fall through into default */
+ }
+
+ default:
+ {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ int size = operands.size();
+
+ if (size == 0)
+ {
+ throw new InvalidSyntaxException(Msg.formatter.getString("FILTER_MISSING_VALUE", pos), filterstring);
+ }
+
+ if (size == 1)
+ {
+ Object single = operands.elementAt(0);
+
+ if (single != null)
+ {
+ return(single);
+ }
+ }
+
+ String[] strings = new String[size];
+
+ operands.copyInto(strings);
+
+ return(strings);
+ }
+
+ protected void skipWhiteSpace()
+ {
+ int length = filter.length;
+
+ while ((pos < length) && Character.isWhitespace(filter[pos]))
+ {
+ pos++;
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/FilteredServiceListener.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/FilteredServiceListener.java
new file mode 100644
index 000000000..5ff534073
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/FilteredServiceListener.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+public class FilteredServiceListener implements ServiceListener
+{
+ /** Filter for listener. */
+ protected Filter filter;
+
+ /** Real listener. */
+ protected ServiceListener listener;
+
+ /**
+ * Constructor.
+ *
+ * @param filterstring filter for this listener.
+ * @param listener real listener.
+ * @exception InvalidSyntaxException if the filter is invalid.
+ */
+ protected FilteredServiceListener(String filterstring, ServiceListener listener)
+ throws InvalidSyntaxException
+ {
+ filter = new Filter(filterstring);
+ this.listener = listener;
+ }
+
+ /**
+ * Receive notification that a service has had a
+ * change occur in it's lifecycle.
+ *
+ * @param event The ServiceEvent.
+ */
+ public void serviceChanged(ServiceEvent event)
+ {
+ ServiceReference reference = (ServiceReference)event.getServiceReference();
+
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
+ Debug.println("filterServiceEvent("+listenerName+", \""+filter+"\", "+reference.registration.properties+")");
+ }
+
+ if (filter.match(reference))
+ {
+ if (Debug.DEBUG && Debug.DEBUG_EVENTS)
+ {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(listener.hashCode());
+ Debug.println("dispatchFilteredServiceEvent("+listenerName+")");
+ }
+
+ listener.serviceChanged(event);
+ }
+ }
+
+ /**
+ * Get the filter string used by this Filtered listener.
+ *
+ * @return The filter string used by this listener.
+ */
+ public String toString()
+ {
+ return(filter.toString());
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java
new file mode 100644
index 000000000..99d9f2433
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java
@@ -0,0 +1,1859 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.BundleOperation;
+import org.eclipse.osgi.framework.adaptor.EventPublisher;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.adaptor.ServiceRegistry;
+import org.eclipse.osgi.framework.adaptor.Version;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.eventmgr.EventListeners;
+import org.eclipse.osgi.framework.eventmgr.EventManager;
+import org.eclipse.osgi.framework.eventmgr.EventQueue;
+import org.eclipse.osgi.framework.eventmgr.EventSource;
+import org.eclipse.osgi.framework.internal.protocol.ContentHandlerFactory;
+import org.eclipse.osgi.framework.internal.protocol.StreamHandlerFactory;
+import org.eclipse.osgi.framework.security.action.CreateThread;
+import org.eclipse.osgi.framework.util.Headers;
+import org.eclipse.osgi.framework.util.ManifestElement;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.PackageSpecification;
+import org.osgi.framework.AdminPermission;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServicePermission;
+
+/**
+ * Core OSGi Framework class.
+ */
+public class Framework implements EventSource, EventPublisher {
+
+ /** FrameworkAdaptor specific functions. */
+ protected FrameworkAdaptor adaptor;
+
+ /** framework properties object. */
+ protected Properties properties;
+
+ /** Has the service space been started */
+ protected boolean active;
+
+ /** The bundles installed in the framework */
+ protected BundleRepository bundles;
+
+ /** Package Admin object. This object manages the exported packages. */
+ protected PackageAdmin packageAdmin;
+
+ /** Package Admin object. This object manages the exported packages. */
+ protected PermissionAdmin permissionAdmin;
+
+ /** Startlevel object. This object manages the framework and bundle startlevels */
+ protected StartLevelImpl startLevelImpl;
+
+ /** Startlevel factory object */
+ protected StartLevelFactory startLevelFactory;
+
+ /** The ServiceRegistry */
+ protected ServiceRegistry serviceRegistry;
+
+ /** next free service id. */
+ protected long serviceid;
+
+ /** List of BundleContexts for bundle's BundleListeners. */
+ protected EventListeners bundleEvent;
+ protected static final int BUNDLEEVENT = 1;
+ /** List of BundleContexts for bundle's SynchronousBundleListeners. */
+ protected EventListeners bundleEventSync;
+ protected static final int BUNDLEEVENTSYNC = 2;
+ /** List of BundleContexts for bundle's ServiceListeners. */
+ protected EventListeners serviceEvent;
+ protected static final int SERVICEEVENT = 3;
+ /** List of BundleContexts for bundle's FrameworkListeners. */
+ protected EventListeners frameworkEvent;
+ protected static final int FRAMEWORKEVENT = 4;
+ /** EventManager for event delivery. */
+ protected EventManager eventManager;
+
+ /* Reservation object for install synchronization */
+ protected Hashtable installLock;
+
+ /** System Bundle object */
+ protected SystemBundle systemBundle;
+
+ /** Single object for permission checks */
+ protected AdminPermission adminPermission;
+
+ /**
+ * Constructor for the Framework instance.
+ * This method initializes the framework to an unlaunched state.
+ *
+ */
+ public Framework(FrameworkAdaptor adaptor) {
+ initialize(adaptor);
+ }
+
+ /**
+ * Initialize the framework to an unlaunched state.
+ * This method is called by the Framework constructor.
+ *
+ */
+ protected void initialize(FrameworkAdaptor adaptor) {
+ long start = System.currentTimeMillis();
+ this.adaptor = adaptor;
+ active = false;
+
+ installSecurityManager();
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
+ Debug.println(
+ "SecurityManager: " + System.getSecurityManager());
+ Debug.println(
+ "ProtectionDomain of Framework.class: \n"
+ + this.getClass().getProtectionDomain());
+ }
+
+ /* initialize the adaptor */
+ adaptor.initialize(this);
+ try {
+ adaptor.initializeStorage();
+ adaptor.compactStorage();
+ } catch (IOException e) /* fatal error */ {
+ e.printStackTrace();
+
+ throw new RuntimeException(
+ Msg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+
+ /* This must be done before calling any of the
+ * framework getProperty methods.
+ */
+ initializeProperties(adaptor.getProperties());
+
+ /* initialize admin objects */
+ packageAdmin = new PackageAdmin(this);
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ try {
+ permissionAdmin =
+ new PermissionAdmin(this, adaptor.getPermissionStorage());
+ } catch (IOException e) /* fatal error */ {
+ e.printStackTrace();
+
+ throw new RuntimeException(
+ Msg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+ }
+
+ startLevelFactory = new StartLevelFactory(this);
+ startLevelImpl = new StartLevelImpl(this);
+
+ /* create the event manager and top level event dispatchers */
+ eventManager = new EventManager("Framework Event Dispatcher");
+ bundleEvent = new EventListeners();
+ bundleEventSync = new EventListeners();
+ serviceEvent = new EventListeners();
+ frameworkEvent = new EventListeners();
+
+ /* create the service registry */
+ serviceid = 1;
+ serviceRegistry = adaptor.getServiceRegistry();
+
+ installLock = new Hashtable(13);
+
+ /* create the system bundle */
+ createSystemBundle();
+
+ /* install URLStreamHandlerFactory */
+ URL.setURLStreamHandlerFactory(
+ new StreamHandlerFactory(systemBundle.context, adaptor));
+
+ /* install ContentHandlerFactory for OSGi URLStreamHandler support */
+ URLConnection.setContentHandlerFactory(
+ new ContentHandlerFactory(systemBundle.context));
+
+ /* create bundle objects for all installed bundles. */
+ Vector bundleDatas = adaptor.getInstalledBundles();
+
+ bundles = new BundleRepository(
+ bundleDatas == null ?
+ adaptor.getVectorInitialCapacity() :
+ bundleDatas.size() + 1,
+ packageAdmin);
+
+ /* add the system bundle to the Bundle Repository */
+ bundles.add(systemBundle);
+
+ if (bundleDatas != null) {
+ int size = bundleDatas.size();
+ for (int i=0; i<size; i++) {
+ BundleData bundledata = (BundleData) bundleDatas.elementAt(i);
+ try {
+ int absl = bundledata.getStartLevel();
+ Bundle bundle = Bundle.createBundle(bundledata,bundledata.getLocation(),this,absl);
+ bundles.add(bundle);
+ } catch (BundleException be) {
+ // This is not a fatal error. Publish the framework event, but
+ // since no log service is probably running we will also print a
+ // stack trace.
+ publishFrameworkEvent(FrameworkEvent.ERROR,systemBundle,be);
+ //be.printStackTrace();
+ }
+ }
+ }
+
+ // initialize package admin; this must be done after the system bundle
+ // has been added to the state.
+ packageAdmin.initialize();
+
+ systemBundle.getBundleLoader(); // initialize the bundle loader in case someone accesses it directly
+
+ if (Debug.DEBUG)
+ System.out.println("Initialize the framework: " + (System.currentTimeMillis() - start));
+ }
+
+ private void createSystemBundle() {
+ try {
+ String resource = Constants.OSGI_SYSTEMBUNDLE_MANIFEST;
+ InputStream in = getClass().getResourceAsStream(resource);
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ if (in == null) {
+ Debug.println(
+ "Unable to find system bundle manifest " + resource);
+ }
+ }
+
+ // now get any extra packages and services that the adaptor wants to export
+ // and merge this into the system bundle's manifest
+ String exportPackages = adaptor.getExportPackages();
+ String exportServices = adaptor.getExportServices();
+ Headers manifest = Headers.parseManifest(in);
+ if (exportPackages != null) {
+ String value = (String) manifest.get(Constants.EXPORT_PACKAGE);
+ if (value == null) {
+ value = exportPackages;
+ }
+ else {
+ value += "," + exportPackages;
+ }
+ manifest.set(Constants.EXPORT_PACKAGE,null);
+ manifest.set(Constants.EXPORT_PACKAGE,value);
+ }
+ if (exportServices != null) {
+ String value = (String) manifest.get(Constants.EXPORT_SERVICE);
+ if (value == null) {
+ value = exportServices;
+ }
+ else {
+ value += "," + exportServices;
+ }
+ manifest.set(Constants.EXPORT_SERVICE,null);
+ manifest.set(Constants.EXPORT_SERVICE,value);
+ }
+
+ BundleDescription description = adaptor.getPlatformAdmin().getFactory().createBundleDescription(manifest, Constants.SYSTEM_BUNDLE_LOCATION, 0);
+ if (description == null)
+ throw new BundleException("Unable to construct System Bundle description");
+ adaptor.getState().addBundle(description);
+ // force resolution so packages are properly linked
+ adaptor.getState().resolve();
+
+ systemBundle = createSystemBundle(manifest);
+
+ SystemBundleLoader.clearSystemPackages();
+ PackageSpecification[] packages = description.getPackages();
+ if (packages != null) {
+ String[] systemPackages = new String[packages.length];
+ for (int i = 0; i < packages.length; i++) {
+ PackageSpecification spec = packages[i];
+ if (spec.getName().equals(Constants.OSGI_FRAMEWORK_PACKAGE)) {
+ String version = spec.getVersionSpecification().toString();
+ if (version != null)
+ properties.put(Constants.FRAMEWORK_VERSION, version);
+ }
+ systemPackages[i] = spec.getName();
+ }
+ // remember the system packages.
+ if (System.getProperty("osgi.autoExportSystemPackages") != null)
+ SystemBundleLoader.setSystemPackages(systemPackages);
+ }
+ } catch (BundleException e) /* fatal error */ {
+ e.printStackTrace();
+ throw new RuntimeException(Msg.formatter.getString("OSGI_SYSTEMBUNDLE_CREATE_EXCEPTION", e.getMessage()));
+ }
+ }
+
+ /**
+ * Initialize the System properties by copying properties from
+ * the adaptor properties object.
+ * This method is called by the initialize method.
+ *
+ */
+ protected void initializeProperties(Properties adaptorProperties) {
+ properties = System.getProperties();
+
+ Enumeration enum = adaptorProperties.propertyNames();
+ while (enum.hasMoreElements()) {
+ String key = (String) enum.nextElement();
+
+ if (properties.getProperty(key) == null) {
+ properties.put(key, adaptorProperties.getProperty(key));
+ }
+ }
+
+ properties.put(
+ Constants.FRAMEWORK_VENDOR,
+ Constants.OSGI_FRAMEWORK_VENDOR);
+ properties.put(
+ Constants.FRAMEWORK_VERSION,
+ Constants.OSGI_FRAMEWORK_VERSION);
+
+ // Needed for communication with Bundle Server
+ properties.put(
+ Constants.OSGI_IMPL_VERSION_KEY,
+ Constants.OSGI_IMPL_VERSION);
+
+ String value = properties.getProperty(Constants.FRAMEWORK_PROCESSOR);
+ if (value == null) {
+ value = properties.getProperty(Constants.JVM_OS_ARCH);
+ if (value != null) {
+ properties.put(Constants.FRAMEWORK_PROCESSOR, value);
+ }
+ }
+
+ value = properties.getProperty(Constants.FRAMEWORK_OS_NAME);
+ if (value == null) {
+ value = properties.getProperty(Constants.JVM_OS_NAME);
+ if (value != null) {
+ properties.put(Constants.FRAMEWORK_OS_NAME, value);
+ }
+ }
+
+ value = properties.getProperty(Constants.FRAMEWORK_OS_VERSION);
+ if (value == null) {
+ value = properties.getProperty(Constants.JVM_OS_VERSION);
+ if (value != null) {
+ int space = value.indexOf(' ');
+ if (space > 0) {
+ value = value.substring(0, space);
+ }
+ properties.put(Constants.FRAMEWORK_OS_VERSION, value);
+ }
+ }
+
+ value = properties.getProperty(Constants.FRAMEWORK_WINDOWING_SYSTEM);
+ if (value == null) {
+ //TODO can we pull this property from an Eclipse property?
+ //value = properties.getProperty(Constants.SOME_WS_PROPERTY);
+ if (value != null) {
+ properties.put(Constants.FRAMEWORK_WINDOWING_SYSTEM, value);
+ }
+ }
+
+ value = properties.getProperty(Constants.FRAMEWORK_LANGUAGE);
+ if (value == null) {
+ value = properties.getProperty(Constants.JVM_USER_LANGUAGE);
+ // set default locale for VM
+ if (value != null) {
+ properties.put(Constants.FRAMEWORK_LANGUAGE, value);
+ StringTokenizer tokenizer = new StringTokenizer(value, "_"); //$NON-NLS-1$
+ int segments = tokenizer.countTokens();
+ try {
+ switch (segments) {
+ case 2 :
+ Locale userLocale =
+ new Locale(
+ tokenizer.nextToken(),
+ tokenizer.nextToken());
+ Locale.setDefault(userLocale);
+ break;
+ case 3 :
+ userLocale =
+ new Locale(
+ tokenizer.nextToken(),
+ tokenizer.nextToken(),
+ tokenizer.nextToken());
+ Locale.setDefault(userLocale);
+ break;
+ }
+ } catch (NoSuchElementException e) {
+ // fall through and use the default
+ }
+
+ }
+ }
+
+ value =
+ properties.getProperty(
+ Constants.FRAMEWORK_EXECUTIONENVIRONMENT,
+ "");
+ String j2meConfig =
+ properties.getProperty(Constants.J2ME_MICROEDITION_CONFIGURATION);
+ String j2meProfile =
+ properties.getProperty(Constants.J2ME_MICROEDITION_PROFILES);
+ StringBuffer ee = new StringBuffer(value);
+ if (j2meConfig != null
+ && j2meConfig.length() > 0
+ && j2meProfile != null
+ && j2meProfile.length() > 0) {
+ int ic = value.indexOf(j2meConfig);
+ if (!(ic >= 0)
+ || !(ic + j2meConfig.length() < value.length()
+ && value.charAt(ic + j2meConfig.length()) == '/')
+ || !(value
+ .startsWith(j2meProfile, ic + j2meConfig.length() + 1))) {
+ if (ee.length() > 0) {
+ ee.append(",");
+ }
+ ee.append(j2meConfig).append('/').append(j2meProfile);
+ }
+ }
+ properties.put(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, ee.toString());
+
+ value = properties.getProperty(Constants.KEY_VM);
+ if (value == null) {
+ value = properties.getProperty(Constants.JVM_VM_NAME);
+ if (value != null) {
+ properties.put(Constants.KEY_VM, value);
+ }
+ }
+
+ value = properties.getProperty(Constants.KEY_COUNTRY);
+ if (value == null) {
+ value = properties.getProperty(Constants.JVM_USER_REGION);
+ if (value != null) {
+ properties.put(Constants.KEY_COUNTRY, value);
+ }
+ }
+
+ value = properties.getProperty(Constants.KEY_ADDRESSLENGTH);
+ if (value == null) {
+ properties.put(
+ Constants.KEY_ADDRESSLENGTH,
+ Constants.DEFAULT_ADDRESSLENGTH);
+ } else if (!value.equals("32") && !value.equals("64")) {
+ System.err.println(
+ Msg.formatter.getString(
+ "PROPERTIES_INVALID_ADDRESSLENGTH",
+ value));
+ }
+
+ value = properties.getProperty(Constants.KEY_ENDIAN);
+ if (value == null) {
+ properties.put(Constants.KEY_ENDIAN, Constants.DEFAULT_ENDIAN);
+ } else if (
+ !value.equalsIgnoreCase("le") && !value.equalsIgnoreCase("be")) {
+ System.err.println(
+ Msg.formatter.getString("PROPERTIES_INVALID_ENDIAN", value));
+ }
+
+ value = properties.getProperty(Constants.KEY_IMPLTYPE);
+ if (value == null) {
+ value = properties.getProperty(Constants.JVM_CONFIGURATION);
+ if (value != null) {
+ if (value.equals("foun")) {
+ properties.put(
+ Constants.KEY_IMPLTYPE,
+ Constants.IMPLTYPE_FOUNDATION);
+ } else if (value.equals("max")) {
+ properties.put(
+ Constants.KEY_IMPLTYPE,
+ Constants.IMPLTYPE_MAX);
+ } else if (value.equals("rm") || value.equals("gwp")) {
+ properties.put(
+ Constants.KEY_IMPLTYPE,
+ Constants.IMPLTYPE_GWP);
+ } else {
+ properties.put(
+ Constants.KEY_IMPLTYPE,
+ Constants.IMPLTYPE_UNDEFINED);
+ }
+ } else {
+ properties.put(
+ Constants.KEY_IMPLTYPE,
+ Constants.IMPLTYPE_UNDEFINED);
+ }
+ }
+ }
+
+ /**
+ * This method return the state of the framework.
+ *
+ */
+ protected boolean isActive() {
+ return (active);
+ }
+
+ /**
+ * This method is called to destory the framework instance.
+ *
+ */
+ public synchronized void close() {
+ if (active) {
+ shutdown();
+ }
+
+ synchronized (bundles) {
+ List allBundles = bundles.getBundles();
+ int size = allBundles.size();
+
+ for (int i = 0; i < size; i++) {
+ Bundle bundle = (Bundle) allBundles.get(i);
+
+ bundle.close();
+ }
+
+ bundles.removeAllBundles();
+ }
+
+ serviceRegistry = null;
+
+ if (bundleEvent != null) {
+ bundleEvent.removeAllListeners();
+ bundleEvent = null;
+ }
+
+ if (bundleEventSync != null) {
+ bundleEventSync.removeAllListeners();
+ bundleEventSync = null;
+ }
+
+ if (serviceEvent != null) {
+ serviceEvent.removeAllListeners();
+ serviceEvent = null;
+ }
+
+ if (frameworkEvent != null) {
+ frameworkEvent.removeAllListeners();
+ frameworkEvent = null;
+ }
+
+ if (eventManager != null) {
+ eventManager.close();
+ eventManager = null;
+ }
+
+ permissionAdmin = null;
+ packageAdmin = null;
+ adaptor = null;
+ }
+
+ /**
+ * Start the framework.
+ *
+ * When the framework is started. The following actions occur:
+ *
+ * 1. Event handling is enabled. Events can now be delivered to listeners.
+ * 2. All bundles which are recorded as started are started as described
+ * in the Bundle.start() method. These bundles are the bundles that
+ * were started when the framework was last stopped.
+ * Reports any exceptions that occur during startup using FrameworkEvents.
+ * 3. A FrameworkEvent of type FrameworkEvent.STARTED is broadcast.
+ *
+ */
+ public synchronized void launch() {
+ /* Return if framework already started */
+ if (active) {
+ return;
+ }
+
+ /* mark framework as started */
+ active = true;
+
+ /* Resume systembundle */
+ try {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Trying to launch framework");
+ }
+
+ systemBundle.resume();
+ } catch (BundleException be) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Framework launch exception: " + be.getMessage());
+ Debug.printStackTrace(be.getNestedException());
+ }
+
+ publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, be);
+ }
+ }
+
+ /**
+ * Stop the framework.
+ *
+ * When the framework is stopped. The following actions occur:
+ *
+ * 1. Suspend all started bundles as described in the Bundle.stop method
+ * except that the bundle is recorded as started. These bundles will
+ * be restarted when the framework is next started.
+ * Reports any exceptions that occur during stopping using FrameworkEvents.
+ * 2. Event handling is disabled.
+ *
+ */
+ public synchronized void shutdown() {
+ /* Return if framework already stopped */
+ if (!active) {
+ return;
+ }
+
+ /* Suspend systembundle */
+ try {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Trying to shutdown Framework");
+ }
+
+ systemBundle.suspend();
+ } catch (BundleException be) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println(
+ "Framework shutdown exception: " + be.getMessage());
+ Debug.printStackTrace(be.getNestedException());
+ }
+
+ publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, be);
+ }
+
+ try {
+ adaptor.compactStorage();
+ } catch (IOException e) {
+ publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, e);
+
+ }
+
+ /* mark framework as stopped */
+ active = false;
+ }
+
+ /**
+ * Create a new Bundle object.
+ *
+ * @param id Unique context id assigned to bundle
+ * @param file the bundle's file
+ * @param localStore adaptor specific object for the bundle's local storage
+ * @param location identity string for the bundle
+ */
+ public Bundle createBundle(BundleData bundledata, String location, int startlevel) throws BundleException {
+ verifyExecutionEnvironment(bundledata.getManifest());
+ return Bundle.createBundle(bundledata, location, this, startlevel);
+ }
+
+ /**
+ * Create the SystemBundle object.
+ *
+ * @param manifest System Bundle's manifest
+ */
+ protected SystemBundle createSystemBundle(Headers manifest) throws BundleException {
+ return new SystemBundle(manifest, this);
+ }
+
+ /**
+ * Verifies that the framework supports one of the required Execution Environments
+ *
+ * @param manifest BundleManifest of the bundle to verify the Execution Enviroment for
+ * @return boolean true if the required Execution Enviroment is available.
+ * @throws BundleException if the framework does not support the required Execution Environment.
+ */
+ protected boolean verifyExecutionEnvironment(Dictionary manifest) throws BundleException {
+ String headerValue = (String) manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
+ /* If no required EE is in the manifest return true */
+ if (headerValue == null) {
+ return true;
+ }
+ ManifestElement[] bundleRequiredEE = ManifestElement.parseBasicCommaSeparation(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT,headerValue);
+ if (bundleRequiredEE.length == 0) {
+ return true;
+ }
+
+ String systemEE =
+ System.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
+ if (systemEE != null) {
+ ManifestElement[] systemEEs = ManifestElement.parseBasicCommaSeparation(
+ Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT,
+ systemEE);
+ for (int i = 0; i < systemEEs.length; i++) {
+ for (int j = 0; j < bundleRequiredEE.length; j++) {
+ if (systemEEs[i].getValue().equals(bundleRequiredEE[j].getValue())) {
+ return true;
+ }
+ }
+ }
+ }
+
+ /* If we got here then no matching EE is available, throw exception. */
+ StringBuffer bundleEE = new StringBuffer(25);
+ for (int i = 0; i < bundleRequiredEE.length; i++) {
+ if (i > 0) {
+ bundleEE.append(",");
+ }
+ bundleEE.append(bundleRequiredEE[i]);
+ }
+ throw new BundleException(
+ Msg.formatter.getString(
+ "BUNDLE_INSTALL_REQUIRED_EE_EXCEPTION",
+ bundleEE.toString()));
+ }
+
+ /**
+ * Retrieve the value of the named environment property.
+ * Values are provided for the following properties:
+ * <dl>
+ * <dt><code>org.osgi.framework.version</code>
+ * <dd>The version of the framework.
+ * <dt><code>org.osgi.framework.vendor</code>
+ * <dd>The vendor of this framework implementation.
+ * <dt><code>org.osgi.framework.language</code>
+ * <dd>The language being used.
+ * See ISO 639 for possible values.
+ * <dt><code>org.osgi.framework.os.name</code>
+ * <dd>The name of the operating system of the hosting
+ * computer.
+ * <dt><code>org.osgi.framework.os.version</code>
+ * <dd>The version number of the operating system
+ * of the hosting computer.
+ * <dt><code>org.osgi.framework.processor</code>
+ * <dd>The name of the processor of the hosting
+ * computer.
+ * </dl>
+ *
+ * <p>Note: These last four properties are used by the
+ * <code>Bundle-NativeCode</code> manifest header's
+ * matching algorithm for selecting native code.
+ *
+ * @param key The name of the requested property.
+ * @return The value of the requested property, or <code>null</code> if
+ * the property is undefined.
+ */
+ public String getProperty(String key) {
+ return properties.getProperty(key);
+ }
+
+ /**
+ * Retrieve the value of the named environment property.
+ * Values are provided for the following properties:
+ * <dl>
+ * <dt><code>org.osgi.framework.version</code>
+ * <dd>The version of the framework.
+ * <dt><code>org.osgi.framework.vendor</code>
+ * <dd>The vendor of this framework implementation.
+ * <dt><code>org.osgi.framework.language</code>
+ * <dd>The language being used.
+ * See ISO 639 for possible values.
+ * <dt><code>org.osgi.framework.os.name</code>
+ * <dd>The name of the operating system of the hosting
+ * computer.
+ * <dt><code>org.osgi.framework.os.version</code>
+ * <dd>The version number of the operating system
+ * of the hosting computer.
+ * <dt><code>org.osgi.framework.processor</code>
+ * <dd>The name of the processor of the hosting
+ * computer.
+ * </dl>
+ *
+ * <p>Note: These last four properties are used by the
+ * <code>Bundle-NativeCode</code> manifest header's
+ * matching algorithm for selecting native code.
+ *
+ * @param key The name of the requested property.
+ * @param def A default value is the requested property is not present.
+ * @return The value of the requested property, or the default value if
+ * the property is undefined.
+ */
+ protected String getProperty(String key, String def) {
+ return properties.getProperty(key, def);
+ }
+
+ /**
+ * Set a system property.
+ *
+ * @param key The name of the property to set.
+ * @param value The value to set.
+ * @return The previous value of the property or null if the property
+ * was not previously set.
+ */
+ protected Object setProperty(String key, String value) {
+ return properties.put(key, value);
+ }
+
+ /**
+ * Install a bundle from a location.
+ *
+ * The bundle is obtained from the location
+ * parameter as interpreted by the framework
+ * in an implementation dependent way. Typically, location
+ * will most likely be a URL.
+ *
+ * @param location The location identifier of the bundle to install.
+ * @return The Bundle object of the installed bundle.
+ */
+ protected Bundle installBundle(final String location)
+ throws BundleException {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("install from location: " + location);
+ }
+
+ return installWorker(location, new PrivilegedExceptionAction() {
+ public Object run() throws BundleException {
+ /* Map the identity to a URLConnection */
+ URLConnection source =
+ adaptor.mapLocationToURLConnection(location);
+
+ /* call the worker to install the bundle */
+ return installWorkerPrivileged(location, source);
+ }
+ });
+ }
+
+ /**
+ * Install a bundle from an InputStream.
+ *
+ * <p>This method performs all the steps listed in
+ * {@link #installBundle(java.lang.String)}, except the
+ * bundle's content will be read from the InputStream.
+ * The location identifier specified will be used
+ * as the identity of the bundle.
+ *
+ * @param location The location identifier of the bundle to install.
+ * @param in The InputStream from which the bundle will be read.
+ * @return The Bundle of the installed bundle.
+ */
+ protected Bundle installBundle(final String location, final InputStream in)
+ throws BundleException {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("install from inputstream: " + location + ", " + in);
+ }
+
+ return installWorker(location, new PrivilegedExceptionAction() {
+ public Object run() throws BundleException {
+ /* Map the InputStream to a URLConnection */
+ URLConnection source = new BundleSource(in);
+
+ /* call the worker to install the bundle */
+ return installWorkerPrivileged(location, source);
+ }
+ });
+ }
+
+ /**
+ * Worker method to install a bundle. It obtains the reservation
+ * for the location and calls the specified action.
+ *
+ * @param location The location identifier of the bundle to install.
+ * @param action A PrivilegedExceptionAction which calls the real worker.
+ * @return The {@link Bundle} of the installed bundle.
+ * @exception BundleException If the action throws an error.
+ */
+ protected Bundle installWorker(
+ String location,
+ PrivilegedExceptionAction action)
+ throws BundleException {
+ synchronized (installLock) {
+ while (true) {
+ /* Check that the bundle is not already installed. */
+ Bundle bundle = getBundleByLocation(location);
+
+ /* If already installed, return bundle object */
+ if (bundle != null) {
+ return bundle;
+ }
+
+ Thread current = Thread.currentThread();
+
+ /* Check for and make reservation */
+ Thread reservation =
+ (Thread) installLock.put(location, current);
+
+ /* if the location is not already reserved */
+ if (reservation == null) {
+ /* we have made the reservation and can continue */
+ break;
+ }
+
+ /* the location was already reserved */
+
+ /* If the reservation is held by the current thread,
+ * we have recursed to install the same bundle!
+ */
+ if (current.equals(reservation)) {
+ throw new BundleException(
+ Msg.formatter.getString(
+ "BUNDLE_INSTALL_RECURSION_EXCEPTION"));
+ }
+
+ try {
+ /* wait for the reservation to be released */
+ installLock.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ /* Don't call adaptor while holding the install lock */
+ try {
+ Bundle bundle = (Bundle) AccessController.doPrivileged(action);
+
+ publishBundleEvent(BundleEvent.INSTALLED, bundle);
+
+ return bundle;
+ } catch (PrivilegedActionException e) {
+ throw (BundleException) e.getException();
+ } finally {
+ synchronized (installLock) {
+ /* release reservation */
+ installLock.remove(location);
+
+ /* wake up all waiters */
+ installLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Worker method to install a bundle. It calls the FrameworkAdaptor object
+ * to install the bundle in persistent storage.
+ *
+ * @param location The location identifier of the bundle to install.
+ * @param source The URLConnection from which the bundle will be read.
+ * @return The {@link Bundle} of the installed bundle.
+ * @exception BundleException If the provided stream cannot be read.
+ */
+ protected Bundle installWorkerPrivileged(
+ String location,
+ URLConnection source)
+ throws BundleException {
+ BundleOperation storage = adaptor.installBundle(location, source);
+
+ Bundle bundle;
+ try {
+ BundleData bundledata = storage.begin();
+ bundle = createBundle(bundledata,location,startLevelImpl.getInitialBundleStartLevel());
+
+ // Check for a bundle already installed with the same UniqueId
+ // and version.
+ if (bundle.getGlobalName() != null) {
+ Bundle installedBundle =
+ getBundleByUniqueId(bundle.getGlobalName(),bundle.getVersion().toString());
+ if (installedBundle != null) {
+ throw new BundleException(
+ Msg.formatter.getString(
+ "BUNDLE_INSTALL_SAME_UNIQUEID",
+ bundle.getGlobalName(), bundle.getVersion().toString()));
+ }
+ }
+ try {
+ String[] nativepaths = selectNativeCode(bundle);
+ if (nativepaths != null) {
+ bundledata.installNativeCode(nativepaths);
+ }
+ bundle.load();
+
+ storage.commit(false);
+ } catch (BundleException be) {
+ synchronized (bundles) {
+ bundle.unload();
+ }
+
+ bundle.close();
+
+ throw be;
+ }
+ /* bundle has been successfully installed */
+
+ bundles.add(bundle);
+ } catch (BundleException e) {
+ publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, e);
+ try {
+ storage.undo();
+ } catch (BundleException ee) {
+ publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, ee);
+ }
+ throw e;
+ }
+
+ return bundle;
+ }
+
+ /**
+ * Selects a native code clause and return a list
+ * of the bundle entries for native code to be installed.
+ *
+ * @param bundle Bundle's manifest
+ * @return Vector of Strings of the bundle entries to install
+ * or <tt>null</tt> if there are no native code clauses.
+ * @throws BundleException If there is no suitable clause.
+ */
+ public String[] selectNativeCode(org.osgi.framework.Bundle bundle)
+ throws BundleException {
+ String headerValue = (String) ((Bundle) bundle).bundledata.getManifest().get(Constants.BUNDLE_NATIVECODE);
+
+ if (headerValue == null) {
+ return (null);
+ }
+
+ ManifestElement[] elements = ManifestElement.parseNativeCodeDescription(headerValue);
+ BundleNativeCode[] bundleNativeCode = new BundleNativeCode[elements.length];
+
+ /* Pass 1: perform processor/osname matching */
+ String processor = getProperty(Constants.FRAMEWORK_PROCESSOR);
+ String osname = getProperty(Constants.FRAMEWORK_OS_NAME);
+
+ int[] score = new int[elements.length];
+ int matches = 0;
+ int maxresult = 0;
+ int index = 0;
+
+ for (int i = 0; i < elements.length; i++) {
+ bundleNativeCode[i] = new BundleNativeCode(elements[i]);
+ int result = bundleNativeCode[i].matchProcessorOSName(processor, osname);
+ score[i] = result;
+
+ if (result > 0) {
+ matches++;
+
+ if (result > maxresult) {
+ maxresult = result;
+
+ index = i;
+ }
+ }
+ }
+
+ switch (matches) {
+ case 0 :
+ {
+ throw new BundleException(
+ Msg.formatter.getString(
+ "BUNDLE_NATIVECODE_MATCH_EXCEPTION"));
+ }
+ case 1 :
+ {
+ return bundleNativeCode[index].getPaths();
+
+ }
+ default :
+ {
+ /* continue with next pass */
+ break;
+ }
+ }
+
+ /* Pass 2: perform osversion matching */
+ Version osversion;
+ try {
+ osversion =
+ new org.eclipse.osgi.framework.adaptor.Version(getProperty(Constants.FRAMEWORK_OS_VERSION));
+ } catch (Exception e) {
+ osversion = org.eclipse.osgi.framework.adaptor.Version.emptyVersion;
+ }
+
+ matches = 0;
+ maxresult = 0;
+
+ Version[] bestVersion = new Version[elements.length];
+ Version maxVersion = org.eclipse.osgi.framework.adaptor.Version.emptyVersion;
+
+ for (int i = 0; i < elements.length; i++) {
+ if (score[i] > 0) {
+ BundleNativeCode bnc = bundleNativeCode[i];
+
+ Version result = bnc.matchOSVersion(osversion);
+ bestVersion[i] = result;
+
+ if (result != null) /* null is no match */ {
+ matches++;
+
+ if (result.compareTo(maxVersion) > 0) {
+ maxVersion = result;
+
+ index = i;
+ }
+ }
+ }
+ }
+
+ switch (matches) {
+ case 0 :
+ {
+ throw new BundleException(
+ Msg.formatter.getString(
+ "BUNDLE_NATIVECODE_MATCH_EXCEPTION"));
+ }
+ case 1 :
+ {
+ return bundleNativeCode[index].getPaths();
+ }
+ default :
+ {
+ /* discard all but the highest result */
+ for (int i = 0; i < elements.length; i++) {
+ Version result = bestVersion[i];
+
+ if (result.compareTo(maxVersion) < 0) {
+ score[i] = 0;
+ }
+ }
+
+ /* continue with next pass */
+ break;
+ }
+ }
+
+ /* Pass 2.5: perform windowing system matching */
+ String windowingsystem = getProperty(Constants.FRAMEWORK_WINDOWING_SYSTEM);
+ matches = 0;
+ maxresult = 0;
+
+ int[] bestMatch = new int[elements.length];
+
+ for (int i = 0; i < elements.length; i++) {
+ if (score[i] > 0) {
+ BundleNativeCode bnc = bundleNativeCode[i];
+
+ int result = bnc.matchWindowingSystem(windowingsystem);
+ bestMatch[i] = result;
+
+ if (result > 0) /* 0 is no match */ {
+ matches++;
+
+ if (result > maxresult) {
+ maxresult = result;
+ index = i;
+ }
+ }
+ }
+
+ switch (matches) {
+ case 0 :
+ {
+ throw new BundleException(
+ Msg.formatter.getString(
+ "BUNDLE_NATIVECODE_MATCH_EXCEPTION"));
+ }
+ case 1 :
+ {
+ return bundleNativeCode[index].getPaths();
+ }
+ default :
+ {
+ /* discard all but the highest result */
+ for (int j = 0; j < elements.length; j++) {
+ int result = bestMatch[j];
+
+ if (result < maxresult) {
+ score[j] = 0;
+ }
+ }
+
+ /* continue with next pass */
+ break;
+ }
+ }
+ }
+
+ /* Pass 3: perform language matching */
+ String language = getProperty(Constants.FRAMEWORK_LANGUAGE);
+
+ matches = 0;
+ maxresult = 0;
+
+ for (int i = 0; i < elements.length; i++) {
+ int result = score[i];
+
+ if (result > 0) {
+ BundleNativeCode bnc = bundleNativeCode[i];
+
+ result = bnc.matchLanguage(language);
+ score[i] = result;
+
+ if (result > 0) {
+ matches++;
+
+ if (result > maxresult) {
+ maxresult = result;
+
+ index = i;
+ }
+ }
+ }
+ }
+
+ switch (matches) {
+ case 0 :
+ {
+ throw new BundleException(
+ Msg.formatter.getString(
+ "BUNDLE_NATIVECODE_MATCH_EXCEPTION"));
+ }
+ default :
+ {
+ return bundleNativeCode[index].getPaths();
+ }
+ }
+ }
+
+ /**
+ * Retrieve the bundle that has the given unique identifier.
+ *
+ * @param id The identifier of the bundle to retrieve.
+ * @return A {@link Bundle} object, or <code>null</code>
+ * if the identifier doesn't match any installed bundle.
+ */
+ // changed visibility to gain access through the adaptor
+ public Bundle getBundle(long id) {
+ synchronized (bundles) {
+ return bundles.getBundle(id);
+ }
+ }
+
+ /**
+ * Retrieve the bundle that has the given unique identifier.
+ *
+ * @param id The identifier of the bundle to retrieve.
+ * @return A {@link Bundle} object, or <code>null</code>
+ * if the identifier doesn't match any installed bundle.
+ */
+ protected Bundle getBundleByUniqueId(String uniqueId, String version)
+ {
+ synchronized (bundles)
+ {
+ return bundles.getBundle(uniqueId, version);
+ }
+ }
+
+
+ /**
+ * Retrieve a list of all installed bundles.
+ * The list is valid at the time
+ * of the call to getBundles, but the framework is a very dynamic
+ * environment and bundles can be installed or uninstalled at anytime.
+ *
+ * @return A Vector of {@link Bundle} objects, one
+ * object per installed bundle.
+ */
+ protected BundleRepository getBundles()
+ {
+ return(bundles);
+ }
+
+ /**
+ * Resume a bundle.
+ *
+ * @param bundle Bundle to resume.
+ */
+ protected void resumeBundle(Bundle bundle) {
+ if (!bundle.isActive()) {
+ try {
+ int status = bundle.bundledata.getStatus();
+
+ if ((status & Constants.BUNDLE_STARTED) == 0) {
+ return;
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Trying to start bundle " + bundle);
+ }
+
+ bundle.resume();
+ } catch (BundleException be) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println(
+ "Bundle resume exception: " + be.getMessage());
+ Debug.printStackTrace(be.getNestedException());
+ }
+
+ publishFrameworkEvent(FrameworkEvent.ERROR, bundle, be);
+ }
+ }
+ }
+
+ /**
+ * Suspend a bundle.
+ *
+ * @param bundle Bundle to suspend.
+ * @param lock true if state change lock should be held
+ * when returning from this method.
+ * @return true if bundle was active and is now suspended.
+ */
+ protected boolean suspendBundle(Bundle bundle, boolean lock) {
+ boolean changed = false;
+
+ if (bundle.isActive()) {
+ try {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Trying to suspend bundle " + bundle);
+ }
+
+ bundle.suspend(lock);
+ } catch (BundleException be) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println(
+ "Bundle suspend exception: " + be.getMessage());
+ Debug.printStackTrace(be.getNestedException());
+ }
+
+ publishFrameworkEvent(FrameworkEvent.ERROR, bundle, be);
+ }
+
+ if (!bundle.isActive()) {
+ changed = true;
+ }
+ }
+
+ return (changed);
+ }
+
+ /**
+ * Locate an installed bundle with a given identity.
+ *
+ * @param location string for the bundle
+ * @return Bundle object for bundle with the specified location or null
+ * if no bundle is installed with the specified location.
+ */
+ protected Bundle getBundleByLocation(String location) {
+ synchronized (bundles) {
+ // this is not optimized; do not think it will get called
+ // that much.
+ List allBundles = bundles.getBundles();
+ int size = allBundles.size();
+ for (int i = 0; i < size; i++) {
+ Bundle bundle = (Bundle) allBundles.get(i);
+ if (location.equals(bundle.getLocation())) {
+ return (bundle);
+ }
+ }
+ }
+
+ return (null);
+ }
+
+ /**
+ * Locate an installed bundle with a given unique ID.
+ *
+ * @param uniqueId The Unique Id for the bundle
+ * @return Bundle object for bundle with the specified Unique or null
+ * if no bundle is installed with the specified location.
+ */
+ protected Bundle[] getBundleByUniqueId(String uniqueId)
+ {
+ return bundles.getBundles(uniqueId);
+ }
+
+ protected Bundle getBundleByClassLoader(BundleClassLoader classloader)
+ {
+ synchronized (bundles)
+ {
+ // this is not optimized; do not think it will get called
+ // that much.
+ List allBundles = bundles.getBundles();
+ int size = allBundles.size();
+ for (int i = 0; i < size; i++) {
+ Bundle bundle = (Bundle) allBundles.get(i);
+ if (bundle instanceof BundleHost) {
+ BundleLoader loader = ((BundleHost)bundle).basicGetBundleLoader();
+ if (loader != null && loader.getClassLoader() == classloader)
+ return bundle;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a list of <tt>ServiceReference</tt> objects. This method returns a list of
+ * <tt>ServiceReference</tt> objects for services which implement and were registered under
+ * the specified class and match the specified filter criteria.
+ *
+ * <p>The list is valid at the time of the call to this method, however as the Framework is
+ * a very dynamic environment, services can be modified or unregistered at anytime.
+ *
+ * <p><tt>filter</tt> is used to select the registered service whose
+ * properties objects contain keys and values which satisfy the filter.
+ * See {@link Filter}for a description of the filter string syntax.
+ *
+ * <p>If <tt>filter</tt> is <tt>null</tt>, all registered services
+ * are considered to match the filter.
+ * <p>If <tt>filter</tt> cannot be parsed, an {@link InvalidSyntaxException}will
+ * be thrown with a human readable message where the filter became unparsable.
+ *
+ * <p>The following steps are required to select a service:
+ * <ol>
+ * <li>If the Java Runtime Environment supports permissions, the caller is checked for the
+ * <tt>ServicePermission</tt> to get the service with the specified class.
+ * If the caller does not have the correct permission, <tt>null</tt> is returned.
+ * <li>If the filter string is not <tt>null</tt>, the filter string is
+ * parsed and the set of registered services which satisfy the filter is
+ * produced.
+ * If the filter string is <tt>null</tt>, then all registered services
+ * are considered to satisfy the filter.
+ * <li>If <code>clazz</code> is not <tt>null</tt>, the set is further reduced to
+ * those services which are an <tt>instanceof</tt> and were registered under the specified class.
+ * The complete list of classes of which a service is an instance and which
+ * were specified when the service was registered is available from the
+ * service's {@link Constants#OBJECTCLASS}property.
+ * <li>An array of <tt>ServiceReference</tt> to the selected services is returned.
+ * </ol>
+ *
+ * @param clazz The class name with which the service was registered, or
+ * <tt>null</tt> for all services.
+ * @param filter The filter criteria.
+ * @return An array of <tt>ServiceReference</tt> objects, or
+ * <tt>null</tt> if no services are registered which satisfy the search.
+ * @exception InvalidSyntaxException If <tt>filter</tt> contains
+ * an invalid filter string which cannot be parsed.
+ */
+ protected ServiceReference[] getServiceReferences(
+ String clazz,
+ String filterstring)
+ throws InvalidSyntaxException {
+
+ Filter filter =
+ (filterstring == null) ? null : new Filter(filterstring);
+
+ ServiceReference[] references=null;
+
+ if (clazz != null) {
+ try /* test for permission to get clazz */ {
+ checkGetServicePermission(clazz);
+ } catch (SecurityException se) {
+ return (null);
+ }
+ }
+
+ synchronized (serviceRegistry) {
+ Vector services = serviceRegistry.lookupServiceReferences(clazz, filter);
+ if (services == null) {
+ return null;
+ }
+
+ if (clazz == null) {
+ for (int i = services.size()-1; i >= 0; i--) {
+ ServiceReference ref = (ServiceReference) services.elementAt(i);
+ String[] classes = ref.getClasses();
+ try { /* test for permission to the classes */
+ checkGetServicePermission(classes);
+ } catch (SecurityException se) {
+ services.removeElementAt(i);
+ }
+ }
+ }
+
+ if (services.size() > 0) {
+ references = new ServiceReference[services.size()];
+ services.toArray(references);
+ }
+ }
+ return (references);
+
+ }
+
+ /**
+ * Method to return the next available service id.
+ * This method should be called while holding the
+ * registrations lock.
+ *
+ * @return next service id.
+ */
+ protected long getNextServiceId() {
+ long id = serviceid;
+ serviceid++;
+ return (id);
+ }
+
+ /**
+ * Creates a <code>File</code> object for a file in the
+ * persistent storage area provided for the bundle by the framework.
+ * If the adaptor does not have file system support, this method will
+ * return <code>null</code>.
+ *
+ * <p>A <code>File</code> object for the base directory of the
+ * persistent storage area provided for the context bundle by the framework
+ * can be obtained by calling this method with the empty string ("")
+ * as the parameter.
+ * See {@link #getBundle()} for a definition of context bundle.
+ */
+ protected File getDataFile(final Bundle bundle, final String filename) {
+ return (File) AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return bundle.bundledata.getDataFile(filename);
+ }
+ });
+ }
+
+ /**
+ * Check for AdminPermission.
+ */
+ protected void checkAdminPermission() {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ if (adminPermission == null) {
+ adminPermission = new AdminPermission();
+ }
+
+ sm.checkPermission(adminPermission);
+ }
+ }
+
+ /**
+ * Check for permission to register a service.
+ *
+ * The caller must have permission for ALL names.
+ */
+ protected void checkRegisterServicePermission(String[] names) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ int len = names.length;
+
+ for (int i = 0; i < len; i++) {
+ sm.checkPermission(
+ new ServicePermission(
+ names[i],
+ ServicePermission.REGISTER));
+ }
+ }
+ }
+
+ /**
+ * Check for permission to get a service.
+ *
+ * The caller must have permission for at least ONE name.
+ */
+ protected void checkGetServicePermission(String[] names) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ SecurityException se = null;
+
+ int len = names.length;
+
+ for (int i = 0; i < len; i++) {
+ try {
+ sm.checkPermission(
+ new ServicePermission(names[i], ServicePermission.GET));
+
+ return;
+ } catch (SecurityException e) {
+ se = e;
+ }
+ }
+
+ throw se;
+ }
+ }
+
+ /**
+ * Check for permission to get a service.
+ */
+ protected void checkGetServicePermission(String name) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ sm.checkPermission(
+ new ServicePermission(name, ServicePermission.GET));
+ }
+ }
+
+ /**
+ * This is necessary for running from a JXE, otherwise the
+ * SecurityManager is set much later than we would like!
+ */
+ protected void installSecurityManager() {
+ String securityManager = System.getProperty("java.security.manager");
+ if (securityManager != null) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm == null) {
+ if (securityManager.length() < 1) {
+ securityManager = "java.lang.SecurityManager";
+ }
+
+ try {
+ Class clazz = Class.forName(securityManager);
+ sm = (SecurityManager) clazz.newInstance();
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
+ Debug.println(
+ "Setting SecurityManager to: " + sm);
+ }
+
+ System.setSecurityManager(sm);
+
+ return;
+ } catch (ClassNotFoundException e) {
+ } catch (ClassCastException e) {
+ } catch (InstantiationException e) {
+ } catch (IllegalAccessException e) {
+ }
+
+ throw new NoClassDefFoundError(securityManager);
+ }
+ }
+ }
+
+ /**
+ * Create a thread which inherits the AccessControlContext
+ * of the framework.
+ *
+ * @param target The Runnable target for the thread.
+ * @param name The name of the thread.
+ * @return A Thread object.
+ */
+ protected Thread createThread(Runnable target, String name) {
+ return (Thread) AccessController.doPrivileged(
+ new CreateThread(target, name));
+ }
+
+ /**
+ * Deliver a FrameworkEvent.
+ *
+ * @param type FrameworkEvent type.
+ * @param bundle Affected bundle.
+ * @param throwable Related exception or null.
+ */
+ public void publishFrameworkEvent(int type, org.osgi.framework.Bundle bundle, Throwable throwable) {
+ if (frameworkEvent != null) {
+ final FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
+
+ if (System.getSecurityManager() == null){
+ publishFrameworkEventPrivileged(event);
+ }
+ else {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ publishFrameworkEventPrivileged(event);
+ return null;
+ }
+ });
+ }
+ }
+ }
+
+ public void publishFrameworkEventPrivileged(FrameworkEvent event) {
+ /* queue to hold set of listeners */
+ EventQueue listeners = new EventQueue(eventManager);
+
+ /* queue to hold set of BundleContexts w/ listeners */
+ EventQueue contexts = new EventQueue(eventManager);
+
+ /* synchronize while building the listener list */
+ synchronized (frameworkEvent) {
+ /* add set of BundleContexts w/ listeners to queue */
+ contexts.queueListeners(frameworkEvent, Framework.this);
+
+ /* synchronously dispatch to populate listeners queue */
+ contexts.dispatchEventSynchronous(FRAMEWORKEVENT, listeners);
+ }
+
+ /* dispatch event to set of listeners */
+ listeners.dispatchEventAsynchronous(FRAMEWORKEVENT, event);
+ }
+
+ /**
+ * Deliver a BundleEvent to SynchronousBundleListeners (synchronous).
+ * and BundleListeners (asynchronous).
+ *
+ * @param type BundleEvent type.
+ * @param bundle Affected bundle or null.
+ */
+ public void publishBundleEvent(int type, org.osgi.framework.Bundle bundle) {
+ if ((bundleEventSync != null) || (bundleEvent != null)) {
+ final BundleEvent event = new BundleEvent(type, bundle);
+
+ if (System.getSecurityManager() == null){
+ publishBundleEventPrivileged(event);
+ }
+ else {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ publishBundleEventPrivileged(event);
+ return null;
+ }
+ });
+ }
+ }
+ }
+ public void publishBundleEventPrivileged(BundleEvent event) {
+ /* Dispatch BundleEvent to SynchronousBundleListeners */
+ if (bundleEventSync != null) {
+ /* queue to hold set of listeners */
+ EventQueue listeners = new EventQueue(eventManager);
+
+ /* queue to hold set of BundleContexts w/ listeners */
+ EventQueue contexts = new EventQueue(eventManager);
+
+ /* synchronize while building the listener list */
+ synchronized (bundleEventSync) {
+ /* add set of BundleContexts w/ listeners to queue */
+ contexts.queueListeners(bundleEventSync, Framework.this);
+
+ /* synchronously dispatch to populate listeners queue */
+ contexts.dispatchEventSynchronous(BUNDLEEVENTSYNC, listeners);
+ }
+
+ /* dispatch event to set of listeners */
+ listeners.dispatchEventSynchronous(BUNDLEEVENTSYNC, event);
+ }
+
+ /* Dispatch BundleEvent to BundleListeners */
+ if (bundleEvent != null) {
+ /* queue to hold set of listeners */
+ EventQueue listeners = new EventQueue(eventManager);
+
+ /* queue to hold set of BundleContexts w/ listeners */
+ EventQueue contexts = new EventQueue(eventManager);
+
+ /* synchronize while building the listener list */
+ synchronized (bundleEvent) {
+ /* add set of BundleContexts w/ listeners to queue */
+ contexts.queueListeners(bundleEvent, Framework.this);
+
+ /* synchronously dispatch to populate listeners queue */
+ contexts.dispatchEventSynchronous(BUNDLEEVENT, listeners);
+ }
+
+ /* dispatch event to set of listeners */
+ listeners.dispatchEventAsynchronous(BUNDLEEVENT, event);
+ }
+ }
+
+ /**
+ * Deliver a ServiceEvent.
+ *
+ * @param type ServiceEvent type.
+ * @param service Affected service.
+ */
+ public void publishServiceEvent(int type, org.osgi.framework.ServiceReference reference) {
+ if (serviceEvent != null) {
+ final ServiceEvent event = new ServiceEvent(type, reference);
+
+ if (System.getSecurityManager() == null){
+ publishServiceEventPrivileged(event);
+ }
+ else {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ publishServiceEventPrivileged(event);
+ return null;
+ }
+ });
+ }
+ }
+ }
+
+ public void publishServiceEventPrivileged(ServiceEvent event) {
+
+ /* queue to hold set of listeners */
+ EventQueue listeners = new EventQueue(eventManager);
+
+ /* queue to hold set of BundleContexts w/ listeners */
+ EventQueue contexts = new EventQueue(eventManager);
+
+ /* synchronize while building the listener list */
+ synchronized (serviceEvent) {
+ /* add set of BundleContexts w/ listeners to queue */
+ contexts.queueListeners(serviceEvent, Framework.this);
+
+ /* synchronously dispatch to populate listeners queue */
+ contexts.dispatchEventSynchronous(SERVICEEVENT, listeners);
+ }
+
+ /* dispatch event to set of listeners */
+ listeners.dispatchEventSynchronous(SERVICEEVENT, event);
+ }
+
+ /**
+ * Top level event dispatcher for the framework.
+ *
+ * @param l BundleContext for receiving bundle
+ * @param lo BundleContext for receiving bundle
+ * @param action Event class type
+ * @param object EventQueue to populate
+ */
+ public void dispatchEvent(Object l, Object lo, int action, Object object) {
+ try {
+ BundleContext context = (BundleContext) l;
+
+ if (context.bundle != null) /* if context still valid */ {
+ EventQueue queue = (EventQueue) object;
+
+ switch (action) {
+ case BUNDLEEVENT :
+ {
+ queue.queueListeners(context.bundleEvent, context);
+ break;
+ }
+ case BUNDLEEVENTSYNC :
+ {
+ queue.queueListeners(
+ context.bundleEventSync,
+ context);
+ break;
+ }
+ case SERVICEEVENT :
+ {
+ queue.queueListeners(context.serviceEvent, context);
+ break;
+ }
+ case FRAMEWORKEVENT :
+ {
+ queue.queueListeners(
+ context.frameworkEvent,
+ context);
+ break;
+ }
+ }
+ }
+ } catch (Throwable t) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println(
+ "Exception in Top level event dispatcher: "
+ + t.getMessage());
+ Debug.printStackTrace(t);
+ }
+
+ publisherror : {
+ if (action == FRAMEWORKEVENT) {
+ FrameworkEvent event = (FrameworkEvent) object;
+ if (event.getType() == FrameworkEvent.ERROR) {
+ break publisherror; /* avoid infinite loop */
+ }
+ }
+
+ BundleContext context = (BundleContext) l;
+ publishFrameworkEvent(FrameworkEvent.ERROR, context.bundle, t);
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/KeyedElement.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/KeyedElement.java
new file mode 100644
index 000000000..fe22c1f29
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/KeyedElement.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.internal.core;
+
+public interface KeyedElement {
+public int getKeyHashCode();
+public boolean compare(KeyedElement other);
+public Object getKey();
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/KeyedHashSet.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/KeyedHashSet.java
new file mode 100644
index 000000000..f0a19fc99
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/KeyedHashSet.java
@@ -0,0 +1,375 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class KeyedHashSet {
+ protected static final int MINIMUM_SIZE = 7;
+ protected int elementCount = 0;
+ protected KeyedElement[] elements;
+ protected boolean replace;
+ private int capacity;
+public KeyedHashSet() {
+ this(MINIMUM_SIZE, true);
+}
+public KeyedHashSet(boolean replace) {
+ this(MINIMUM_SIZE, replace);
+}
+public KeyedHashSet(int capacity) {
+ this(capacity, true);
+}
+public KeyedHashSet(int capacity, boolean replace) {
+ elements = new KeyedElement[Math.max(MINIMUM_SIZE, capacity * 2)];
+ this.replace = replace;
+ this.capacity = capacity;
+}
+public KeyedHashSet(KeyedHashSet original) {
+ elements = new KeyedElement[original.elements.length];
+ System.arraycopy(original.elements,0,elements,0,original.elements.length);
+ elementCount = original.elementCount;
+ replace = original.replace;
+ capacity = original.capacity;
+}
+/**
+ * Adds an element to this set. If an element with the same key already exists,
+ * replaces it depending on the replace flag.
+ * @return true if the element was added/stored, false otherwise
+ */
+public boolean add(KeyedElement element) {
+ int hash = hash(element);
+
+ // search for an empty slot at the end of the array
+ for (int i = hash; i < elements.length; i++) {
+ if (elements[i] == null) {
+ elements[i] = element;
+ elementCount++;
+ // grow if necessary
+ if (shouldGrow())
+ expand();
+ return true;
+ }
+ if (elements[i].compare(element)) {
+ if (replace) {
+ elements[i] = element;
+ return true;
+ } else
+ return false;
+ }
+ }
+
+ // search for an empty slot at the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ if (elements[i] == null) {
+ elements[i] = element;
+ elementCount++;
+ // grow if necessary
+ if (shouldGrow())
+ expand();
+ return true;
+ }
+ if (elements[i].compare(element)) {
+ if (replace) {
+ elements[i] = element;
+ return true;
+ } else
+ return false;
+ }
+ }
+
+ // if we didn't find a free slot, then try again with the expanded set
+ expand();
+ return add(element);
+}
+public void addAll(KeyedElement[] elements) {
+ for (int i = 0; i < elements.length; i++)
+ add(elements[i]);
+}
+public boolean contains(KeyedElement element) {
+ return get(element) != null;
+}
+public boolean containsKey(Object key) {
+ return getByKey(key) != null;
+}
+public KeyedElement[] elements() {
+ return (KeyedElement[]) elements(new KeyedElement[elementCount]);
+}
+public Object[] elements(Object[] result) {
+ int j = 0;
+ for (int i = 0; i < elements.length; i++) {
+ KeyedElement element = elements[i];
+ if (element != null)
+ result[j++] = element;
+ }
+ return result;
+}
+/**
+ * The array isn't large enough so double its size and rehash
+ * all its current values.
+ */
+protected void expand() {
+ KeyedElement[] oldElements = elements;
+ elements = new KeyedElement[elements.length * 2];
+
+ int maxArrayIndex = elements.length - 1;
+ for (int i = 0; i < oldElements.length; i++) {
+ KeyedElement element = oldElements[i];
+ if (element != null) {
+ int hash = hash(element);
+ while (elements[hash] != null) {
+ hash++;
+ if (hash > maxArrayIndex)
+ hash = 0;
+ }
+ elements[hash] = element;
+ }
+ }
+}
+/**
+ * Returns the set element with the given id, or null
+ * if not found.
+ */
+public KeyedElement getByKey(Object key) {
+ if (elementCount == 0)
+ return null;
+ int hash = keyHash(key);
+
+ // search the last half of the array
+ for (int i = hash; i < elements.length; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return null;
+ if (element.getKey().equals(key))
+ return element;
+ }
+
+ // search the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return null;
+ if (element.getKey().equals(key))
+ return element;
+ }
+
+ // nothing found so return null
+ return null;
+}
+
+/**
+ * Returns the set element with the given id, or null
+ * if not found.
+ */
+public KeyedElement get(KeyedElement key) {
+ if (elementCount == 0)
+ return null;
+ int hash = hash(key);
+
+ // search the last half of the array
+ for (int i = hash; i < elements.length; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return null;
+ if (element.compare(key))
+ return element;
+ }
+
+ // search the beginning of the array
+ for (int i = 0; i < hash - 1; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return null;
+ if (element.compare(key))
+ return element;
+ }
+
+ // nothing found so return null
+ return null;
+}
+public boolean isEmpty() {
+ return elementCount == 0;
+}
+/**
+ * The element at the given index has been removed so move
+ * elements to keep the set properly hashed.
+ */
+protected void rehashTo(int anIndex) {
+
+ int target = anIndex;
+ int index = anIndex + 1;
+ if (index >= elements.length)
+ index = 0;
+ KeyedElement element = elements[index];
+ while (element != null) {
+ int hashIndex = hash(element);
+ boolean match;
+ if (index < target)
+ match = !(hashIndex > target || hashIndex <= index);
+ else
+ match = !(hashIndex > target && hashIndex <= index);
+ if (match) {
+ elements[target] = element;
+ target = index;
+ }
+ index++;
+ if (index >= elements.length)
+ index = 0;
+ element = elements[index];
+ }
+ elements[target] = null;
+}
+public boolean removeByKey(Object key) {
+ if (elementCount == 0)
+ return false;
+ int hash = keyHash(key);
+
+ for (int i = hash; i < elements.length; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return false;
+ if (element.getKey().equals(key)) {
+ rehashTo(i);
+ elementCount--;
+ return true;
+ }
+ }
+
+ for (int i = 0; i < hash - 1; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return false;
+ if (element.getKey().equals(key)) {
+ rehashTo(i);
+ elementCount--;
+ return true;
+ }
+ }
+
+ return true;
+}
+public boolean remove(KeyedElement toRemove) {
+ if (elementCount == 0)
+ return false;
+
+ int hash = hash(toRemove);
+
+ for (int i = hash; i < elements.length; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return false;
+ if (element.compare(toRemove)) {
+ rehashTo(i);
+ elementCount--;
+ return true;
+ }
+ }
+
+ for (int i = 0; i < hash - 1; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ return false;
+ if (element.compare(toRemove)) {
+ rehashTo(i);
+ elementCount--;
+ return true;
+ }
+ }
+ return false;
+}
+private int hash(KeyedElement key) {
+ return Math.abs(key.getKeyHashCode()) % elements.length;
+}
+private int keyHash(Object key) {
+ return Math.abs(key.hashCode()) % elements.length;
+}
+public void removeAll(KeyedElement[] elements) {
+ for (int i = 0; i < elements.length; i++)
+ remove(elements[i]);
+}
+private boolean shouldGrow() {
+ return elementCount > elements.length * 0.75;
+}
+public int size() {
+ return elementCount;
+}
+public String toString() {
+ StringBuffer result = new StringBuffer(100);
+ result.append("{");
+ boolean first = true;
+ for (int i = 0; i < elements.length; i++) {
+ if (elements[i] != null) {
+ if (first)
+ first = false;
+ else
+ result.append(", ");
+ result.append(elements[i]);
+ }
+ }
+ result.append("}");
+ return result.toString();
+}
+
+public int countCollisions() {
+ int result = 0;
+ int lastHash = 0;
+ boolean found = false;
+ for (int i = 0; i < elements.length; i++) {
+ KeyedElement element = elements[i];
+ if (element == null)
+ found = false;
+ else {
+ int hash = hash(element);
+ if (found)
+ if (lastHash == hash)
+ result++;
+ else
+ found = false;
+ else {
+ lastHash = hash;
+ found = true;
+ }
+ }
+ }
+ return result;
+}
+
+public Iterator iterator() {
+ return new KeyedHashSetIterator();
+}
+
+class KeyedHashSetIterator implements Iterator {
+ private int currentIndex = -1;
+ private int found;
+ public boolean hasNext() {
+ return found < elementCount;
+ }
+ public Object next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ while (++currentIndex < elements.length)
+ if (elements[currentIndex] != null) {
+ found++;
+ return elements[currentIndex];
+ }
+ // this would mean we have less elements than we thought
+ throw new NoSuchElementException();
+ }
+ public void remove() {
+ // as allowed by the API
+ throw new UnsupportedOperationException();
+ }
+}
+public void clear() {
+ elements = new KeyedElement[Math.max(MINIMUM_SIZE, capacity * 2)];
+ elementCount = 0;
+}
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Msg.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Msg.java
new file mode 100644
index 000000000..72bfd2cc3
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Msg.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+/**
+ * This class retrieves strings from a resource bundle
+ * and returns them, formatting them with MessageFormat
+ * when required.
+ * <p>
+ * It is used by the system classes to provide national
+ * language support, by looking up messages in the
+ * <code>
+ * org.eclipse.osgi.framework.internal.core.ExternalMessages
+ * </code>
+ * resource bundle. Note that if this file is not available,
+ * or an invalid key is looked up, or resource bundle support
+ * is not available, the key itself will be returned as the
+ * associated message. This means that the <em>KEY</em> should
+ * a reasonable human-readable (english) string.
+ */
+
+public class Msg
+{
+
+ static public org.eclipse.osgi.framework.msg.MessageFormat formatter;
+
+ // Attempt to load the message bundle.
+ static
+ {
+ formatter = new org.eclipse.osgi.framework.msg.MessageFormat("org.eclipse.osgi.framework.internal.core.ExternalMessages");
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/MultiSourcePackage.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/MultiSourcePackage.java
new file mode 100644
index 000000000..2c485b895
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/MultiSourcePackage.java
@@ -0,0 +1,23 @@
+package org.eclipse.osgi.framework.internal.core;
+
+public class MultiSourcePackage extends PackageSource {
+ BundleLoaderProxy[] suppliers;
+
+ MultiSourcePackage(String id, BundleLoaderProxy[] suppliers) {
+ this.id= id;
+ this.suppliers= suppliers;
+ }
+
+ public BundleLoaderProxy[] getSuppliers() {
+ return suppliers;
+ }
+
+ public boolean isMultivalued() {
+ return true;
+ }
+
+ public BundleLoaderProxy getSupplier() {
+ return null;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/NothingToUpdateException.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/NothingToUpdateException.java
new file mode 100644
index 000000000..495a7d8a1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/NothingToUpdateException.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+
+/**
+ * Exception denotes that a bundle does not need to be updated. The
+ * There is no updated version available.
+ */
+public class NothingToUpdateException extends IOException {
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/NullPackageSource.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/NullPackageSource.java
new file mode 100644
index 000000000..3a3804741
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/NullPackageSource.java
@@ -0,0 +1,28 @@
+package org.eclipse.osgi.framework.internal.core;
+
+/**
+ * This class is used to optimize finding provided-packages for a bundle.
+ * If the package cannot be found in a list of required bundles then this class
+ * is used to cache a null package source so that the search does not need to
+ * be done again.
+ */
+public class NullPackageSource extends PackageSource {
+ public NullPackageSource(String name) {
+ this.id = name;
+ }
+ public BundleLoaderProxy getSupplier() {
+ return null;
+ }
+ public boolean isMultivalued() {
+ return false;
+ }
+ public BundleLoaderProxy[] getSuppliers() {
+ return null;
+ }
+ public boolean isNullSource(){
+ return true;
+ }
+ public String toString() {
+ return id + " -> null";
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/OSGi.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/OSGi.java
new file mode 100644
index 000000000..f59d59377
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/OSGi.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+
+/**
+ * Main class for the OSGi framework. This class is used to start the framework for production use.
+ * Objects of this class represent an instance of the OSGi framework and
+ * can be used to control the framework.
+ */
+public class OSGi
+{
+ protected Framework framework;
+
+ /**
+ * Constructs an OSGi object with the specified FrameworkAdaptor.
+ * This method creates an OSGi framework.
+ *
+ * @param adaptor An adaptor object for the framework to use.
+ */
+ public OSGi(FrameworkAdaptor adaptor)
+ {
+ displayBanner();
+
+ framework = createFramework(adaptor);
+ }
+
+ /**
+ * Destroy the OSGi framework. This method stops the framework if
+ * it has been started. All resources associated with the framework
+ * are release and the OSGi object is no longer usable.
+ *
+ */
+ public void close()
+ {
+ framework.close();
+ }
+
+ /**
+ * Start the framework.
+ *
+ * The framework is started as described in the OSGi Framework
+ * specification.
+ */
+ public void launch()
+ {
+ framework.launch();
+ }
+
+ /**
+ * Stop the framework.
+ *
+ * The framework is stopped as described in the OSGi Framework
+ * specification.
+ */
+ public void shutdown()
+ {
+ framework.shutdown();
+ }
+
+ /**
+ * This method returns the state of the OSGi framework.
+ *
+ * @return true of the framework is launched, false if shutdown.
+ */
+ public boolean isActive()
+ {
+ return(framework.isActive());
+ }
+
+ /**
+ * Retrieve the BundleContext for the system bundle.
+ *
+ * @return The system bundle's BundleContext.
+ */
+ public org.osgi.framework.BundleContext getBundleContext()
+ {
+ return(framework.systemBundle.getContext());
+ }
+
+ /**
+ * Create the internal framework object.
+ * This method can be overridden to create a secure framework.
+ *
+ * @param adaptor FrameworkAdaptor object for the framework.
+ * @return New Framework object.
+ */
+ protected Framework createFramework(FrameworkAdaptor adaptor)
+ {
+ return(new Framework(adaptor));
+ }
+
+ /**
+ * Display the banner to System.out.
+ *
+ */
+ protected void displayBanner()
+ {
+ System.out.println();
+ System.out.print(Msg.formatter.getString("ECLIPSE_OSGI_NAME"));
+ System.out.print(" ");
+ System.out.println(Msg.formatter.getString("ECLIPSE_OSGI_VERSION"));
+ System.out.println();
+ System.out.println(Msg.formatter.getString("OSGI_VERSION"));
+ System.out.println();
+ System.out.println(Msg.formatter.getString("ECLIPSE_COPYRIGHT"));
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdmin.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdmin.java
new file mode 100644
index 000000000..2397899a8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdmin.java
@@ -0,0 +1,896 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.debug.DebugOptions;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.HostSpecification;
+import org.eclipse.osgi.service.resolver.PackageSpecification;
+import org.eclipse.osgi.service.resolver.State;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.FrameworkEvent;
+
+/**
+ * PackageAdmin service for the OSGi specification.
+ *
+ * Framework service which allows bundle programmers to inspect the packages
+ * exported in the framework and eagerly update or uninstall bundles.
+ *
+ * If present, there will only be a single instance of this service
+ * registered in the framework.
+ *
+ * <p> The term <i>exported package</i> (and the corresponding interface
+ * {@link ExportedPackage}) refers to a package that has actually been
+ * exported (as opposed to one that is available for export).
+ *
+ * <p> Note that the information about exported packages returned by this
+ * service is valid only until the next time {@link #refreshPackages} is
+ * called.
+ * If an ExportedPackage becomes stale, (that is, the package it references
+ * has been updated or removed as a result of calling
+ * PackageAdmin.refreshPackages()),
+ * its getName() and getSpecificationVersion() continue to return their
+ * old values, isRemovalPending() returns true, and getExportingBundle()
+ * and getImportingBundles() return null.
+ */
+public class PackageAdmin implements org.osgi.service.packageadmin.PackageAdmin {
+
+ /** framework object */
+ protected Framework framework;
+
+ /** BundleLoaders that are pending removal. Value is BundleLoader */
+ protected Vector removalPending;
+
+ protected KeyedHashSet exportedPackages;
+ protected KeyedHashSet exportedBundles;
+
+ private long cumulativeTime;
+
+ /**
+ * Constructor.
+ *
+ * @param framework Framework object.
+ */
+ protected PackageAdmin(Framework framework) {
+ this.framework = framework;
+ }
+
+ protected void initialize() {
+ removalPending = new Vector(10, 10);
+
+ exportedPackages = new KeyedHashSet(false);
+ exportedPackages = getExportedPackages(exportedPackages);
+
+ exportedBundles = new KeyedHashSet(false);
+ exportedBundles = getExportedBundles(exportedBundles);
+ }
+
+ private KeyedHashSet getExportedPackages(KeyedHashSet packageSet) {
+ State state = framework.adaptor.getState();
+ PackageSpecification[] packageSpecs = state.getExportedPackages();
+
+ for (int i=0; i<packageSpecs.length; i++) {
+ BundleDescription bundleSpec = packageSpecs[i].getSupplier();
+ if (bundleSpec != null) {
+ HostSpecification hostSpec = bundleSpec.getHost();
+ if(hostSpec != null){
+ bundleSpec = hostSpec.getSupplier();
+ }
+ BundleHost bundle = (BundleHost)framework.getBundle(bundleSpec.getBundleId());
+ if (bundle != null) {
+ ExportedPackageImpl packagesource =
+ new ExportedPackageImpl(packageSpecs[i],bundle.getLoaderProxy());
+ packageSet.add(packagesource);
+ }
+ else {
+ // TODO log error
+ }
+ }
+ else {
+ // TODO log error
+ }
+ }
+
+ return packageSet;
+ }
+
+ private KeyedHashSet getExportedBundles(KeyedHashSet bundleSet){
+ State state = framework.adaptor.getState();
+ BundleDescription[] bundleDescripts = state.getResolvedBundles();
+ for(int i=0; i<bundleDescripts.length; i++) {
+ BundleDescription bundledes = bundleDescripts[i];
+ HostSpecification hostSpec = bundledes.getHost();
+ if(hostSpec != null){
+ bundledes = hostSpec.getSupplier();
+ }
+ BundleHost bundle = (BundleHost) framework.getBundle(bundledes.getBundleId());
+ if (bundle != null && bundle instanceof BundleHost) {
+ BundleLoaderProxy loaderProxy = bundle.getLoaderProxy();
+ bundleSet.add(loaderProxy);
+ }
+ }
+ return bundleSet;
+ }
+
+ protected void cleanup() {
+ removalPending = null;
+ exportedPackages = null;
+ exportedBundles = null;
+ }
+
+ protected void addRemovalPending(BundleLoaderProxy loaderProxy) {
+ removalPending.addElement(loaderProxy);
+ }
+
+ protected void deleteRemovalPending(BundleLoaderProxy loaderProxy) throws BundleException{
+
+ boolean exporting = loaderProxy.inUse();
+ if (exporting) {
+ /* Reaching here is an internal error */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("BundleLoader.unexportPackager returned true! " + loaderProxy);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ throw new BundleException(Msg.formatter.getString("OSGI_INTERNAL_ERROR"));
+ }
+ unexportResources(loaderProxy);
+ BundleLoader loader = loaderProxy.getBundleLoader();
+ loader.clear();
+ loader.close();
+ removalPending.remove(loaderProxy);
+ }
+
+ /**
+ * Gets the packages exported by the specified bundle.
+ *
+ * @param bundle The bundle whose exported packages are to be returned,
+ * or <tt>null</tt> if all the packages currently
+ * exported in the framework are to be returned. If the
+ * specified bundle is the system bundle (that is, the
+ * bundle with id 0), this method returns all the packages
+ * on the system classpath whose name does not start with
+ * "java.". In an environment where the exhaustive list
+ * of packages on the system classpath is not known in
+ * advance, this method will return all currently known
+ * packages on the system classpath, that is, all packages
+ * on the system classpath that contains one or more classes
+ * that have been loaded.
+ *
+ * @return The array of packages exported by the specified bundle,
+ * or <tt>null</tt> if the specified bundle has not exported any packages.
+ */
+ public org.osgi.service.packageadmin.ExportedPackage[] getExportedPackages(org.osgi.framework.Bundle bundle) {
+ // need to make sure the dependacies are marked before this call.
+ framework.bundles.markDependancies();
+
+ KeyedElement[] elements = exportedPackages.elements();
+ if (bundle != null) {
+ Vector result = new Vector();
+ for (int i=0; i<elements.length; i++) {
+ ExportedPackageImpl pkgElement = (ExportedPackageImpl) elements[i];
+ if (pkgElement.supplier.getBundle() == bundle) {
+ result.add(pkgElement);
+ }
+ }
+ if (result.size() == 0) {
+ return null;
+ }
+ ExportedPackageImpl[] pkgElements = new ExportedPackageImpl[result.size()];
+ return (ExportedPackage[]) result.toArray(pkgElements);
+ } else {
+ if (elements.length == 0) {
+ return null;
+ }
+ ExportedPackageImpl[] pkgElements = new ExportedPackageImpl[elements.length];
+ System.arraycopy(elements,0,pkgElements,0,pkgElements.length);
+ return pkgElements;
+ }
+ }
+
+ /**
+ * Gets the bundles exported by the specified bundle.
+ *
+ * @param bundle The bundle whose exported bundles are to be returned,
+ * or <tt>null</tt> if all the bundles currently
+ * exported in the framework are to be returned.
+ *
+ * @return The array of bundles exported by the specified bundle,
+ * or <tt>null</tt> if the specified bundle has not exported any bundles.
+ */
+ // public ExportedBundle[] getExportedBundles(org.osgi.framework.Bundle bundle) {
+ // synchronized (framework.bundles) {
+ // if (bundleExporters != null) {
+ // Vector bundles = new Vector(bundleExporters.size());
+ //
+ // Enumeration enum = bundleExporters.elements();
+ // while (enum.hasMoreElements()) {
+ // ExportedBundle exportedBundle = (ExportedBundle) enum.nextElement();
+ //
+ // if ((bundle == null)
+ // || (bundle.equals(exportedBundle.getExportingBundle()))) {
+ // bundles.addElement(exportedBundle);
+ // }
+ // }
+ //
+ // int size = bundles.size();
+ //
+ // if (size > 0) {
+ // ExportedBundle[] exported = new ExportedBundle[size];
+ //
+ // bundles.copyInto(exported);
+ //
+ // return (exported);
+ // }
+ // }
+ // }
+ //
+ // TODO implement this using the state object
+ // return (null);
+ // }
+ /**
+ * Gets the ExportedPackage with the specified package name. All exported
+ * packages
+ * will be checked for the specified name. In an environment where the
+ * exhaustive list of packages on the system classpath is not known in
+ * advance, this method attempts to see if the named package is on the
+ * system classpath.
+ * This
+ * means that this method may discover an ExportedPackage that was
+ * not present in the list returned by <tt>getExportedPackages()</tt>.
+ *
+ * @param name The name of the exported package to be returned.
+ *
+ * @return The exported package with the specified name, or <tt>null</tt>
+ * if no expored package with that name exists.
+ */
+ public org.osgi.service.packageadmin.ExportedPackage getExportedPackage(String packageName) {
+ // need to make sure the dependacies are marked before this call.
+ framework.bundles.markDependancies();
+ return (ExportedPackageImpl) exportedPackages.getByKey(packageName);
+ }
+
+ /**
+ * Gets the ExportedBundle with the specified Unique ID. All exported
+ * bundles will be checked for the specified Unique ID.
+ * @param uniqueId The Unique ID of the exported bundle to be returned.
+ *
+ * @return The exported bundle with the specified Unique ID, or <tt>null</tt>
+ * if no exported bundle with that Unique ID exists.
+ */
+ // public ExportedBundle getExportedBundle(String uniqueId) {
+ // synchronized (framework.bundles) {
+ // if (bundleExporters != null) {
+ // return (bundleExporters.getBundle(uniqueId));
+ // }
+ // }
+ // TODO implement this using the state object
+ // return null;
+ // }
+ /**
+ * Forces the update (replacement) or removal of packages exported by
+ * the specified bundles.
+ *
+ * <p> If no bundles are specified, this method will update or remove any
+ * packages exported by any bundles that were previously updated or
+ * uninstalled. The technique by which this is accomplished
+ * may vary among different framework implementations. One permissible
+ * implementation is to stop and restart the Framework.
+ *
+ * <p> This method returns to the caller immediately and then performs the
+ * following steps in its own thread:
+ *
+ * <ol>
+ * <p>
+ * <li> Compute a graph of bundles starting with the specified ones. If no
+ * bundles are specified, compute a graph of bundles starting with
+ * previously updated or uninstalled ones.
+ * Any bundle that imports a package that is currently exported
+ * by a bundle in the graph is added to the graph. The graph is fully
+ * constructed when there is no bundle outside the graph that imports a
+ * package from a bundle in the graph. The graph may contain
+ * <tt>UNINSTALLED</tt> bundles that are currently still
+ * exporting packages.
+ *
+ * <p>
+ * <li> Each bundle in the graph will be stopped as described in the
+ * <tt>Bundle.stop</tt> method.
+ *
+ * <p>
+ * <li> Each bundle in the graph that is in the
+ * <tt>RESOLVED</tt> state is moved
+ * to the <tt>INSTALLED</tt> state.
+ * The effect of this step is that bundles in the graph are no longer
+ * <tt>RESOLVED</tt>.
+ *
+ * <p>
+ * <li> Each bundle in the graph that is in the UNINSTALLED state is
+ * removed from the graph and is now completely removed from the framework.
+ *
+ * <p>
+ * <li> Each bundle in the graph that was in the
+ * <tt>ACTIVE</tt> state prior to Step 2 is started as
+ * described in the <tt>Bundle.start</tt> method, causing all
+ * bundles required for the restart to be resolved.
+ * It is possible that, as a
+ * result of the previous steps, packages that were
+ * previously exported no longer are. Therefore, some bundles
+ * may be unresolvable until another bundle
+ * offering a compatible package for export has been installed in the
+ * framework.
+ * </ol>
+ *
+ * <p> For any exceptions that are thrown during any of these steps, a
+ * <tt>FrameworkEvent</tt> of type <tt>ERROR</tt> is
+ * broadcast, containing the exception.
+ *
+ * @param bundles the bundles whose exported packages are to be updated or
+ * removed,
+ * or <tt>null</tt> for all previously updated or uninstalled bundles.
+ *
+ * @exception SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void refreshPackages(org.osgi.framework.Bundle[] input) {
+ framework.checkAdminPermission();
+
+ Bundle[] copy = null;
+ if (input != null) {
+ synchronized (input) {
+ int size = input.length;
+
+ copy = new Bundle[size];
+
+ System.arraycopy(input, 0, copy, 0, size);
+ }
+ }
+
+ final Bundle[] bundles = copy;
+
+ Thread refresh = framework.createThread(new Runnable() {
+ public void run() {
+ refreshPackages(bundles);
+ }
+ }, "Refresh Packages");
+
+ refresh.start();
+ }
+
+ /**
+ * Worker routine called on a seperate thread to perform the actual work.
+ *
+ * @param Seed for the graph.
+ */
+ protected void refreshPackages(Bundle[] refresh) {
+ try {
+ Vector graph = null;
+ synchronized (framework.bundles) {
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: Initialize graph");
+ }
+ // make sure the dependencies are marked first
+ framework.bundles.markDependancies();
+
+ graph = computeAffectedBundles(refresh);
+
+ // get the state.
+ State state = framework.adaptor.getState();
+ // resolve the state.
+ state.resolve(false);
+
+ // process the delta. This will set the state of all
+ // the bundles in the graph.
+ processDelta(graph);
+ }
+ /*
+ * Resume the suspended bundles outside of the synchronization block.
+ * This will cause the bundles to be resolved using the most up-to-date
+ * generations of the bundles.
+ */
+ resumeBundles(graph);
+
+ } finally {
+ framework.publishFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, framework.systemBundle, null);
+ }
+
+ }
+
+ private void resumeBundles(Vector graph) {
+
+
+ Bundle[] refresh = new Bundle[graph.size()];
+ boolean[] previouslyResolved = new boolean[graph.size()];
+ graph.copyInto(refresh);
+ Util.sort(refresh, 0, graph.size());
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: restart the bundles");
+ }
+ for (int i = 0; i < refresh.length; i++) {
+ Bundle bundle = (Bundle) refresh[i];
+ if (bundle.isResolved() && !bundle.isFragment())
+ framework.resumeBundle(bundle);
+ }
+ }
+
+ private void processDelta(Vector graph){
+ Bundle[] refresh = new Bundle[graph.size()];
+ boolean[] previouslyResolved = new boolean[graph.size()];
+ graph.copyInto(refresh);
+ Util.sort(refresh, 0, graph.size());
+
+ Vector notify = new Vector();
+ try {
+ try {
+ /*
+ * Suspend each bundle and grab its state change lock.
+ */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: Suspend each bundle and acquire its state change lock");
+ }
+ for (int i = refresh.length - 1; i >= 0; i--) {
+ Bundle changedBundle = refresh[i];
+ previouslyResolved[i]=changedBundle.isResolved();
+ if (changedBundle.isActive() && !changedBundle.isFragment()) {
+ boolean suspended = framework.suspendBundle(changedBundle, true);
+ if (!suspended) {
+ throw new BundleException(Msg.formatter.getString("BUNDLE_STATE_CHANGE_EXCEPTION"));
+ }
+ } else {
+ changedBundle.beginStateChange();
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ if (changedBundle.stateChanging == null) {
+ Debug.println("Bundle state change lock is clear! " + changedBundle);
+ Debug.printStackTrace(new Exception("Stack trace"));
+ }
+ }
+ }
+ /*
+ * Refresh the bundles which will unexport the packages.
+ * This will move RESOLVED bundles to the INSTALLED state.
+ */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: refresh the bundles");
+ }
+ /*
+ * Unimport detached BundleLoaders for bundles in the graph.
+ */
+ for (int i = removalPending.size()-1; i >= 0; i--)
+ {
+ BundleLoaderProxy loaderProxy = (BundleLoaderProxy)removalPending.elementAt(i);
+
+ if (graph.contains(loaderProxy.getBundle()))
+ {
+ framework.bundles.unMarkDependancies(loaderProxy);
+ }
+ }
+
+ for (int i = 0; i < refresh.length; i++) {
+ Bundle changedBundle = refresh[i];
+ changedBundle.refresh();
+ }
+
+ /*
+ * Cleanup detached BundleLoaders for bundles in the graph.
+ */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: unexport the removal pending packages");
+ }
+ for (int i = removalPending.size() - 1; i >= 0; i--) {
+ BundleLoaderProxy loaderProxy = (BundleLoaderProxy) removalPending.elementAt(i);
+ Bundle removedBundle = loaderProxy.getBundle();
+
+ if (graph.contains(removedBundle)) {
+ deleteRemovalPending(loaderProxy);
+ }
+ }
+
+ // update the exported package and bundle lists.
+ exportedPackages = getExportedPackages(exportedPackages);
+ exportedBundles = getExportedBundles(exportedBundles);
+
+ // set the resolved bundles state
+ for (int i=0; i<refresh.length; i++) {
+ Bundle changedBundle = refresh[i];
+ BundleDescription bundleDes = changedBundle.getBundleDescription();
+ if (bundleDes != null) {
+ if (bundleDes.isResolved()){
+ if (changedBundle.isFragment()) {
+ BundleHost host = (BundleHost) framework.getBundle(bundleDes.getHost().getSupplier().getBundleId());
+ if (((BundleFragment) changedBundle).setHost(host)){
+ // if a fragment attaches to an active host then
+ // its state would have changed to ACTIVE; no need
+ // to set its state to RESOLVED in this case.
+ if (!changedBundle.isActive()) {
+ changedBundle.resolve();
+ }
+ }
+ }
+ else {
+ changedBundle.resolve();
+ }
+ if (!previouslyResolved[i]) {
+ notify.addElement(changedBundle);
+ }
+ }
+ else {
+ if (previouslyResolved[i]) {
+ notify.addElement(changedBundle);
+ }
+ }
+ }
+ }
+ } finally {
+ /*
+ * Release the state change locks.
+ */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: release the state change locks");
+ }
+ for (int i = 0; i < refresh.length; i++) {
+ Bundle changedBundle = refresh[i];
+ changedBundle.completeStateChange();
+ }
+ }
+ /*
+ * Take this opportunity to clean up the adaptor storage.
+ */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: clean up adaptor storage");
+ }
+ try {
+ framework.adaptor.compactStorage();
+ } catch (IOException e) {
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages exception: " + e.getMessage());
+ Debug.printStackTrace(e);
+ }
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, new BundleException(Msg.formatter.getString("BUNDLE_REFRESH_FAILURE"), e));
+ }
+ } catch (BundleException e) {
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages exception: " + e.getMessage());
+ Debug.printStackTrace(e.getNestedException());
+ }
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, new BundleException(Msg.formatter.getString("BUNDLE_REFRESH_FAILURE"), e));
+ }
+
+
+ // send out any resolved/unresolved events
+ for (int i=0; i<notify.size(); i++) {
+ Bundle changedBundle = (Bundle) notify.elementAt(i);
+ framework.publishBundleEvent(changedBundle.isResolved() ? BundleEvent.RESOLVED : BundleEvent.UNRESOLVED, changedBundle);
+ }
+
+ }
+
+ private void unresolvePermissions(Vector bundles, Hashtable packages) {
+ /*
+ * All bundles must be notified of the unexported packages so that
+ * they may unresolve permissions if necessary.
+ * This done after resolve bundles so that unresolved permissions
+ * can be immediately resolved.
+ */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: unresolve permissions");
+ }
+ int size = bundles.size();
+ for (int i = 0; i < size; i++) {
+ Bundle bundle = (Bundle) bundles.elementAt(i);
+ bundle.unresolvePermissions(packages);
+ }
+ }
+
+ private Vector computeAffectedBundles(Bundle[] refresh) {
+ Vector graph = new Vector();
+
+ if (refresh == null) {
+ int size = removalPending.size();
+ for(int i=0; i<size; i++) {
+ BundleLoaderProxy loaderProxy = (BundleLoaderProxy) removalPending.elementAt(i);
+ Bundle bundle = loaderProxy.getBundle();
+ if (!graph.contains(bundle)) {
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN)
+ {
+ Debug.println(" refresh: "+bundle);
+ }
+ graph.addElement(bundle);
+
+ // add in any dependents of the removal pending loader.
+ Bundle[] dependents = loaderProxy.getDependentBundles();
+ for (int j = 0; j < dependents.length; j++)
+ {
+ if (!graph.contains(dependents[j]))
+ {
+ graph.addElement(dependents[j]);
+ }
+ }
+ }
+ }
+ }
+ else {
+ for (int i=0; i<refresh.length; i++) {
+ Bundle bundle = refresh[i];
+ if (bundle == framework.systemBundle) {
+ continue;
+ }
+ if (bundle.isFragment()) {
+ // if it is a fragment then put the host in the graph
+ BundleHost host = (BundleHost) bundle.getHost();
+ if (host != null) {
+ if (!graph.contains(host)){
+ graph.addElement(host);
+ }
+ }
+ }
+
+ if (!graph.contains(bundle)) {
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN)
+ {
+ Debug.println(" refresh: "+bundle);
+ }
+ graph.addElement(bundle);
+ }
+ }
+ }
+
+ /*
+ * If there is nothing to do, then return.
+ */
+ if (graph.size() == 0){
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN){
+ Debug.println("refreshPackages: Empty graph");
+ }
+
+ return graph;
+ }
+
+ /*
+ * Complete graph.
+ */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN){
+ Debug.println("refreshPackages: Complete graph");
+ }
+
+ boolean changed;
+ do {
+ changed = false;
+ int size = graph.size();
+ for (int i=size-1; i>=0; i--) {
+ Bundle bundle = (Bundle) graph.elementAt(i);
+ if (!bundle.isFragment())
+ {
+ BundleLoaderProxy loaderProxy = ((BundleHost) bundle).getLoaderProxy();
+ if (loaderProxy != null)
+ {
+ // add any dependents
+ Bundle[] dependents = loaderProxy.getDependentBundles();
+ for (int j = 0; j < dependents.length; j++)
+ {
+ if (!graph.contains(dependents[j]))
+ {
+ graph.addElement(dependents[j]);
+ changed = true;
+ }
+ }
+ }
+ // add in any fragments
+ org.osgi.framework.Bundle[] frags = bundle.getFragments();
+ if (frags != null)
+ {
+ for (int j = 0; j < frags.length; j++)
+ {
+ if (!graph.contains(frags[j]))
+ {
+ graph.addElement(frags[j]);
+ changed = true;
+ }
+ }
+ }
+ }
+ else {
+ // add in the host.
+ Bundle host = (Bundle) bundle.getHost();
+ if (host != null) {
+ if (!graph.contains(host)) {
+ graph.addElement(host);
+ changed = true;
+ }
+ }
+ }
+ }
+
+ // look for the bundles in removalPending list
+ for (int i = removalPending.size() - 1; i >= 0; i--) {
+ BundleLoaderProxy removedLoaderProxy = (BundleLoaderProxy) removalPending.elementAt(i);
+ Bundle removedBundle = removedLoaderProxy.getBundle();
+
+ if (graph.contains(removedBundle)) {
+ // add in the dependents of the removedLoaderProxy
+ Bundle[] dependents = removedLoaderProxy.getDependentBundles();
+ for (int k = 0; k<dependents.length; k++){
+ if (!graph.contains(dependents[k])) {
+ graph.addElement(dependents[k]);
+ changed = true;
+ }
+ }
+ }
+ }
+ } while (changed);
+
+ return graph;
+ }
+
+ /**
+ * Sets all the bundles in the state that are resolved to the resolved
+ * state. This should only be called when the framework is launching.
+ *
+ */
+ protected void setResolvedBundles(){
+ State state = framework.adaptor.getState();
+ if (!state.isResolved()) {
+ state.resolve(false);
+ }
+ BundleDescription[] descriptions = state.getBundles();
+ for (int i=0; i<descriptions.length; i++) {
+ long bundleId = descriptions[i].getBundleId();
+ Bundle bundle = framework.getBundle(bundleId);
+ if (bundle != null) {
+ if (descriptions[i].isResolved()){
+ if (bundle.isFragment()) {
+ BundleHost host = (BundleHost)framework.getBundle(descriptions[i].getHost().getSupplier().getBundleId());
+ if (((BundleFragment)bundle).setHost(host)) {
+ bundle.resolve();
+ }
+ }
+ else {
+ bundle.resolve();
+ }
+ }
+ }
+ else {
+ // TODO log error!!
+ }
+ }
+ }
+ /**
+ * Attempt to resolve all unresolved bundles. When this method returns
+ * all bundles are resolved that can be resolved. A resolved bundle
+ * has exported and imported packages. An unresolved bundle neither
+ * exports nor imports packages.
+ *
+ */
+ protected void resolveBundles() {
+ long start = 0;
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN_TIMING)
+ start = System.currentTimeMillis();
+ /*
+ * Resolve the bundles. This will make there exported packages available.
+ */
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: resolve bundles");
+ }
+
+ BundleDescription[] resolved;
+ // get the state.
+ State state = framework.adaptor.getState();
+ // resolve the state and get the state delta
+ state.resolve(false);
+
+ Vector notify = new Vector();
+ synchronized (framework.bundles)
+ {
+ List allBundles = framework.bundles.getBundles();
+ int size = allBundles.size();
+ for (int i = 0; i < size; i++)
+ {
+ Bundle bundle = (Bundle) allBundles.get(i);
+ BundleDescription changedBundleDes = bundle.getBundleDescription();
+ boolean previouslyResolved = bundle.isResolved();
+
+ if (bundle != framework.systemBundle && changedBundleDes != null)
+ {
+ if (changedBundleDes.isResolved() && !previouslyResolved )
+ {
+ if (bundle.isFragment())
+ {
+ BundleHost host = (BundleHost) framework.getBundle(changedBundleDes.getHost().getSupplier().getBundleId());
+ if (((BundleFragment) bundle).setHost(host))
+ {
+ bundle.resolve();
+ notify.add(bundle);
+ }
+ }
+ else
+ {
+ bundle.resolve();
+ notify.add(bundle);
+ }
+ }
+ else if (!changedBundleDes.isResolved() && previouslyResolved)
+ {
+ // Need to log error. This should not happen since the state should not touch
+ // bundles that we did not pass in to the resolve() call.
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR,bundle,
+ new BundleException(Msg.formatter.getString("STATE_UNRESOLVED_WRONG_BUNDLE",bundle.getLocation())));
+ }
+ } else
+ {
+ if (bundle != framework.systemBundle)
+ {
+ framework.publishFrameworkEvent(
+ FrameworkEvent.ERROR,
+ bundle,
+ new BundleException(Msg.formatter.getString("BUNDLE_NOT_IN_STATE", bundle.getLocation())));
+ }
+ }
+ }
+
+ // update the exported package and bundle lists.
+ exportedPackages = getExportedPackages(exportedPackages);
+ exportedBundles = getExportedBundles(exportedBundles);
+ }
+ for (int i=0; i<notify.size(); i++) {
+ Bundle bundle = (Bundle) notify.elementAt(i);
+ if (bundle != null) {
+ framework.publishBundleEvent(bundle.isResolved() ? BundleEvent.RESOLVED : BundleEvent.UNRESOLVED, bundle);
+ }
+ }
+ if (Debug.DEBUG && Debug.DEBUG_PACKAGEADMIN_TIMING) {
+ cumulativeTime = cumulativeTime + System.currentTimeMillis() - start;
+ DebugOptions.getDefault().setOption("debug.packageadmin/timing/value", Long.toString(cumulativeTime));
+ }
+ }
+
+ protected void unexportResources(BundleLoaderProxy proxy) {
+ KeyedElement[] bundles = exportedBundles.elements();
+ for (int i=0; i<bundles.length; i++) {
+ BundleLoaderProxy loaderProxy = (BundleLoaderProxy) bundles[i];
+ if (loaderProxy == proxy) {
+ exportedBundles.remove(proxy);
+ }
+ }
+
+ KeyedElement[] packages = exportedPackages.elements();
+ for (int i=0; i<packages.length; i++) {
+ PackageSource source = (PackageSource) packages[i];
+ BundleLoaderProxy sourceProxy = source.getSupplier();
+ if (sourceProxy==proxy) {
+ exportedPackages.remove(source);
+ }
+ }
+ // make the proxy stale
+ proxy.setStale();
+ }
+
+ protected BundleDescription[] getBundleDescriptions(Vector graph) {
+ ArrayList result = new ArrayList();
+ int size = graph.size();
+ for(int i=0; i<size; i++){
+ Bundle bundle = (Bundle) graph.elementAt(i);
+ BundleDescription bundleDes = bundle.getBundleDescription();
+ if (bundleDes != null) {
+ result.add(bundleDes);
+ }
+ }
+ return (BundleDescription[]) result.toArray(new BundleDescription[result.size()]);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageSource.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageSource.java
new file mode 100644
index 000000000..219cea217
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageSource.java
@@ -0,0 +1,25 @@
+package org.eclipse.osgi.framework.internal.core;
+
+public abstract class PackageSource implements KeyedElement {
+ protected String id;
+
+ public String getId() {
+ return id;
+ }
+ public abstract boolean isMultivalued();
+ public abstract BundleLoaderProxy getSupplier();
+ public abstract BundleLoaderProxy[] getSuppliers();
+
+ public boolean compare(KeyedElement other) {
+ return id.equals(((PackageSource)other).getId());
+ }
+ public int getKeyHashCode() {
+ return id.hashCode();
+ }
+ public Object getKey() {
+ return id;
+ }
+ public boolean isNullSource(){
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PermissionAdmin.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PermissionAdmin.java
new file mode 100644
index 000000000..348303416
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PermissionAdmin.java
@@ -0,0 +1,723 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.ProtectionDomain;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.adaptor.PermissionStorage;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.service.permissionadmin.PermissionInfo;
+
+/**
+ * Permission Admin service for the OSGi specification.
+ *
+ * The Permission Admin service allows operators to
+ * manage the permissions of bundles. There is at most one Permission Admin
+ * service present in the Framework.
+ * <p>
+ * Access to the Permission Admin service is protected by
+ * corresponding
+ * <tt>ServicePermission</tt>. In addition the <tt>AdminPermission</tt>
+ * is required to actually set permissions.
+ *
+ * <p>Bundle permissions are managed using a permission table. A bundle's location
+ * serves as the key into this permission table. The value of a table entry is
+ * the set of permissions (of type <tt>PermissionInfo</tt>) granted to the
+ * bundle with the given location.
+ * A bundle may have an entry in the permission table prior to being installed
+ * in the Framework.
+ *
+ * <p>The permissions specified in <tt>setDefaultPermissions</tt> are used as the
+ * default
+ * permissions which are granted to all bundles that do not have an entry in
+ * the permission table.
+ *
+ * <p>Any changes to a bundle's permissions in the permission table will take
+ * effect no later than when bundle's <tt>java.security.ProtectionDomain</tt>
+ * is involved in a permission check, and will be made persistent.
+ *
+ * <p>Only permission classes on the system classpath or from an exported
+ * package are considered during a permission check.
+ * Additionally, only permission classes that are subclasses of
+ * <tt>java.security.Permission</tt> and define a 2-argument constructor
+ * that takes a <i>name</i> string and an <i>actions</i> string can be used.
+ * <p>
+ * Permissions implicitly granted by the Framework (for example, a bundle's
+ * permission to access its persistent storage area) cannot be changed, and
+ * are not reflected in the permissions returned by <tt>getPermissions</tt>
+ * and <tt>getDefaultPermissions</tt>.
+ */
+public class PermissionAdmin implements org.osgi.service.permissionadmin.PermissionAdmin
+{
+ /** framework object */
+ protected Framework framework;
+
+ /** permission storage object */
+ protected PermissionStorage storage;
+
+ /** The permissions to use if no other permissions can be determined */
+ protected PermissionInfo[] defaultDefaultPermissionInfos;
+
+ /** The basic implied permissions for a bundle */
+ protected PermissionInfo[] baseImpliedPermissionInfos;
+
+ /** The permission collection containing the default assigned permissions */
+ protected BundleCombinedPermissions defaultAssignedPermissions;
+
+ /**
+ * Construstor.
+ *
+ * @param framework Framework object.
+ */
+ protected PermissionAdmin(Framework framework, PermissionStorage storage)
+ {
+ this.framework = framework;
+ this.storage = storage;
+
+ defaultDefaultPermissionInfos = getPermissionInfos(Constants.OSGI_DEFAULT_DEFAULT_PERMISSIONS);
+ baseImpliedPermissionInfos = getPermissionInfos(Constants.OSGI_BASE_IMPLIED_PERMISSIONS);
+
+// if (Debug.DEBUG)
+// {
+// // This is necessary to allow File.getAbsolutePath() in debug statements.
+// int _length = baseImpliedPermissionInfos.length;
+//
+// PermissionInfo[] debugBaseImpliedPermissionInfos = new PermissionInfo[_length + 1];
+//
+// System.arraycopy(baseImpliedPermissionInfos, 0, debugBaseImpliedPermissionInfos, 0, _length);
+//
+// debugBaseImpliedPermissionInfos[_length] = new PermissionInfo("(java.util.PropertyPermission \"user.dir\" \"read\")");
+//
+// baseImpliedPermissionInfos = debugBaseImpliedPermissionInfos;
+// }
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Default default assigned bundle permissions");
+ if (defaultDefaultPermissionInfos == null)
+ {
+ Debug.println(" <none>");
+ }
+ else
+ {
+ for (int i = 0; i < defaultDefaultPermissionInfos.length; i++)
+ {
+ Debug.println(" "+defaultDefaultPermissionInfos[i]);
+ }
+ }
+
+ Debug.println("Base implied bundle permissions");
+ if (baseImpliedPermissionInfos == null)
+ {
+ Debug.println(" <none>");
+ }
+ else
+ {
+ for (int i = 0; i < baseImpliedPermissionInfos.length; i++)
+ {
+ Debug.println(" "+baseImpliedPermissionInfos[i]);
+ }
+ }
+ }
+
+ defaultAssignedPermissions = new BundleCombinedPermissions(null);
+ defaultAssignedPermissions.setAssignedPermissions(createDefaultAssignedPermissions(getDefaultPermissions()));
+ }
+
+ /**
+ * Gets the permissions assigned to the bundle with the specified
+ * location.
+ *
+ * @param location The location of the bundle whose permissions are to
+ * be returned.
+ *
+ * @return The permissions assigned to the bundle with the specified
+ * location, or <tt>null</tt> if that bundle has not been assigned any
+ * permissions.
+ */
+ public PermissionInfo[] getPermissions(String location)
+ {
+ if (location == null)
+ {
+ throw new NullPointerException();
+ }
+
+ PermissionStorage storage = new org.eclipse.osgi.framework.security.action.PermissionStorage(this.storage);
+
+ try
+ {
+ String[] data = storage.getPermissionData(location);
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Getting permissions for location: "+location);
+ if (data == null)
+ {
+ Debug.println(" <none>");
+ }
+ else
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ Debug.println(" "+data[i]);
+ }
+ }
+ }
+
+ return makePermissionInfo(data);
+ }
+ catch (IOException e)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);
+
+ return null;
+ }
+ }
+
+ /**
+ * Assigns the specified permissions to the bundle with the specified
+ * location.
+ *
+ * @param location The location of the bundle that will be assigned the
+ * permissions.
+ * @param perms The permissions to be assigned, or <tt>null</tt>
+ * if the specified location is to be removed from the permission table.
+ * @exception SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt>.
+ */
+ public void setPermissions(String location, PermissionInfo[] permissions)
+ {
+ framework.checkAdminPermission();
+
+ if (location == null)
+ {
+ throw new NullPointerException();
+ }
+
+ PermissionStorage storage = new org.eclipse.osgi.framework.security.action.PermissionStorage(this.storage);
+
+ try
+ {
+ String[] data = makePermissionData(permissions);
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Setting permissions for location: "+location);
+ if (data == null)
+ {
+ Debug.println(" <none>");
+ }
+ else
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ Debug.println(" "+data[i]);
+ }
+ }
+ }
+
+ storage.setPermissionData(location, data);
+ }
+ catch (IOException e)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);
+
+ return;
+ }
+
+ Bundle bundle = framework.getBundleByLocation(location);
+
+ if ((bundle != null) && (bundle.getBundleId() != 0))
+ {
+ ProtectionDomain domain = bundle.getProtectionDomain();
+
+ if (domain != null)
+ {
+ BundleCombinedPermissions combined = (BundleCombinedPermissions)domain.getPermissions();
+
+ if (permissions == null)
+ {
+ combined.setAssignedPermissions(defaultAssignedPermissions);
+ }
+ else
+ {
+ combined.setAssignedPermissions(createPermissions(permissions, bundle));
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the bundle locations that have permissions assigned to them,
+ * that is, bundle locations for which an entry
+ * exists in the permission table.
+ *
+ * @return The locations of bundles that have been assigned any
+ * permissions, or <tt>null</tt> if the permission table is empty.
+ */
+ public String[] getLocations()
+ {
+ PermissionStorage storage = new org.eclipse.osgi.framework.security.action.PermissionStorage(this.storage);
+
+ try
+ {
+ String[] locations = storage.getLocations();
+
+ return locations;
+ }
+ catch (IOException e)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);
+
+ return null;
+ }
+ }
+
+ /**
+ * Gets the default permissions.
+ *
+ * <p>These are the permissions granted to any bundle that does not
+ * have permissions assigned to its location.
+ *
+ * @return The default permissions, or <tt>null</tt> if default
+ * permissions have not been defined.
+ */
+ public PermissionInfo[] getDefaultPermissions()
+ {
+ PermissionStorage storage = new org.eclipse.osgi.framework.security.action.PermissionStorage(this.storage);
+
+ try
+ {
+ String[] data = storage.getPermissionData(null);
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Getting default permissions");
+ if (data == null)
+ {
+ Debug.println(" <none>");
+ }
+ else
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ Debug.println(" "+data[i]);
+ }
+ }
+ }
+
+ return makePermissionInfo(data);
+ }
+ catch (IOException e)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);
+
+ return null;
+ }
+ }
+
+ /**
+ * Sets the default permissions.
+ *
+ * <p>These are the permissions granted to any bundle that does not
+ * have permissions assigned to its location.
+ *
+ * @param permissions The default permissions.
+ * @exception SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt>.
+ */
+ public void setDefaultPermissions(PermissionInfo[] permissions)
+ {
+ framework.checkAdminPermission();
+
+ PermissionStorage storage = new org.eclipse.osgi.framework.security.action.PermissionStorage(this.storage);
+
+ try
+ {
+ String[] data = makePermissionData(permissions);
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Setting default permissions");
+ if (data == null)
+ {
+ Debug.println(" <none>");
+ }
+ else
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ Debug.println(" "+data[i]);
+ }
+ }
+ }
+
+ storage.setPermissionData(null, data);
+ }
+ catch (IOException e)
+ {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);
+
+ return;
+ }
+
+ defaultAssignedPermissions.setAssignedPermissions(createDefaultAssignedPermissions(permissions));
+ }
+
+ /**
+ * Make a PermissionInfo array from an array of encoded permission Strings.
+ *
+ * @param data Array of encoded permission Strings
+ * @return Array of PermissionInfo objects.
+ */
+ protected PermissionInfo[] makePermissionInfo(String[] data)
+ {
+ if (data == null)
+ {
+ return null;
+ }
+
+ int size = data.length;
+
+ PermissionInfo[] permissions = new PermissionInfo[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ permissions[i] = new PermissionInfo(data[i]);
+ }
+
+ return permissions;
+ }
+
+ /**
+ * Make an array of encoded permission Strings from a PermissionInfo array.
+ *
+ * @param permissions Array of PermissionInfor objects.
+ * @return Array of encoded permission Strings
+ */
+ protected String[] makePermissionData(PermissionInfo[] permissions)
+ {
+ if (permissions == null)
+ {
+ return null;
+ }
+
+ int size = permissions.length;
+
+ String[] data = new String[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ data[i] = permissions[i].getEncoded();
+ }
+
+ return data;
+ }
+
+ /**
+ * This method is called by the Bundle object to create the
+ * PermissionCollection used by the bundle's ProtectionDomain.
+ *
+ * @param location Location string of the bundle.
+ * @return BundleCombinedPermission object with the bundle's
+ * dynamic permissions.
+ */
+ protected PermissionCollection createPermissionCollection(Bundle bundle)
+ {
+ BundlePermissionCollection implied = getImpliedPermissions(bundle);
+
+ BundleCombinedPermissions combined = new BundleCombinedPermissions(implied);
+
+ BundlePermissionCollection assigned = getAssignedPermissions(bundle);
+
+ combined.setAssignedPermissions(assigned);
+
+ return combined;
+ }
+
+ /**
+ * Creates the default assigned permissions for bundles that
+ * have no assigned permissions.
+ * The default permissions are assigned via the PermissionAdmin service
+ * and may change dynamically.
+ *
+ * @return A PermissionCollection of the default assigned permissions.
+ */
+ protected BundlePermissionCollection createDefaultAssignedPermissions(PermissionInfo[] info)
+ {
+ if (Debug.DEBUG &&Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Creating default assigned permissions");
+ }
+
+ if (info == null)
+ {
+ info = defaultDefaultPermissionInfos;
+ }
+
+ return createPermissions(info, null);
+ }
+
+ /**
+ * Returns the assigned permissions for a bundle.
+ * These permissions are assigned via the PermissionAdmin service
+ * and may change dynamically.
+ *
+ * @param bundle The bundle to create the permissions for.
+ * @return A PermissionCollection of the assigned permissions.
+ */
+ protected BundlePermissionCollection getAssignedPermissions(Bundle bundle)
+ {
+ String location = bundle.getLocation();
+
+ PermissionInfo[] info = getPermissions(location);
+
+ if (info == null)
+ {
+ return defaultAssignedPermissions;
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Creating assigned permissions for "+bundle);
+ }
+
+ return createPermissions(info, bundle);
+ }
+
+ /**
+ * Returns the implied permissions for a bundle.
+ * These permissions never change.
+ *
+ * @param bundle The bundle to create the permissions for.
+ * @return A PermissionCollection of the implied permissions.
+ */
+ protected BundlePermissionCollection getImpliedPermissions(Bundle bundle)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Creating implied permissions for "+bundle);
+ }
+
+ BundlePermissionCollection collection = createPermissions(baseImpliedPermissionInfos, bundle);
+
+ Permission permission = new BundleResourcePermission(bundle.getBundleId());
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Created permission: "+permission);
+ }
+
+ collection.add(permission);
+
+ return collection;
+ }
+
+ /**
+ * Read the permissions from the specified resource.
+ *
+ * @return An array of PermissionInfo objects from the specified
+ * resource.
+ */
+ protected PermissionInfo[] getPermissionInfos(String resource)
+ {
+ PermissionInfo[] info = null;
+
+ InputStream in = getClass().getResourceAsStream(resource);
+
+ if (in != null)
+ {
+ try
+ {
+ Vector permissions = new Vector();
+
+ BufferedReader reader;
+ try
+ {
+ reader = new BufferedReader(new InputStreamReader(in, "UTF8"));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ reader = new BufferedReader(new InputStreamReader(in));
+ }
+
+ while (true)
+ {
+ String line = reader.readLine();
+
+ if (line == null) /* EOF */
+ {
+ break;
+ }
+
+ line = line.trim();
+
+ if ((line.length() == 0) || line.startsWith("#") || line.startsWith("//")) /* comments */
+ {
+ continue;
+ }
+
+ try
+ {
+ permissions.addElement(new PermissionInfo(line));
+ }
+ catch (IllegalArgumentException iae)
+ {
+ /* incorrectly encoded permission */
+
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, iae);
+ }
+ }
+
+ int size = permissions.size();
+
+ if (size > 0)
+ {
+ info = new PermissionInfo[size];
+
+ permissions.copyInto(info);
+ }
+ }
+ catch (IOException e)
+ {
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+ }
+
+ return info;
+ }
+
+ /**
+ * Create a PermissionCollection from a PermissionInfo array.
+ *
+ * @param info Array of PermissionInfo objects.
+ * @param bundle The target bundle for the permissions.
+ * @return A PermissionCollection containing Permission objects.
+ */
+ protected BundlePermissionCollection createPermissions(PermissionInfo[] info, final Bundle bundle)
+ {
+ BundlePermissionCollection collection = new BundlePermissions(framework.packageAdmin);
+
+ /* add the permissions */
+ int size = info.length;
+ for (int i = 0; i < size; i++)
+ {
+ PermissionInfo perm = info[i];
+
+ String type = perm.getType();
+
+ if (type.equals("java.io.FilePermission"))
+ {
+ /* map FilePermissions for relative names to
+ * the bundle's data area
+ */
+ String name = perm.getName();
+
+ if (!name.equals("<<ALL FILES>>"))
+ {
+ File file = new File(name);
+
+ if (!file.isAbsolute()) /* relative name */
+ {
+ if (bundle == null) /* default permissions */
+ {
+ continue; /* no relative file permissions */
+ }
+
+ File target = framework.getDataFile(bundle, name);
+
+ if (target == null) /* no bundle data file area */
+ {
+ continue; /* no relative file permissions */
+ }
+
+ perm = new PermissionInfo(type, target.getPath(), perm.getActions());
+ }
+ }
+ }
+
+ collection.add(createPermission(perm));
+ }
+
+ return collection;
+ }
+
+ /**
+ * Create a Permission object from a PermissionInfo object.
+ * If the type of the permission is not loadable from
+ * this object's classloader (i.e. the system classloader)
+ * then an UnresolvedPermission is returned.
+ *
+ * @param info Description of the desired permission.
+ * @return A permission object.
+ */
+ protected Permission createPermission(PermissionInfo info)
+ {
+ String type = info.getType();
+ String name = info.getName();
+ String actions = info.getActions();
+
+ UnresolvedPermission permission = new UnresolvedPermission(type, name, actions);
+
+ try
+ {
+ /* Only search the system classloader (ours) at this point.
+ * Permission classes exported by bundles will be
+ * resolved later.
+ * This is done so that permission classes exported by bundles
+ * may be easily unresolved during packageRefresh.
+ */
+ Class clazz = Class.forName(type);
+
+ Permission resolved = permission.resolve(clazz);
+
+ if (resolved != null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Created permission: "+resolved);
+ }
+
+ return resolved;
+ }
+ }
+ catch (ClassNotFoundException e)
+ {
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Created permission: "+permission);
+ }
+
+ return permission;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PermissionsHash.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PermissionsHash.java
new file mode 100644
index 000000000..6010f2894
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PermissionsHash.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ * A simple Hashtable based collection of Permission objects.
+ * <p>
+ * The class' .implies method simply scans each permission
+ * individually and asks if the permission should be granted.
+ * No addition semantics is provided by the collection, so it is
+ * not possible to grant permissions whose "grantedness" is
+ * split across multiple stored Permissions.
+ * <p>
+ * Instances of this class can be used to store heterogeneous
+ * collections of permissions, as long as it is not necessary
+ * to remember when multiple occurances of .equal permissions
+ * are added.
+ *
+ */
+class PermissionsHash extends PermissionCollection
+{
+ /**
+ * A hashtable to store the elements of the collection.
+ */
+ Hashtable perms = new Hashtable(8);
+
+ /**
+ * Constructs a new instance of this class.
+ *
+ */
+ public PermissionsHash()
+ {
+ super();
+ }
+
+ /**
+ * Adds the argument to the collection.
+ *
+ * @param permission java.security.Permission
+ * the permission to add to the collection.
+ * @exception IllegalStateException
+ * if the collection is read only.
+ */
+ public void add(Permission perm)
+ {
+ if (isReadOnly())
+ {
+ throw new SecurityException();
+ }
+
+ perms.put(perm, perm);
+ }
+
+ /**
+ * Answers an enumeration of the permissions
+ * in the receiver.
+ *
+ * @return Enumeration
+ * the permissions in the receiver.
+ */
+ public Enumeration elements()
+ {
+ return perms.keys();
+ }
+
+ /**
+ * Indicates whether the argument permission is implied
+ * by the permissions contained in the receiver.
+ *
+ * @return boolean
+ * <code>true</code> if the argument permission
+ * is implied by the permissions in the receiver,
+ * and <code>false</code> if it is not.
+ * @param permission java.security.Permission
+ * the permission to check
+ */
+ public boolean implies(Permission perm)
+ {
+ Permission p = (Permission)perms.get(perm);
+
+ if ((p != null) && p.implies(perm))
+ {
+ return true;
+ }
+
+ Enumeration enum = elements();
+
+ while (enum.hasMoreElements())
+ {
+ if (((Permission) enum.nextElement()).implies(perm))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceReference.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceReference.java
new file mode 100644
index 000000000..09ca761dc
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceReference.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+
+/**
+ * A reference to a service.
+ *
+ * The framework returns ServiceReference objects from the
+ * {@link BundleContext#getServiceReference BundleContext.getServiceReference} and
+ * {@link BundleContext#getServiceReferences BundleContext.getServiceReferences} methods.
+ * <p>A ServiceReference may be shared between bundles and
+ * can be used to examine the properties of the service and to
+ * get the service object
+ * (See {@link BundleContext#getService BundleContext.getService}).
+ * <p>A registered service <i>may</i> have multiple, distinct ServiceReference
+ * objects which refer to it.
+ * However these ServiceReference objects will have
+ * the same <code>hashCode</code> and the <code>equals</code> method
+ * will return <code>true</code> when compared.
+ */
+
+public class ServiceReference implements org.osgi.framework.ServiceReference, Comparable
+{
+ /** Registered Service object. */
+ protected ServiceRegistration registration;
+
+ /**
+ * Construct a reference.
+ *
+ */
+ protected ServiceReference(ServiceRegistration registration)
+ {
+ this.registration = registration;
+ }
+
+ /**
+ * Get the value of a service's property.
+ *
+ * <p>This method will continue to return property values after the
+ * service has been unregistered. This is so that references to
+ * unregistered service can be interrogated.
+ * (For example: ServiceReference objects stored in the log.)
+ *
+ * @param key Name of the property.
+ * @return Value of the property or <code>null</code> if there is
+ * no property by that name.
+ */
+ public Object getProperty(String key)
+ {
+ return(registration.getProperty(key));
+ }
+
+ /**
+ * Get the list of key names for the service's properties.
+ *
+ * <p>This method will continue to return the keys after the
+ * service has been unregistered. This is so that references to
+ * unregistered service can be interrogated.
+ * (For example: ServiceReference objects stored in the log.)
+ *
+ * @return The list of property key names.
+ */
+ public String[] getPropertyKeys()
+ {
+ return(registration.getPropertyKeys());
+ }
+
+ /**
+ * Return the bundle which registered the service.
+ *
+ * <p>This method will always return <code>null</code> when the
+ * service has been unregistered. This can be used to
+ * determine if the service has been unregistered.
+ *
+ * @return The bundle which registered the service.
+ * @see BundleContext#registerService
+ */
+ public org.osgi.framework.Bundle getBundle()
+ {
+ return(registration.getBundle());
+ }
+
+ /**
+ * Return the list of bundles which are using the service.
+ *
+ * <p>This method will always return <code>null</code> when the
+ * service has been unregistered.
+ *
+ * @return The array of bundles using the service or null if
+ * no bundles are using the service.
+ * @see BundleContext#getService
+ */
+ public org.osgi.framework.Bundle[] getUsingBundles()
+ {
+ return(registration.getUsingBundles());
+ }
+
+ /**
+ * Return the classes under which the referenced service was registered.
+ *
+ * @return array of class names.
+ */
+ protected String[] getClasses()
+ {
+ return(registration.clazzes);
+ }
+
+ /**
+ * Return the serviceid of the ServiceRegistration.
+ *
+ * @return service.id of the service
+ */
+ protected long getId()
+ {
+ return(registration.serviceid);
+ }
+
+ /**
+ * Return the serviceranking of the ServiceRegistration.
+ *
+ * @return service.ranking of the service
+ */
+ protected int getRanking()
+ {
+ return(registration.serviceranking);
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return a hash code value for this object.
+ */
+ public int hashCode()
+ {
+ return(registration.hashCode());
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param obj the reference object with which to compare.
+ * @return <code>true</code> if this object is the same as the obj
+ * argument; <code>false</code> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return (true);
+ }
+
+ if (!(obj instanceof ServiceReference))
+ {
+ return (false);
+ }
+
+ ServiceReference other = (ServiceReference) obj;
+
+ return(registration == other.registration);
+ }
+
+ /**
+ * Return a string representation of this reference.
+ *
+ * @return String
+ */
+ public String toString()
+ {
+ return(registration.toString());
+ }
+
+ /**
+ *
+ * Compares two service objects and returns an indication as to which is higher ranked
+ * based on service ranking and service_ID.
+ *
+ * The service with the higher service ranking (as specified in its service.ranking property)
+ * is defined to be higher ranked.
+ *
+ * If there is a tie in ranking, the service with the lowest
+ * service ID (as specified in its service.id property); that is,
+ * the service that was registered first is returned.
+ *
+ * This is the same algorithm used by BundleContext.getServiceReference.
+ *
+ * @return int
+ * int = 0 if this object's ranking and
+ * service id are equivalent
+ *
+ * int < 0 if this object's ranking is lower
+ * than the argument's ranking or if the
+ * rankings are equal, this object's service
+ * id is greater than the argument's service
+ * id.
+ *
+ * int > 0 if this object's ranking is higher
+ * than than the argument's ranking or if the
+ * rankings are equal, this object's service
+ * id is less than the argument's service id.
+ *
+ *
+ * @param another Object
+ * an object to compare the receiver to
+ * @exception ClassCastException
+ * if the argument can not be converted
+ * into something comparable with the
+ * receiver.
+ */
+ public int compareTo(Object object)
+ {
+ ServiceReference other = (ServiceReference)object;
+
+ int compare = this.getRanking() - other.getRanking();
+
+ if (compare == 0) /* rankings are equal */
+ { /* check service id */
+ return (int)(other.getId() - this.getId());
+ }
+
+ return compare;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceRegistration.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceRegistration.java
new file mode 100644
index 000000000..b6a5dbeef
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceRegistration.java
@@ -0,0 +1,779 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.util.*;
+import org.osgi.framework.ServiceEvent;
+
+/**
+ * A registered service.
+ *
+ * The framework returns a ServiceRegistration object when a
+ * {@link BundleContext#registerService BundleContext.registerService}
+ * method is successful. This object is for the private use of
+ * the registering bundle and should not be shared with other bundles.
+ * <p>The ServiceRegistration object may be used to update the properties
+ * for the service or to unregister the service.
+ *
+ * <p>If the ServiceRegistration is garbage collected the framework may remove
+ * the service. This implies that if a
+ * bundle wants to keep its service registered, it should keep the
+ * ServiceRegistration object referenced.
+ */
+
+public class ServiceRegistration implements org.osgi.framework.ServiceRegistration
+{
+ /** Reference to this registration. */
+ protected ServiceReference reference;
+
+ /** Internal framework object. */
+ protected Framework framework;
+
+ /** context which registered this service. */
+ protected BundleContext context;
+
+ /** bundle which registered this service. */
+ protected Bundle bundle;
+
+ /** list of contexts using the service.
+ * Access to this should be protected by the registrationLock */
+ protected Vector contextsUsing;
+
+ /** service classes for this registration. */
+ protected String[] clazzes;
+
+ /** service object for this registration. */
+ protected Object service;
+
+ /** properties for this registration. */
+ protected Properties properties;
+
+ /** service id. */
+ protected long serviceid;
+
+ /** service ranking. */
+ protected int serviceranking;
+
+ /* internal object to use for synchronization */
+ protected Object registrationLock = new Object();
+
+ /** This boolean will be true when the service registration is unregistered */
+ protected boolean unregistered;
+
+ /**
+ * Construct a ServiceRegistration and register the service
+ * in the framework's service registry.
+ *
+ */
+ protected ServiceRegistration(BundleContext context, String[] clazzes, Object service, Dictionary properties)
+ {
+ this.context = context;
+ this.bundle = context.bundle;
+ this.framework = context.framework;
+ this.clazzes = clazzes; /* must be set before calling createProperties. */
+ this.service = service;
+ this.contextsUsing = null;
+ this.unregistered = false;
+ this.reference = new ServiceReference(this);
+
+ synchronized (framework.serviceRegistry)
+ {
+ serviceid = framework.getNextServiceId(); /* must be set before calling createProperties. */
+ this.properties = createProperties(properties); /* must be valid after unregister is called. */
+
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("registerService["+bundle+"]("+this+")");
+ }
+
+ framework.serviceRegistry.publishService(context,this);
+ }
+
+ /* must not hold the registrations lock when this event is published */
+ framework.publishServiceEvent(ServiceEvent.REGISTERED, reference);
+ }
+
+ /**
+ * Unregister the service.
+ * Remove a service registration from the framework's service
+ * registry.
+ * All {@link ServiceReference} objects for this registration
+ * can no longer be used to interact with the service.
+ *
+ * <p>The following steps are followed to unregister a service:
+ * <ol>
+ * <li>The service is removed from the framework's service
+ * registry so that it may no longer be used.
+ * {@link ServiceReference}s for the service may no longer be used
+ * to get a service object for the service.
+ * <li>A {@link ServiceEvent} of type {@link ServiceEvent#UNREGISTERING}
+ * is synchronously sent so that bundles using this service
+ * may release their use of the service.
+ * <li>For each bundle whose use count for this service is greater
+ * than zero:
+ * <ol>
+ * <li>The bundle's use count for this service is set to zero.
+ * <li>If the service was registered with a {@link ServiceFactory},
+ * the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
+ * is called to release the service object for the bundle.
+ * </ol>
+ * </ol>
+ *
+ * @exception java.lang.IllegalStateException If
+ * this ServiceRegistration has already been unregistered.
+ * @see BundleContext#ungetService
+ */
+ public void unregister()
+ {
+ synchronized (registrationLock)
+ {
+ if (service == null) /* in the process of unregisterING */
+ {
+ throw new IllegalStateException(Msg.formatter.getString("SERVICE_ALREADY_UNREGISTERED_EXCEPTION"));
+ }
+
+ /* remove this object from the service registry */
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("unregisterService["+bundle+"]("+this+")");
+ }
+
+ synchronized (framework.serviceRegistry)
+ {
+ framework.serviceRegistry.unpublishService(context,this);
+ }
+
+ service = null; /* mark unregisterING */
+ }
+
+ /* must not hold the registrationLock when this event is published */
+ framework.publishServiceEvent(ServiceEvent.UNREGISTERING, reference);
+
+ /* we have published the ServiceEvent, now mark the service fully unregistered */
+ unregistered = true;
+
+ int size = 0;
+ BundleContext[] users = null;
+
+ synchronized (registrationLock)
+ {
+ if (contextsUsing != null)
+ {
+ size = contextsUsing.size();
+
+ if (size > 0)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("unregisterService: releasing users");
+ }
+
+ users = new BundleContext[size];
+ contextsUsing.copyInto(users);
+ }
+ }
+ }
+
+ /* must not hold the registrationLock while releasing services */
+ for (int i = 0; i < size; i++)
+ {
+ releaseService(users[i]);
+ }
+
+ contextsUsing = null;
+
+ reference = null; /* mark registration dead */
+ context = null;
+
+ /* These fields must be valid after unregister is called:
+ * properties
+ */
+ }
+
+
+ /**
+ * Returns a {@link ServiceReference} object for this registration.
+ * The {@link ServiceReference} object may be shared with other bundles.
+ *
+ * @exception java.lang.IllegalStateException If
+ * this ServiceRegistration has already been unregistered.
+ * @return A {@link ServiceReference} object.
+ */
+ public org.osgi.framework.ServiceReference getReference()
+ {
+ /* use reference instead of unregistered so that ServiceFactorys, called
+ * by releaseService after the registration is unregistered, can
+ * get the ServiceReference. Note this technically may voilate the spec
+ * but makes more sense.
+ */
+ if (reference == null)
+ {
+ throw new IllegalStateException(Msg.formatter.getString("SERVICE_ALREADY_UNREGISTERED_EXCEPTION"));
+ }
+
+ return(reference);
+ }
+
+ /**
+ * Update the properties associated with this service.
+ *
+ * <p>The key "objectClass" cannot be modified by this method. It's
+ * value is set when the service is registered.
+ *
+ * <p>The following steps are followed to modify a service's properties:
+ * <ol>
+ * <li>The service's properties are replaced with the provided properties.
+ * <li>A {@link ServiceEvent} of type {@link ServiceEvent#MODIFIED}
+ * is synchronously sent.
+ * </ol>
+ *
+ * @param properties The properties for this service.
+ * Changes should not be made to this object after calling this method.
+ * To update the service's properties this method should be called again.
+ * @exception java.lang.IllegalStateException If
+ * this ServiceRegistration has already been unregistered.
+ *
+ * @exception IllegalArgumentException If the <tt>properties</tt>
+ * parameter contains case variants of the same key name.
+ */
+ public void setProperties(Dictionary props)
+ {
+ synchronized (registrationLock)
+ {
+ if (service == null) /* in the process of unregistering */
+ {
+ throw new IllegalStateException(Msg.formatter.getString("SERVICE_ALREADY_UNREGISTERED_EXCEPTION"));
+ }
+
+ this.properties = createProperties(props);
+ }
+
+ /* must not hold the registrationLock when this event is published */
+ framework.publishServiceEvent(ServiceEvent.MODIFIED, reference);
+ }
+
+ /**
+ * Construct a properties object from the dictionary for this
+ * ServiceRegistration.
+ *
+ * @param props The properties for this service.
+ * @return A Properties object for this ServiceRegistration.
+ */
+ protected Properties createProperties(Dictionary props)
+ {
+ Properties properties = new Properties(props);
+
+ properties.setProperty(Constants.OBJECTCLASS, null); /* remove user provided key if any */
+ properties.setProperty(Constants.OBJECTCLASS, clazzes);
+
+ properties.setProperty(Constants.SERVICE_ID, null); /* remove user provided key if any */
+ properties.setProperty(Constants.SERVICE_ID, new Long(serviceid));
+
+ Object ranking = properties.getProperty(Constants.SERVICE_RANKING);
+
+ serviceranking = (ranking instanceof Integer) ?
+ ((Integer)ranking).intValue() :
+ 0;
+
+ return(properties);
+ }
+
+ /**
+ * Get the value of a service's property.
+ *
+ * <p>This method will continue to return property values after the
+ * service has been unregistered. This is so that references to
+ * unregistered service can be interrogated.
+ * (For example: ServiceReference objects stored in the log.)
+ *
+ * @param key Name of the property.
+ * @return Value of the property or <code>null</code> if there is
+ * no property by that name.
+ */
+ protected Object getProperty(String key)
+ {
+ synchronized (registrationLock)
+ {
+ return(properties.getProperty(key));
+ }
+ }
+
+ /**
+ * Get the list of key names for the service's properties.
+ *
+ * <p>This method will continue to return the keys after the
+ * service has been unregistered. This is so that references to
+ * unregistered service can be interrogated.
+ * (For example: ServiceReference objects stored in the log.)
+ *
+ * @return The list of property key names.
+ */
+ protected String[] getPropertyKeys()
+ {
+ synchronized (registrationLock)
+ {
+ return(properties.getPropertyKeys());
+ }
+ }
+
+ /**
+ * Return the bundle which registered the service.
+ *
+ * <p>This method will always return <code>null</code> when the
+ * service has been unregistered. This can be used to
+ * determine if the service has been unregistered.
+ *
+ * @return The bundle which registered the service.
+ * @see BundleContext.registerService
+ */
+ protected Bundle getBundle()
+ {
+ if (reference == null)
+ {
+ return(null);
+ }
+
+ return(bundle);
+ }
+
+ /**
+ * Get a service object for the using BundleContext.
+ *
+ * @param user BundleContext using service.
+ * @return Service object
+ */
+ protected Object getService(BundleContext user)
+ {
+ synchronized (registrationLock)
+ {
+ if (unregistered) /* service unregistered */
+ {
+ return(null);
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("getService["+user.bundle+"]("+this+")");
+ }
+
+ Hashtable servicesInUse = user.servicesInUse;
+
+ ServiceUse use = (ServiceUse)servicesInUse.get(reference);
+
+ if (use == null)
+ {
+ use = new ServiceUse(user, this);
+
+ Object service = use.getService();
+
+ if (service != null)
+ {
+ servicesInUse.put(reference, use);
+
+ if (contextsUsing == null)
+ {
+ contextsUsing = new Vector(10, 10);
+ }
+
+ contextsUsing.addElement(user);
+ }
+
+ return(service);
+ }
+ else
+ {
+ return(use.getService());
+ }
+ }
+ }
+
+ /**
+ * Unget a service for the using BundleContext.
+ *
+ * @param user BundleContext using service.
+ * @return <code>false</code> if the context bundle's use count for the service
+ * is zero or if the service has been unregistered,
+ * otherwise <code>true</code>.
+ */
+ protected boolean ungetService(BundleContext user)
+ {
+ synchronized (registrationLock)
+ {
+ if (unregistered)
+ {
+ return(false);
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ String bundle = (user.bundle == null) ? "" : user.bundle.toString();
+ Debug.println("ungetService["+bundle+"]("+this+")");
+ }
+
+ Hashtable servicesInUse = user.servicesInUse;
+
+ if (servicesInUse != null)
+ {
+ ServiceUse use = (ServiceUse)servicesInUse.get(reference);
+
+ if (use != null)
+ {
+ if (use.ungetService())
+ {
+ /* use count is now zero */
+ servicesInUse.remove(reference);
+
+ contextsUsing.removeElement(user);
+ }
+
+ return(true);
+ }
+ }
+
+ return(false);
+ }
+ }
+
+ /**
+ * Release the service for the using BundleContext.
+ *
+ * @param user BundleContext using service.
+ */
+ protected void releaseService(BundleContext user)
+ {
+ synchronized (registrationLock)
+ {
+ if (reference == null) /* registration dead */
+ {
+ return;
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ String bundle = (user.bundle == null) ? "" : user.bundle.toString();
+ Debug.println("releaseService["+bundle+"]("+this+")");
+ }
+
+ Hashtable servicesInUse = user.servicesInUse;
+
+ if (servicesInUse != null)
+ {
+ ServiceUse use = (ServiceUse)servicesInUse.remove(reference);
+
+ if (use != null)
+ {
+ use.releaseService();
+
+ contextsUsing.removeElement(user);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return the list of bundle which are using this service.
+ *
+ * @return Array of Bundles using this service.
+ */
+ protected Bundle[] getUsingBundles()
+ {
+ synchronized (registrationLock)
+ {
+ if (unregistered) /* service unregistered */
+ {
+ return(null);
+ }
+
+ if (contextsUsing == null)
+ {
+ return(null);
+ }
+
+ int size = contextsUsing.size();
+
+ if (size == 0)
+ {
+ return(null);
+ }
+
+ Bundle[] bundles = new Bundle[size];
+
+ /* Copy vector of BundleContext into an array of Bundle. */
+ for (int i = 0; i < size; i++)
+ {
+ bundles[i] = ((BundleContext)contextsUsing.elementAt(i)).bundle;
+ }
+
+ return(bundles);
+ }
+ }
+
+ /**
+ * Return a String representation of this object.
+ *
+ * @return String representation of this object.
+ */
+ public String toString()
+ {
+ String[] clazzes = this.clazzes;
+ int size = clazzes.length;
+ StringBuffer sb = new StringBuffer(50 * size);
+
+ sb.append('{');
+
+ for (int i = 0; i < size; i++)
+ {
+ if (i > 0)
+ {
+ sb.append(", ");
+ }
+ sb.append(clazzes[i]);
+ }
+
+ sb.append("}=");
+ sb.append(properties);
+
+ return(sb.toString());
+ }
+
+ /**
+ * Hashtable for service properties.
+ */
+ static class Properties extends Headers
+ {
+ /**
+ * Create a properties object for the service.
+ *
+ * @param properties The properties for this service.
+ */
+ private Properties(int size, Dictionary props)
+ {
+ super((size << 1) + 1);
+
+ if (props != null)
+ {
+ synchronized (props)
+ {
+ Enumeration enum = props.keys();
+
+ while (enum.hasMoreElements())
+ {
+ Object key = enum.nextElement();
+
+ if (key instanceof String)
+ {
+ String header = (String)key;
+
+ setProperty(header, props.get(header));
+ }
+ }
+ }
+ }
+ }
+ /**
+ * Create a properties object for the service.
+ *
+ * @param properties The properties for this service.
+ */
+ protected Properties(Dictionary props)
+ {
+ this((props == null) ? 2 : Math.max(2, props.size()), props);
+ }
+
+ /**
+ * Get a clone of the value of a service's property.
+ *
+ * @param key header name.
+ * @return Clone of the value of the property or <code>null</code> if there is
+ * no property by that name.
+ */
+ protected Object getProperty(String key)
+ {
+ return(cloneValue(get(key)));
+ }
+
+ /**
+ * Get the list of key names for the service's properties.
+ *
+ * @return The list of property key names.
+ */
+ protected synchronized String[] getPropertyKeys()
+ {
+ int size = size();
+
+ String[] keynames = new String[size];
+
+ Enumeration enum = keys();
+
+ for (int i = 0; i < size; i++)
+ {
+ keynames[i] = (String)enum.nextElement();
+ }
+
+ return(keynames);
+ }
+
+ /**
+ * Put a clone of the property value into this property object.
+ *
+ * @param key Name of property.
+ * @param value Value of property.
+ * @return previous property value.
+ */
+ protected synchronized Object setProperty(String key, Object value)
+ {
+ return(set(key, cloneValue(value)));
+ }
+
+ /**
+ * Attempt to clone the value if necessary and possible.
+ *
+ * For some strange reason, you can test to see of an Object is
+ * Cloneable but you can't call the clone method since it is
+ * protected on Object!
+ *
+ * @param value object to be cloned.
+ * @return cloned object or original object if we didn't clone it.
+ */
+ protected static Object cloneValue(Object value)
+ {
+ if (value != null)
+ {
+ if (value instanceof String) /* shortcut String */
+ {
+ return(value);
+ }
+
+ Class clazz = value.getClass();
+
+ if (clazz.isArray())
+ {
+ Class type = clazz.getComponentType();
+
+ if (type.isPrimitive())
+ {
+ if (Integer.TYPE.isAssignableFrom(type))
+ {
+ return(((int[])value).clone());
+ }
+
+ if (Long.TYPE.isAssignableFrom(type))
+ {
+ return(((long[])value).clone());
+ }
+
+ if (Byte.TYPE.isAssignableFrom(type))
+ {
+ return(((byte[])value).clone());
+ }
+
+ if (Short.TYPE.isAssignableFrom(type))
+ {
+ return(((short[])value).clone());
+ }
+
+ if (Character.TYPE.isAssignableFrom(type))
+ {
+ return(((char[])value).clone());
+ }
+
+ if (Float.TYPE.isAssignableFrom(type))
+ {
+ return(((float[])value).clone());
+ }
+
+ if (Double.TYPE.isAssignableFrom(type))
+ {
+ return(((double[])value).clone());
+ }
+
+ if (Boolean.TYPE.isAssignableFrom(type))
+ {
+ return(((boolean[])value).clone());
+ }
+ }
+ else /* non-primitive array object */
+ {
+ return(((Object[])value).clone());
+ }
+ }
+ else /* non array object */
+ {
+ try
+ {
+ return(clazz.getMethod("clone", null).invoke(value, null));
+ }
+ catch (Exception e)
+ {
+ /* clone is not a public method on value's class */
+ }
+ catch (Error e)
+ {
+ /* JCL does not support reflection; try some well known types */
+
+ if (value instanceof Vector)
+ {
+ return(((Vector)value).clone());
+ }
+
+ if (value instanceof Hashtable)
+ {
+ return(((Hashtable)value).clone());
+ }
+ }
+ }
+ }
+
+ return(value);
+ }
+
+ public synchronized String toString()
+ {
+ String keys[] = getPropertyKeys();
+
+ int size = keys.length;
+
+ StringBuffer sb = new StringBuffer(20 * size);
+
+ sb.append('{');
+
+ int n = 0;
+ for (int i = 0; i < size; i++)
+ {
+ String key = keys[i];
+ if (!key.equals(Constants.OBJECTCLASS))
+ {
+ if (n > 0)
+ {
+ sb.append(", ");
+ }
+
+ sb.append(key);
+ sb.append('=');
+ sb.append(get(key));
+ n++;
+ }
+ }
+
+ sb.append('}');
+
+ return(sb.toString());
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceUse.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceUse.java
new file mode 100644
index 000000000..c68b6ba87
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/ServiceUse.java
@@ -0,0 +1,333 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.ServiceFactory;
+
+/**
+ * This class represents the use of a service by a bundle. One is created for each
+ * service acquired by a bundle. This class manages the calls to ServiceFactory
+ * and the bundle's use count.
+ */
+
+public class ServiceUse
+{
+ /** ServiceFactory object if the service instance represents a factory,
+ null otherwise */
+ protected ServiceFactory factory;
+ /** Service object either registered or that returned by
+ ServiceFactory.getService() */
+ protected Object service;
+ /** BundleContext associated with this service use */
+ protected BundleContext context;
+ /** ServiceDescription of the registered service */
+ protected ServiceRegistration registration;
+ /** bundle's use count for this service */
+ protected int useCount;
+ /** Internal framework object. */
+ protected Framework framework;
+
+ /**
+ * Constructs a service use encapsulating the service object.
+ * Objects of this class should be constrcuted while holding the
+ * registrations lock.
+ *
+ * @param context bundle getting the service
+ * @param registration ServiceRegistration of the service
+ */
+ protected ServiceUse(BundleContext context, ServiceRegistration registration)
+ {
+ this.context = context;
+ this.framework = context.framework;
+ this.registration = registration;
+ this.useCount = 0;
+
+ Object service = registration.service;
+ if (service instanceof ServiceFactory)
+ {
+ factory = (ServiceFactory)service;
+ this.service = null;
+ }
+ else
+ {
+ this.factory = null;
+ this.service = service;
+ }
+ }
+
+ /**
+ * Get a service's service object.
+ * Retrieves the service object for a service.
+ * A bundle's use of a service is tracked by a
+ * use count. Each time a service's service object is returned by
+ * {@link #getService}, the context bundle's use count for the service
+ * is incremented by one. Each time the service is release by
+ * {@link #ungetService}, the context bundle's use count
+ * for the service is decremented by one.
+ * When a bundle's use count for a service
+ * drops to zero, the bundle should no longer use the service.
+ * See {@link #getBundle()} for a definition of context bundle.
+ *
+ * <p>The following steps are followed to get the service object:
+ * <ol>
+ * <li>The context bundle's use count for this service is incremented by one.
+ * <li>If the context bundle's use count for the service is now one and
+ * the service was registered with a {@link ServiceFactory},
+ * the {@link ServiceFactory#getService ServiceFactory.getService} method
+ * is called to create a service object for the context bundle.
+ * This service object is cached by the framework.
+ * While the context bundle's use count for the service is greater than zero,
+ * subsequent calls to get the services's service object for the context bundle
+ * will return the cached service object.
+ * <br>If the service object returned by the {@link ServiceFactory}
+ * is not an <code>instanceof</code>
+ * all the classes named when the service was registered or
+ * the {@link ServiceFactory} throws an exception,
+ * <code>null</code> is returned and a
+ * {@link FrameworkEvent} of type {@link FrameworkEvent#ERROR} is broadcast.
+ * <li>The service object for the service is returned.
+ * </ol>
+ *
+ * @return A service object for the service associated with this
+ * reference.
+ */
+ protected Object getService()
+ {
+ if ((useCount == 0) && (factory != null))
+ {
+ Bundle factorybundle = registration.context.bundle;
+ Object service;
+
+ try
+ {
+ service = AccessController.doPrivileged(new PrivilegedAction ()
+ {
+ public Object run()
+ {
+ return factory.getService(context.bundle, registration);
+ }
+ });
+ }
+ catch (Throwable t)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println(factory + ".getService() exception: " + t.getMessage());
+ Debug.printStackTrace(t);
+ }
+
+ BundleException be = new BundleException(Msg.formatter.getString("SERVICE_FACTORY_EXCEPTION", factory.getClass().getName(), "getService"), t);
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
+
+ return(null);
+ }
+
+ if (service == null)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println(factory + ".getService() returned null.");
+ }
+
+ BundleException be = new BundleException(Msg.formatter.getString("SERVICE_OBJECT_NULL_EXCEPTION", factory.getClass().getName()));
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
+
+ return(null);
+ }
+
+ String[] clazzes = registration.clazzes;
+ int size = clazzes.length;
+
+ PackageAdmin packageAdmin = framework.packageAdmin;
+
+ for (int i = 0; i < size; i++)
+ {
+ Class clazz;
+
+ try
+ {
+ clazz = factorybundle.loadClass(clazzes[i],false);
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ // TODO this code really never should be run. If the factory's classloader cannot
+ // see the required class (above) then it is not available. Looking in a global list of
+ // package exporters etc is not a good plan in the face of modules and multiple
+ // versions. For now just put in a dummy classload call to reduce code impact.
+ clazz = Class.forName(clazzes[i]);
+// clazz = packageAdmin.loadClass(clazzes[i]);
+ }
+ catch (ClassNotFoundException ee)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println(clazzes[i] + " class not found");
+ }
+
+ BundleException be = new BundleException(Msg.formatter.getString("SERVICE_CLASS_NOT_FOUND_EXCEPTION", clazzes[i]), e);
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
+
+ return(null);
+ }
+ }
+
+ if (!clazz.isInstance(service))
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println("Service object from ServiceFactory is not an instanceof "+clazzes[i]);
+ }
+
+ BundleException be = new BundleException(Msg.formatter.getString("SERVICE_NOT_INSTANCEOF_CLASS_EXCEPTION", factory.getClass().getName(), clazzes[i]));
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
+
+ return(null);
+ }
+ }
+
+ this.service = service;
+ }
+
+ useCount++;
+
+ return(this.service);
+ }
+
+ /**
+ * Unget a service's service object.
+ * Releases the service object for a service.
+ * If the context bundle's use count for the service is zero, this method
+ * returns <code>false</code>. Otherwise, the context bundle's use count for the
+ * service is decremented by one.
+ * See {@link #getBundle()} for a definition of context bundle.
+ *
+ * <p>The service's service object
+ * should no longer be used and all references to it should be destroyed
+ * when a bundle's use count for the service
+ * drops to zero.
+ *
+ * <p>The following steps are followed to unget the service object:
+ * <ol>
+ * <li>If the context bundle's use count for the service is zero or
+ * the service has been unregistered,
+ * <code>false</code> is returned.
+ * <li>The context bundle's use count for this service is decremented by one.
+ * <li>If the context bundle's use count for the service is now zero and
+ * the service was registered with a {@link ServiceFactory},
+ * the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
+ * is called to release the service object for the context bundle.
+ * <li><code>true</code> is returned.
+ * </ol>
+ *
+ * @return <code>true</code> if the context bundle's use count for the service
+ * is zero otherwise <code>false</code>.
+ */
+ protected boolean ungetService()
+ {
+ if (useCount == 0)
+ {
+ return(true);
+ }
+
+ useCount--;
+
+ if (useCount == 0)
+ {
+ if (factory != null)
+ {
+ try
+ {
+ AccessController.doPrivileged(new PrivilegedAction ()
+ {
+ public Object run()
+ {
+ factory.ungetService(context.bundle, registration, service);
+
+ return null;
+ }
+ });
+ }
+ catch (Throwable t)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println(factory + ".ungetService() exception");
+ Debug.printStackTrace(t);
+ }
+
+ Bundle factorybundle = registration.context.bundle;
+ BundleException be = new BundleException(Msg.formatter.getString("SERVICE_FACTORY_EXCEPTION", factory.getClass().getName(), "ungetService"), t);
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
+ }
+
+ service = null;
+ }
+
+ return(true);
+ }
+
+ return(false);
+ }
+
+ /**
+ * Release a service's service object.
+ * <ol>
+ * <li>The bundle's use count for this service is set to zero.
+ * <li>If the service was registered with a {@link ServiceFactory},
+ * the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
+ * is called to release the service object for the bundle.
+ * </ol>
+ */
+ protected void releaseService()
+ {
+ if ((useCount > 0) && (factory != null))
+ {
+ try
+ {
+ AccessController.doPrivileged(new PrivilegedAction ()
+ {
+ public Object run()
+ {
+ factory.ungetService(context.bundle, registration, service);
+
+ return null;
+ }
+ });
+ }
+ catch (Throwable t)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_SERVICES)
+ {
+ Debug.println(factory + ".ungetService() exception");
+ Debug.printStackTrace(t);
+ }
+
+ Bundle factorybundle = registration.context.bundle;
+ BundleException be = new BundleException(Msg.formatter.getString("SERVICE_FACTORY_EXCEPTION", factory.getClass().getName(), "ungetService"), t);
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
+ }
+
+ service = null;
+ }
+
+ useCount = 0;
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SingleSourcePackage.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SingleSourcePackage.java
new file mode 100644
index 000000000..05cb89c7c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SingleSourcePackage.java
@@ -0,0 +1,26 @@
+package org.eclipse.osgi.framework.internal.core;
+
+public class SingleSourcePackage extends PackageSource {
+ BundleLoaderProxy supplier;
+
+ public SingleSourcePackage(String name, BundleLoaderProxy supplier) {
+ this.id = name;
+ this.supplier = supplier;
+ }
+
+ public BundleLoaderProxy getSupplier() {
+ return supplier;
+ }
+
+ public boolean isMultivalued() {
+ return false;
+ }
+
+ public BundleLoaderProxy[] getSuppliers() {
+ return new BundleLoaderProxy[] { supplier };
+ }
+
+ public String toString() {
+ return id + " -> " + supplier;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevel.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevel.java
new file mode 100644
index 000000000..76642c3ad
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevel.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+
+/**
+ * StartLevel service for the OSGi specification.
+ *
+ * Framework service which allows management of framework and bundle startlevels.
+ *
+ * If present, there will only be a single instance of this service
+ * registered in the framework.
+ *
+ */
+public class StartLevel implements org.osgi.service.startlevel.StartLevel {
+
+ protected StartLevelImpl startLevelImpl;
+ protected org.osgi.framework.Bundle callerBundle;
+
+ /** This constructor is called by the StartLevel factory */
+ protected StartLevel (org.osgi.framework.Bundle callerBundle, Framework framework) {
+ this.callerBundle = callerBundle;
+ this.startLevelImpl = framework.startLevelImpl;
+ }
+
+
+ /**
+ * Return the initial start level value that is assigned
+ * to a Bundle when it is first installed.
+ *
+ * @return The initial start level value for Bundles.
+ * @see #setInitialBundleStartLevel
+ */
+ public int getInitialBundleStartLevel() {
+ return startLevelImpl.getInitialBundleStartLevel();
+ }
+
+ /**
+ * Set the initial start level value that is assigned
+ * to a Bundle when it is first installed.
+ *
+ * <p>The initial bundle start level will be set to the specified start level. The
+ * initial bundle start level value will be persistently recorded
+ * by the Framework.
+ *
+ * <p>When a Bundle is installed via <tt>BundleContext.installBundle</tt>,
+ * it is assigned the initial bundle start level value.
+ *
+ * <p>The default initial bundle start level value is 1
+ * unless this method has been
+ * called to assign a different initial bundle
+ * start level value.
+ *
+ * <p>This method does not change the start level values of installed
+ * bundles.
+ *
+ * @param startlevel The initial start level for newly installed bundles.
+ * @throws IllegalArgumentException If the specified start level is less than or
+ * equal to zero.
+ * @throws SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setInitialBundleStartLevel(int startlevel) {
+ startLevelImpl.setInitialBundleStartLevel(startlevel);
+ }
+
+ /**
+ * Return the active start level value of the Framework.
+ *
+ * If the Framework is in the process of changing the start level
+ * this method must return the active start level if this
+ * differs from the requested start level.
+ *
+ * @return The active start level value of the Framework.
+ */
+ public int getStartLevel() {
+ return startLevelImpl.getStartLevel();
+ }
+ /**
+ * Modify the active start level of the Framework.
+ *
+ * <p>The Framework will move to the requested start level. This method
+ * will return immediately to the caller and the start level
+ * change will occur asynchronously on another thread.
+ *
+ * <p>If the specified start level is
+ * higher than the active start level, the
+ * Framework will continue to increase the start level
+ * until the Framework has reached the specified start level,
+ * starting bundles at each
+ * start level which are persistently marked to be started as described in the
+ * <tt>Bundle.start</tt> method.
+ *
+ * At each intermediate start level value on the
+ * way to and including the target start level, the framework must:
+ * <ol>
+ * <li>Change the active start level to the intermediate start level value.
+ * <li>Start bundles at the intermediate start level in
+ * ascending order by <tt>Bundle.getBundleId</tt>.
+ * </ol>
+ * When this process completes after the specified start level is reached,
+ * the Framework will broadcast a Framework event of
+ * type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to announce it has moved to the specified
+ * start level.
+ *
+ * <p>If the specified start level is lower than the active start level, the
+ * Framework will continue to decrease the start level
+ * until the Framework has reached the specified start level
+ * stopping bundles at each
+ * start level as described in the <tt>Bundle.stop</tt> method except that their
+ * persistently recorded state indicates that they must be restarted in the
+ * future.
+ *
+ * At each intermediate start level value on the
+ * way to and including the specified start level, the framework must:
+ * <ol>
+ * <li>Stop bundles at the intermediate start level in
+ * descending order by <tt>Bundle.getBundleId</tt>.
+ * <li>Change the active start level to the intermediate start level value.
+ * </ol>
+ * When this process completes after the specified start level is reached,
+ * the Framework will broadcast a Framework event of
+ * type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to announce it has moved to the specified
+ * start level.
+ *
+ * <p>If the specified start level is equal to the active start level, then
+ * no bundles are started or stopped, however, the Framework must broadcast
+ * a Framework event of type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to
+ * announce it has finished moving to the specified start level. This
+ * event may arrive before the this method return.
+ *
+ * @param startlevel The requested start level for the Framework.
+ * @throws IllegalArgumentException If the specified start level is less than or
+ * equal to zero.
+ * @throws SecurityException If the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setStartLevel(int newSL) {
+ startLevelImpl.setStartLevel(newSL, callerBundle);
+ }
+
+ /**
+ * Return the persistent state of the specified bundle.
+ *
+ * <p>This method returns the persistent state of a bundle.
+ * The persistent state of a bundle indicates whether a bundle
+ * is persistently marked to be started when it's start level is
+ * reached.
+ *
+ * @return <tt>true</tt> if the bundle is persistently marked to be started,
+ * <tt>false</tt> if the bundle is not persistently marked to be started.
+ * @exception java.lang.IllegalArgumentException If the specified bundle has been uninstalled.
+ */
+ public boolean isBundlePersistentlyStarted(org.osgi.framework.Bundle bundle) {
+ return startLevelImpl.isBundlePersistentlyStarted(bundle);
+ }
+
+ /**
+ * Return the assigned start level value for the specified Bundle.
+ *
+ * @param bundle The target bundle.
+ * @return The start level value of the specified Bundle.
+ * @exception java.lang.IllegalArgumentException If the specified bundle has been uninstalled.
+ */
+ public int getBundleStartLevel(org.osgi.framework.Bundle bundle) {
+
+ return startLevelImpl.getBundleStartLevel(bundle);
+ }
+
+ /**
+ * Assign a start level value to the specified Bundle.
+ *
+ * <p>The specified bundle will be assigned the specified start level. The
+ * start level value assigned to the bundle will be persistently recorded
+ * by the Framework.
+ *
+ * If the new start level for the bundle is lower than or equal to the active start level of
+ * the Framework, the Framework will start the specified bundle as described
+ * in the <tt>Bundle.start</tt> method if the bundle is persistently marked
+ * to be started. The actual starting of this bundle must occur asynchronously.
+ *
+ * If the new start level for the bundle is higher than the active start level of
+ * the Framework, the Framework will stop the specified bundle as described
+ * in the <tt>Bundle.stop</tt> method except that the persistently recorded
+ * state for the bundle indicates that the bundle must be restarted in the
+ * future. The actual stopping of this bundle must occur asynchronously.
+ *
+ * @param bundle The target bundle.
+ * @param startlevel The new start level for the specified Bundle.
+ * @throws IllegalArgumentException
+ * If the specified bundle has been uninstalled or
+ * if the specified start level is less than or equal to zero, or the specified bundle is
+ * the system bundle.
+ * @throws SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setBundleStartLevel(org.osgi.framework.Bundle bundle, int newSL) {
+ startLevelImpl.setBundleStartLevel(bundle, newSL);
+
+ }
+
+
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelEvent.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelEvent.java
new file mode 100644
index 000000000..3a32068ed
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelEvent.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.EventObject;
+
+/**
+ * StartLevel Event for the OSGi framework.
+ *
+ * Event which signifies that a start level change has been requested for the framework or for a bundle.
+ *
+ */
+class StartLevelEvent extends EventObject {
+
+ public final static int INC_FW_SL = 0x00000000;
+ public final static int DEC_FW_SL = 0x00000001;
+ public final static int CHANGE_BUNDLE_SL = 0x00000002;
+
+ /**
+ * Event Type
+ */
+ private transient int type;
+
+ /**
+ * StartLevel - value depends on event type:
+ * INC_FW_SL - value is the current active startlevel to be incremented
+ * DEC_FW_SL - value is the current active startlevel to be decremented
+ * CHANGE_BUNDLE_SL - value is the new bundle startlevel
+ *
+ */
+ private transient int sl;
+
+ /**
+ * The final target startlevel - when this is reached, a FrameworkEvent.STARTLEVEL_CHANGED event must be published
+ */
+ private transient int finalSL;
+
+ /**
+ * Bundle related to the event.
+ * bugbug If the bundle is the System Bundle, then it is a change to the Framework active startlevel.
+ * Otherwise it is a change to the specified bundle.
+ */
+ private transient Bundle bundle;
+
+ /**
+ * Framework object
+ */
+ private transient Framework framework;
+
+
+
+ /**
+ * Creates a StartLevel event regarding the specified bundle.
+ *
+ * @param int type The type of startlevel event (inc or dec)
+ * @param int sl The next requested startlevel (the interim startlevel)
+ * @param int finalSL the ultimate requested startlevel we are on our way to
+ * @param bundle The affected bundle, or system bundle if it is for the framework
+ * @param framework The framework object so we can retrieve things like active startlevel
+ */
+ public StartLevelEvent(int type, int sl, int finalSL, Bundle bundle, Framework framework)
+ {
+ super(bundle);
+ this.type = type;
+ this.sl = sl;
+ this.finalSL = finalSL;
+ this.bundle = bundle;
+ this.framework = framework;
+ }
+
+ public int getType() {
+ return this.type;
+ }
+
+ public int getSL() {
+ return this.sl;
+ }
+
+ public int getFinalSL() {
+ return this.finalSL;
+ }
+
+ public Bundle getBundle() {
+ return this.bundle;
+ }
+
+ public Framework getFramework() {
+ return this.framework;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelFactory.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelFactory.java
new file mode 100644
index 000000000..28c063f97
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) The Open Services Gateway Initiative (2000, 2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative (OSGI)
+ * Specification may be subject to third party intellectual property rights,
+ * including without limitation, patent rights (such a third party may or may
+ * not be a member of OSGi). OSGi is not responsible and shall not be held
+ * responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS IS"
+ * basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT
+ * NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
+ * INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS
+ * FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY LOSS OF
+ * PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF BUSINESS,
+ * OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL, PUNITIVE OR
+ * CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS DOCUMENT OR THE
+ * INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+package org.eclipse.osgi.framework.internal.core;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Service Factory class, providing StartLevel objects
+ * to those requesting org.osgi.service.startlevel.StartLevel service.
+ */
+public class StartLevelFactory implements ServiceFactory {
+
+ /** need a reference to the framework */
+ Framework framework;
+
+ protected StartLevelFactory(Framework framework) {
+ this.framework = framework;
+ }
+
+ /**
+ * Returns a StartLevel object, created for each requesting bundle.
+ *
+ * @param callerBundle bundle, requested to get StartLevel service.
+ * @pre callerBundle!=null
+ * @param sReg ServiceRegistration of the StartLevel service
+ * @pre sReg!=null
+ * @return StartLevel object
+ */
+ public Object getService(Bundle callerBundle, ServiceRegistration sReg) {
+ return new StartLevel(callerBundle, framework);
+ }
+
+
+ /**
+ * Does nothing, as the StartLevel bundle does not keep references to StartLevel objects.
+ *
+ * @param callerBundle bundle requesting to unget StartLevel service.
+ * @param sReg ServiceRegistration of StartLevel
+ * @param obj Service object, already been got by this bundle.
+ */
+ public void ungetService(Bundle callerBundle, ServiceRegistration sReg, Object obj) {
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelImpl.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelImpl.java
new file mode 100644
index 000000000..35c67863e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelImpl.java
@@ -0,0 +1,487 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.eventmgr.EventListeners;
+import org.eclipse.osgi.framework.eventmgr.EventManager;
+import org.eclipse.osgi.framework.eventmgr.EventQueue;
+import org.eclipse.osgi.framework.eventmgr.EventSource;
+import org.osgi.framework.FrameworkEvent;
+
+/**
+ * StartLevel service implementation for the OSGi specification.
+ *
+ * Framework service which allows management of framework and bundle startlevels.
+ *
+ * If present, there will only be a single instance of this service
+ * registered in the framework.
+ *
+ */
+public class StartLevelImpl implements EventSource {
+
+ protected static Framework framework;
+ protected static EventManager eventManager;
+ protected static EventListeners startLevelListeners;
+ protected static StartLevelListener startLevelListener;
+
+ /** The framework beginning startlevel. Default is 1 */
+ protected int frameworkBeginningStartLevel = 1;
+
+ /** The initial bundle start level for newly installed bundles */
+ protected int initialBundleStartLevel = 1;
+ // default value is 1 for compatibility mode
+
+ /** The currently active framework start level */
+ private static int activeSL=0;
+
+ /** The requested framework start level */
+ private static int requestedSL=0;
+
+ /** An object used to lock the active startlevel while it is being referenced */
+ private static final Object lock = new Object();
+
+ /** This constructor is called by the Framework */
+ protected StartLevelImpl (Framework framework) {
+ StartLevelImpl.framework = framework;
+ }
+
+ protected void initialize() {
+ initialBundleStartLevel = framework.adaptor.getInitialBundleStartLevel();
+
+ // Set Framework Beginning Start Level Property
+ String value = framework.getProperty(Constants.KEY_FRAMEWORKBEGINNINGSTARTLEVEL);
+ if (value == null) {
+ value = Constants.DEFAULT_STARTLEVEL;
+ } else {
+ try {
+ if (Integer.parseInt(value) <= 0) {
+ System.err.println(Msg.formatter.getString("PROPERTIES_INVALID_FW_STARTLEVEL", Constants.DEFAULT_STARTLEVEL));
+ value = Constants.DEFAULT_STARTLEVEL;
+ }
+ } catch (NumberFormatException nfe) {
+ System.err.println(Msg.formatter.getString("PROPERTIES_INVALID_FW_STARTLEVEL", Constants.DEFAULT_STARTLEVEL));
+ value = Constants.DEFAULT_STARTLEVEL;
+ }
+ }
+ framework.setProperty(Constants.KEY_FRAMEWORKBEGINNINGSTARTLEVEL, value);
+ frameworkBeginningStartLevel = Integer.parseInt(value);
+
+
+ // create an event manager and a start level listener
+ eventManager = new EventManager("Start Level Event Dispatcher");
+
+ startLevelListener = new StartLevelListener();
+ startLevelListeners = new EventListeners();
+ startLevelListeners.addListener(startLevelListener, startLevelListener);
+ }
+
+ protected void cleanup() {
+ eventManager = null;
+ startLevelListeners.removeAllListeners();
+ startLevelListeners = null;
+ startLevelListener = null;
+ }
+
+ /**
+ * Return the initial start level value that is assigned
+ * to a Bundle when it is first installed.
+ *
+ * @return The initial start level value for Bundles.
+ * @see #setInitialBundleStartLevel
+ */
+ public int getInitialBundleStartLevel() {
+ return initialBundleStartLevel;
+ }
+
+ /**
+ * Return the initial start level used when the framework is started.
+ *
+ * @return The framework start level.
+ */
+ public int getFrameworkStartLevel() {
+ return frameworkBeginningStartLevel;
+ }
+
+ /**
+ * Set the initial start level value that is assigned
+ * to a Bundle when it is first installed.
+ *
+ * <p>The initial bundle start level will be set to the specified start level. The
+ * initial bundle start level value will be persistently recorded
+ * by the Framework.
+ *
+ * <p>When a Bundle is installed via <tt>BundleContext.installBundle</tt>,
+ * it is assigned the initial bundle start level value.
+ *
+ * <p>The default initial bundle start level value is 1
+ * unless this method has been
+ * called to assign a different initial bundle
+ * start level value.
+ *
+ * <p>This method does not change the start level values of installed
+ * bundles.
+ *
+ * @param startlevel The initial start level for newly installed bundles.
+ * @throws IllegalArgumentException If the specified start level is less than or
+ * equal to zero.
+ * @throws SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setInitialBundleStartLevel(int startlevel) {
+ framework.checkAdminPermission();
+ if (startlevel <= 0) {
+ throw new IllegalArgumentException();
+ }
+ initialBundleStartLevel = startlevel;
+ framework.adaptor.setInitialBundleStartLevel(startlevel);
+ }
+ /**
+ * Return the active start level value of the Framework.
+ *
+ * If the Framework is in the process of changing the start level
+ * this method must return the active start level if this
+ * differs from the requested start level.
+ *
+ * @return The active start level value of the Framework.
+ */
+ public int getStartLevel() {
+ return activeSL;
+ }
+ /**
+ * Modify the active start level of the Framework.
+ *
+ * <p>The Framework will move to the requested start level. This method
+ * will return immediately to the caller and the start level
+ * change will occur asynchronously on another thread.
+ *
+ * <p>If the specified start level is
+ * higher than the active start level, the
+ * Framework will continue to increase the start level
+ * until the Framework has reached the specified start level,
+ * starting bundles at each
+ * start level which are persistently marked to be started as described in the
+ * <tt>Bundle.start</tt> method.
+ *
+ * At each intermediate start level value on the
+ * way to and including the target start level, the framework must:
+ * <ol>
+ * <li>Change the active start level to the intermediate start level value.
+ * <li>Start bundles at the intermediate start level in
+ * ascending order by <tt>Bundle.getBundleId</tt>.
+ * </ol>
+ * When this process completes after the specified start level is reached,
+ * the Framework will broadcast a Framework event of
+ * type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to announce it has moved to the specified
+ * start level.
+ *
+ * <p>If the specified start level is lower than the active start level, the
+ * Framework will continue to decrease the start level
+ * until the Framework has reached the specified start level
+ * stopping bundles at each
+ * start level as described in the <tt>Bundle.stop</tt> method except that their
+ * persistently recorded state indicates that they must be restarted in the
+ * future.
+ *
+ * At each intermediate start level value on the
+ * way to and including the specified start level, the framework must:
+ * <ol>
+ * <li>Stop bundles at the intermediate start level in
+ * descending order by <tt>Bundle.getBundleId</tt>.
+ * <li>Change the active start level to the intermediate start level value.
+ * </ol>
+ * When this process completes after the specified start level is reached,
+ * the Framework will broadcast a Framework event of
+ * type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to announce it has moved to the specified
+ * start level.
+ *
+ * <p>If the specified start level is equal to the active start level, then
+ * no bundles are started or stopped, however, the Framework must broadcast
+ * a Framework event of type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to
+ * announce it has finished moving to the specified start level. This
+ * event may arrive before the this method return.
+ *
+ * @param startlevel The requested start level for the Framework.
+ * @throws IllegalArgumentException If the specified start level is less than or
+ * equal to zero.
+ * @throws SecurityException If the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setStartLevel(int newSL, org.osgi.framework.Bundle callerBundle) {
+ if (newSL <= 0) {
+ throw new IllegalArgumentException(Msg.formatter.getString("STARTLEVEL_EXCEPTION_INVALID_REQUESTED_STARTLEVEL", ""+newSL));
+ }
+ framework.checkAdminPermission();
+
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL){
+ Debug.println("StartLevelImpl: setStartLevel: "+newSL+"; callerBundle = "+callerBundle.getBundleId());
+ }
+ doSetStartLevel(newSL, false, (Bundle)callerBundle);
+
+ }
+
+ protected void setStartLevel(int newSL) {
+ setStartLevel(newSL, framework.systemBundle);
+ }
+
+ /**
+ * Internal method to allow the framework to be launched synchronously by calling the
+ * StartLevelListener worker calls directly
+ *
+ * This method does not return until all bundles that should be started are started
+ */
+ protected void launch(int startlevel) {
+
+ doSetStartLevel(startlevel, true, framework.systemBundle);
+ }
+
+ /**
+ * Internal method to shut down the framework synchronously by setting the startlevel to zero
+ * and calling the StartLevelListener worker calls directly
+ *
+ * This method does not return until all bundles are stopped and the framework is shut down.
+ */
+ protected void shutdown() {
+
+ doSetStartLevel(0, true, framework.systemBundle);
+ }
+
+ /**
+ * Internal worker method to set the startlevel
+ *
+ * @param new start level value
+ * @param boolean - true if start level change should be done synchronously, false for asynchronously
+ */
+ private void doSetStartLevel (int newSL, boolean sync, Bundle callerBundle) {
+
+ int tempSL = activeSL;
+
+ if (sync) {
+ if (newSL > tempSL) {
+ for (int i=tempSL; i < newSL; i++) {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("sync - incrementing Startlevel from "+tempSL);
+ }
+
+ tempSL++;
+
+ startLevelListener.incFWSL(new StartLevelEvent(StartLevelEvent.INC_FW_SL, i+1, newSL, callerBundle, framework));
+ }
+ } else {
+ for (int i=tempSL; i > newSL; i--) {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("sync - decrementing Startlevel from "+tempSL);
+ }
+ tempSL--;
+
+ startLevelListener.decFWSL(new StartLevelEvent(StartLevelEvent.DEC_FW_SL, i-1, newSL, callerBundle, framework));
+ }
+ }
+ } else {
+ if (newSL > tempSL) {
+ for (int i=tempSL; i < newSL; i++) {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("async - incrementing Startlevel from "+tempSL);
+ }
+ tempSL++;
+
+ // do the startlevel change asynchronously
+ issueEvent(new StartLevelEvent(StartLevelEvent.INC_FW_SL, i+1, newSL, callerBundle, framework));
+ }
+ } else {
+ for (int i=tempSL; i > newSL; i--) {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("async - decrementing Startlevel from "+tempSL);
+ }
+ tempSL--;
+
+ // do the startlevel change asynchronously
+ issueEvent(new StartLevelEvent(StartLevelEvent.DEC_FW_SL, i-1, newSL, callerBundle, framework));
+ }
+ }
+ }
+ framework.publishFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, callerBundle, null);
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("StartLevelImpl: doSetStartLevel: STARTLEVEL_CHANGED event published");
+ }
+ }
+
+ /**
+ * This method is used within the package to save the actual active startlevel value for the framework.
+ * Externally the setStartLevel method must be used.
+ *
+ * @param newSL - the new startlevel to save
+ */
+ protected void saveActiveStartLevel(int newSL) {
+ synchronized (lock) {
+ activeSL = newSL;
+ }
+ }
+
+ /**
+ * Return the persistent state of the specified bundle.
+ *
+ * <p>This method returns the persistent state of a bundle.
+ * The persistent state of a bundle indicates whether a bundle
+ * is persistently marked to be started when it's start level is
+ * reached.
+ *
+ * @return <tt>true</tt> if the bundle is persistently marked to be started,
+ * <tt>false</tt> if the bundle is not persistently marked to be started.
+ * @exception java.lang.IllegalArgumentException If the specified bundle has been uninstalled.
+ */
+ public boolean isBundlePersistentlyStarted(org.osgi.framework.Bundle bundle) {
+
+ if (bundle.getState() == Bundle.UNINSTALLED) {
+ throw new IllegalArgumentException (Msg.formatter.getString("BUNDLE_UNINSTALLED_EXCEPTION"));
+ }
+ Bundle b = (Bundle) bundle;
+ int status = b.bundledata.getStatus();
+ return((status & org.eclipse.osgi.framework.internal.core.Constants.BUNDLE_STARTED) == Constants.BUNDLE_STARTED);
+ }
+ /**
+ * Return the assigned start level value for the specified Bundle.
+ *
+ * @param bundle The target bundle.
+ * @return The start level value of the specified Bundle.
+ * @exception java.lang.IllegalArgumentException If the specified bundle has been uninstalled.
+ */
+ public int getBundleStartLevel(org.osgi.framework.Bundle bundle) {
+
+ if (bundle.getState() == Bundle.UNINSTALLED) {
+ throw new IllegalArgumentException (Msg.formatter.getString("BUNDLE_UNINSTALLED_EXCEPTION"));
+ }
+ return((Bundle)bundle).startLevel;
+ }
+
+ /**
+ * Assign a start level value to the specified Bundle.
+ *
+ * <p>The specified bundle will be assigned the specified start level. The
+ * start level value assigned to the bundle will be persistently recorded
+ * by the Framework.
+ *
+ * If the new start level for the bundle is lower than or equal to the active start level of
+ * the Framework, the Framework will start the specified bundle as described
+ * in the <tt>Bundle.start</tt> method if the bundle is persistently marked
+ * to be started. The actual starting of this bundle must occur asynchronously.
+ *
+ * If the new start level for the bundle is higher than the active start level of
+ * the Framework, the Framework will stop the specified bundle as described
+ * in the <tt>Bundle.stop</tt> method except that the persistently recorded
+ * state for the bundle indicates that the bundle must be restarted in the
+ * future. The actual stopping of this bundle must occur asynchronously.
+ *
+ * @param bundle The target bundle.
+ * @param startlevel The new start level for the specified Bundle.
+ * @throws IllegalArgumentException
+ * If the specified bundle has been uninstalled or
+ * if the specified start level is less than or equal to zero, or the specified bundle is
+ * the system bundle.
+ * @throws SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setBundleStartLevel(org.osgi.framework.Bundle bundle, int newSL) {
+
+ String exceptionText="";
+ if (bundle.getBundleId()==0) { // system bundle has id=0
+ exceptionText = Msg.formatter.getString("STARTLEVEL_CANT_CHANGE_SYSTEMBUNDLE_STARTLEVEL");
+ } else if (bundle.getState() == Bundle.UNINSTALLED) {
+ exceptionText = Msg.formatter.getString("BUNDLE_UNINSTALLED_EXCEPTION");
+ } else if (newSL <= 0) {
+ exceptionText = Msg.formatter.getString("STARTLEVEL_EXCEPTION_INVALID_REQUESTED_STARTLEVEL", ""+newSL);
+ }
+ if (exceptionText.length()>0) {
+ throw new IllegalArgumentException(exceptionText);
+ }
+
+ try {
+ // if the bundle's startlevel is not already at the requested startlevel
+ if (newSL != ((org.eclipse.osgi.framework.internal.core.Bundle)bundle).startLevel) {
+ Bundle b = (Bundle) bundle;
+ b.bundledata.setStartLevel(newSL);
+ b.bundledata.save();
+ ((org.eclipse.osgi.framework.internal.core.Bundle)bundle).startLevel = newSL;
+
+ framework.checkAdminPermission();
+
+ // handle starting or stopping the bundle asynchronously
+ issueEvent(new StartLevelEvent(StartLevelEvent.CHANGE_BUNDLE_SL, newSL, newSL, (Bundle)bundle, framework));
+ }
+ } catch (IOException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
+ }
+
+ }
+
+ /**
+ * This method sends the StartLevelEvent to the EventManager for dispatching
+ *
+ * @param sle The event to be queued to the Event Manager
+ */
+ private void issueEvent(StartLevelEvent sle) {
+
+ /* queue to hold set of listeners */
+ EventQueue queue = new EventQueue(eventManager);
+
+ /* add set of UserAdminListeners to queue */
+ queue.queueListeners(startLevelListeners, this);
+
+ /* dispatch event to set of listeners */
+ queue.dispatchEventAsynchronous(sle.getType(), sle);
+ }
+
+ /**
+ * This method is the call back that is called once for each listener.
+ * This method must cast the EventListener object to the appropriate listener
+ * class for the event type and call the appropriate listener method.
+ *
+ * @param listener This listener must be cast to the appropriate listener
+ * class for the events created by this source and the appropriate listener method
+ * must then be called.
+ * @param listenerObject This is the optional object that was passed to
+ * ListenerList.addListener when the listener was added to the ListenerList.
+ * @param eventAction This value was passed to the EventQueue object via one of its
+ * dispatchEvent* method calls. It can provide information (such
+ * as which listener method to call) so that this method
+ * can complete the delivery of the event to the listener.
+ * @param eventObject This object was passed to the EventQueue object via one of its
+ * dispatchEvent* method calls. This object was created by the event source and
+ * is passed to this method. It should contain all the necessary information (such
+ * as what event object to pass) so that this method
+ * can complete the delivery of the event to the listener.
+ */
+ public void dispatchEvent(Object listener, Object listenerObject, int eventAction, Object eventObject) {
+ StartLevelListener sll = (StartLevelListener)listener;
+ switch (eventAction) {
+ case StartLevelEvent.INC_FW_SL:
+ sll.incFWSL((StartLevelEvent)eventObject);
+ break;
+ case StartLevelEvent.DEC_FW_SL:
+ sll.decFWSL((StartLevelEvent)eventObject);
+ break;
+ case StartLevelEvent.CHANGE_BUNDLE_SL:
+ sll.setBundleSL((StartLevelEvent)eventObject);
+ break;
+ }
+ }
+
+
+
+
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelListener.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelListener.java
new file mode 100644
index 000000000..ac49185be
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/StartLevelListener.java
@@ -0,0 +1,384 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.EventListener;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.util.ManifestElement;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+
+/**
+ * A <tt>StartLevel</tt> Event listener.
+ *
+ * <p><tt>StartLevelListener</tt> listens for StartLevelEvents and handles them by initiating
+ * changes in bundle or framework startlevels.
+ * @see StartLevelEvent
+ */
+class StartLevelListener implements EventListener {
+ /** bundle started first as the logservice bundle */
+ protected Bundle logservice;
+
+ /** lock to synchronize framework startlevel changes */
+ private Object lock = new Object();
+
+ private Framework framework;
+
+ protected StartLevelListener() {
+ super();
+ }
+
+
+ /**
+ * Increment the active startlevel by one
+ */
+ protected void incFWSL(StartLevelEvent startLevelEvent) {
+ synchronized (lock) {
+ framework = startLevelEvent.getFramework();
+ int activeSL = startLevelEvent.getSL();
+
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: incFWSL: saving activeSL of " + activeSL);
+ }
+
+ framework.startLevelImpl.saveActiveStartLevel(activeSL);
+
+ Bundle [] launch;
+ BundleRepository bundles = framework.bundles;
+
+ launch = getInstalledBundles(bundles);
+
+ if (activeSL==1) { // framework was not active
+
+ /* Load all installed bundles */
+ loadInstalledBundles(launch);
+
+ /* attempt to resolve all bundles */
+ framework.packageAdmin.setResolvedBundles();
+
+ /* Resume all bundles */
+ resumeBundles(launch, true);
+
+
+ /* publish the framework started event */
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL)
+ {
+ Debug.println("SLL: Framework started");
+ }
+
+ framework.publishFrameworkEvent(FrameworkEvent.STARTED, startLevelEvent.getBundle(), null);
+
+ } else {
+ // incrementing an already active framework
+ resumeBundles(launch, false);
+ }
+ }
+ }
+
+ /**
+ * Build an array of all installed bundles to be launch.
+ * The returned array is sorted by increasing startlevel/id order.
+ * @param bundles - the bundles installed in the framework
+ * @return A sorted array of bundles
+ */
+ private Bundle[] getInstalledBundles(BundleRepository bundles) {
+
+ /* make copy of bundles vector in case it is modified during launch */
+ Bundle[] installedBundles;
+
+ synchronized (bundles)
+ {
+ List allBundles = bundles.getBundles();
+ installedBundles = new Bundle[allBundles.size()];
+ allBundles.toArray(installedBundles);
+
+ /* sort bundle array in ascending startlevel / bundle id order
+ * so that bundles are started in ascending order.
+ */
+ Util.sort(installedBundles, 0, installedBundles.length);
+ }
+
+ return installedBundles;
+ }
+
+ private void loadInstalledBundles(Bundle[] installedBundles) {
+ logservice = null;
+
+ for (int i = 0; i < installedBundles.length; i++)
+ {
+ Bundle bundle = installedBundles[i];
+
+ try
+ {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL)
+ {
+ Debug.println("SLL: Trying to load bundle "+bundle);
+ }
+
+ bundle.load();
+
+ // TODO Do we really need to do this here? This requries the loading of the manifest which
+ // otherwise is not needed. Removing for now.
+ /* Locate logservice bundle */
+// if (logservice == null)
+// {
+// String exportSpec = (String)bundle.getBundleData().getManifest().get(Constants.EXPORT_SERVICE);
+// // TODO what parsing should go here?
+// ManifestElement[] exportedServices = ManifestElement.parseBasicCommaSeparation(Constants.EXPORT_SERVICE, exportSpec);
+// if (exportedServices != null)
+// {
+// for (int j=0; j<exportedServices.length; j++){
+// if(exportedServices[j].getValue().equals(Constants.OSGI_LOGSERVICE_NAME)){
+// int status = bundle.bundledata.getStatus();
+// if ((status & Constants.BUNDLE_STARTED) != 0)
+// {
+// logservice = bundle;
+// break;
+// }
+// }
+// }
+// }
+// }
+ } catch (BundleException be)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL)
+ {
+ Debug.println("SLL: Bundle load exception: "+be.getMessage());
+ Debug.printStackTrace(be.getNestedException());
+ }
+
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, be);
+ }
+ }
+ }
+
+ private void resumeBundles(Bundle[] launch, boolean launchingFW) {
+ BundleException sbe = null;
+ if (launchingFW) {
+ /* Start the system bundle */
+
+ try
+ {
+ framework.systemBundle.context.start();
+ } catch (BundleException be)
+ {
+ // TODO: We may have to do something more drastic here if the SystemBundle did not start.
+ sbe = be;
+ }
+
+
+ }
+ /* Resume all bundles that were previously started and whose startlevel is <= the active startlevel */
+
+ int fwsl = framework.startLevelImpl.getStartLevel();
+ for (int i = 0; i < launch.length; i++)
+ {
+ int bsl = launch[i].startLevel;
+
+ if (bsl < fwsl) {
+ // skip bundles who should have already been started
+ continue;
+ }
+ else if (bsl==fwsl) {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: Active sl = "+fwsl +
+ "; Bundle "+launch[i].getBundleId()+" sl = "+bsl);
+ }
+ framework.resumeBundle(launch[i]);
+ } else {
+ // can stop resuming bundles since any remaining bundles have a greater startlevel than the framework active startlevel
+ break;
+ }
+ }
+
+ if (sbe == null)
+ {
+ framework.systemBundle.state = Bundle.ACTIVE;
+ } else
+ {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL)
+ {
+ Debug.println("SLL: Bundle resume exception: "+sbe.getMessage());
+ Debug.printStackTrace(sbe.getNestedException());
+ }
+
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, sbe);
+ }
+ }
+
+ /**
+ * Decrement the active startlevel by one
+ * The startlevel value passed within the startLevelEvent is the
+ * new desired active framework startlevel
+ */
+ protected void decFWSL(StartLevelEvent startLevelEvent) {
+ synchronized (lock) {
+ framework = startLevelEvent.getFramework();
+ int activeSL = startLevelEvent.getSL();
+
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: decFWSL: saving activeSL of " + activeSL);
+ }
+
+ framework.startLevelImpl.saveActiveStartLevel(activeSL);
+
+ BundleRepository bundles = framework.bundles;
+
+ if (activeSL == 0) { // stopping the framework
+
+
+ framework.systemBundle.state = Bundle.STOPPING;
+
+ /* stop all running bundles */
+
+ suspendAllBundles(bundles);
+
+ unloadAllBundles(bundles);
+
+ } else {
+ // just decrementing the active startlevel - framework is not shutting down
+ synchronized (bundles) {
+ // get the list of installed bundles, sorted by startlevel
+ Bundle [] shutdown = this.getInstalledBundles(bundles);
+ for (int i = shutdown.length - 1; i >= 0; i--) {
+ int bsl = shutdown[i].startLevel;
+ if (bsl>activeSL+1) {
+ // don't need to mess with bundles with startlevel > the previous active - they should
+ // already have been stopped
+ continue;
+ } else if (bsl<=activeSL) {
+ // don't need to keep going - we've stopped all we're going to stop
+ break;
+ } else if (shutdown[i].isActive() ) {
+ // if bundle is active or starting, then stop the bundle
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: stopping bundle " + shutdown[i].getBundleId());
+ }
+ framework.suspendBundle(shutdown[i],false);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void suspendAllBundles(BundleRepository bundles) {
+ synchronized (bundles) {
+ boolean changed;
+ do
+ {
+ changed = false;
+
+ Bundle[] shutdown = this.getInstalledBundles(bundles);
+
+ // shutdown all running bundles
+ for (int i = shutdown.length - 1; i >= 0; i--)
+ {
+ Bundle bundle = shutdown[i];
+
+
+ if (framework.suspendBundle(bundle, false))
+ {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: stopped bundle " + bundle.getBundleId());
+ }
+ changed = true;
+ }
+
+ }
+ }
+ while (changed);
+
+ try
+ {
+ framework.systemBundle.context.stop();
+ } catch (BundleException sbe)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL)
+ {
+ Debug.println("SLL: Bundle suspend exception: "+sbe.getMessage());
+ Debug.printStackTrace(sbe.getNestedException());
+ }
+
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, sbe);
+ }
+
+ framework.systemBundle.state = Bundle.STARTING;
+ }
+ }
+
+ private void unloadAllBundles(BundleRepository bundles) {
+ synchronized (bundles)
+ {
+ /* unload all installed bundles */
+ List allBundles = bundles.getBundles();
+ int size = allBundles.size();
+
+ for (int i = 0; i < size; i++)
+ {
+ Bundle bundle = (Bundle)allBundles.get(i);
+
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL)
+ {
+ Debug.println("SLL: Trying to unload bundle "+bundle);
+ }
+
+ try {
+ bundle.refresh();
+ } catch (BundleException e) {
+ // do nothing.
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the bundle's startlevel to the new value
+ * This may cause the bundle to start or stop based on the active framework startlevel
+ */
+ protected void setBundleSL(StartLevelEvent startLevelEvent) {
+ synchronized (lock) {
+ framework = startLevelEvent.getFramework();
+ int activeSL = framework.startLevelImpl.getStartLevel();
+ int newSL = startLevelEvent.getSL();
+ Bundle bundle = startLevelEvent.getBundle();
+
+ int bundlestate = bundle.getState();
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.print("SLL: bundle active="+bundle.isActive());
+ Debug.print("; newSL = " + newSL);
+ Debug.println("; activeSL = " + activeSL);
+ }
+
+ if (bundle.isActive() && (newSL>activeSL)) {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: stopping bundle "+bundle.getBundleId());
+ }
+ framework.suspendBundle(bundle, false);
+ } else {
+ if ( !bundle.isActive() && (newSL<=activeSL) ) {
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: starting bundle "+bundle.getBundleId());
+ }
+ framework.resumeBundle(bundle);
+ }
+ }
+ if (Debug.DEBUG && Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: Bundle Startlevel set to "+newSL);
+ }
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundle.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundle.java
new file mode 100644
index 000000000..fefdc7a0e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundle.java
@@ -0,0 +1,329 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.Permission;
+import java.util.Hashtable;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.util.Headers;
+import org.osgi.framework.BundleException;
+
+/**
+ * This class subclasses Bundle to provide a system Bundle
+ * so that the framework can be represented as a bundle and
+ * can access the services provided by other bundles.
+ */
+
+public class SystemBundle extends BundleHost
+{
+
+ /**
+ * SystemBundle object constructor.
+ *
+ * @param manifest Bundle's manifest
+ * @param framework Framework this bundle is running in
+ */
+ protected SystemBundle(Headers manifest, Framework framework) throws BundleException
+ {
+ this( new SystemBundleData(manifest), Constants.SYSTEM_BUNDLE_LOCATION, framework);
+ }
+
+ /**
+ * Private SystemBundle object constructor.
+ * This method creates the SystemBundle and its BundleContext.
+ * The SystemBundle's state is set to STARTING.
+ * This method is called when the framework is constructed.
+ *
+ * @param bundledata the bundle's BundleData
+ * @param location identity string for the bundle
+ * @param framework Framework this bundle is running in
+ */
+ private SystemBundle(BundleData bundledata, String location, Framework framework) throws BundleException
+ {
+ super(bundledata, location, framework, 0); // startlevel=0 means framework stopped
+ state = STARTING;
+ context = createContext();
+ }
+
+ public BundleLoader getBundleLoader() {
+ if (loader == null) {
+ synchronized (this) {
+ if (loader == null)
+ try {
+ loader = new SystemBundleLoader(this, getBundleDescription());
+ } catch (BundleException e) {
+ // TODO log something here
+ e.printStackTrace();
+ return null;
+ }
+ }
+ }
+ return loader;
+ }
+ /**
+ * Load the bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ */
+ protected void load() throws BundleException
+ {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null)
+ {
+ domain = getClass().getProtectionDomain();
+ }
+ }
+
+ /**
+ * Reload from a new bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ * @param newBundle
+ * @return false
+ */
+ protected boolean reload(Bundle newBundle) throws BundleException
+ {
+ return(false);
+ }
+
+ /**
+ * Refresh the bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ */
+ protected void refresh() throws BundleException
+ {
+ }
+
+ /**
+ * Unload the bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ * @return false
+ */
+ protected boolean unload()
+ {
+ return(false);
+ }
+
+ /**
+ * Close the the Bundle's file.
+ * This method closes the BundleContext for the SystemBundle
+ * and sets the SystemBundle's state to UNINSTALLED.
+ *
+ */
+ protected void close()
+ {
+ context.close();
+ context = null;
+
+ state = UNINSTALLED;
+ }
+
+ /**
+ * This method loads a class from the bundle.
+ *
+ * @param name the name of the desired Class.
+ * @param checkPermission indicates whether a permission check should be done.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException if the class definition was not found.
+ */
+ protected Class loadClass(String name, boolean checkPermission) throws ClassNotFoundException
+ {
+ if (checkPermission) {
+ framework.checkAdminPermission();
+ checkValid();
+ }
+ return(Class.forName(name));
+ }
+
+ /**
+ * Find the specified resource in this bundle.
+ * This methods returns null for the system bundle.
+ */
+ public URL getResource(String name)
+ {
+ return(null);
+ }
+
+ /**
+ * Indicate SystemBundle is resolved.
+ *
+ */
+ protected boolean isUnresolved()
+ {
+ return(false);
+ }
+
+ /**
+ * Start this bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ */
+ public void start() throws BundleException
+ {
+ framework.checkAdminPermission();
+ }
+
+ /**
+ * Start the SystemBundle.
+ * This method launches the framework.
+ *
+ */
+ protected void resume() throws BundleException
+ {
+ /* initialize the startlevel service */
+ framework.startLevelImpl.initialize();
+
+ framework.startLevelImpl.launch(framework.startLevelImpl.getFrameworkStartLevel());
+
+ }
+
+ /**
+ * Stop the framework.
+ * This method spawns a thread which will call framework.shutdown.
+ *
+ */
+ public void stop() throws BundleException
+ {
+ framework.checkAdminPermission();
+
+ if (state == ACTIVE)
+ {
+ Thread shutdown = framework.createThread(new Runnable()
+ {
+ public void run()
+ {
+ framework.shutdown();
+ }
+ }, "System Bundle Shutdown");
+
+ shutdown.start();
+ }
+ }
+
+ /**
+ * Stop the SystemBundle.
+ * This method shuts down the framework.
+ *
+ */
+ protected void suspend() throws BundleException
+ {
+
+ framework.startLevelImpl.shutdown();
+ framework.startLevelImpl.cleanup();
+
+ /* clean up the exporting loaders */
+ framework.packageAdmin.cleanup();
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("->Framework shutdown");
+ }
+ }
+
+ /**
+ * Update this bundle.
+ * This method spawns a thread which will call framework.shutdown
+ * followed by framework.launch.
+ *
+ */
+ public void update() throws BundleException
+ {
+ framework.checkAdminPermission();
+
+ if (state == ACTIVE)
+ {
+ Thread restart = framework.createThread(new Runnable()
+ {
+ public void run()
+ {
+ framework.shutdown();
+
+ framework.launch();
+ }
+ }, "System Bundle Update");
+
+ restart.start();
+ }
+ }
+
+ /**
+ * Update this bundle from an InputStream.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ * @param in The InputStream from which to read the new bundle.
+ */
+ public void update(InputStream in) throws BundleException
+ {
+ update();
+
+ try
+ {
+ in.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ /**
+ * Uninstall this bundle.
+ * This methods overrides the Bundle method and throws an exception.
+ *
+ */
+ public void uninstall() throws BundleException
+ {
+ framework.checkAdminPermission();
+
+ throw new BundleException(Msg.formatter.getString("BUNDLE_SYSTEMBUNDLE_UNINSTALL_EXCEPTION"));
+ }
+
+ /**
+ * Determine whether the bundle has the requested
+ * permission.
+ * This methods overrides the Bundle method and returns <code>true</code>.
+ *
+ * @param permission The requested permission.
+ * @return <code>true</code>
+ */
+ public boolean hasPermission(Object permission)
+ {
+ if (domain != null)
+ {
+ if (permission instanceof Permission)
+ {
+ return domain.implies((Permission)permission);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * No work to do for the SystemBundle.
+ *
+ * @param unresolvedPackages A list of the package which have been unresolved
+ * as a result of a packageRefresh
+ */
+ protected void unresolvePermissions(Hashtable unresolvedPackages)
+ {
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleActivator.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleActivator.java
new file mode 100644
index 000000000..d33de4b65
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleActivator.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.eclipse.osgi.framework.debug.DebugOptions;
+import org.osgi.framework.*;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * This class activates the System Bundle.
+ */
+
+public class SystemBundleActivator implements BundleActivator
+{
+ protected BundleContext context;
+ protected SystemBundle bundle;
+ protected Framework framework;
+ protected ServiceRegistration packageAdmin;
+ protected ServiceRegistration permissionAdmin;
+ protected ServiceRegistration startLevel;
+ protected ServiceRegistration debugOptions;
+
+ public SystemBundleActivator()
+ {
+ }
+
+ public void start(BundleContext context) throws Exception
+ {
+ this.context = context;
+ bundle = (SystemBundle)context.getBundle();
+ framework = bundle.framework;
+
+ if (framework.packageAdmin != null)
+ {
+ packageAdmin = register(Constants.OSGI_PACKAGEADMIN_NAME, framework.packageAdmin);
+ }
+
+ if (framework.permissionAdmin != null)
+ {
+ permissionAdmin = register(Constants.OSGI_PERMISSIONADMIN_NAME, framework.permissionAdmin);
+ }
+
+ if (framework.startLevelImpl != null)
+ {
+ startLevel = register(Constants.OSGI_STARTLEVEL_NAME, framework.startLevelFactory);
+ }
+
+ DebugOptions dbgOptions = null;
+ if ((dbgOptions = DebugOptions.getDefault()) != null){
+ debugOptions = register(
+ org.eclipse.osgi.service.environment.DebugOptions.class.getName(),
+ dbgOptions);
+ }
+ // TODO we need a way to configure in framework services without modifying
+ // this class.
+
+
+ // Always call the adaptor.frameworkStart() at the end of this method.
+ framework.adaptor.frameworkStart(context);
+ }
+
+ public void stop(BundleContext context) throws Exception
+ {
+ // Always call the adaptor.frameworkStop() at the begining of this method.
+ framework.adaptor.frameworkStop(context);
+
+ if (packageAdmin != null)
+ {
+ packageAdmin.unregister();
+ }
+
+ if (permissionAdmin != null)
+ {
+ permissionAdmin.unregister();
+ }
+
+ if (startLevel != null)
+ {
+ startLevel.unregister();
+ }
+
+ if (debugOptions != null)
+ {
+ debugOptions.unregister();
+ }
+
+ framework = null;
+ bundle = null;
+ this.context = null;
+ }
+
+ /**
+ * Register a service object.
+ *
+ */
+ protected ServiceRegistration register(String name, Object service)
+ {
+ Hashtable properties = new Hashtable(7);
+
+ Dictionary headers = bundle.getHeaders();
+
+ properties.put(Constants.SERVICE_VENDOR, headers.get(Constants.BUNDLE_VENDOR));
+
+ properties.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+
+ properties.put(Constants.SERVICE_PID, bundle.getBundleId() + "." + service.getClass().getName());
+
+ return context.registerService(name, service, properties);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleData.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleData.java
new file mode 100644
index 000000000..049d18e55
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleData.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.util.Headers;
+
+
+public class SystemBundleData implements BundleData {
+
+ Headers manifest;
+ Version version;
+
+ public SystemBundleData(Headers manifest){
+ this.manifest = manifest;
+ String sVersion=(String)manifest.get(Constants.BUNDLE_VERSION);
+ if (sVersion != null) {
+ version = new Version(sVersion);
+ }
+ }
+
+ public BundleClassLoader createClassLoader(
+ ClassLoaderDelegate delegate,
+ ProtectionDomain domain,
+ String[] bundleclasspath) {
+ return null;
+ }
+
+ public URL getEntry(String path) {
+ return null;
+ }
+
+ public Enumeration getEntryPaths(String path) {
+ return null;
+ }
+
+ public String findLibrary(String libname) {
+ return null;
+ }
+
+ public void installNativeCode(String[] nativepaths) {
+ }
+
+ public File getDataFile(String path) {
+ return null;
+ }
+
+ public Dictionary getManifest() {
+ return manifest;
+ }
+
+ public long getBundleID() {
+ return 0;
+ }
+
+ public String getLocation(){
+ return Constants.SYSTEM_BUNDLE_LOCATION;
+ }
+
+ public Dictionary getHeaders() {
+ return manifest;
+ }
+
+ public void close() {
+ }
+
+ public void open(){
+ }
+
+ public boolean isFragment() {
+ return false;
+ }
+
+ public void setBundle(org.osgi.framework.Bundle bundle){
+ // do nothing.
+ }
+
+
+ // The system bundle does not have any meta data capabilities so the following methods just
+ // do nothing or return dummy values.
+ public int getStartLevel() {
+ return 0;
+ }
+
+ public int getStatus() {
+ return 0;
+ }
+
+ public void setStartLevel(int value) {
+ }
+
+ public void setStatus(int value) {
+ }
+
+ public void save() throws IOException {
+ }
+
+ public String getUniqueId() {
+ return (String)getHeaders().get(Constants.BUNDLE_GLOBALNAME);
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+ public String getClassPath() {
+ return (String)getHeaders().get(Constants.BUNDLE_CLASSPATH);
+ }
+ public String getActivator() {
+ return (String)getHeaders().get(Constants.BUNDLE_ACTIVATOR);
+ }
+ public String getDynamicImports(){
+ return (String)getHeaders().get(Constants.DYNAMICIMPORT_PACKAGE);
+ }
+ public String getExecutionEnvironment(){
+ return (String)getHeaders().get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleLoader.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleLoader.java
new file mode 100644
index 000000000..1f3a8fb47
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/SystemBundleLoader.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.osgi.framework.BundleException;
+
+/**
+ * The System Bundle's BundleLoader. This BundleLoader is used by ImportClassLoaders
+ * to load a resource that is exported by the System Bundle.
+ */
+public class SystemBundleLoader extends BundleLoader {
+
+ ClassLoader classLoader;
+
+ /**
+ * List of packages that the system bundle exports.
+ */
+ static protected String[] systemPackages = null;
+
+ protected synchronized static void clearSystemPackages() {
+ systemPackages = null;
+ }
+ protected static void setSystemPackages(String[] value) {
+ systemPackages = value;
+ }
+
+ protected static String[] getSystemPackages() {
+ return systemPackages;
+ }
+
+ /**
+ * @param bundle The system bundle.
+ * @param bundledata The system bundle BundleData
+ * @param manifest The system bundle manifest.
+ * @throws BundleException On any error.
+ */
+ protected SystemBundleLoader(BundleHost bundle, BundleDescription description) throws BundleException {
+ super(bundle, description);
+ this.classLoader = getClass().getClassLoader();
+ }
+
+ /**
+ * The ClassLoader that loads OSGi framework classes is used to find the class.
+ */
+ public Class findClass(String name) throws ClassNotFoundException {
+ return classLoader.loadClass(name);
+ }
+
+ /**
+ * This method will always return null.
+ */
+ public String findLibrary(String name) {
+ return null;
+ }
+
+ /**
+ * The ClassLoader that loads OSGi framework classes is used to find the class.
+ */
+ protected Class findLocalClass(String name) {
+ Class clazz = null;
+ try {
+ clazz = classLoader.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ // Do nothing, will return null
+ }
+ return clazz;
+ }
+
+ /**
+ * The ClassLoader that loads OSGi framework classes is used to find the resource.
+ */
+ protected URL findLocalResource(String name) {
+ return classLoader.getResource(name);
+ }
+
+ /**
+ * The ClassLoader that loads OSGi framework classes is used to find the resource.
+ */
+ protected Enumeration findLocalResources(String name) {
+ try {
+ return classLoader.getResources(name);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * The ClassLoader that loads OSGi framework classes is used to find the resource.
+ */
+ public URL findResource(String name) {
+ return classLoader.getResource(name);
+ }
+
+ /**
+ * The ClassLoader that loads OSGi framework classes is used to find the resource.
+ */
+ public Enumeration findResources(String name) throws IOException {
+ return classLoader.getResources(name);
+ }
+
+ /**
+ * Do nothing on a close.
+ */
+ protected void close() {
+ // Do nothing.
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/UnresolvedPermission.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/UnresolvedPermission.java
new file mode 100644
index 000000000..36e51edae
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/UnresolvedPermission.java
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.lang.reflect.Constructor;
+import java.security.Permission;
+import java.security.PermissionCollection;
+
+import org.eclipse.osgi.framework.debug.Debug;
+
+/**
+ * Holds permissions which are of an unknown type when a
+ * policy file is read
+ *
+ */
+final class UnresolvedPermission extends Permission
+{
+ /**
+ * The type of permission this will be
+ */
+ private String type;
+ /**
+ * the action string
+ */
+ private String actions;
+ /**
+ * name of the permission
+ */
+ private String name;
+
+ private static Class[] constructorArgs;
+
+ static
+ {
+ Class string = String.class;
+ constructorArgs = new Class[]{string, string};
+ }
+
+ /**
+ * Constructs a new instance of this class with its
+ * type, name, and certificates set to the arguments
+ * by definition, actions are ignored
+ *
+ * @param String type class of permission object
+ * @param String name of the permission
+ * @param String actions
+ * @param Certificate[] certs
+ */
+ UnresolvedPermission(String type, String name, String actions)
+ {
+ super(type);
+ this.name = name;
+ this.type = type;
+ this.actions = actions;
+ }
+
+ /**
+ * Compares the argument to the receiver, and answers true
+ * if they represent the <em>same</em> object using a class
+ * specific comparison. In this case, the receiver and the
+ * object must have the same class, permission name,
+ * actions, and certificates
+ *
+ * @param obj the object to compare with this object
+ * @return <code>true</code>
+ * if the object is the same as this object
+ * <code>false</code>
+ * if it is different from this object
+ * @see hashCode
+ */
+ public boolean equals (Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (!(obj instanceof UnresolvedPermission))
+ {
+ return false;
+ }
+
+ UnresolvedPermission perm = (UnresolvedPermission)obj;
+
+ return type.equals(perm.type) && name.equals(perm.name) &&
+ actions.equals(perm.actions);
+ }
+
+ /**
+ * Indicates whether the argument permission is implied
+ * by the receiver. UnresolvedPermission objects imply
+ * nothing because nothing is known about them yet.
+ *
+ * @return boolean always replies false
+ * @param p java.security.Permission
+ * the permission to check
+ */
+ public boolean implies(Permission p)
+ {
+ return false;
+ }
+
+ /**
+ * Answers a new PermissionCollection for holding permissions
+ * of this class. Answer null if any permission collection can
+ * be used.
+ *
+ * @return a new PermissionCollection or null
+ *
+ * @see BasicPermissionCollection
+ */
+ public PermissionCollection newPermissionCollection()
+ {
+ return new UnresolvedPermissionCollection();
+ }
+
+ /**
+ * Answers the actions associated with the receiver.
+ * Since UnresolvedPermission objects have no actions, answer
+ * the empty string.
+ *
+ * @return String
+ * the actions associated with the receiver.
+ */
+ public String getActions()
+ {
+ return "";
+ }
+
+ /**
+ * Answers an integer hash code for the receiver. Any two
+ * objects which answer <code>true</code> when passed to
+ * <code>equals</code> must answer the same value for this
+ * method.
+ *
+ * @return int
+ * the receiver's hash
+ *
+ * @see equals
+ */
+ public int hashCode()
+ {
+ return toString().hashCode();
+ }
+
+ /**
+ * Answers a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return String
+ * a printable representation for the receiver.
+ */
+ public String toString()
+ {
+ return "(unresolved " + type + " " + name + " " + actions +")";
+ }
+
+ Permission resolve(Class clazz)
+ {
+ if (clazz.getName().equals(type))
+ {
+ try
+ {
+ Constructor constructor = clazz.getConstructor(constructorArgs);
+
+ Permission permission = (Permission)constructor.newInstance(new Object[] {name, actions});
+
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Resolved "+this);
+ }
+
+ return permission;
+ }
+ catch (Exception e)
+ {
+ /* Ignore any error trying to resolve the permission */
+ if (Debug.DEBUG && Debug.DEBUG_SECURITY)
+ {
+ Debug.println("Exception trying to resolve permission");
+ Debug.printStackTrace(e);
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/UnresolvedPermissionCollection.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/UnresolvedPermissionCollection.java
new file mode 100644
index 000000000..9ccd3fd51
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/UnresolvedPermissionCollection.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * Holds permissions which are of an unknown type when a
+ * policy file is read
+ *
+ */
+final class UnresolvedPermissionCollection extends PermissionCollection
+{
+ /** hash of permission class names => Vectors of UnresolvedPermissions for that class */
+ Hashtable permissions = new Hashtable(8);
+
+ UnresolvedPermissionCollection()
+ {
+ super();
+ }
+
+ public void add(Permission permission)
+ {
+ if (isReadOnly())
+ {
+ throw new IllegalStateException();
+ }
+
+ String name = permission.getName();
+
+ Vector elements;
+
+ synchronized (permissions)
+ {
+ elements = (Vector)permissions.get(name);
+
+ if (elements == null)
+ {
+ elements = new Vector(10, 10);
+
+ permissions.put(name, elements);
+ }
+ }
+
+ elements.addElement(permission);
+ }
+
+ public Enumeration elements()
+ {
+ return (new Enumeration()
+ {
+ Enumeration vEnum, pEnum = permissions.elements();
+ Object next = findNext();
+ private Object findNext()
+ {
+ if (vEnum != null)
+ {
+ if (vEnum.hasMoreElements())
+ return (vEnum.nextElement());
+ }
+ if (!pEnum.hasMoreElements()) return (null);
+ vEnum = ((Vector)pEnum.nextElement()).elements();
+ return (vEnum.nextElement());
+ }
+ public boolean hasMoreElements()
+ {
+ return (next != null);
+ }
+ public Object nextElement()
+ {
+ Object result = next;
+ next = findNext();
+ return (result);
+ }
+ });
+ }
+
+ public boolean implies(Permission permission)
+ {
+ return false;
+ }
+
+ Vector getPermissions(String name)
+ {
+ return (Vector)permissions.get(name);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Util.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Util.java
new file mode 100644
index 000000000..e9a50ad8a
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Util.java
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+/**
+ * This class contains utility functions.
+ */
+public class Util
+{
+ /**
+ * Performs a quicksort of the given objects
+ * by their string representation in ascending order.
+ * <p>
+ *
+ * @param array The array of objects to sort
+ */
+ public static void sort(Object[] array)
+ {
+ qsort(array, 0, array.length - 1);
+ }
+
+ /**
+ * Sorts the array of objects by their string representation
+ * in ascending order.
+ * <p>
+ * This is a version of C.A.R Hoare's Quick Sort algorithm.
+ *
+ * @param array the array of objects to sort
+ * @param start the start index to begin sorting
+ * @param stop the end index to stop sorting
+ *
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end >= array.length</code>
+ */
+ public static void qsort(Object[] array, int start, int stop)
+ {
+ if (start >= stop)
+ return;
+
+ int left = start; // left index
+ int right = stop; // right index
+ Object temp; // for swapping
+
+ // arbitrarily establish a partition element as the midpoint of the array
+ String mid = String.valueOf(array[(start + stop) / 2]);
+
+ // loop through the array until indices cross
+ while (left <= right)
+ {
+ // find the first element that is smaller than the partition element from the left
+ while ((left < stop) && (String.valueOf(array[left]).compareTo(mid) < 0))
+ {
+ ++left;
+ }
+ // find an element that is smaller than the partition element from the right
+ while ((right > start) && (mid.compareTo(String.valueOf(array[right])) < 0))
+ {
+ --right;
+ }
+ // if the indices have not crossed, swap
+ if (left <= right)
+ {
+ temp = array[left];
+ array[left] = array[right];
+ array[right] = temp;
+ ++left;
+ --right;
+ }
+ }
+ // sort the left partition, if the right index has not reached the left side of array
+ if (start < right)
+ {
+ qsort(array, start, right);
+ }
+ // sort the right partition, if the left index has not reached the right side of array
+ if (left < stop)
+ {
+ qsort(array, left, stop);
+ }
+ }
+
+ /**
+ * Sorts the specified range in the array in ascending order.
+ *
+ * @param array the Object array to be sorted
+ * @param start the start index to sort
+ * @param end the last + 1 index to sort
+ *
+ * @exception ClassCastException when an element in the array does not
+ * implement Comparable or elements cannot be compared to each other
+ * @exception IllegalArgumentException when <code>start > end</code>
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end > array.size()</code>
+ */
+ public static void sort(Object[] array, int start, int end)
+ {
+ int middle = (start + end) / 2;
+ if (start + 1 < middle) sort(array, start, middle);
+ if (middle + 1 < end) sort(array, middle, end);
+ if (start + 1 >= end) return; // this case can only happen when this method is called by the user
+ if (((Comparable)array[middle-1]).compareTo(array[middle]) <= 0) return;
+ if (start + 2 == end)
+ {
+ Object temp = array[start];
+ array[start] = array[middle];
+ array[middle] = temp;
+ return;
+ }
+ int i1 = start, i2 = middle, i3 = 0;
+ Object[] merge = new Object[end - start];
+ while (i1 < middle && i2 < end)
+ {
+ merge[i3++] = ((Comparable)array[i1]).compareTo(array[i2]) <= 0 ?
+ array[i1++] : array[i2++];
+ }
+ if (i1 < middle) System.arraycopy(array, i1, merge, i3, middle - i1);
+ System.arraycopy(merge, 0, array, start, i2 - start);
+ }
+
+ /**
+ * Sorts the specified range in the array in descending order.
+ *
+ * @param array the Object array to be sorted
+ * @param start the start index to sort
+ * @param end the last + 1 index to sort
+ *
+ * @exception ClassCastException when an element in the array does not
+ * implement Comparable or elements cannot be compared to each other
+ * @exception IllegalArgumentException when <code>start > end</code>
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end > array.size()</code>
+ */
+ public static void dsort(Object[] array, int start, int end)
+ {
+ // first sort in ascending order
+ sort (array, start, end);
+ // then swap the elements in the array
+ swap (array);
+ }
+
+ /**
+ * Reverse the elements in the array.
+ *
+ * @param array the Object array to be reversed
+ */
+ public static void swap(Object[] array)
+ {
+ int start = 0;
+ int end = array.length-1;
+ while (start < end) {
+ Object temp = array[start];
+ array[start++] = array[end];
+ array[end--] = temp;
+ }
+ }
+
+ /**
+ * Returns a string representation of the object
+ * in the given length.
+ * If the string representation of the given object
+ * is longer then it is truncated.
+ * If it is shorter then it is padded with the blanks
+ * to the given total length.
+ * If the given object is a number then the padding
+ * is done on the left, otherwise on the right.
+ *
+ * @param object the object to convert
+ * @param length the length the output string
+ */
+ public static String toString(Object object, int length)
+ {
+ boolean onLeft = object instanceof Number;
+ return toString(object, length, ' ', onLeft);
+ }
+
+ /**
+ * Returns a string representation of the object
+ * in the given length.
+ * If the string representation of the given object
+ * is longer then it is truncated.
+ * If it is shorter then it is padded to the left or right
+ * with the given character to the given total length.
+ *
+ * @param object the object to convert
+ * @param length the length the output string
+ * @param pad the pad character
+ * @param onLeft if <code>true</code> pad on the left, otherwise an the right
+ */
+ public static String toString(Object object, int length, char pad, boolean onLeft)
+ {
+ String input = String.valueOf(object);
+ int size = input.length();
+ if (size >= length)
+ {
+ int start = (onLeft) ? size - length : 0;
+ return input.substring(start, length);
+ }
+
+ StringBuffer padding = new StringBuffer(length - size);
+ for (int i = size; i < length; i++) padding.append(pad);
+
+ StringBuffer stringBuffer = new StringBuffer(length);
+ if (onLeft) stringBuffer.append(padding);
+ stringBuffer.append(input);
+ if (!onLeft) stringBuffer.append(padding);
+ return stringBuffer.toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/default.permissions b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/default.permissions
new file mode 100644
index 000000000..c0a8e67d9
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/default.permissions
@@ -0,0 +1,25 @@
+########################################################################
+# Copyright (c) 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+########################################################################
+
+# Lines beginning with '#' or '//' are comments
+#
+# This file contains the default permissions to be granted
+# to bundles with no specific permission if there are no
+# default permission set. This file must be UTF8 encoded.
+#
+# In this file, FilePermissions with relative names are not
+# mapped to an individual bundle's data directory.
+#
+# The permissions are listed one per
+# line in PermissionInfo encoded format.
+# See org.osgi.service.permissionadmin.PermissionInfo
+
+(java.security.AllPermission)
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/implied.permissions b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/implied.permissions
new file mode 100644
index 000000000..0693e8dd3
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/implied.permissions
@@ -0,0 +1,48 @@
+########################################################################
+# Copyright (c) 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+########################################################################
+
+# Lines beginning with '#' or '//' are comments
+#
+# This file contains the implied permissions to be granted
+# to all bundles. This file must be UTF8 encoded.
+#
+# In this file, FilePermissions with relative names are
+# mapped to an individual bundle's data directory.
+#
+# The permissions are listed one per
+# line in PermissionInfo encoded format.
+# See org.osgi.service.permissionadmin.PermissionInfo
+
+(java.util.PropertyPermission "java.vendor" "read")
+(java.util.PropertyPermission "java.specification.version" "read")
+(java.util.PropertyPermission "line.separator" "read")
+(java.util.PropertyPermission "java.class.version" "read")
+(java.util.PropertyPermission "java.specification.name" "read")
+(java.util.PropertyPermission "java.vendor.url" "read")
+(java.util.PropertyPermission "java.vm.version" "read")
+(java.util.PropertyPermission "os.name" "read")
+(java.util.PropertyPermission "os.arch" "read")
+(java.util.PropertyPermission "os.version" "read")
+(java.util.PropertyPermission "java.version" "read")
+(java.util.PropertyPermission "java.vm.specification.version" "read")
+(java.util.PropertyPermission "java.vm.specification.name" "read")
+(java.util.PropertyPermission "java.specification.vendor" "read")
+(java.util.PropertyPermission "java.vm.vendor" "read")
+(java.util.PropertyPermission "file.separator" "read")
+(java.util.PropertyPermission "path.separator" "read")
+(java.util.PropertyPermission "java.vm.name" "read")
+(java.util.PropertyPermission "java.vm.specification.vendor" "read")
+(java.util.PropertyPermission "org.osgi.framework.*" "read")
+(java.io.FilePermission "" "read")
+(java.io.FilePermission "-" "read,write,delete")
+
+# Added for OSGi SP R3
+(org.osgi.framework.PackagePermission "java.*" "import")
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/osname.aliases b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/osname.aliases
new file mode 100644
index 000000000..741c2acd3
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/osname.aliases
@@ -0,0 +1,35 @@
+########################################################################
+# Copyright (c) 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+########################################################################
+
+#OS Aliases Description
+
+AIX # IBM
+DigitalUnix # Compaq
+FreeBSD # Free BSD
+HPUX # Hewlett Packard
+IRIX # Sillicon Graphics
+Linux # Open source
+MacOS # Apple
+NetBSD # Open source
+Netware # Novell
+OpenBSD # Open source
+OS2 OS/2 # IBM
+QNX procnto # QNX Neutrino 2.1
+Solaris # Sun
+SunOS # Sun
+VxWorks # WindRiver Systems
+Windows95 "Windows 95" Win95 # Microsoft
+Windows98 "Windows 98" Win98 # Microsoft
+WindowsNT "Windows NT" WinNT # Microsoft
+WindowsCE "Windows CE" WinCE # Microsoft
+Windows2000 "Windows 2000" Win2000 # Microsoft
+WindowsXP "Windows XP" WinXP # Microsoft
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/processor.aliases b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/processor.aliases
new file mode 100644
index 000000000..05af81243
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/processor.aliases
@@ -0,0 +1,23 @@
+########################################################################
+# Copyright (c) 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+########################################################################
+
+#Processor Aliases Description
+
+68k # Motorola 68000 and up
+ARM # Intel Strong ARM
+Alpha # Compaq (ex DEC)
+Ignite psc1k # PTSC
+Mips # SGI
+PArisc # Hewlett Packard PA Risc
+PowerPC power ppc # Motorola/IBM Power PC
+Sparc # SUN
+x86 pentium i386 i486 i586 i686 # Intel
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ContentHandlerFactory.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ContentHandlerFactory.java
new file mode 100644
index 000000000..8f601e75f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ContentHandlerFactory.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol;
+
+import java.net.ContentHandler;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+import org.eclipse.osgi.framework.tracker.ServiceTracker;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.url.URLConstants;
+
+/**
+ * The ContentHandlerFactory is registered with the JVM to provide content handlers
+ * to requestors. The ContentHandlerFactory will first look for built-in content handlers.
+ * If a built in handler exists, this factory will return null. Otherwise, this ContentHandlerFactory
+ * will search the service registry for a maching Content-Handler and, if found, return a
+ * proxy for that content handler.
+ */
+public class ContentHandlerFactory implements java.net.ContentHandlerFactory{
+
+ private ServiceTracker contentHandlerTracker;
+ private BundleContext context;
+
+ private static final String contentHandlerClazz = "java.net.ContentHandler";
+
+ private Hashtable proxies;
+
+ public ContentHandlerFactory(BundleContext context)
+ {
+ this.context = context;
+
+ proxies = new Hashtable(5);
+
+ //We need to track content handler registrations
+ contentHandlerTracker = new ServiceTracker(context,contentHandlerClazz,null);
+ contentHandlerTracker.open();
+ }
+
+
+ /**
+ * @see java.net.ContentHandlerFactory#createContentHandler(String)
+ */
+ public ContentHandler createContentHandler(String contentType) {
+ //first, we check to see if there exists a built in content handler for
+ //this content type. we can not overwrite built in ContentHandlers
+ String builtInHandlers = System.getProperty("java.content.handler.pkgs");
+ Class clazz = null;
+ if(builtInHandlers != null)
+ {
+ //replace '/' with a '.' and all characters not allowed in a java class name
+ //with a '_'.
+
+ // find all characters not allowed in java names
+ String convertedContentType = contentType.replace('.','_');
+ convertedContentType = convertedContentType.replace('/','.');
+ convertedContentType = convertedContentType.replace('-','_');
+ StringTokenizer tok = new StringTokenizer(builtInHandlers,"|");
+ while(tok.hasMoreElements())
+ {
+ StringBuffer name = new StringBuffer();
+ name.append(tok.nextToken());
+ name.append(".");
+ name.append(convertedContentType);
+ try
+ {
+ clazz = Class.forName(name.toString());
+ if(clazz != null)
+ {
+ return (null); //this class exists, it is a built in handler, let the JVM handle it
+ }
+ }
+ catch(ClassNotFoundException ex)
+ {} //keep looking
+ }
+ }
+
+ //first check to see if the handler is in the cache
+ ContentHandlerProxy proxy = (ContentHandlerProxy)proxies.get(contentType);
+ if(proxy != null)
+ {
+ return (proxy);
+ }
+ org.osgi.framework.ServiceReference[] serviceReferences = contentHandlerTracker.getServiceReferences();
+ if(serviceReferences != null)
+ {
+ for(int i=0;i<serviceReferences.length;i++)
+ {
+ String[] contentHandler = (String[])(serviceReferences[i].getProperty(URLConstants.URL_CONTENT_MIMETYPE));
+ if(contentHandler != null)
+ {
+ for(int j=0;j<contentHandler.length;j++)
+ {
+ if(contentHandler[j].equals(contentHandler))
+ {
+ ContentHandler handler = (ContentHandler)context.getService(serviceReferences[i]);
+ proxy = new ContentHandlerProxy(contentType, serviceReferences[i],context);
+ proxies.put(contentType,handler);
+ return (proxy);
+ }
+
+ }
+ }
+ }
+ }
+
+ //If we can't find the content handler in the service registry, return Proxy with DefaultContentHandler set.
+ //We need to do this because if we return null, we won't get called again for this content type.
+ proxy = new ContentHandlerProxy(contentType, null,context);
+ proxies.put(contentType,proxy);
+ return (proxy);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ContentHandlerProxy.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ContentHandlerProxy.java
new file mode 100644
index 000000000..b3376af99
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ContentHandlerProxy.java
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol;
+
+import java.io.IOException;
+import java.net.ContentHandler;
+import java.net.URLConnection;
+
+import org.eclipse.osgi.framework.tracker.ServiceTracker;
+import org.eclipse.osgi.framework.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.url.URLConstants;
+
+
+/**
+ * The ContentHandlerProxy is a ContentHandler that acts as a proxy for registered ContentHandlers.
+ * When a ContentHandler is requested from the ContentHandlerFactory and it exists in the service
+ * registry, a ContentHandlerProxy is created which will pass all the requests from the requestor to
+ * the real ContentHandler. We can't return the real ContentHandler from the ContentHandlerFactory
+ * because the JVM caches ContentHandlers and therefore would not support a dynamic environment of
+ * ContentHandlers being registered and unregistered.
+ */
+public class ContentHandlerProxy extends ContentHandler implements ServiceTrackerCustomizer{
+
+ protected ContentHandler realHandler;
+
+ protected ServiceTracker contentHandlerServiceTracker;
+
+ protected BundleContext context;
+ protected ServiceReference contentHandlerServiceReference;
+
+ protected String contentType;
+
+ protected int ranking = -1;
+
+ public ContentHandlerProxy(String contentType, ServiceReference reference, BundleContext context)
+ {
+ this.context = context;
+ this.contentType = contentType;
+
+ if(reference != null)
+ {
+ setNewHandler(reference,getRank(reference));
+ }
+ //In this case, the proxy is constructed for a Content Handler that is not currently registered.
+ //Use the DefaultContentHandler until a ContentHandler for this mime-type is registered
+ else
+ {
+ realHandler = new DefaultContentHandler();
+ }
+ contentHandlerServiceTracker = new ServiceTracker(context,ContentHandler.class.getName(),this);
+ contentHandlerServiceTracker.open();
+ }
+
+ private void setNewHandler(ServiceReference reference, int rank)
+ {
+ this.contentHandlerServiceReference = reference;
+ Object property = reference.getProperty(Constants.SERVICE_RANKING);
+ this.ranking = rank;
+ this.realHandler = (ContentHandler)context.getService(reference);
+ }
+
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(ServiceReference)
+ */
+ public Object addingService(ServiceReference reference) {
+ //check to see if our contentType is being registered by another service
+ String[] contentTypes = (String[])reference.getProperty(URLConstants.URL_CONTENT_MIMETYPE);
+ for(int i=0;i<contentTypes.length;i++)
+ {
+ if(contentTypes[i].equals(contentType))
+ {
+ //If our contentType is registered by another service, check the service ranking and switch
+ //URLStreamHandlers if nessecary.
+
+ int newServiceRanking = getRank(reference);
+
+ //int newServiceRanking = ((Integer)reference.getProperty(Constants.SERVICE_RANKING)).intValue();
+ if(newServiceRanking > ranking)
+ {
+ setNewHandler(reference,newServiceRanking);
+ }
+ return (reference);
+ }
+ }
+
+ //we don't want to continue hearing events about a ContentHandler service not registered under our contentType
+ return (null);
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(ServiceReference, Object)
+ */
+
+ public void modifiedService(ServiceReference reference, Object service)
+ {
+ int newrank = getRank(reference);
+
+ if(reference == contentHandlerServiceReference)
+ {
+ if(newrank < ranking) //The ContentHandler we are currently
+ //using has dropped it's ranking below a ContentHandler registered for the same protocol.
+ //We need to swap out ContentHandlers.
+ //this should get us the highest ranked service, if available
+ {
+ ServiceReference newReference = contentHandlerServiceTracker.getServiceReference();
+ if(newReference != contentHandlerServiceReference && newReference != null)
+ {
+ setNewHandler(newReference,((Integer)newReference.getProperty(Constants.SERVICE_RANKING)).intValue());
+ }
+ }
+ }
+ else if(newrank > ranking) //the service changed is another URLHandler that we are not currently using
+ //If it's ranking is higher, we must swap it in.
+ {
+ setNewHandler(reference,newrank);
+ }
+
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(ServiceReference, Object)
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ //check to see if our URLStreamHandler was unregistered. If so, look
+ //for a lower ranking URLHandler
+ if(reference == contentHandlerServiceReference)
+ {
+ //this should get us the highest ranking service left, if available
+ ServiceReference newReference = contentHandlerServiceTracker.getServiceReference();
+ if(newReference != null)
+ {
+ setNewHandler(newReference,getRank(newReference));
+ }
+ else
+ {
+ ranking = -1;
+ realHandler = new DefaultContentHandler();
+ }
+
+ }
+
+ }
+
+
+ /**
+ * @see java.net.ContentHandler#getContent(URLConnection)
+ */
+
+ public Object getContent(URLConnection uConn) throws IOException {
+ return realHandler.getContent(uConn);
+ }
+
+ private int getRank(ServiceReference reference)
+ {
+ Object property = reference.getProperty(Constants.SERVICE_RANKING);
+
+ return (property instanceof Integer)
+ ? ((Integer)property).intValue() : 0;
+ }
+
+
+ class DefaultContentHandler extends ContentHandler {
+
+ /**
+ * @see java.net.ContentHandler#getContent(URLConnection)
+ */
+ public Object getContent(URLConnection uConn) throws IOException {
+ return uConn.getInputStream();
+ }
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/NullURLStreamHandlerService.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/NullURLStreamHandlerService.java
new file mode 100644
index 000000000..4a7c14e42
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/NullURLStreamHandlerService.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.osgi.service.url.URLStreamHandlerService;
+import org.osgi.service.url.URLStreamHandlerSetter;
+
+/**
+ * The NullURLStreamService is created when a registered URLStreamHandler service
+ * with an associated URLStreamHandlerProxy becomes unregistered. The associated
+ * URLStreamHandlerProxy must still handle all future requests for the now unregistered
+ * scheme (the JVM caches URLStreamHandlers making up impossible to "unregister" them).
+ * When requests come in for an unregistered URLStreamHandlerService, the
+ * NullURLStreamHandlerService is used in it's place.
+ */
+
+public class NullURLStreamHandlerService implements URLStreamHandlerService {
+
+ /**
+ * @see java.net.URLStreamHandler#openConnection(URL)
+ */
+ public URLConnection openConnection(URL u) throws IOException
+ {
+ throw new MalformedURLException();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#equals(URL, URL)
+ */
+ public boolean equals(URL url1, URL url2) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#getDefaultPort()
+ */
+ public int getDefaultPort() {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#getHostAddress(URL)
+ */
+ public InetAddress getHostAddress(URL url) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#hashCode(URL)
+ */
+ public int hashCode(URL url) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#hostsEqual(URL, URL)
+ */
+ public boolean hostsEqual(URL url1, URL url2) {
+ throw new IllegalStateException();
+ }
+
+
+
+ /**
+ * @see java.net.URLStreamHandler#sameFile(URL, URL)
+ */
+ public boolean sameFile(URL url1, URL url2) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#setURL(URL, String, String, int, String, String, String, String, String)
+ */
+ public void setURL(
+ URL u,
+ String protocol,
+ String host,
+ int port,
+ String authority,
+ String userInfo,
+ String file,
+ String query,
+ String ref) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#setURL(URL, String, String, int, String, String)
+ * @deprecated
+ */
+ public void setURL(
+ URL u,
+ String protocol,
+ String host,
+ int port,
+ String file,
+ String ref) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#toExternalForm(URL)
+ */
+ public String toExternalForm(URL url) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see org.osgi.service.url.URLStreamHandlerService#parseURL(URLStreamHandlerSetter, URL, String, int, int)
+ */
+ public void parseURL(
+ URLStreamHandlerSetter realHandler,
+ URL u,
+ String spec,
+ int start,
+ int limit) {
+ throw new IllegalStateException();
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ProtocolActivator.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ProtocolActivator.java
new file mode 100644
index 000000000..aa7a1e753
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/ProtocolActivator.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol;
+
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.osgi.framework.BundleContext;
+
+public interface ProtocolActivator {
+
+ public void start (BundleContext context, FrameworkAdaptor adaptor);
+
+}
+
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/StreamHandlerFactory.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/StreamHandlerFactory.java
new file mode 100644
index 000000000..b9e056923
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/StreamHandlerFactory.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol;
+
+import java.net.URLStreamHandler;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.tracker.ServiceTracker;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.url.URLConstants;
+
+/**
+ * This class contains the URL stream handler factory for the OSGi framework.
+ */
+public class StreamHandlerFactory implements java.net.URLStreamHandlerFactory
+{
+ /** BundleContext to system bundle */
+ protected BundleContext context;
+
+ /** internal adaptor object */
+ protected FrameworkAdaptor adaptor;
+
+ private ServiceTracker urlStreamHandlerTracker;
+
+ protected static final String urlStreamHandlerClazz = "org.osgi.service.url.URLStreamHandlerService";
+
+ private Hashtable proxies;
+
+ /**
+ * Create the factory.
+ *
+ * @param context BundleContext for the system bundle
+ */
+ public StreamHandlerFactory(BundleContext context,FrameworkAdaptor adaptor)
+ {
+ this.context = context;
+ this.adaptor = adaptor;
+
+ proxies = new Hashtable(15);
+ urlStreamHandlerTracker = new ServiceTracker(context,urlStreamHandlerClazz,null);
+ urlStreamHandlerTracker.open();
+ }
+
+ /**
+ * Creates a new URLStreamHandler instance for the specified
+ * protocol.
+ *
+ * @param protocol The desired protocol
+ * @return a URLStreamHandler for the specific protocol.
+ */
+ public URLStreamHandler createURLStreamHandler(String protocol)
+ {
+ if (protocol.equals(Constants.OSGI_URL_PROTOCOL))
+ {
+ return null;
+ }
+
+ //first check for built in handlers
+ String builtInHandlers = System.getProperty("java.protocol.handler.pkgs");
+ Class clazz = null;
+ if(builtInHandlers != null)
+ {
+ StringTokenizer tok = new StringTokenizer(builtInHandlers,"|");
+ while(tok.hasMoreElements())
+ {
+ StringBuffer name = new StringBuffer();
+ name.append(tok.nextToken());
+ name.append(".");
+ name.append(protocol);
+ name.append(".Handler");
+ try
+ {
+ clazz = Class.forName(name.toString());
+ if(clazz != null)
+ {
+ return (null); //this class exists, it is a built in handler, let the JVM handle it
+ }
+ }
+ catch(ClassNotFoundException ex)
+ {} //keep looking
+ }
+ }
+
+ //internal protocol handlers
+ String name = "org.eclipse.osgi.framework.internal.protocol."+protocol+".Handler";
+
+ try
+ {
+ clazz = Class.forName(name);
+ }
+
+ //Now we checdk the service registry
+ catch (ClassNotFoundException e)
+ {
+
+ //first check to see if the handler is in the cache
+ URLStreamHandlerProxy handler = (URLStreamHandlerProxy)proxies.get(protocol);
+ if(handler != null)
+ {
+ return (handler);
+ }
+
+ //look through the service registry for a URLStramHandler registered for this protocol
+ org.osgi.framework.ServiceReference[] serviceReferences = urlStreamHandlerTracker.getServiceReferences();
+ if(serviceReferences != null)
+ {
+ for(int i=0;i<serviceReferences.length;i++)
+ {
+ String[] protocols = (String[])(serviceReferences[i].getProperty(URLConstants.URL_HANDLER_PROTOCOL));
+ if(protocols != null)
+ {
+ for(int j=0;j<protocols.length;j++)
+ {
+ if(protocols[j].equals(protocol))
+ {
+ handler = new URLStreamHandlerProxy(protocol,serviceReferences[i],context);
+ proxies.put(protocol,handler);
+ return (handler);
+ }
+ }
+ }
+ }
+ }
+ return (null);
+ }
+
+ if (clazz == null)
+ {
+ return null;
+ }
+
+ try
+ {
+ URLStreamHandler handler = (URLStreamHandler)clazz.newInstance();
+
+ if (handler instanceof ProtocolActivator)
+ {
+ ((ProtocolActivator)handler).start(context,adaptor);
+ }
+
+ return handler;
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/URLStreamHandlerProxy.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/URLStreamHandlerProxy.java
new file mode 100644
index 000000000..7c028a743
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/URLStreamHandlerProxy.java
@@ -0,0 +1,287 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+import org.eclipse.osgi.framework.tracker.ServiceTracker;
+import org.eclipse.osgi.framework.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.url.URLConstants;
+import org.osgi.service.url.URLStreamHandlerService;
+
+
+/**
+ * The URLStreamHandlerProxy is a URLStreamHandler that acts as a proxy for registered
+ * URLStreamHandlerServices. When a URLStreamHandler is requested from the URLStreamHandlerFactory
+ * and it exists in the service registry, a URLStreamHandlerProxy is created which will pass all the
+ * requests from the requestor to the real URLStreamHandlerService. We can't return the real
+ * URLStreamHandlerService from the URLStreamHandlerFactory because the JVM caches URLStreamHandlers
+ * and therefore would not support a dynamic environment of URLStreamHandlerServices being registered
+ * and unregistered.
+ */
+
+public class URLStreamHandlerProxy extends URLStreamHandler implements ServiceTrackerCustomizer{
+
+ protected URLStreamHandlerService realHandlerService;
+
+ protected URLStreamHandlerSetter urlSetter;
+
+ protected ServiceTracker urlStreamHandlerServiceTracker;
+
+ protected boolean handlerRegistered;
+
+ protected BundleContext context;
+ protected ServiceReference urlStreamServiceReference;
+
+ protected String protocol;
+
+ protected int ranking = -1;
+
+ public URLStreamHandlerProxy(String protocol, ServiceReference reference, BundleContext context)
+ {
+ handlerRegistered = true;
+
+ this.context = context;
+ this.protocol = protocol;
+
+
+ urlSetter = new URLStreamHandlerSetter(this);
+
+ //set the ranking
+ Object property = reference.getProperty(Constants.SERVICE_RANKING);
+ ranking = (property instanceof Integer) ? ((Integer)property).intValue() : 0;
+
+ this.realHandlerService = (URLStreamHandlerService)context.getService(reference);
+ this.urlStreamServiceReference = reference;
+
+ urlStreamHandlerServiceTracker = new ServiceTracker(context,StreamHandlerFactory.urlStreamHandlerClazz,this);
+ urlStreamHandlerServiceTracker.open();
+ }
+
+ private void setNewHandler(ServiceReference reference,int rank)
+ {
+ this.urlStreamServiceReference = reference;
+ this.ranking = rank;
+ this.realHandlerService = (URLStreamHandlerService)context.getService(reference);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#equals(URL, URL)
+ */
+ protected boolean equals(URL url1, URL url2) {
+ return realHandlerService.equals(url1, url2);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#getDefaultPort()
+ */
+ protected int getDefaultPort() {
+ return realHandlerService.getDefaultPort();
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#getHostAddress(URL)
+ */
+ protected InetAddress getHostAddress(URL url) {
+ return realHandlerService.getHostAddress(url);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#hashCode(URL)
+ */
+ protected int hashCode(URL url) {
+ return realHandlerService.hashCode(url);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#hostsEqual(URL, URL)
+ */
+ protected boolean hostsEqual(URL url1, URL url2) {
+ return realHandlerService.hostsEqual(url1, url2);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#openConnection(URL)
+ */
+ protected URLConnection openConnection(URL url) throws IOException {
+ return realHandlerService.openConnection(url);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#parseURL(URL, String, int, int)
+ */
+ protected void parseURL(URL url, String str, int start, int end) {
+ realHandlerService.parseURL(urlSetter,url, str, start, end);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#sameFile(URL, URL)
+ */
+ protected boolean sameFile(URL url1, URL url2) {
+ return realHandlerService.sameFile(url1, url2);
+ }
+
+
+ /**
+ * @see java.net.URLStreamHandler#toExternalForm(URL)
+ */
+ protected String toExternalForm(URL url) {
+ return realHandlerService.toExternalForm(url);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#setURL(URL, String, String, int, String, String, String, String, String)
+ */
+ public void setURL(
+ URL u,
+ String protocol,
+ String host,
+ int port,
+ String authority,
+ String userInfo,
+ String file,
+ String query,
+ String ref) {
+ super.setURL(
+ u,
+ protocol,
+ host,
+ port,
+ authority,
+ userInfo,
+ file,
+ query,
+ ref);
+ }
+
+ /**
+ * @see java.net.URLStreamHandler#setURL(URL, String, String, int, String, String)
+ * @deprecated
+ */
+
+ public void setURL(
+ URL url,
+ String protocol,
+ String host,
+ int port,
+ String file,
+ String ref) {
+
+ //using non-deprecated URLStreamHandler.setURL method.
+ //setURL(URL u, String protocol, String host, int port, String authority, String userInfo, String file, String query, String ref)
+ super.setURL(url, protocol, host, port, null,null,file, null,ref);
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(ServiceReference)
+ */
+ public Object addingService(ServiceReference reference) {
+ //check to see if our protocol is being registered by another service
+ String[] protocols = (String[])reference.getProperty(URLConstants.URL_HANDLER_PROTOCOL);
+ for(int i=0;i<protocols.length;i++)
+ {
+ if(protocols[i].equals(protocol))
+ {
+ //If our protocol is registered by another service, check the service ranking and switch
+ //URLStreamHandlers if nessecary.
+
+ Object property = reference.getProperty(Constants.SERVICE_RANKING);
+
+ int newServiceRanking = (property instanceof Integer)
+ ? ((Integer)property).intValue() : 0;
+
+ if(!handlerRegistered)
+ {
+ setNewHandler(reference,newServiceRanking);
+ handlerRegistered = true;
+ }
+
+ if(newServiceRanking > ranking)
+ {
+ setNewHandler(reference,newServiceRanking);
+ }
+ return (reference);
+ }
+ }
+
+ //we don't want to continue hearing events about a URLStreamHandlerService not registered under our protocol
+ return (null);
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(ServiceReference, Object)
+ */
+
+ // check to see if the ranking has changed. If so, re-select a new URLHandler
+ public void modifiedService(ServiceReference reference, Object service)
+ {
+ int newrank = getRank(reference);
+
+ if(reference == urlStreamServiceReference)
+ {
+ if(newrank < ranking) //The URLHandler we are currently
+ //using has dropped it's ranking below a URLHandler registered for the same protocol.
+ //We need to swap out URLHandlers.
+ //this should get us the highest ranked service, if available
+ {
+ ServiceReference newReference = urlStreamHandlerServiceTracker.getServiceReference();
+ if(newReference != urlStreamServiceReference && newReference != null)
+ {
+ setNewHandler(newReference,((Integer)newReference.getProperty(Constants.SERVICE_RANKING)).intValue());
+ }
+ }
+ }
+ else if(newrank > ranking) //the service changed is another URLHandler that we are not currently using
+ //If it's ranking is higher, we must swap it in.
+ {
+ setNewHandler(reference,newrank);
+ }
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(ServiceReference, Object)
+ */
+ public void removedService(ServiceReference reference, Object service) {
+ //check to see if our URLStreamHandler was unregistered. If so, look
+ //for a lower ranking URLHandler
+ if(reference == urlStreamServiceReference)
+ {
+ //this should get us the highest ranking service left, if available
+ ServiceReference newReference = urlStreamHandlerServiceTracker.getServiceReference();
+ if(newReference != null)
+ {
+ setNewHandler(newReference,getRank(newReference));
+ }
+ else
+ {
+ handlerRegistered = false;
+ realHandlerService = new NullURLStreamHandlerService();
+ ranking = -1;
+ }
+
+ }
+
+ }
+
+ private int getRank(ServiceReference reference)
+ {
+ return ((Integer)reference.getProperty(Constants.SERVICE_RANKING)).intValue();
+ }
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/URLStreamHandlerSetter.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/URLStreamHandlerSetter.java
new file mode 100644
index 000000000..740033f4e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/protocol/URLStreamHandlerSetter.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol;
+
+import java.net.URL;
+
+public class URLStreamHandlerSetter
+
+ implements org.osgi.service.url.URLStreamHandlerSetter {
+
+ protected URLStreamHandlerProxy handlerProxy;
+
+ public URLStreamHandlerSetter(URLStreamHandlerProxy handler)
+ {
+ this.handlerProxy = handler;
+ }
+
+ /**
+ * @see org.osgi.service.url.URLStreamHandlerSetter#setURL(URL, String, String, int, String, String)
+ */
+ public void setURL(
+ URL url,
+ String protocol,
+ String host,
+ int port,
+ String file,
+ String ref)
+ {
+ handlerProxy.setURL(url,protocol,host,port,file,ref);
+ }
+
+ /**
+ * @see org.osgi.service.url.URLStreamHandlerSetter#setURL(URL, String, String, int, String, String, String, String, String)
+ */
+ public void setURL(
+ URL url,
+ String protocol,
+ String host,
+ int port,
+ String authority,
+ String userInfo,
+ String path,
+ String query,
+ String ref) {
+ handlerProxy.setURL(url,protocol,host,port,authority,userInfo,path,query,ref);
+ }
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/launcher/EclipseLauncher.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/launcher/EclipseLauncher.java
new file mode 100644
index 000000000..f51c35709
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/launcher/EclipseLauncher.java
@@ -0,0 +1,733 @@
+package org.eclipse.osgi.framework.launcher;
+
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.StringTokenizer;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.util.Enumeration;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.OSGi;
+import org.eclipse.osgi.framework.util.Tokenizer;
+
+//import org.eclipse.osgi.platform.OsgiPlatform;
+//import org.eclipse.osgi.framework.Framework;
+import org.eclipse.osgi.service.environment.EnvironmentInfo;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class EclipseLauncher implements EnvironmentInfo {
+
+ /** default console port */
+ protected String consolePort = "";
+
+ /** flag to indicate whether or not to start the console */
+ protected boolean console = false;
+
+ /** string containing the classname of the adaptor to be used in this framework instance */
+ protected String adaptorClassName =
+ "org.eclipse.osgi.framework.internal.defaultadaptor.DefaultAdaptor";
+
+ protected final String IdeAdentClassName =
+ "org.eclipse.osgi.framework.internal.core.ide.IdeAgent";
+
+ protected final String osgiConsoleClazz =
+ "org.eclipse.osgi.framework.internal.core.FrameworkConsole";
+
+ /** array of adaptor arguments to be passed to FrameworkAdaptor.initialize() */
+ String[] adaptorArgs = null;
+
+ /** array of application arguments to be passed to Eclipse applications */
+ String[] applicationArgs = null;
+
+ /* Components that can be installed and activated optionally. */
+ private static final String OSGI_CONSOLE_COMPONENT_NAME = "OSGi Console";
+ private static final String OSGI_CONSOLE_COMPONENT = "osgiconsole.jar";
+
+ // command line arguments
+ private static final String INSTALL_ARG = "-install"; //$NON-NLS-1$
+ private static final String META_AREA = ".metadata"; //$NON-NLS-1$
+
+
+ Properties bootOptions;
+ String installLocation; // install
+ String debugOptionsFilename;
+ boolean debugMode;
+
+ Runnable handler;
+ String allArgs[];
+ String passThru[];
+
+ private FrameworkAdaptor platform;
+ private OSGi osgi;
+ private ServiceRegistration bootloaderRegistration;
+ private BundleContext context;
+
+ public static void main(String args[]) {
+ try {
+ new EclipseLauncher().doIt(args);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ public void doIt(String[] args) throws Exception {
+ allArgs = args;
+ passThru = parseArgs(args);
+
+ String osgiInstance = System.getProperty("osgi.instance");
+ platform = doAdaptor();
+
+ osgi = doOSGi(platform);
+ osgi.launch();
+ if (console) {
+ doConsole(osgi, passThru);
+ }
+ context = osgi.getBundleContext();
+
+ // initialize eclipse URL handling
+ String metaPath = osgiInstance + META_AREA;
+ String nl,ws,os;
+ nl = context.getProperty(Constants.FRAMEWORK_LANGUAGE);
+ os = context.getProperty(Constants.FRAMEWORK_OS_NAME);
+ ws = context.getProperty(Constants.FRAMEWORK_WINDOWING_SYSTEM);
+
+ registerEclipseableService();
+ loadBundles();
+ }
+
+ /**
+ * Processes the -adaptor command line argument.
+ *
+ * Parses the arguments to get the adaptor class name, the adaptor dir or filename,
+ * and the adaptor file size.
+ *
+ * @return a FrameworkAdaptor object
+ */
+ protected FrameworkAdaptor doAdaptor() throws Exception {
+
+ Class adaptorClass = Class.forName(adaptorClassName);
+ Class[] constructorArgs = new Class[] { String[].class };
+ Constructor constructor = adaptorClass.getConstructor(constructorArgs);
+ return (FrameworkAdaptor) constructor.newInstance(
+ new Object[] { adaptorArgs });
+ }
+
+ /**
+ * Creates the OSGi framework object.
+ *
+ * @param The FrameworkAdaptor object
+ */
+ protected OSGi doOSGi(FrameworkAdaptor adaptor) {
+ return new OSGi(adaptor);
+ }
+
+ /*
+ public void startConsole() {
+ console = new Console(null);
+ consoleThread = new Thread(console, "Console Thread");
+ consoleThread.start();
+ }
+ */
+ /*
+ private static void installRuntimeBundle() throws BundleException {
+ Bundle runtimeBundle = context.installBundle(System.getProperty("eclipse.pluginbase") + System.getProperty("eclipse.runtime"));
+ runtimeBundle.start(); //TODO This should not be here, because we are not sure that the bundle is resolved. We should have a listener or something like that
+ }
+ */
+
+ public Properties getBootOptions() {
+ return bootOptions;
+ }
+/*
+ public String[] getCommandLineArgs() {
+ // TODO: what are we suppose to return here? Versus getFrameworkArgs()/getFrameworkArgs()
+ return appArgs; //This is a temporary hack
+ }
+*/
+ public Runnable getSplashHandler() {
+ return handler;
+ }
+
+ private void registerEclipseableService() {
+ Hashtable properties = new Hashtable(3);
+ bootloaderRegistration =
+ context.registerService(
+ EnvironmentInfo.class.getName(),
+ this,
+ properties);
+ }
+/*
+ public boolean inDevelopmentMode() {
+ return platform.inDevelopmentMode();
+ }
+*/
+ public String getOSArch() {
+ return context.getProperty(Constants.FRAMEWORK_PROCESSOR);
+ }
+ public String getNL() {
+ return context.getProperty(Constants.FRAMEWORK_LANGUAGE);
+ }
+ public String getOS() {
+ return context.getProperty(Constants.FRAMEWORK_OS_NAME);
+ }
+ public String getWS() {
+ return context.getProperty(Constants.FRAMEWORK_WINDOWING_SYSTEM);
+ }
+
+ public String[] getAllArgs() {
+ return allArgs;
+ }
+
+ public String[] getFrameworkArgs() {
+ // TODO: what are we suppose to return here? Versus getCommandLineArgs()/getApplicationArgs()
+ return this.adaptorArgs;
+ }
+
+ public String[] getApplicationArgs() {
+ // TODO: what are we suppose to return here? Versus getCommandLineArgs()/getFrameworkArgs()
+ return applicationArgs;
+ }
+ public boolean inDebugMode() {
+ return debugMode;
+ }
+
+ /**
+ * Invokes the OSGi Console on another thread
+ *
+ * @param osgi The current OSGi instance for the console to attach to
+ * @param consoleArgs An String array containing commands from the command line
+ * for the console to execute
+ */
+ protected void doConsole(OSGi osgi, String[] consoleArgs) {
+
+ Constructor consoleConstructor;
+ Object osgiconsole;
+ Class[] parameterTypes;
+ Object[] parameters;
+
+ try {
+ Class osgiconsoleClass = Class.forName(osgiConsoleClazz);
+ if (consolePort.length() == 0) {
+ parameterTypes = new Class[] { OSGi.class, String[].class };
+ parameters = new Object[] { osgi, consoleArgs };
+ } else {
+ parameterTypes = new Class[] { OSGi.class, int.class, String[].class };
+ parameters = new Object[] { osgi, new Integer(consolePort), consoleArgs };
+ }
+ consoleConstructor = osgiconsoleClass.getConstructor(parameterTypes);
+ osgiconsole = consoleConstructor.newInstance(parameters);
+
+ Thread t = new Thread(((Runnable) osgiconsole), OSGI_CONSOLE_COMPONENT_NAME);
+ t.start();
+ } catch (NumberFormatException nfe) {
+ System.err.println("Invalid console port: " + consolePort);
+ } catch (Exception ex) {
+ informAboutMissingComponent(OSGI_CONSOLE_COMPONENT_NAME, OSGI_CONSOLE_COMPONENT);
+ }
+
+ }
+
+ /**
+ * Informs the user about a missing component.
+ *
+ * @param component The name of the component
+ * @param jar The jar file that contains the component
+ */
+ void informAboutMissingComponent(String component, String jar) {
+ System.out.println();
+ System.out.print("Warning: The requested component '" + component + "' is not included in this runtime.");
+ System.out.println(" Add '" + jar + "' to the classpath or rebuild the jxe with it.");
+ System.out.println();
+ }
+
+/*
+ private String[] processCommandLine(String[] args)
+ throws Exception {
+ int[] configArgs = new int[100];
+ configArgs[0] = -1;
+
+ if (args == null) {
+ return null;
+ }
+ // need to initialize the first element to something that could not be an index.
+ int configArgIndex = 0;
+ for (int i = 0; i < args.length; i++) {
+ boolean found = false;
+ // check for args without parameters (i.e., a flag arg)
+
+ // None at this level so far...
+
+ if (found) {
+ configArgs[configArgIndex++] = i;
+ continue;
+ }
+
+ // check for args with parameters. If we are at the last argument or if the next one
+ // has a '-' as the first character, then we can't have an arg with a parm so continue.
+ if (i == args.length - 1 || args[i + 1].startsWith("-")) { //$NON-NLS-1$
+ continue;
+ }
+ String arg = args[++i];
+
+ // look for the install location.
+ if (args[i - 1].equalsIgnoreCase(INSTALL_ARG)) {
+ found = true;
+ installLocation = arg;
+ }
+
+ // done checking for args. Remember where an arg was found
+ if (found) {
+ configArgs[configArgIndex++] = i - 1;
+ configArgs[configArgIndex++] = i;
+ }
+ }
+
+ // remove all the arguments consumed by this argument parsing
+ if (configArgIndex == 0)
+ return args;
+ String[] passThruArgs = new String[args.length - configArgIndex];
+ configArgIndex = 0;
+ int i, j;
+ for (j = 0, i = 0; i < args.length; i++) {
+ if (i == configArgs[configArgIndex]) {
+ configArgIndex++;
+ args[i] = null;
+ } else
+ passThruArgs[j++] = args[i];
+ }
+ for (j = 0, i = 0; i < args.length; i++) {
+ if (args[i] == null)
+ continue;
+ args[j++] = args[i];
+ }
+ processedArgs = args;
+ return passThruArgs;
+ }
+*/
+ private static final String OPTIONS = ".options"; //$NON-NLS-1$
+ private static Properties debugOptions;
+
+ private void loadDebugOptions() {
+
+ // if no debug option was specified, don't even bother to try.
+ // Must ensure that the options slot is null as this is the signal to the
+ // platform that debugging is not enabled.
+ if (debugMode) {
+ debugOptions = null;
+ return;
+ }
+ debugOptions = new Properties();
+ URL optionsFile;
+ if (debugOptionsFilename == null) {
+ // default options location is user.dir (install location may be r/o so
+ // is not a good candidate for a trace options that need to be updatable by
+ // by the user)
+ String userDir = System.getProperty("user.dir").replace(File.separatorChar, '/'); //$NON-NLS-1$
+ if (!userDir.endsWith("/")) //$NON-NLS-1$
+ userDir += "/"; //$NON-NLS-1$
+ debugOptionsFilename = "file:" + userDir + OPTIONS; //$NON-NLS-1$
+ }
+ try {
+ optionsFile = getURL(debugOptionsFilename);
+ } catch (MalformedURLException e) {
+ System.out.println("Unable to construct URL for options file: " + debugOptionsFilename); //$NON-NLS-1$
+ e.printStackTrace(System.out);
+ return;
+ }
+ System.out.println("Debug-Options:\n " + debugOptionsFilename); //$NON-NLS-1$
+ try {
+ InputStream input = optionsFile.openStream();
+ try {
+ debugOptions.load(input);
+ } finally {
+ input.close();
+ }
+ } catch (FileNotFoundException e) {
+ // Its not an error to not find the options file
+ } catch (IOException e) {
+ System.out.println("Could not parse the options file: " + optionsFile); //$NON-NLS-1$
+ e.printStackTrace(System.out);
+ }
+ // trim off all the blanks since properties files don't do that.
+ for (Iterator i = debugOptions.keySet().iterator(); i.hasNext();) {
+ Object key = i.next();
+ debugOptions.put(key, ((String) debugOptions.get(key)).trim());
+ }
+ setupOptions();
+ }
+ /**
+ * Setup the debug flags for the given debug options. This method will likely
+ * be called twice. Once when loading the options file from the command
+ * line or install dir and then again when we have loaded options from the
+ * specific platform metaarea.
+ */
+ public static void setupOptions() {
+ // TODO: need to fix this and decide how/which options we want.
+ // TODO: see InternalBootLoader::setupOptions();
+ /*
+ options.put(OPTION_STARTTIME, Long.toString(System.currentTimeMillis()));
+ DelegatingURLClassLoader.DEBUG = getBooleanOption(OPTION_LOADER_DEBUG, false);
+ DelegatingURLClassLoader.DEBUG_SHOW_CREATE = getBooleanOption(OPTION_LOADER_SHOW_CREATE, true);
+ DelegatingURLClassLoader.DEBUG_SHOW_ACTIVATE = getBooleanOption(OPTION_LOADER_SHOW_ACTIVATE, true);
+ DelegatingURLClassLoader.DEBUG_SHOW_ACTIONS = getBooleanOption(OPTION_LOADER_SHOW_ACTIONS, true);
+ DelegatingURLClassLoader.DEBUG_SHOW_SUCCESS = getBooleanOption(OPTION_LOADER_SHOW_SUCCESS, true);
+ DelegatingURLClassLoader.DEBUG_SHOW_FAILURE = getBooleanOption(OPTION_LOADER_SHOW_FAILURE, true);
+ DelegatingURLClassLoader.DEBUG_FILTER_CLASS = getListOption(OPTION_LOADER_FILTER_CLASS);
+ DelegatingURLClassLoader.DEBUG_FILTER_LOADER = getListOption(OPTION_LOADER_FILTER_LOADER);
+ DelegatingURLClassLoader.DEBUG_FILTER_RESOURCE = getListOption(OPTION_LOADER_FILTER_RESOURCE);
+ DelegatingURLClassLoader.DEBUG_FILTER_NATIVE = getListOption(OPTION_LOADER_FILTER_NATIVE);
+ PlatformURLConnection.DEBUG = getBooleanOption(OPTION_URL_DEBUG, false);
+ PlatformURLConnection.DEBUG_CONNECT = getBooleanOption(OPTION_URL_DEBUG_CONNECT, true);
+ PlatformURLConnection.DEBUG_CACHE_LOOKUP = getBooleanOption(OPTION_URL_DEBUG_CACHE_LOOKUP, true);
+ PlatformURLConnection.DEBUG_CACHE_COPY = getBooleanOption(OPTION_URL_DEBUG_CACHE_COPY, true);
+ BootLoader.CONFIGURATION_DEBUG = getBooleanOption(OPTION_CONFIGURATION_DEBUG, false);
+
+ DelegatingURLClassLoader.MONITOR_PLUGINS = getBooleanOption(OPTION_MONITOR_PLUGINS, DelegatingURLClassLoader.MONITOR_PLUGINS);
+ DelegatingURLClassLoader.MONITOR_CLASSES = getBooleanOption(OPTION_MONITOR_CLASSES, DelegatingURLClassLoader.MONITOR_CLASSES);
+ DelegatingURLClassLoader.MONITOR_BUNDLES = getBooleanOption(OPTION_MONITOR_BUNDLES, DelegatingURLClassLoader.MONITOR_BUNDLES);
+
+ DelegatingURLClassLoader.TRACE_FILENAME = options.getProperty(OPTION_TRACE_FILENAME, DelegatingURLClassLoader.TRACE_FILENAME);
+ DelegatingURLClassLoader.TRACE_FILTERS = options.getProperty(OPTION_TRACE_FILTERS, DelegatingURLClassLoader.TRACE_FILTERS);
+ DelegatingURLClassLoader.TRACE_CLASSES = getBooleanOption(OPTION_TRACE_CLASSES, DelegatingURLClassLoader.TRACE_CLASSES);
+ DelegatingURLClassLoader.TRACE_PLUGINS = getBooleanOption(OPTION_TRACE_PLUGINS, DelegatingURLClassLoader.TRACE_PLUGINS);
+
+ DelegatingURLClassLoader.DEBUG_PROPERTIES = getBooleanOption(OPTION_LOADER_PROPERTIES, DelegatingURLClassLoader.DEBUG_PROPERTIES);
+ DelegatingURLClassLoader.DEBUG_PACKAGE_PREFIXES = getBooleanOption(OPTION_LOADER_PACKAGE_PREFIXES, DelegatingURLClassLoader.DEBUG_PACKAGE_PREFIXES);
+ DelegatingURLClassLoader.DEBUG_PACKAGE_PREFIXES_SUCCESS = getBooleanOption(OPTION_LOADER_PACKAGE_PREFIXES_SUCCESS, DelegatingURLClassLoader.DEBUG_PACKAGE_PREFIXES_SUCCESS);
+ DelegatingURLClassLoader.DEBUG_PACKAGE_PREFIXES_FAILURE = getBooleanOption(OPTION_LOADER_PACKAGE_PREFIXES_FAILURE, DelegatingURLClassLoader.DEBUG_PACKAGE_PREFIXES_FAILURE);
+ */
+ }
+
+ /**
+ * Helper method that creates an URL object from the given string
+ * representation. The string must correspond to a valid URL or file system
+ * path.
+ */
+ private static URL getURL(String urlString) throws MalformedURLException {
+ try {
+ return new URL(urlString);
+ } catch (MalformedURLException e) {
+ // if it is not a well formed URL, tries to create a "file:" URL
+ try {
+ return new File(urlString).toURL();
+ } catch (MalformedURLException ex) {
+ // re-throw the original exception if nothing works
+ throw e;
+ }
+ }
+ }
+ /**
+ * Parses the command line arguments and remembers them so they can be processed later.
+ *
+ * @param args The command line arguments
+ * @return String [] Any arguments that should be passed to the console
+ */
+ private String[] parseArgs(String[] args) {
+ Vector consoleArgsVector = new Vector();
+ for (int i = 0; i < args.length; i++) {
+ boolean match = false;
+
+ // Have to check for args that may be contained in double quotes but broken up by spaces - for example
+ // -adaptor::"bundledir=c:/my bundle dir":reset should all be one arg, but java breaks it into 3 args,
+ // ignoring the quotes. Must put it back together into one arg.
+ String fullarg = args[i];
+ int quoteidx = fullarg.indexOf("\"");
+ if (quoteidx > 0) {
+ if (quoteidx == fullarg.lastIndexOf("\"")) {
+ boolean stillparsing = true;
+ i++;
+ while (i < args.length && stillparsing) {
+ fullarg = fullarg + " " + args[i];
+ i++;
+ if (quoteidx < fullarg.lastIndexOf("\"")) {
+ stillparsing = false;
+ }
+ }
+ }
+ } else {
+ // IDE can't pass double quotes due to known eclipse bug (see Bugzilla 93201). Allowing for use of single quotes.
+ quoteidx = fullarg.indexOf("'");
+ if (quoteidx > 0) {
+ if (quoteidx == fullarg.lastIndexOf("'")) {
+ boolean stillparsing = true;
+ i++;
+ while (i < args.length && stillparsing) {
+ fullarg = fullarg + " " + args[i];
+ i++;
+ if (quoteidx < fullarg.lastIndexOf("'")) {
+ stillparsing = false;
+ }
+ }
+ }
+ fullarg = fullarg.replace('\'', '\"');
+ }
+ }
+
+ Tokenizer tok = new Tokenizer(fullarg);
+ if (tok.hasMoreTokens()) {
+ String command = tok.getString(" ");
+ StringTokenizer subtok = new StringTokenizer(command, ":");
+ String subcommand = subtok.nextToken().toLowerCase();
+
+ if (matchCommand("-console", subcommand, 4)) {
+ _console(command);
+ match = true;
+ }
+ if (matchCommand("-adaptor", subcommand, 2)) {
+ _adaptor(command);
+ match = true;
+ }
+ if (matchCommand("-install", subcommand, 2)) {
+ _install(command);
+ match = true;
+ }
+ if (matchCommand("-debug", subcommand, 2)) {
+ _debug(command);
+ match = true;
+ }
+ if (matchCommand("-application", subcommand, 3)) {
+ _application(command);
+ match = true;
+ }
+
+ if (match == false) {
+ // if the command doesn't match any of the known commands, save it to pass
+ // to the console
+ consoleArgsVector.addElement(fullarg);
+ }
+ }
+ }
+ // convert arguments to be passed to console into a string array for the Console
+ String[] consoleArgsArray = new String[consoleArgsVector.size()];
+ Enumeration e = consoleArgsVector.elements();
+ for (int i = 0; i < consoleArgsArray.length; i++) {
+ consoleArgsArray[i] = (String) e.nextElement();
+ }
+ return consoleArgsArray;
+ }
+
+ public static boolean matchCommand(
+ String command,
+ String input,
+ int minLength) {
+ if (minLength <= 0) {
+ minLength = command.length();
+ }
+
+ int length = input.length();
+
+ if (minLength > length) {
+ length = minLength;
+ }
+
+ return (command.regionMatches(0, input, 0, length));
+ }
+
+ protected void _debug(String command) {
+ StringTokenizer tok = new StringTokenizer(command, ":");
+ // first token is always "-console"
+ String cmd = tok.nextToken();
+ if (tok.hasMoreTokens()) {
+ debugOptionsFilename = tok.nextToken();
+ }
+ debugMode = true;
+ }
+
+ protected void _install(String command) {
+ StringTokenizer tok = new StringTokenizer(command, ":");
+ // first token is always "-console"
+ String cmd = tok.nextToken();
+ if (tok.hasMoreTokens()) {
+ installLocation = tok.nextToken();
+ }
+ }
+ /**
+ * Remembers that the -console option has been requested.
+ */
+ protected void _console(String command) {
+ console = true;
+ StringTokenizer tok = new StringTokenizer(command, ":");
+ // first token is always "-console"
+ String cmd = tok.nextToken();
+ if (tok.hasMoreTokens()) {
+ consolePort = tok.nextToken();
+ }
+ }
+
+ /**
+ * Remembers that the -adaptor option has been requested. Parses off the adaptor class
+ * file name, the adaptor file name, and the size if they are there.
+ *
+ * @param tok The rest of the -adaptor parameter string that contains the class file name,
+ * and possibly the adaptor file and file size.
+ */
+ protected void _adaptor(String command) {
+ Tokenizer tok = new Tokenizer(command);
+ // first token is always "-adaptor"
+ String cmd = tok.getToken(":");
+ tok.getChar(); // advance to next token
+ // and next token is either adaptor class name or ":" if we should use the default adaptor
+ String adp = tok.getToken(":");
+ if (adp!=null && adp.length() > 0) {
+ adaptorClassName = adp;
+ }
+
+ // following tokens are arguments to be processed by the adaptor implementation class
+ // they may be enclosed in quotes
+ // store them in a vector until we know how many there are
+ Vector v = new Vector();
+ parseloop : while (true) {
+ tok.getChar(); // advance to next token
+ String arg = tok.getString(":");
+ if (arg == null) {
+ break parseloop;
+ } else {
+ v.addElement(arg);
+ }
+ }
+ // now that we know how many args there are, move args from vector to String []
+ if (v != null) {
+ int numArgs = v.size();
+ adaptorArgs = new String[numArgs];
+ Enumeration e = v.elements();
+ for (int i = 0; i < numArgs; i++) {
+ adaptorArgs[i] = (String) e.nextElement();
+ }
+ }
+ }
+
+ /**
+ * Remembers that the -application option has been requested. Parses off the application parameters
+ * into a String []
+ *
+ * @param tok The rest of the -application parameter string that contains the application arguments
+ */
+ protected void _application(String command) {
+ Tokenizer tok = new Tokenizer(command);
+ // first token is always "-adaptor"
+ String cmd = tok.getToken(":");
+ // following tokens are arguments to be processed by the adaptor implementation class
+ // they may be enclosed in quotes
+ // store them in a vector until we know how many there are
+ Vector v = new Vector();
+ parseloop : while (true) {
+ tok.getChar(); // advance to next token
+ String arg = tok.getToken(";");
+ if (arg == null) {
+ break parseloop;
+ } else {
+ v.addElement(arg);
+ }
+ }
+ // now that we know how many args there are, move args from vector to String []
+ if (v != null) {
+ int numArgs = v.size();
+ applicationArgs = new String[numArgs];
+ Enumeration e = v.elements();
+ for (int i = 0; i < numArgs; i++) {
+ applicationArgs[i] = (String) e.nextElement();
+ }
+ }
+ }
+
+ public String[] loadBundles() {
+ String name;
+ File dir;
+ ServiceReference packageAdminRef;
+ PackageAdmin packageAdmin=null;
+ String location;
+
+ String tmp1 = System.getProperty("osgi.bundles");
+ StringTokenizer tokenizer = new StringTokenizer(tmp1, ",");
+ Vector list = new Vector();
+ while (tokenizer.hasMoreTokens()) {
+ list.addElement(tokenizer.nextToken());
+ }
+
+ String[] bundles = new String[list.size()];
+ list.toArray(bundles);
+
+ packageAdminRef = context.getServiceReference("org.osgi.service.packageadmin.PackageAdmin");
+ if (packageAdminRef != null) {
+ packageAdmin = (org.osgi.service.packageadmin.PackageAdmin)context.getService(packageAdminRef);
+ if (packageAdmin == null) return bundles;
+ }
+
+ String syspath = getSysPath();
+ Bundle bundle;
+ Vector installed = new Vector();
+ Vector ignored = new Vector();
+ for (int i = 0; i < bundles.length; i++) {
+ name = bundles[i];
+ if (name == null)
+ continue;
+ try {
+ location = "reference:file:/"+syspath+"/"+name;
+ bundle = context.installBundle(location);
+ installed.addElement(bundle);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.err.println("Ignoring " + name);
+ ignored.addElement(name);
+ continue;
+ }
+ }
+ Bundle a[] = new Bundle[installed.size()];
+ installed.toArray(a);
+ packageAdmin.refreshPackages(a);
+
+ Enumeration e;
+ e = installed.elements();
+ while (e.hasMoreElements()) {
+ bundle = (Bundle)e.nextElement();
+ try {
+ bundle.start();
+ } catch (BundleException ex) {
+ ex.printStackTrace();
+ System.err.println("Starting " + bundle.getLocation());
+ }
+ }
+ String tmp[] = new String[ignored.size()];
+ ignored.toArray(tmp);
+ return tmp;
+ }
+
+ private String getSysPath() {
+ String syspath;
+ syspath = System.getProperty("osgi.syspath");
+ if (syspath==null)
+ throw new RuntimeException("Missing osgi.syspath properties.");
+ if (!syspath.equals("workspace"))
+ return syspath;
+
+ Class clazz = EclipseLauncher.class;
+ ClassLoader cl = clazz.getClassLoader();
+ String thisname = clazz.getName();
+ String classname = thisname.replace('.', '/') + ".class";
+ URL url = cl.getResource(classname);
+ int idx;
+ String pathname = url.getFile();
+ idx = pathname.indexOf(classname);
+ pathname = pathname.substring(0, idx);
+
+ syspath = pathname;
+ idx = syspath.indexOf("org.eclipse.osgi.framework.core");
+ syspath = syspath.substring(0, idx);
+ return syspath;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/launcher/Launcher.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/launcher/Launcher.java
new file mode 100644
index 000000000..e720f313f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/launcher/Launcher.java
@@ -0,0 +1,423 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.launcher;
+
+import java.lang.reflect.Constructor;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.OSGi;
+import org.eclipse.osgi.framework.util.Tokenizer;
+
+/**
+ * <p>This class provides an entry point for launching the OSGi framework.
+ * It configures the OSGi framework according to the command line arguments:
+ * <ul>
+ * <li><b>-con[sole][:<i>port</i>]</b><br>
+ * Starts the OSGi framework with a console window. Any command line arguments not recognized are passed
+ * to the console for it to execute. If a port is specified the console will listen on that
+ * port for commands. If no port is specified, the console will use System.in and System.out.
+ * </li>
+ * <li><b>-adaptor[:adaptor-name][adaptor-args]</b>
+ * <pre>
+ * [adaptor-name] := "" | fully qualified class name of the FrameworkAdapter
+ * [adaptor-args] := *( ":" [value])
+ * [value] := [token] | [quoted-string]
+ *
+ * This allows
+ *
+ * -adaptor::"bundledir=c:\jarbundles":reset DefaultAdaptor is chosen with args[] {"bundledir=c:\jarbundles", "reset"}
+ * -adaptor:com.foo.MyAdaptor com.foo.MyAdaptor chosen with args[] {}
+ * </pre>
+ * <p>-adaptor specifies the implementation class for the FrameworkAdapter to be used.
+ * args contains a list of FrameworkAdaptor arguments, separated by ":". FrameworkAdaptor arguments
+ * format is defined by the adaptor implementation class. They
+ * are passed to the adaptor class as an array of Strings.
+ * Example arguments used by the DefaultAdaptor are:
+ * <ul>
+ * <li>"bundledir=<i>directory"</i>. The directory to be used by the adaptor to store data.
+ * <li>reset</i>. Perform the reset action to clear the bundledir.
+ * <p>Actions can be defined by an adaptor. Multiple actions can be specified,
+ * separated by ":".
+ * </ul>
+ * <p>It is up to the adaptor implementation to define reasonable defaults if it's required
+ * arguments are not specified.
+ * <p>If -adaptor is not specified, or if no adaptor classname is specified, DefaultAdaptor will be
+ * used, which is file based and stores the files in the \bundles directory
+ * relative to the current directory.
+ * </ul>
+ * <li>-app[lication]:application-args
+ * <pre>
+ * [application-args] := *( ":" [value])
+ * [value] := [token] | [quoted-string]
+ * </pre>
+ * <p>This argument allows arguments to be passed to specific applications at launch time. This is for eclipse
+ * plugins installed as applications. The arguments are as Eclipse currently needs them - one list of key=value pairs
+ * which are parsed by the applications. The application peels off only the args that apply to it. Others are ignored.
+ * </li>
+ * <p>
+ * Any other command line arguments are passed on to the console window
+ * of the framework if started with the -console option. If the console is not started,
+ * any unrecognized arguments will be ignored and a message displayed.
+ * <p>
+ * If none of the options above are specified, the OSGi framework is started:
+ * <ul>
+ * <li>with the Default FrameworkAdaptor
+ * <li>without a console window
+ * <li>without the remote agent
+ * </ul>
+ */
+public class Launcher {
+// :TODO Need to NLS enable this class.
+
+ /** default console port */
+ protected String consolePort = "";
+
+ /** flag to indicate whether or not to start the console */
+ protected boolean console = false;
+
+ /** string containing the classname of the adaptor to be used in this framework instance */
+ protected String adaptorClassName = "org.eclipse.osgi.framework.internal.defaultadaptor.DefaultAdaptor";
+
+ protected final String IdeAdentClassName = "org.eclipse.osgi.framework.internal.core.ide.IdeAgent";
+
+ protected final String osgiConsoleClazz = "org.eclipse.osgi.framework.internal.core.FrameworkConsole";
+
+ /** array of adaptor arguments to be passed to FrameworkAdaptor.initialize() */
+ String[] adaptorArgs = null;
+
+ /** array of application arguments to be passed to Eclipse applications */
+ String[] applicationArgs = null;
+
+ /* Components that can be installed and activated optionally. */
+ private static final String OSGI_CONSOLE_COMPONENT_NAME = "OSGi Console";
+ private static final String OSGI_CONSOLE_COMPONENT = "osgiconsole.jar";
+
+ /**
+ * main method for Launcher. This method creates an Launcher object
+ * and kicks off the actual launch of a Framework instance.
+ *
+ * @param args The command line arguments
+ */
+ public static void main(String args[]) {
+
+ new Launcher().doIt(args);
+
+ }
+
+ /**
+ * Default constructor. Nothing at all to do here.
+ */
+ public Launcher() {}
+
+ /**
+ * Performs the actual launch based on the command line arguments
+ *
+ * @param args The command line arguments
+ */
+ protected void doIt(String[] args) {
+ String[] consoleArgs = parseArgs(args);
+
+ FrameworkAdaptor adaptor = null;
+ try {
+ adaptor = doAdaptor();
+ } catch (Exception e) {
+ System.out.println("Unable to create FrameworkAdaptor: ");
+ e.printStackTrace();
+ return;
+ }
+
+ OSGi osgi = doOSGi(adaptor);
+ if (osgi != null) {
+ if (console) {
+ doConsole(osgi, consoleArgs);
+ }
+ else {
+ osgi.launch();
+ }
+ }
+ doApplication();
+ }
+
+ /**
+ * Parses the command line arguments and remembers them so they can be processed later.
+ *
+ * @param args The command line arguments
+ * @return String [] Any arguments that should be passed to the console
+ */
+ private String[] parseArgs(String[] args) {
+ Vector consoleArgsVector = new Vector();
+ for (int i = 0; i < args.length; i++) {
+ boolean match = false;
+
+ // Have to check for args that may be contained in double quotes but broken up by spaces - for example
+ // -adaptor::"bundledir=c:/my bundle dir":reset should all be one arg, but java breaks it into 3 args,
+ // ignoring the quotes. Must put it back together into one arg.
+ String fullarg = args[i];
+ int quoteidx = fullarg.indexOf("\"");
+ if (quoteidx > 0) {
+ if (quoteidx == fullarg.lastIndexOf("\"")) {
+ boolean stillparsing = true;
+ i++;
+ while (i < args.length && stillparsing) {
+ fullarg = fullarg + " " + args[i];
+ i++;
+ if (quoteidx < fullarg.lastIndexOf("\"")) {
+ stillparsing = false;
+ }
+ }
+ }
+ } else {
+ // IDE can't pass double quotes due to known eclipse bug (see Bugzilla 93201). Allowing for use of single quotes.
+ quoteidx = fullarg.indexOf("'");
+ if (quoteidx > 0) {
+ if (quoteidx == fullarg.lastIndexOf("'")) {
+ boolean stillparsing = true;
+ i++;
+ while (i < args.length && stillparsing) {
+ fullarg = fullarg + " " + args[i];
+ i++;
+ if (quoteidx < fullarg.lastIndexOf("'")) {
+ stillparsing = false;
+ }
+ }
+ }
+ fullarg = fullarg.replace('\'', '\"');
+ }
+ }
+
+ Tokenizer tok = new Tokenizer(fullarg);
+ if (tok.hasMoreTokens()) {
+ String command = tok.getString(" ");
+ StringTokenizer subtok = new StringTokenizer(command, ":");
+ String subcommand = subtok.nextToken().toLowerCase();
+
+ if (matchCommand("-console", subcommand, 4)) {
+ _console(command);
+ match = true;
+ }
+ if (matchCommand("-adaptor", subcommand, 2)) {
+ _adaptor(command);
+ match = true;
+ }
+ if (matchCommand("-application", subcommand, 3)) {
+ _application(command);
+ match = true;
+ }
+
+ if (match == false) {
+ // if the command doesn't match any of the known commands, save it to pass
+ // to the console
+ consoleArgsVector.addElement(fullarg);
+ }
+ }
+ }
+ // convert arguments to be passed to console into a string array for the Console
+ String[] consoleArgsArray = new String[consoleArgsVector.size()];
+ Enumeration e = consoleArgsVector.elements();
+ for (int i = 0; i < consoleArgsArray.length; i++) {
+ consoleArgsArray[i] = (String) e.nextElement();
+ }
+ return consoleArgsArray;
+ }
+
+ public static boolean matchCommand(String command, String input, int minLength) {
+ if (minLength <= 0) {
+ minLength = command.length();
+ }
+
+ int length = input.length();
+
+ if (minLength > length) {
+ length = minLength;
+ }
+
+ return (command.regionMatches(0, input, 0, length));
+ }
+
+ /**
+ * Remembers that the -console option has been requested.
+ */
+ protected void _console(String command) {
+ console = true;
+ StringTokenizer tok = new StringTokenizer(command, ":");
+ // first token is always "-console"
+ String cmd = tok.nextToken();
+ if (tok.hasMoreTokens()) {
+ consolePort = tok.nextToken();
+ }
+ }
+
+ /**
+ * Remembers that the -adaptor option has been requested. Parses off the adaptor class
+ * file name, the adaptor file name, and the size if they are there.
+ *
+ * @param tok The rest of the -adaptor parameter string that contains the class file name,
+ * and possibly the adaptor file and file size.
+ */
+ protected void _adaptor(String command) {
+ Tokenizer tok = new Tokenizer(command);
+ // first token is always "-adaptor"
+ String cmd = tok.getToken(":");
+ tok.getChar(); // advance to next token
+ // and next token is either adaptor class name or ":" if we should use the default adaptor
+ String adp = tok.getToken(":");
+ if (adp.length() > 0){
+ adaptorClassName = adp;
+ }
+
+ // The following tokens are arguments to be processed by the adaptor implementation class.
+ // They may be enclosed in quotes.
+ // Store them in a vector until we know how many there are.
+ Vector v = new Vector();
+ parseloop : while (true) {
+ tok.getChar(); // advance to next token
+ String arg = tok.getString(":");
+ if (arg == null) {
+ break parseloop;
+ } else {
+ v.addElement(arg);
+ }
+ }
+ // now that we know how many args there are, move args from vector to String[]
+ if (v != null) {
+ int numArgs = v.size();
+ adaptorArgs = new String[numArgs];
+ Enumeration e = v.elements();
+ for (int i = 0; i < numArgs; i++) {
+ adaptorArgs[i] = (String) e.nextElement();
+ }
+ }
+ }
+
+ /**
+ * Remembers that the -application option has been requested. Parses off the application parameters
+ * into a String []
+ *
+ * @param tok The rest of the -application parameter string that contains the application arguments
+ */
+ protected void _application(String command) {
+ Tokenizer tok = new Tokenizer(command);
+ // first token is always "-adaptor"
+ String cmd = tok.getToken(":");
+ // following tokens are arguments to be processed by the adaptor implementation class
+ // they may be enclosed in quotes
+ // store them in a vector until we know how many there are
+ Vector v = new Vector();
+ parseloop : while (true) {
+ tok.getChar(); // advance to next token
+ String arg = tok.getString(":");
+ if (arg == null) {
+ break parseloop;
+ } else {
+ v.addElement(arg);
+ }
+ }
+ // now that we know how many args there are, move args from vector to String []
+ if (v != null) {
+ int numArgs = v.size();
+ applicationArgs = new String[numArgs];
+ Enumeration e = v.elements();
+ for (int i = 0; i < numArgs; i++) {
+ applicationArgs[i] = (String) e.nextElement();
+ }
+ }
+ }
+
+
+ /**
+ * Processes the -adaptor command line argument.
+ *
+ * Parses the arguments to get the adaptor class name, the adaptor dir or filename,
+ * and the adaptor file size.
+ *
+ * @return a FrameworkAdaptor object
+ */
+ protected FrameworkAdaptor doAdaptor() throws Exception {
+
+ Class adaptorClass = Class.forName(adaptorClassName);
+ Class[] constructorArgs = new Class[] { String[].class };
+ Constructor constructor = adaptorClass.getConstructor(constructorArgs);
+ return (FrameworkAdaptor) constructor.newInstance(
+ new Object[] { adaptorArgs });
+ }
+
+ /**
+ * Processes the -application command line argument.
+ *
+ */
+ protected void doApplication() {
+ // nothing to do right now - these are argumnents for eclipse applications
+
+ }
+
+
+ /**
+ * Creates the OSGi framework object.
+ *
+ * @param The FrameworkAdaptor object
+ */
+ protected OSGi doOSGi(FrameworkAdaptor adaptor) {
+ return new OSGi(adaptor);
+ }
+
+ /**
+ * Invokes the OSGi Console on another thread
+ *
+ * @param osgi The current OSGi instance for the console to attach to
+ * @param consoleArgs An String array containing commands from the command line
+ * for the console to execute
+ */
+ protected void doConsole(OSGi osgi, String[] consoleArgs) {
+
+ Constructor consoleConstructor;
+ Object osgiconsole;
+ Class[] parameterTypes;
+ Object[] parameters;
+
+ try {
+ Class osgiconsoleClass = Class.forName(osgiConsoleClazz);
+ if (consolePort.length() == 0) {
+ parameterTypes = new Class[] { OSGi.class, String[].class };
+ parameters = new Object[] { osgi, consoleArgs };
+ } else {
+ parameterTypes = new Class[] { OSGi.class, int.class, String[].class };
+ parameters = new Object[] { osgi, new Integer(consolePort), consoleArgs };
+ }
+ consoleConstructor = osgiconsoleClass.getConstructor(parameterTypes);
+ osgiconsole = consoleConstructor.newInstance(parameters);
+
+ Thread t = new Thread(((Runnable) osgiconsole), OSGI_CONSOLE_COMPONENT_NAME);
+ t.start();
+ } catch (NumberFormatException nfe) {
+ System.err.println("Invalid console port: " + consolePort);
+ } catch (Exception ex) {
+ informAboutMissingComponent(OSGI_CONSOLE_COMPONENT_NAME, OSGI_CONSOLE_COMPONENT);
+ }
+
+ }
+
+ /**
+ * Informs the user about a missing component.
+ *
+ * @param component The name of the component
+ * @param jar The jar file that contains the component
+ */
+ void informAboutMissingComponent(String component, String jar) {
+ System.out.println();
+ System.out.print("Warning: The requested component '" + component + "' is not included in this runtime.");
+ System.out.println(" Add '" + jar + "' to the classpath or rebuild the jxe with it.");
+ System.out.println();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/msg/MessageFormat.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/msg/MessageFormat.java
new file mode 100644
index 000000000..14fbfa3f8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/msg/MessageFormat.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.msg;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class MessageFormat
+{
+
+ // ResourceBundle holding the messages.
+ private ResourceBundle bundle;
+ private Locale locale;
+
+ public MessageFormat(String bundleName)
+ {
+ init(bundleName, Locale.getDefault(), this.getClass());
+ }
+
+ public MessageFormat(String bundleName, Locale locale)
+ {
+ init(bundleName, locale, this.getClass());
+ }
+
+ public MessageFormat(String bundleName, Locale locale, Class clazz)
+ {
+ init(bundleName, locale, clazz);
+ }
+
+ protected void init(final String bundleName, final Locale locale, final Class clazz)
+ {
+ bundle = (ResourceBundle)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ ClassLoader loader = clazz.getClassLoader();
+
+ if (loader == null)
+ {
+ loader = ClassLoader.getSystemClassLoader();
+ }
+
+ try
+ {
+ return ResourceBundle.getBundle(bundleName, locale, loader);
+ }
+ catch (MissingResourceException e)
+ {
+ return null;
+ }
+ }
+ });
+
+ this.locale = locale;
+ }
+
+ /**
+ * Return the Locale object used for this MessageFormat object.
+ * @return Locale of this object.
+ */
+ public Locale getLocale()
+ {
+ return locale;
+ }
+
+ /**
+ * Retrieves a message which has no arguments.
+ * @param msg String
+ * the key to look up.
+ * @return String
+ * the message for that key in the system
+ * message bundle.
+ */
+ public String getString (String msg)
+ {
+ if (bundle == null)
+ {
+ return msg;
+ }
+
+ try
+ {
+ return bundle.getString(msg);
+ }
+ catch (MissingResourceException e)
+ {
+ return msg;
+ }
+ }
+
+ /**
+ * Retrieves a message which takes 1 argument.
+ * @param msg String
+ * the key to look up.
+ * @param arg Object
+ * the object to insert in the formatted output.
+ * @return String
+ * the message for that key in the system
+ * message bundle.
+ */
+ public String getString (String msg, Object arg)
+ {
+ return getString(msg, new Object[] {arg});
+ }
+
+ /**
+ * Retrieves a message which takes 1 integer argument.
+ * @param msg String
+ * the key to look up.
+ * @param arg int
+ * the integer to insert in the formatted output.
+ * @return String
+ * the message for that key in the system
+ * message bundle.
+ */
+ public String getString (String msg, int arg)
+ {
+ return getString(msg, new Object[] {Integer.toString(arg)});
+ }
+
+ /**
+ * Retrieves a message which takes 1 character argument.
+ * @param msg String
+ * the key to look up.
+ * @param arg char
+ * the character to insert in the formatted output.
+ * @return String
+ * the message for that key in the system
+ * message bundle.
+ */
+ public String getString (String msg, char arg)
+ {
+ return getString(msg, new Object[] {String.valueOf(arg)});
+ }
+
+ /**
+ * Retrieves a message which takes 2 arguments.
+ * @param msg String
+ * the key to look up.
+ * @param arg1 Object
+ * an object to insert in the formatted output.
+ * @param arg2 Object
+ * another object to insert in the formatted output.
+ * @return String
+ * the message for that key in the system
+ * message bundle.
+ */
+ public String getString (String msg, Object arg1, Object arg2)
+ {
+ return getString(msg, new Object[] {arg1, arg2});
+ }
+
+ /**
+ * Retrieves a message which takes several arguments.
+ * @param msg String
+ * the key to look up.
+ * @param args Object[]
+ * the objects to insert in the formatted output.
+ * @return String
+ * the message for that key in the system
+ * message bundle.
+ */
+ public String getString (String msg, Object[] args)
+ {
+ String format = msg;
+
+ if (bundle != null)
+ {
+ try
+ {
+ format = bundle.getString(msg);
+ }
+ catch (MissingResourceException e)
+ {
+ }
+ }
+
+ return format(format, args);
+ }
+
+ /**
+ * Generates a formatted text string given a source string
+ * containing "argument markers" of the form "{argNum}"
+ * where each argNum must be in the range 0..9. The result
+ * is generated by inserting the toString of each argument
+ * into the position indicated in the string.
+ * <p>
+ * To insert the "{" character into the output, use a single
+ * backslash character to escape it (i.e. "\{"). The "}"
+ * character does not need to be escaped.
+ * @param format String
+ * the format to use when printing.
+ * @param args Object[]
+ * the arguments to use.
+ * @return String
+ * the formatted message.
+ */
+ public static String format (String format, Object[] args)
+ {
+ StringBuffer answer = new StringBuffer();
+ String[] argStrings = new String[args.length];
+
+ for (int i = 0; i < args.length; ++i)
+ {
+ if (args[i] == null)
+ argStrings[i] = "<null>";
+ else
+ argStrings[i] = args[i].toString();
+ }
+
+ int lastI = 0;
+
+ for (int i = format.indexOf('{', 0); i >= 0; i = format.indexOf('{', lastI))
+ {
+ if (i != 0 && format.charAt(i-1) == '\\')
+ {
+ // It's escaped, just print and loop.
+ if (i != 1)
+ {
+ answer.append(format.substring(lastI,i-1));
+ }
+ answer.append('{');
+ lastI = i+1;
+ }
+ else
+ {
+ // It's a format character.
+ if (i > format.length()-3)
+ {
+ // Bad format, just print and loop.
+ answer.append(format.substring(lastI, format.length()));
+ lastI = format.length();
+ }
+ else
+ {
+ int argnum = (byte) Character.digit(format.charAt(i+1), 10);
+ if (argnum < 0 || format.charAt(i+2) != '}')
+ {
+ // Bad format, just print and loop.
+ answer.append(format.substring(lastI, i+1));
+ lastI = i+1;
+ }
+ else
+ {
+ // Got a good one!
+ answer.append(format.substring(lastI, i));
+ if (argnum >= argStrings.length)
+ {
+ answer.append("<missing argument>");
+ }
+ else
+ {
+ answer.append(argStrings[argnum]);
+ }
+ lastI = i + 3;
+ }
+ }
+ }
+ }
+
+ if (lastI < format.length())
+ {
+ answer.append(format.substring(lastI, format.length()));
+ }
+
+ return answer.toString();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/CreateThread.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/CreateThread.java
new file mode 100644
index 000000000..70b536f91
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/CreateThread.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.security.action;
+
+import java.security.PrivilegedAction;
+
+/**
+ * Creat a Thread PrivilegedAction class.
+ */
+
+public class CreateThread implements PrivilegedAction
+{
+ private Runnable target;
+ private String name;
+
+ public CreateThread(Runnable target, String name)
+ {
+ this.target = target;
+ this.name = name;
+ }
+
+ public Object run()
+ {
+ return new Thread(target, name);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/GetProperty.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/GetProperty.java
new file mode 100644
index 000000000..e002c5f60
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/GetProperty.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.security.action;
+
+import java.security.PrivilegedAction;
+
+/**
+ * Get System Property PrivilegedAction class.
+ */
+
+public class GetProperty implements PrivilegedAction
+{
+ private String property;
+ private String def;
+
+ public GetProperty(String property)
+ {
+ this.property = property;
+ }
+
+ public GetProperty(String property, String def)
+ {
+ this.property = property;
+ this.def = def;
+ }
+
+ /**
+ @exception java.lang.NullPointerException
+ @exception java.lang.IllegalArgumentException
+ */
+ public Object run()
+ {
+ return System.getProperty(property, def);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/PermissionStorage.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/PermissionStorage.java
new file mode 100644
index 000000000..212cd037d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/security/action/PermissionStorage.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.security.action;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * PermissionStorage privileged action class.
+ */
+
+public class PermissionStorage implements org.eclipse.osgi.framework.adaptor.PermissionStorage, PrivilegedExceptionAction
+{
+ private org.eclipse.osgi.framework.adaptor.PermissionStorage storage;
+ private String location;
+ private String[] data;
+ private int action;
+ private static final int GET = 1;
+ private static final int SET = 2;
+ private static final int LOCATION = 3;
+
+ public PermissionStorage(org.eclipse.osgi.framework.adaptor.PermissionStorage storage)
+ {
+ this.storage = storage;
+ }
+
+ public Object run() throws IOException
+ {
+ switch (action)
+ {
+ case GET:
+ return storage.getPermissionData(location);
+ case SET:
+ storage.setPermissionData(location, data);
+ return null;
+ case LOCATION:
+ return storage.getLocations();
+ }
+
+ throw new UnsupportedOperationException();
+ }
+
+ public String[] getPermissionData(String location) throws IOException
+ {
+ this.location = location;
+ this.action = GET;
+
+ try
+ {
+ return (String[]) AccessController.doPrivileged(this);
+ }
+ catch (PrivilegedActionException e)
+ {
+ throw (IOException) e.getException();
+ }
+ }
+
+ public String[] getLocations() throws IOException
+ {
+ this.action = LOCATION;
+
+ try
+ {
+ return (String[]) AccessController.doPrivileged(this);
+ }
+ catch (PrivilegedActionException e)
+ {
+ throw (IOException) e.getException();
+ }
+ }
+
+ public void setPermissionData(String location, String[] data) throws IOException
+ {
+ this.location = location;
+ this.data = data;
+ this.action = SET;
+
+ try
+ {
+ AccessController.doPrivileged(this);
+ }
+ catch (PrivilegedActionException e)
+ {
+ throw (IOException) e.getException();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/tracker/ServiceTracker.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/tracker/ServiceTracker.java
new file mode 100644
index 000000000..653969014
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/tracker/ServiceTracker.java
@@ -0,0 +1,884 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.tracker;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The <tt>ServiceTracker</tt> class simplifies using services
+ * from the Framework's service registry.
+ * <p>
+ * A <tt>ServiceTracker</tt> object is constructed with
+ * search criteria and a
+ * <tt>ServiceTrackerCustomizer</tt> object.
+ * A <tt>ServiceTracker</tt> object can use the <tt>ServiceTrackerCustomizer</tt> object
+ * to customize the service objects to be tracked.
+ * The <tt>ServiceTracker</tt> object can then be opened to begin tracking
+ * all services in the
+ * Framework's service registry that match the specified
+ * search criteria. The <tt>ServiceTracker</tt> object correctly handles all
+ * of the details of listening to <tt>ServiceEvent</tt> objects and
+ * getting and ungetting services.
+ * <p>
+ * The <tt>getServiceReferences</tt> method can be called to get
+ * references to the services being tracked. The <tt>getService</tt>
+ * and <tt>getServices</tt> methods can be called to get the service
+ * objects for the tracked service.
+ */
+
+public class ServiceTracker implements ServiceTrackerCustomizer
+{
+ /* set this to true to compile in debug messages */
+ private static final boolean DEBUG = false;
+
+ /**
+ * Bundle context this <tt>ServiceTracker</tt> object is tracking against.
+ */
+ protected final BundleContext context;
+
+ /**
+ * Filter specifying search criteria for the services to track.
+ */
+ protected final Filter filter;
+
+ /**
+ * Tracked services: <tt>ServiceReference</tt> object -> customized Object
+ * and <tt>ServiceListener</tt> object
+ */
+ private Tracked tracked;
+
+ /** <tt>ServiceTrackerCustomizer</tt> object for this tracker. */
+ private ServiceTrackerCustomizer customizer;
+
+ /**
+ * Create a <tt>ServiceTracker</tt> object on the specified <tt>ServiceReference</tt> object.
+ *
+ * <p>The service referenced by the specified <tt>ServiceReference</tt> object
+ * will be tracked by this <tt>ServiceTracker</tt> object.
+ *
+ * @param context <tt>BundleContext</tt> object against which the tracking is done.
+ * @param reference <tt>ServiceReference</tt> object for the service to be tracked.
+ * @param customizer The customizer object to call when services are
+ * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+ * If customizer is <tt>null</tt>, then this <tt>ServiceTracker</tt> object will be used
+ * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt>
+ * object will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+ */
+ public ServiceTracker(BundleContext context, ServiceReference reference,
+ ServiceTrackerCustomizer customizer)
+ {
+ this.context = context;
+ this.customizer = (customizer == null) ? this : customizer;
+
+ try
+ {
+ this.filter = context.createFilter("("+Constants.SERVICE_ID+"="+reference.getProperty(Constants.SERVICE_ID).toString()+")");
+ }
+ catch (InvalidSyntaxException e)
+ {
+ throw new RuntimeException("unexpected InvalidSyntaxException: "+e.getMessage());
+ }
+ }
+
+ /**
+ * Create a <tt>ServiceTracker</tt> object on the specified class name.
+ *
+ * <p>Services registered under the specified class name will be tracked
+ * by this <tt>ServiceTracker</tt> object.
+ *
+ * @param context <tt>BundleContext</tt> object against which the tracking is done.
+ * @param clazz Class name of the services to be tracked.
+ * @param customizer The customizer object to call when services are
+ * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+ * If customizer is <tt>null</tt>, then this <tt>ServiceTracker</tt> object will be used
+ * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt> object
+ * will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+ */
+ public ServiceTracker(BundleContext context, String clazz,
+ ServiceTrackerCustomizer customizer)
+ {
+ this.context = context;
+ this.customizer = (customizer == null) ? this : customizer;
+
+ try
+ {
+ this.filter = context.createFilter("("+Constants.OBJECTCLASS+"="+clazz+")");
+ }
+ catch (InvalidSyntaxException e)
+ {
+ throw new RuntimeException("unexpected InvalidSyntaxException: "+e.getMessage());
+ }
+
+ if (clazz == null)
+ {
+ throw new NullPointerException();
+ }
+ }
+
+ /**
+ * Create a <tt>ServiceTracker</tt> object on the specified <tt>Filter</tt> object.
+ *
+ * <p>Services which match the specified <tt>Filter</tt> object will be tracked
+ * by this <tt>ServiceTracker</tt> object.
+ *
+ * @param context <tt>BundleContext</tt> object against which the tracking is done.
+ * @param filter <tt>Filter</tt> object to select the services to be tracked.
+ * @param customizer The customizer object to call when services are
+ * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+ * If customizer is null, then this <tt>ServiceTracker</tt> object will be used
+ * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt>
+ * object will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+ */
+ public ServiceTracker(BundleContext context, Filter filter,
+ ServiceTrackerCustomizer customizer)
+ {
+ this.context = context;
+ this.filter = filter;
+ this.customizer = (customizer == null) ? this : customizer;
+
+ if ((context == null) || (filter == null))
+ {
+ throw new NullPointerException();
+ }
+ }
+
+ /**
+ * Open this <tt>ServiceTracker</tt> object and begin tracking services.
+ *
+ * <p>Services which match the search criteria specified when
+ * this <tt>ServiceTracker</tt> object was created are now tracked
+ * by this <tt>ServiceTracker</tt> object.
+ *
+ * @throws java.lang.IllegalStateException if the <tt>BundleContext</tt>
+ * object with which this <tt>ServiceTracker</tt> object was created is no longer valid.
+ */
+ public synchronized void open()
+ {
+ if (tracked == null)
+ {
+ if (DEBUG)
+ {
+ System.out.println("ServiceTracker.open: "+filter);
+ }
+
+ tracked = new Tracked(customizer, filter);
+
+ ServiceReference[] references;
+
+ synchronized (tracked)
+ {
+ context.addServiceListener(tracked);
+
+ try
+ {
+ references = context.getServiceReferences(null, filter.toString());
+ }
+ catch (InvalidSyntaxException e)
+ {
+ throw new RuntimeException("unexpected InvalidSyntaxException");
+ }
+ }
+
+ /* Call tracked outside of synchronized region */
+ if (references != null)
+ {
+ int size = references.length;
+
+ for (int i=0; i < size; i++)
+ {
+ ServiceReference reference = references[i];
+
+ /* if the service is still registered */
+ if (reference.getBundle() != null)
+ {
+ tracked.track(reference);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Close this <tt>ServiceTracker</tt> object.
+ *
+ * <p>This method should be called when this <tt>ServiceTracker</tt> object
+ * should end the tracking of services.
+ */
+
+ public synchronized void close()
+ {
+ if (tracked != null)
+ {
+ if (DEBUG)
+ {
+ System.out.println("ServiceTracker.close: "+filter);
+ }
+
+ tracked.close();
+
+ ServiceReference references[] = getServiceReferences();
+
+ Tracked outgoing = tracked;
+ tracked = null;
+
+ try
+ {
+ context.removeServiceListener(outgoing);
+ }
+ catch (IllegalStateException e)
+ {
+ /* In case the context was stopped. */
+ }
+
+ if (references != null)
+ {
+ for (int i = 0; i < references.length; i++)
+ {
+ outgoing.untrack(references[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Properly close this <tt>ServiceTracker</tt> object when finalized.
+ * This method calls the <tt>close</tt> method to close this <tt>ServiceTracker</tt> object
+ * if it has not already been closed.
+ *
+ */
+ protected void finalize() throws Throwable
+ {
+ close();
+ }
+
+ /**
+ * Default implementation of the <tt>ServiceTrackerCustomizer.addingService</tt> method.
+ *
+ * <p>This method is only called when this <tt>ServiceTracker</tt> object
+ * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+ *
+ * The default implementation returns the result of
+ * calling <tt>getService</tt>, on the
+ * <tt>BundleContext</tt> object with which this <tt>ServiceTracker</tt> object was created,
+ * passing the specified <tt>ServiceReference</tt> object.
+ * <p>This method can be overridden in a subclass to customize
+ * the service object to be tracked for the service
+ * being added. In that case, take care not
+ * to rely on the default implementation of removedService that will unget the service.
+ *
+ * @param reference Reference to service being added to this
+ * <tt>ServiceTracker</tt> object.
+ * @return The service object to be tracked for the service
+ * added to this <tt>ServiceTracker</tt> object.
+ * @see ServiceTrackerCustomizer
+ */
+ public Object addingService(ServiceReference reference)
+ {
+ return context.getService(reference);
+ }
+
+ /**
+ * Default implementation of the <tt>ServiceTrackerCustomizer.modifiedService</tt> method.
+ *
+ * <p>This method is only called when this <tt>ServiceTracker</tt> object
+ * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+ *
+ * The default implementation does nothing.
+ *
+ * @param reference Reference to modified service.
+ * @param service The service object for the modified service.
+ * @see ServiceTrackerCustomizer
+ */
+ public void modifiedService(ServiceReference reference, Object service)
+ {
+ }
+
+ /**
+ * Default implementation of the <tt>ServiceTrackerCustomizer.removedService</tt> method.
+ *
+ * <p>This method is only called when this <tt>ServiceTracker</tt> object
+ * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+ *
+ * The default implementation
+ * calls <tt>ungetService</tt>, on the
+ * <tt>BundleContext</tt> object with which this <tt>ServiceTracker</tt> object was created,
+ * passing the specified <tt>ServiceReference</tt> object.
+ * <p>This method can be overridden in a subclass. If the default
+ * implementation of <tt>addingService</tt> method was used, this method must unget the service.
+ *
+ * @param reference Reference to removed service.
+ * @param service The service object for the removed service.
+ * @see ServiceTrackerCustomizer
+ */
+ public void removedService(ServiceReference reference, Object object)
+ {
+ context.ungetService(reference);
+ }
+
+ /**
+ * Wait for at least one service to be tracked by this <tt>ServiceTracker</tt> object.
+ * <p>It is strongly recommended that <tt>waitForService</tt> is not used
+ * during the calling of the <tt>BundleActivator</tt> methods. <tt>BundleActivator</tt> methods are
+ * expected to complete in a short period of time.
+ *
+ * @param timeout time interval in milliseconds to wait. If zero,
+ * the method will wait indefinately.
+ * @return Returns the result of <tt>getService()</tt>.
+ * @throws IllegalArgumentException If the value of timeout is
+ * negative.
+ */
+ public Object waitForService(long timeout) throws InterruptedException
+ {
+ if (timeout < 0)
+ {
+ throw new IllegalArgumentException("timeout value is negative");
+ }
+
+ Object object = getService();
+
+ while (object == null)
+ {
+ Tracked tracked = this.tracked; /* use local var since we are not synchronized */
+
+ if (tracked == null) /* if ServiceTracker is not open */
+ {
+ return null;
+ }
+
+ synchronized (tracked)
+ {
+ if (tracked.size() == 0)
+ {
+ tracked.wait(timeout);
+ }
+ }
+
+ object = getService();
+
+ if (timeout > 0)
+ {
+ return object;
+ }
+ }
+
+ return object;
+ }
+
+ /**
+ * Return an array of <tt>ServiceReference</tt> objects for all services
+ * being tracked by this <tt>ServiceTracker</tt> object.
+ *
+ * @return Array of <tt>ServiceReference</tt> objects or <tt>null</tt> if no service
+ * are being tracked.
+ */
+ public ServiceReference[] getServiceReferences()
+ {
+ Tracked tracked = this.tracked; /* use local var since we are not synchronized */
+
+ if (tracked == null) /* if ServiceTracker is not open */
+ {
+ return null;
+ }
+
+ synchronized (tracked)
+ {
+ int size = tracked.size();
+
+ if (size == 0)
+ {
+ return null;
+ }
+
+ ServiceReference references[] = new ServiceReference[size];
+
+ Enumeration enum = tracked.keys();
+
+ for (int i = 0; i < size; i++)
+ {
+ references[i] = (ServiceReference)enum.nextElement();
+ }
+
+ return references;
+ }
+ }
+
+ /**
+ * Return an array of service objects for all services
+ * being tracked by this <tt>ServiceTracker</tt> object.
+ *
+ * @return Array of service objects or <tt>null</tt> if no service
+ * are being tracked.
+ */
+ public Object[] getServices()
+ {
+ Tracked tracked = this.tracked; /* use local var since we are not synchronized */
+
+ if (tracked == null) /* if ServiceTracker is not open */
+ {
+ return null;
+ }
+
+ synchronized (tracked)
+ {
+ int size = tracked.size();
+
+ if (size == 0)
+ {
+ return null;
+ }
+
+ Object objects[] = new Object[size];
+
+ Enumeration enum = tracked.elements();
+
+ for (int i = 0; i < size; i++)
+ {
+ objects[i] = enum.nextElement();
+ }
+
+ return objects;
+ }
+ }
+
+ /**
+ * Returns a <tt>ServiceReference</tt> object for one of the services
+ * being tracked by this <tt>ServiceTracker</tt> object.
+ *
+ * <p>If multiple services are being tracked, the service
+ * with the highest ranking (as specified in its <tt>service.ranking</tt> property) is
+ * returned.
+ *
+ * <p>If there is a tie in ranking, the service with the lowest
+ * service ID (as specified in its <tt>service.id</tt> property); that is,
+ * the service that was registered first is returned.
+ * <p>This is the same algorithm used by <tt>BundleContext.getServiceReference</tt>.
+ *
+ * @return <tt>ServiceReference</tt> object or <tt>null</tt> if no service is being tracked.
+ */
+ public ServiceReference getServiceReference()
+ {
+ ServiceReference[] references = getServiceReferences();
+
+ int length = (references == null) ? 0 : references.length;
+
+ if (length > 0) /* if a service is being tracked */
+ {
+ int index = 0;
+
+ if (length > 1) /* if more than one service, select highest ranking */
+ {
+ int rankings[] = new int[length];
+ int count = 0;
+ int maxRanking = Integer.MIN_VALUE;
+
+ for (int i = 0 ; i < length; i++)
+ {
+ Object property = references[i].getProperty(Constants.SERVICE_RANKING);
+
+ int ranking = (property instanceof Integer)
+ ? ((Integer)property).intValue() : 0;
+
+ rankings[i] = ranking;
+
+ if (ranking > maxRanking)
+ {
+ index = i;
+ maxRanking = ranking;
+ count = 1;
+ }
+ else
+ {
+ if (ranking == maxRanking)
+ {
+ count++;
+ }
+ }
+ }
+
+ if (count > 1) /* if still more than one service, select lowest id */
+ {
+ long minId = Long.MAX_VALUE;
+
+ for (int i = 0 ; i < length; i++)
+ {
+ if (rankings[i] == maxRanking)
+ {
+ long id = ((Long)(references[i].getProperty(Constants.SERVICE_ID))).longValue();
+
+ if (id < minId)
+ {
+ index = i;
+ minId = id;
+ }
+ }
+ }
+ }
+ }
+
+ return references[index];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the service object for the specified <tt>ServiceReference</tt> object
+ * if the referenced service is
+ * being tracked by this <tt>ServiceTracker</tt> object.
+ *
+ * @param reference Reference to the desired service.
+ * @return Service object or <tt>null</tt> if the service referenced by the
+ * specified <tt>ServiceReference</tt> object is not being tracked.
+ */
+ public Object getService(ServiceReference reference)
+ {
+ Tracked tracked = this.tracked; /* use local var since we are not synchronized */
+
+ if (tracked == null) /* if ServiceTracker is not open */
+ {
+ return null;
+ }
+
+ return tracked.get(reference);
+ }
+
+ /**
+ * Returns a service object for one of the services
+ * being tracked by this <tt>ServiceTracker</tt> object.
+ *
+ * <p>If any services are being tracked, this method returns the result
+ * of calling <tt>getService(getServiceReference())</tt>.
+ *
+ * @return Service object or <tt>null</tt> if no service is being tracked.
+ */
+ public Object getService()
+ {
+ ServiceReference reference = getServiceReference();
+
+ if (reference != null)
+ {
+ return getService(reference);
+ }
+
+ return null;
+ }
+
+ /**
+ * Remove a service from this <tt>ServiceTracker</tt> object.
+ *
+ * The specified service will be removed from this
+ * <tt>ServiceTracker</tt> object.
+ * If the specified service was being tracked then the
+ * <tt>ServiceTrackerCustomizer.removedService</tt> method will be
+ * called for that service.
+ *
+ * @param reference Reference to the service to be removed.
+ */
+ public void remove(ServiceReference reference)
+ {
+ Tracked tracked = this.tracked; /* use local var since we are not synchronized */
+
+ if (tracked == null) /* if ServiceTracker is not open */
+ {
+ return;
+ }
+
+ tracked.untrack(reference);
+ }
+
+ /**
+ * Return the number of services being tracked by this <tt>ServiceTracker</tt> object.
+ *
+ * @return Number of services being tracked.
+ */
+
+ public int size()
+ {
+ Tracked tracked = this.tracked; /* use local var since we are not synchronized */
+
+ if (tracked == null) /* if ServiceTracker is not open */
+ {
+ return 0;
+ }
+
+ return tracked.size();
+ }
+
+ /**
+ * Returns the tracking count for this <tt>ServiceTracker</tt> object.
+ *
+ * The tracking count is initialized to 0 when this
+ * <tt>ServiceTracker</tt> object is opened. Every time a service is
+ * added or removed from this <tt>ServiceTracker</tt> object
+ * the tracking count is incremented.
+ *
+ * <p>The tracking count can
+ * be used to determine if this <tt>ServiceTracker</tt> object
+ * has added or removed a service by comparing a tracking count value
+ * previously collected with the current tracking count value. If the value
+ * has not changed, then no service has been added or removed from
+ * this <tt>ServiceTracker</tt> object
+ * since the previous tracking count was collected.
+ * @return The tracking count for this <tt>ServiceTracker</tt> object
+ * or -1 if this <tt>ServiceTracker</tt> object is not open.
+ */
+ public int getTrackingCount()
+ {
+ Tracked tracked = this.tracked; /* use local var since we are not synchronized */
+
+ if (tracked == null) /* if ServiceTracker is not open */
+ {
+ return -1;
+ }
+
+ return tracked.getTrackingCount();
+ }
+
+ /**
+ * Inner class to track the services.
+ * This class is a hashtable mapping <tt>ServiceReference</tt> object -> customized Object.
+ * This class also implements the <tt>ServiceListener</tt> interface for the tracker.
+ * This is not a public class. It is only for use by the implementation
+ * of the <tt>ServiceTracker</tt> class.
+ *
+ */
+ static class Tracked extends Hashtable implements ServiceListener
+ {
+ private ServiceTrackerCustomizer customizer; /** ServiceTrackerCustomizer object for this tracker. */
+ private Filter filter; /** The filter used to track */
+ private Vector adding; /** list of ServiceReferences currently being added */
+ private boolean closed; /** true if the tracked object is closed */
+ private int trackingCount; /** modification count */
+
+
+ /**
+ * Tracked constructor.
+ *
+ * @param customizer Customizer object from parent <tt>ServiceTracker</tt> object.
+ * @param filter <tt>Filter</tt> object from parent <tt>ServiceTracker</tt> object.
+ */
+ protected Tracked(ServiceTrackerCustomizer customizer, Filter filter)
+ {
+ super();
+ this.customizer = customizer;
+ this.filter = filter;
+ closed = false;
+ trackingCount = 0;
+ adding = new Vector(10, 10);
+ }
+
+ /**
+ * Called by the parent <tt>ServiceTracker</tt> object when it is closed.
+ */
+ protected void close()
+ {
+ closed = true;
+ }
+
+ /**
+ * Called by the parent <tt>ServiceTracker</tt> object to get
+ * the modification count.
+ *
+ * @return modification count.
+ */
+ protected int getTrackingCount()
+ {
+ return trackingCount;
+ }
+
+ /**
+ * <tt>ServiceListener</tt> method for the <tt>ServiceTracker</tt> class.
+ * This method must NOT be synchronized to avoid deadlock potential.
+ *
+ * @param event <tt>ServiceEvent</tt> object from the framework.
+ */
+ public void serviceChanged(ServiceEvent event)
+ {
+ /* Check if we had a delayed call (which could happen when we close). */
+ if (closed)
+ {
+ return;
+ }
+
+ ServiceReference reference = event.getServiceReference();
+
+ switch (event.getType())
+ {
+ case ServiceEvent.REGISTERED:
+ case ServiceEvent.MODIFIED:
+ if (filter.match(reference))
+ {
+ track(reference);
+ /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+ }
+ else
+ {
+ untrack(reference);
+ /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+ }
+
+ break;
+
+ case ServiceEvent.UNREGISTERING:
+ untrack(reference);
+ /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+
+ break;
+ }
+ }
+
+ /**
+ * Begin to track the referenced service.
+ *
+ * @param reference Reference to a service to be tracked.
+ */
+ protected void track(ServiceReference reference)
+ {
+ Object object = get(reference);
+
+ if (object != null) /* we are already tracking the service */
+ {
+ if (DEBUG)
+ {
+ System.out.println("ServiceTracker.Tracked.track[modified]: "+reference);
+ }
+
+ /* Call customizer outside of synchronized region */
+ customizer.modifiedService(reference, object);
+ /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+
+ return;
+ }
+
+ synchronized (this)
+ {
+ if (adding.indexOf(reference, 0) != -1) /* if this service is already
+ * in the process of being added. */
+ {
+ if (DEBUG)
+ {
+ System.out.println("ServiceTracker.Tracked.track[already adding]: "+reference);
+ }
+
+ return;
+ }
+
+ adding.addElement(reference); /* mark this service is being added */
+ }
+
+ if (DEBUG)
+ {
+ System.out.println("ServiceTracker.Tracked.track[adding]: "+reference);
+ }
+
+ boolean becameUntracked = false;
+
+ /* Call customizer outside of synchronized region */
+ try
+ {
+ object = customizer.addingService(reference);
+ /* If the customizer throws an unchecked exception, it will propagate after the finally */
+ }
+ finally
+ {
+ synchronized (this)
+ {
+ if (adding.removeElement(reference)) /* if the service was not untracked
+ * during the customizer callback */
+ {
+ if (object != null)
+ {
+ put(reference, object);
+
+ trackingCount++; /* increment modification count */
+
+ notifyAll();
+ }
+ }
+ else
+ {
+ becameUntracked = true;
+ }
+ }
+ }
+
+ /* The service became untracked during
+ * the customizer callback.
+ */
+ if (becameUntracked)
+ {
+ if (DEBUG)
+ {
+ System.out.println("ServiceTracker.Tracked.track[removed]: "+reference);
+ }
+
+ /* Call customizer outside of synchronized region */
+ customizer.removedService(reference, object);
+ /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+ }
+ }
+
+ /**
+ * Discontinue tracking the referenced service.
+ *
+ * @param reference Reference to the tracked service.
+ */
+ protected void untrack(ServiceReference reference)
+ {
+ Object object;
+
+ synchronized (this)
+ {
+ if (adding.removeElement(reference)) /* if the service is in the process
+ * of being added */
+ {
+ if (DEBUG)
+ {
+ System.out.println("ServiceTracker.Tracked.untrack[being added]: "+reference);
+ }
+
+ return; /* in case the service is untracked
+ * while in the process of adding */
+ }
+
+ object = this.remove(reference); /* must remove from tracker before calling
+ * customizer callback */
+
+ if (object == null) /* are we actually tracking the service */
+ {
+ return;
+ }
+
+ trackingCount++; /* increment modification count */
+ }
+
+ if (DEBUG)
+ {
+ System.out.println("ServiceTracker.Tracked.untrack[removed]: "+reference);
+ }
+
+ /* Call customizer outside of synchronized region */
+ customizer.removedService(reference, object);
+ /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+ }
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/tracker/ServiceTrackerCustomizer.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/tracker/ServiceTrackerCustomizer.java
new file mode 100644
index 000000000..24b9aef08
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/tracker/ServiceTrackerCustomizer.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.tracker;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The <tt>ServiceTrackerCustomizer</tt> interface allows a <tt>ServiceTracker</tt> object to customize
+ * the service objects that are tracked. The <tt>ServiceTrackerCustomizer</tt> object
+ * is called when a service is being added to the <tt>ServiceTracker</tt> object. The <tt>ServiceTrackerCustomizer</tt> can
+ * then return an object for the tracked service. The <tt>ServiceTrackerCustomizer</tt> object is also
+ * called when a tracked service is modified or has been removed from the
+ * <tt>ServiceTracker</tt> object.
+ *
+ * <p>The methods in this interface may be called as the result of a <tt>ServiceEvent</tt>
+ * being received by a <tt>ServiceTracker</tt> object. Since <tt>ServiceEvent</tt>s are
+ * synchronously delivered by the Framework, it is highly recommended that implementations
+ * of these methods do not
+ * register (<tt>BundleContext.registerService</tt>), modify
+ * (<tt>ServiceRegistration.setProperties</tt>) or unregister
+ * (<tt>ServiceRegistration.unregister</tt>)
+ * a service while being synchronized on any object.
+ */
+public interface ServiceTrackerCustomizer
+{
+ /**
+ * A service is being added to the <tt>ServiceTracker</tt> object.
+ *
+ * <p>This method is called before a service which matched
+ * the search parameters of the <tt>ServiceTracker</tt> object is
+ * added to it. This method should return the
+ * service object to be tracked for this <tt>ServiceReference</tt> object.
+ * The returned service object is stored in the <tt>ServiceTracker</tt> object
+ * and is available from the <tt>getService</tt> and <tt>getServices</tt>
+ * methods.
+ *
+ * @param reference Reference to service being added to the <tt>ServiceTracker</tt> object.
+ * @return The service object to be tracked for the
+ * <tt>ServiceReference</tt> object or <tt>null</tt> if the <tt>ServiceReference</tt> object should not
+ * be tracked.
+ */
+ public abstract Object addingService(ServiceReference reference);
+
+ /**
+ * A service tracked by the <tt>ServiceTracker</tt> object has been modified.
+ *
+ * <p>This method is called when a service being tracked
+ * by the <tt>ServiceTracker</tt> object has had it properties modified.
+ *
+ * @param reference Reference to service that has been modified.
+ * @param service The service object for the modified service.
+ */
+ public abstract void modifiedService(ServiceReference reference, Object service);
+
+ /**
+ * A service tracked by the <tt>ServiceTracker</tt> object has been removed.
+ *
+ * <p>This method is called after a service is no longer being tracked
+ * by the <tt>ServiceTracker</tt> object.
+ *
+ * @param reference Reference to service that has been removed.
+ * @param service The service object for the removed service.
+ */
+ public abstract void removedService(ServiceReference reference, Object service);
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/AdaptorElementFactory.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/AdaptorElementFactory.java
new file mode 100644
index 000000000..733d644c2
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/AdaptorElementFactory.java
@@ -0,0 +1,32 @@
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+
+import java.io.IOException;
+import java.security.ProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.framework.adaptor.core.AbstractBundleData;
+
+public class AdaptorElementFactory {
+
+ public AbstractBundleData getBundleData(DefaultAdaptor adaptor) throws IOException {
+ return new DefaultBundleData(adaptor);
+ }
+
+ /**
+ * Creates the ClassLoader for the BundleData. The ClassLoader created
+ * must use the <code>ClassLoaderDelegate</code> to delegate class, resource
+ * and library loading. The delegate is responsible for finding any resource
+ * or classes imported by the bundle or provided by bundle fragments or
+ * bundle hosts. The <code>ProtectionDomain</code> domain must be used
+ * by the Classloader when defining a class.
+ * @param delegate The <code>ClassLoaderDelegate</code> to delegate to.
+ * @param domain The <code>ProtectionDomain</code> to use when defining a class.
+ * @param bundleclasspath An array of bundle classpaths to use to create this
+ * classloader. This is specified by the Bundle-ClassPath manifest entry.
+ * @param data the bundle data with which this classloader will be associated.
+ * @return The new ClassLoader for the BundleData.
+ */
+ public org.eclipse.osgi.framework.adaptor.BundleClassLoader createClassLoader(ClassLoaderDelegate delegate, ProtectionDomain domain, String[] bundleclasspath, DefaultBundleData data) {
+ return new DefaultClassLoader(delegate, domain, bundleclasspath, data);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/AdaptorMsg.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/AdaptorMsg.java
new file mode 100644
index 000000000..d60c32e7f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/AdaptorMsg.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+
+import org.eclipse.osgi.framework.msg.MessageFormat;
+
+/**
+ * This class retrieves strings from a resource bundle
+ * and returns them, formatting them with MessageFormat
+ * when required.
+ * <p>
+ * It is used by the system classes to provide national
+ * language support, by looking up messages in the
+ * <code>
+ * org.eclipse.osgi.framework.internal.core.ExternalMessages
+ * </code>
+ * resource bundle. Note that if this file is not available,
+ * or an invalid key is looked up, or resource bundle support
+ * is not available, the key itself will be returned as the
+ * associated message. This means that the <em>KEY</em> should
+ * a reasonable human-readable (english) string.
+ */
+
+public class AdaptorMsg
+{
+
+ static public MessageFormat formatter;
+
+ // Attempt to load the message bundle.
+ static
+ {
+ formatter = new MessageFormat("org.eclipse.osgi.framework.internal.defaultadaptor.ExternalMessages");
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/BundleEntry.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/BundleEntry.java
new file mode 100644
index 000000000..1752b156c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/BundleEntry.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public abstract class BundleEntry
+{
+ /**
+ * Return an InputStream for the entry.
+ *
+ * @return InputStream for the entry.
+ * @throws java.io.IOException If an error occurs reading the bundle.
+ */
+ public abstract InputStream getInputStream() throws IOException;
+
+ /**
+ * Return the size of the entry (uncompressed).
+ *
+ * @return size of entry.
+ */
+ public abstract long getSize();
+
+ /**
+ * Return the name of the entry.
+ *
+ * @return name of entry.
+ */
+ public abstract String getName();
+
+
+ /**
+ * Get the modification time for this BundleEntry.
+ * <p>If the modification time has not been set,
+ * this method will return <tt>-1</tt>.
+ *
+ * @return last modification time.
+ */
+ public abstract long getTime();
+
+ public abstract URL getURL();
+
+ /**
+ * Return the name of this BundleEntry by calling getName().
+ *
+ * @return String representation of this BundleEntry.
+ */
+ public String toString()
+ {
+ return(getName());
+ }
+
+ public static class ZipBundleEntry extends BundleEntry
+ {
+ /**
+ * ZipFile for this entry.
+ */
+ private ZipFile zipFile;
+ /**
+ * ZipEntry for this entry.
+ */
+ private ZipEntry zipEntry;
+
+ /**
+ * The BundleFile for this entry.
+ */
+ private File bundleFile;
+
+ /**
+ * Constructs the BundleEntry using a ZipEntry.
+ * @param bundleFile BundleFile object this entry is a member of
+ * @param entry ZipEntry object of this entry
+ */
+ ZipBundleEntry(ZipFile zipFile, ZipEntry entry, File bundleFile)
+ {
+ this.zipFile = zipFile;
+ this.zipEntry = entry;
+ this.bundleFile = bundleFile;
+ }
+
+ /**
+ * Return an InputStream for the entry.
+ *
+ * @return InputStream for the entry
+ * @exception java.io.IOException
+ */
+ public InputStream getInputStream() throws IOException
+ {
+ return(zipFile.getInputStream(zipEntry));
+ }
+ /**
+ * Return size of the uncompressed entry.
+ *
+ * @return size of entry
+ */
+ public long getSize()
+ {
+ return(zipEntry.getSize());
+ }
+ /**
+ * Return name of the entry.
+ *
+ * @return name of entry
+ */
+ public String getName()
+ {
+ return(zipEntry.getName());
+ }
+
+ /**
+ * Get the modification time for this BundleEntry.
+ * <p>If the modification time has not been set,
+ * this method will return <tt>-1</tt>.
+ *
+ * @return last modification time.
+ */
+ public long getTime(){
+ return zipEntry.getTime();
+ }
+
+ public URL getURL() {
+ try {
+ return new URL("jar:file:" + bundleFile.getAbsolutePath() + "!/" + zipEntry.getName());
+ } catch (MalformedURLException e) {
+ //This can not happen.
+ return null;
+ }
+ }
+ }
+
+ public static class FileBundleEntry extends BundleEntry
+ {
+ /**
+ * File for this entry.
+ */
+ private File file;
+
+ private String name;
+
+ /**
+ * Constructs the BundleEntry using a ZipEntry.
+ * @param bundleFile BundleFile object this entry is a member of
+ * @param entry ZipEntry object of this entry
+ */
+ FileBundleEntry(File file, String name)
+ {
+ this.file = file;
+ this.name = name;
+ }
+
+ /**
+ * Return an InputStream for the entry.
+ *
+ * @return InputStream for the entry
+ * @exception java.io.IOException
+ */
+ public InputStream getInputStream() throws IOException
+ {
+ return(new FileInputStream(file));
+ }
+ /**
+ * Return size of the uncompressed entry.
+ *
+ * @return size of entry
+ */
+ public long getSize()
+ {
+ return(file.length());
+ }
+ /**
+ * Return name of the entry.
+ *
+ * @return name of entry
+ */
+ public String getName()
+ {
+ return(name);
+ }
+
+ /**
+ * Get the modification time for this BundleEntry.
+ * <p>If the modification time has not been set,
+ * this method will return <tt>-1</tt>.
+ *
+ * @return last modification time.
+ */
+ public long getTime(){
+ return file.lastModified();
+ }
+
+ public URL getURL() {
+ try {
+ return file.toURL();
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/BundleFile.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/BundleFile.java
new file mode 100644
index 000000000..71c47844b
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/BundleFile.java
@@ -0,0 +1,438 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.protocol.bundle.Handler;
+
+abstract public class BundleFile
+{
+ /**
+ * The File object for this bundle.
+ */
+ protected File bundlefile;
+
+ /**
+ * The BundleData object for this bundle.
+ */
+ protected DefaultBundleData bundledata;
+
+ /**
+ * The String value of the Bundle ID
+ */
+ protected String bundleID;
+
+ /**
+ * BundleFile constructor
+ * @param bundlefile The File object where this bundle is persistently
+ * stored.
+ * @param bundledata The BundleData object for this bundle.
+ */
+ public BundleFile(File bundlefile, DefaultBundleData bundledata)
+ {
+ this.bundlefile = bundlefile;
+ this.bundledata = bundledata;
+ this.bundleID = String.valueOf(bundledata.getBundleID());
+ }
+
+ /**
+ * Returns a File for the bundle entry specified by the path.
+ * If required the content of the bundle entry is extracted into a file
+ * on the file system.
+ * @param path The path to the entry to locate a File for.
+ * @return A File object to access the contents of the bundle entry.
+ */
+ abstract public File getFile(String path);
+
+ /**
+ * Locates a file name in this bundle and returns a BundleEntry object
+ *
+ * @param path path of the entry to locate in the bundle
+ * @return BundleEntry object or null if the file name
+ * does not exist in the bundle
+ */
+ abstract public BundleEntry getEntry(String path);
+
+ /**
+ * Allows to access the entries of the bundle.
+ * Since the bundle content is usually a jar, this
+ * allows to access the jar contents.
+ *
+ * GetEntryPaths allows to enumerate the content of "path".
+ * If path is a directory, it is equivalent to listing the directory
+ * contents. The returned names are either files or directories
+ * themselves. If a returned name is a directory, it finishes with a
+ * slash. If a returned name is a file, it does not finish with a slash.
+ * @param path path of the entry to locate in the bundle
+ * @return an Enumeration of Strings that indicate the paths found or
+ * null if the path does not exist.
+ */
+ abstract public Enumeration getEntryPaths(String path);
+
+ /**
+ * Closes the BundleFile.
+ * @throws IOException if any error occurs.
+ */
+ abstract public void close() throws IOException;
+
+ /**
+ * Opens the BundleFiles.
+ * @throws IOException if any error occurs.
+ */
+ abstract public void open() throws IOException;
+
+ /**
+ * Returns a URL to access the contents of the entry specified by the path
+ * @param path
+ */
+ public URL getURL(String path, int cpEntry) {
+ BundleEntry bundleEntry = getEntry(path);
+ if (bundleEntry == null)
+ return null;
+
+ if (System.getSecurityManager() != null) {
+ // If we are running in security mode, we should create bundle URLs
+ try {
+ return new URL(Constants.OSGI_URL_PROTOCOL, bundleID, cpEntry, path, new Handler(bundleEntry));
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ } else {
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=42871
+ return bundleEntry.getURL();
+ }
+ }
+
+ static public BundleFile createBundleFile(File bundlefile, DefaultBundleData bundledata) throws IOException {
+ if (bundlefile.isDirectory()) {
+ return new DirBundleFile(bundlefile,bundledata);
+ }
+ else {
+ return new ZipBundleFile(bundlefile,bundledata);
+ }
+ }
+
+ static public BundleFile createBundleFile(ZipBundleFile bundlefile,String cp) {
+ return new ZipDirBundleFile(bundlefile,cp);
+ }
+
+ public static class ZipBundleFile extends BundleFile
+ {
+ protected ZipFile zipFile;
+ protected boolean closed = false;
+
+ protected ZipBundleFile(File bundlefile, DefaultBundleData bundledata)
+ throws IOException
+ {
+ super(bundlefile, bundledata);
+ this.closed=true;
+ open();
+ }
+
+ protected ZipEntry getZipEntry(String path)
+ {
+ if (path.length() > 0 && path.charAt(0) == '/')
+ path = path.substring(1);
+ return zipFile.getEntry(path);
+ }
+
+ public File getFile(String entry)
+ {
+ ZipEntry zipEntry = getZipEntry(entry);
+ if (zipEntry == null)
+ {
+ return null;
+ }
+
+ try
+ {
+ File bundleGenerationDir = bundledata.getGenerationDir();
+
+ /* if the generation dir exists, then we have place to cache */
+ if (bundleGenerationDir.exists())
+ {
+ String path = ".cp"; /* put all these entries in this subdir */
+ String name = zipEntry.getName().replace('/', File.separatorChar);
+
+ if ((name.length() > 1) && (name.charAt(0) == File.separatorChar)) /* if name has a leading slash */
+ {
+ path = path.concat(name);
+ }
+ else
+ {
+ path = path + File.separator + name;
+ }
+
+ File nested = new File(bundleGenerationDir, path);
+
+ if (nested.exists())
+ {
+ /* the entry is already cached */
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("File already present: "+nested.getPath());
+ }
+ }
+ else
+ {
+ /* the entry has not been cached */
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Creating file: "+nested.getPath());
+ }
+
+ /* create the necessary directories */
+ File dir = new File(nested.getParent());
+
+ if (!dir.exists() && !dir.mkdirs())
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to create directory: "+dir.getPath());
+ }
+
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+
+ /* copy the entry to the cache */
+ DefaultAdaptor.readFile(zipFile.getInputStream(zipEntry), nested);
+ }
+
+ return nested;
+ }
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.printStackTrace(e);
+ }
+ }
+ return null;
+ }
+
+ public BundleEntry getEntry(String path)
+ {
+ ZipEntry zipEntry = getZipEntry(path);
+ if (zipEntry == null){
+ return null;
+ }
+
+ return new BundleEntry.ZipBundleEntry(zipFile,zipEntry,bundlefile);
+
+ }
+
+ public Enumeration getEntryPaths(String path){
+ if (path == null) {
+ return null;
+ }
+
+ if (path.startsWith("/")){
+ path = path.substring(1);
+ }
+ if (path.length()>0 & !path.endsWith("/")) {
+ path = new StringBuffer(path).append("/").toString();
+ }
+
+ Vector vEntries = new Vector();
+ Enumeration entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry zipEntry = (ZipEntry)entries.nextElement();
+ String entryPath = zipEntry.getName();
+ if (entryPath.startsWith(path)){
+ if (path.length()<entryPath.length()){
+ if (entryPath.lastIndexOf('/')<path.length()) {
+ vEntries.add(entryPath);
+ }
+ else {
+ entryPath = entryPath.substring(path.length());
+ int i = entryPath.indexOf('/');
+ entryPath = path + entryPath.substring(0,i+1);
+ if(!vEntries.contains(entryPath)){
+ vEntries.add(entryPath);
+ }
+ }
+ }
+ }
+ }
+ return vEntries.elements();
+ }
+
+ public void close() throws IOException {
+ if (!closed)
+ {
+ closed = true;
+ zipFile.close();
+ }
+ }
+
+ public void open() throws IOException {
+ if (closed)
+ {
+ zipFile = new ZipFile(this.bundlefile);
+
+ closed = false;
+ }
+ }
+
+ }
+
+ public static class DirBundleFile extends BundleFile {
+
+ protected DirBundleFile(File bundlefile, DefaultBundleData bundledata)
+ throws IOException
+ {
+ super(bundlefile,bundledata);
+ if (!bundlefile.exists() || !bundlefile.isDirectory()){
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_DIRECTORY_EXCEPTION", bundlefile));
+ }
+ }
+
+ public File getFile(String path)
+ {
+ File filePath = new File(this.bundlefile, path);
+ if (filePath.exists()) {
+ return filePath;
+ }
+ return null;
+ }
+
+ public BundleEntry getEntry(String path)
+ {
+ File filePath = new File(this.bundlefile, path);
+ return new BundleEntry.FileBundleEntry(filePath,path);
+ }
+
+ public URL getURL(String path, int cpEntry) {
+ File filePath = new File(this.bundlefile, path);
+ if (!filePath.exists())
+ return null;
+
+
+ if (System.getSecurityManager() != null) {
+ // If we are running in security mode, we should create bundle URLs
+ try {
+ BundleEntry bundleEntry = new BundleEntry.FileBundleEntry(filePath,path);
+ return new URL(Constants.OSGI_URL_PROTOCOL, bundleID, cpEntry, path, new Handler(bundleEntry));
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ } else
+ try {
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=42871
+ return filePath.toURL();
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+ public Enumeration getEntryPaths(final String path){
+ final java.io.File pathFile = new java.io.File(bundlefile,path);
+ if(!pathFile.exists()) return null;
+ if (pathFile.isDirectory()){
+ final String[] fileList = pathFile.list();
+ return new Enumeration(){
+ int cur=0;
+ public boolean hasMoreElements() {
+ return cur < fileList.length;
+ }
+
+ public Object nextElement() {
+ if(cur>=fileList.length){
+ throw new NoSuchElementException();
+ }
+ java.io.File childFile = new java.io.File(pathFile,fileList[cur]);
+ if (childFile.isDirectory()){
+ StringBuffer sb = new StringBuffer(fileList[cur++]).append("/");
+ return sb.toString();
+ }
+ else{
+ return fileList[cur++];
+ }
+ }
+
+ };
+ } else {
+ return new Enumeration(){
+ int cur=0;
+ public boolean hasMoreElements() {
+ return cur < 1;
+ }
+ public Object nextElement() {
+ if (cur==0) {
+ cur=1;
+ return path;
+ } else
+ throw new NoSuchElementException();
+ }
+ };
+ }
+ }
+
+ public void close(){
+ // nothing to do.
+ }
+
+ public void open() {
+ // nothing to do.
+ }
+ }
+
+ public static class ZipDirBundleFile extends BundleFile {
+ ZipBundleFile zipBundlefile;
+ String cp;
+ public ZipDirBundleFile(ZipBundleFile zipBundlefile, String cp){
+ super(zipBundlefile.bundlefile,zipBundlefile.bundledata);
+ this.zipBundlefile = zipBundlefile;
+ this.cp = cp;
+ if (cp.charAt(cp.length()-1) != '/'){
+ this.cp = this.cp + '/';
+ }
+ }
+
+ public void close(){
+ // do nothing.
+ }
+
+ public BundleEntry getEntry(String path) {
+ if (path.length() > 0 && path.charAt(0) == '/')
+ path = path.substring(1);
+ String newpath = new StringBuffer(cp).append(path).toString();
+ return zipBundlefile.getEntry(newpath);
+ }
+
+ public Enumeration getEntryPaths(String path){
+ // getEntryPaths is only valid if this is a root bundle file.
+ return null;
+ }
+
+ public File getFile(String entry){
+ // getFile is only valid if this is a root bundle file.
+ return null;
+ }
+
+ public void open(){
+ // do nothing
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultAdaptor.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultAdaptor.java
new file mode 100644
index 000000000..394fed156
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultAdaptor.java
@@ -0,0 +1,1138 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.adaptor.core.AbstractFrameworkAdaptor;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.util.Headers;
+import org.eclipse.osgi.internal.resolver.StateManager;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.State;
+import org.osgi.framework.*;
+
+/**
+ * //TODO Add comment here
+ */
+public class DefaultAdaptor extends AbstractFrameworkAdaptor
+{
+
+ protected static final String METADATA_ADAPTOR_NEXTID = "METADATA_ADAPTOR_NEXTID";
+ protected static final String METADATA_ADAPTOR_IBSL = "METADATA_ADAPTOR_IBSL";
+
+ /** directory containing installed bundles */
+ protected File bundleRootDir;
+
+ /** String containing bundle dir */
+ protected String bundledir = null;
+
+ /** name of a bundles data directory */
+ protected String dataDirName = "data";
+
+ protected boolean reset = false;
+
+ /** The MetaData for the default adaptor */
+ protected MetaData metadata;
+
+ /** Dictionary containing permission data */
+ protected PermissionStorage permissionStore;
+
+ /** next available bundle id */
+ protected long nextId = 1;
+
+ /** Development ClassPath entries */
+ protected String[] devCP;
+
+ /** Name of the Adaptor manifest file */
+ protected final String ADAPTOR_MANIFEST = "/META-INF/ADAPTOR.MF";
+
+ /** This adaptor's manifest file */
+ protected Headers manifest=null;
+
+ protected AdaptorElementFactory elementFactory = null;
+
+ /** The State Manager */
+ protected StateManager stateManager;
+
+ /**
+ * Constructor for DefaultAdaptor. This constructor parses the arguments passed
+ * and remembers them for later when initialize is called.
+ * <p>No blank spaces should be used in the arguments to the DefaultAdaptor.
+ * The options that DefaultAdaptor recognizes and handles are:
+ * <ul>
+ * <li><b>bundledir=<i>directory name</i></b>If a directory name is specified, the adaptor should initialize
+ * to store bundles in that directory. This arg should be enclosed in "" if it contains the ":"
+ * character (example "bundledir=c:\mydir").
+ * <li><b>reset</b>Resets the bundle storage by deleting the bundledir
+ * </ul>
+ * Any other arguments are ignored.
+ *
+ * @param An array of strings containing arguments.
+ * This object cannot be used until initialize is called.
+ *
+ */
+ public DefaultAdaptor(String[] args)
+ {
+ if (args != null)
+ {
+ for (int i = 0; i < args.length; i++)
+ {
+ String arg = args[i];
+ if (arg.equalsIgnoreCase("reset"))
+ {
+ reset = true;
+ }
+ else if (arg.indexOf("=") != -1)
+ {
+ StringTokenizer tok = new StringTokenizer(args[i], "=");
+ if (tok.countTokens() == 2)
+ {
+ String key = tok.nextToken();
+ if (key.equalsIgnoreCase("bundledir"))
+ {
+ // save file name for initializeStorage to use
+ bundledir = tok.nextToken();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void initialize(EventPublisher eventPublisher)
+ {
+ super.initialize(eventPublisher);
+ getBundleDir();
+ readAdaptorManifest();
+ stateManager = createStateManager();
+ }
+
+ /**
+ * Creates the StateManager for the adaptor
+ * @return the StateManager.
+ */
+ protected StateManager createStateManager(){
+ return new StateManager(bundleRootDir);
+ }
+
+ /**
+ * Get the directory to store the bundles in. Bundledir can be set in 3 different ways.
+ * Priority is:
+ * 1 - OSGI Launcher command line -adaptor argument
+ * 2 - System property org.eclipse.osgi.framework.defaultadaptor.bundledir - could be specified with -D when launching
+ * 3 - osgi.properties - org.eclipse.osgi.framework.defaultadaptor.bundledir property
+ *
+ * Bundledir will be stored back to adaptor properties which
+ * the framework will copy into the System properties.
+ */
+ protected void getBundleDir()
+ {
+ /* if bundledir was not set by the constructor from the -adaptor cmd line arg */
+ if (bundledir == null)
+ {
+ /* check the system properties */
+ bundledir = System.getProperty("org.eclipse.osgi.framework.defaultadaptor.bundledir");
+
+ if (bundledir == null)
+ {
+ /* check the osgi.properties file, but default to "bundles" */
+ bundledir = properties.getProperty("org.eclipse.osgi.framework.defaultadaptor.bundledir", "bundles");
+ }
+ }
+
+ /* store bundledir back into adaptor properties for others to see */
+ properties.put("org.eclipse.osgi.framework.defaultadaptor.bundledir", bundledir);
+
+ bundleRootDir = new File(bundledir);
+ }
+
+ /**
+ * Reads and initializes the adaptor BundleManifest object. The
+ * BundleManifest is used by the getExportPackages() and getExportServices()
+ * methods of the adpator.
+ */
+ protected void readAdaptorManifest() {
+ InputStream in = getClass().getResourceAsStream(ADAPTOR_MANIFEST);
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ if (in == null) {
+ Debug.println("Unable to find adaptor bundle manifest " + ADAPTOR_MANIFEST);
+ }
+ }
+ try {
+ manifest = Headers.parseManifest(in);
+ } catch (BundleException e) {
+ Debug.println("Unable to read adaptor bundle manifest " + ADAPTOR_MANIFEST);
+ }
+ }
+
+ /**
+ * Initialize the persistent storage.
+ *
+ * <p>This method initializes the bundle persistent storage
+ * area.
+ * If a dir was specified in the -adaptor command line option, then it
+ * is used. If not,
+ * if the property
+ * <i>org.eclipse.osgi.framework.defaultadaptor.bundledir</i> is specifed, its value
+ * will be used as the name of the bundle directory
+ * instead of <tt>./bundles</tt>.
+ * If reset was specified on the -adaptor command line option,
+ * then the storage will be cleared.
+ *
+ * @throws IOException If an error occurs initializing the storage.
+ */
+ public void initializeStorage() throws IOException
+ {
+ boolean makedir = false;
+ if (bundleRootDir.exists())
+ {
+ if (reset)
+ {
+ makedir = true;
+ if (!rm(bundleRootDir))
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Could not remove directory: " + bundleRootDir.getPath());
+ }
+ }
+ }
+ else
+ {
+ if (!bundleRootDir.isDirectory())
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Exists but not a directory: " + bundleRootDir.getPath());
+ }
+
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+ }
+ }
+ else
+ {
+ makedir = true;
+ }
+ if (makedir)
+ {
+ if (!bundleRootDir.mkdirs())
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to create directory: " + bundleRootDir.getPath());
+ }
+
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+ }
+
+ metadata = new MetaData(getMetaDataFile(), "Framework metadata");
+ metadata.load();
+ nextId = metadata.getLong(METADATA_ADAPTOR_NEXTID, 1);
+ initialBundleStartLevel = metadata.getInt(METADATA_ADAPTOR_IBSL,1);
+ }
+
+ protected File getMetaDataFile(){
+ return new File(bundleRootDir, ".framework");
+ }
+
+ /**
+ * This method cleans up storage in the specified directory and
+ * any subdirectories.
+ *
+ * @param directory The directory to clean.
+ * @param depth The remaining depth. When depth is zero, this
+ * method will not recurse any deeper
+ * @see #compactStorage
+ */
+ private void compact(File directory)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("compact(" + directory.getPath() + ")");
+ }
+
+ String list[] = directory.list();
+
+ if (list != null)
+ {
+ int len = list.length;
+
+ for (int i = 0; i < len; i++)
+ {
+ if (dataDirName.equals(list[i]))
+ {
+ continue; /* do not examine the bundles data dir. */
+ }
+
+ File target = new File(directory, list[i]);
+
+ /* if the file is a directory */
+ if (target.isDirectory())
+ {
+ File delete = new File(target, ".delete");
+
+ /* and the directory is marked for delete */
+ if (delete.exists())
+ {
+ /* if rm fails to delete the directory *
+ * and .delete was removed
+ */
+ if (!rm(target) && !delete.exists())
+ {
+ try
+ {
+ /* recreate .delete */
+ FileOutputStream out = new FileOutputStream(delete);
+ out.close();
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage());
+ }
+ }
+ }
+ }
+ else
+ {
+ compact(target); /* descend into directory */
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Clean up the persistent storage.
+ *
+ * <p>Cleans up any deferred deletions in persistent storage.
+ *
+ */
+ public void compactStorage()
+ {
+ compact(bundleRootDir);
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getInstalledBundles()
+ */
+ public Vector getInstalledBundles()
+ {
+ String list[] = bundleRootDir.list();
+
+ int len = list.length;
+
+ Vector bundleDatas = new Vector(len << 1, 10);
+
+ /* create bundle objects for all installed bundles. */
+ for (int i = 0; i < len; i++)
+ {
+ try
+ {
+ DefaultBundleData data;
+
+ try
+ {
+ data = (DefaultBundleData)getElementFactory().getBundleData(this);
+ data.initializeExistingBundle(list[i]);
+ }
+ catch (NumberFormatException e)
+ {
+ continue; /* the directory is not a bundle id */
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("BundleData created: " + data);
+ }
+
+ bundleDatas.addElement(data);
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to open Bundle[" + list[i] + "]: " + e.getMessage());
+ Debug.printStackTrace(e);
+ }
+ }
+ }
+
+ return (bundleDatas);
+ }
+
+ /**
+ * Prepare to install a bundle from a URLConnection.
+ * <p>To complete the install,
+ * begin and then commit
+ * must be called on the returned <code>BundleOperation</code> object.
+ * If either of these methods throw a BundleException
+ * or some other error occurs,
+ * then undo must be called on the <code>BundleOperation</code> object
+ * to undo the change to persistent storage.
+ *
+ * @param location Bundle location.
+ * @param source URLConnection from which the bundle may be read.
+ * Any InputStreams returned from the source
+ * (URLConnections.getInputStream) must be closed by the
+ * <code>BundleOperation</code> object.
+ * @return BundleOperation object to be used to complete the install.
+ */
+ public BundleOperation installBundle(final String location, final URLConnection source)
+ {
+ return (new BundleOperation()
+ {
+ private DefaultBundleData data;
+
+ /**
+ * Begin the operation on the bundle (install, update, uninstall).
+ *
+ * @return BundleData object for the target bundle.
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+ public org.eclipse.osgi.framework.adaptor.BundleData begin() throws BundleException
+ {
+ long id;
+
+ try
+ {
+ /*
+ * Open InputStream first to trigger prereq installs, if any,
+ * before allocating bundle id.
+ */
+ InputStream in = source.getInputStream();
+
+ try
+ {
+ try
+ {
+ id = getNextBundleId();
+ metadata.setLong(METADATA_ADAPTOR_NEXTID, id);
+ metadata.save();
+ }
+ catch (IOException e)
+ {
+ throw new BundleException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"), e);
+ }
+
+ if (in instanceof ReferenceInputStream)
+ {
+ URL reference = ((ReferenceInputStream) in).getReference();
+
+ if (!"file".equals(reference.getProtocol()))
+ {
+ throw new BundleException(
+ AdaptorMsg.formatter.getString("ADAPTOR_URL_CREATE_EXCEPTION", reference));
+ }
+
+ data = (DefaultBundleData)getElementFactory().getBundleData(DefaultAdaptor.this);
+ data.initializeReferencedBundle(id, location, reference.getPath());
+ }
+ else
+ {
+ data = (DefaultBundleData)getElementFactory().getBundleData(DefaultAdaptor.this);
+ data.initializeNewBundle(id, location, in);
+ }
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+ catch (IOException ioe)
+ {
+ throw new BundleException(AdaptorMsg.formatter.getString("BUNDLE_READ_EXCEPTION"), ioe);
+ }
+
+ return (data);
+ }
+
+ public void undo()
+ {
+ if (data != null)
+ {
+ try
+ {
+ data.close();
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to close " + data + ": " + e.getMessage());
+ }
+ }
+ }
+
+ if (data != null)
+ {
+ File bundleDir = data.getBundleDir();
+
+ if (!rm(bundleDir))
+ {
+ /* mark this bundle to be deleted to ensure it is fully cleaned up
+ * on next restart.
+ */
+ File delete = new File(bundleDir, ".delete");
+
+ if (!delete.exists())
+ {
+ try
+ {
+ /* create .delete */
+ FileOutputStream out = new FileOutputStream(delete);
+ out.close();
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void commit(boolean postpone) throws BundleException
+ {
+ try
+ {
+ data.save();
+ }
+ catch (IOException e)
+ {
+ throw new BundleException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"), e);
+ }
+ stateManager.install(data.getManifest(),data.getLocation(),data.getBundleID());
+ }
+
+ });
+ }
+
+ /**
+ * Prepare to update a bundle from a URLConnection.
+ * <p>To complete the update,
+ * modify and then commit
+ * will be called on the returned BundleStorage object.
+ * If either of these methods throw a BundleException
+ * or some other error occurs,
+ * then undo will be called on the BundleStorage object
+ * to undo the change to persistent storage.
+ *
+ * @param bundle Bundle to update.
+ * @param source URLConnection from which the bundle may be read.
+ * @return BundleOperation object to be used to complete the update.
+ */
+
+ public BundleOperation updateBundle(
+ final org.eclipse.osgi.framework.adaptor.BundleData bundledata,
+ final URLConnection source)
+ {
+ return (new BundleOperation()
+ {
+ private DefaultBundleData data;
+ private DefaultBundleData newData;
+
+ /**
+ * Perform the change to persistent storage.
+ *
+ * @return Bundle object for the target bundle.
+ */
+ public org.eclipse.osgi.framework.adaptor.BundleData begin() throws BundleException
+ {
+ this.data = (DefaultBundleData) bundledata;
+ try
+ {
+ InputStream in = source.getInputStream();
+ try
+ {
+ if (in instanceof ReferenceInputStream)
+ {
+ URL reference = ((ReferenceInputStream) in).getReference();
+
+ if (!"file".equals(reference.getProtocol()))
+ {
+ throw new BundleException(
+ AdaptorMsg.formatter.getString("ADAPTOR_URL_CREATE_EXCEPTION", reference));
+ }
+
+ // check to make sure we are not just trying to update to the same
+ // directory reference. This would be a no-op.
+ String path = reference.getPath();
+ if (path.equals(data.getName())){
+ throw new BundleException(
+ AdaptorMsg.formatter.getString("ADAPTOR_SAME_REF_UPDATE",reference));
+ }
+
+ try
+ {
+ newData = data.nextGeneration(reference.getPath());
+ }
+ catch (IOException e)
+ {
+ throw new BundleException(
+ AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"),
+ e);
+ }
+
+ File bundleGenerationDir = newData.getGenerationDir();
+
+ if (!bundleGenerationDir.exists())
+ {
+ throw new BundleException(
+ AdaptorMsg.formatter.getString(
+ "ADAPTOR_DIRECTORY_CREATE_EXCEPTION",
+ bundleGenerationDir.getPath()));
+ }
+
+ newData.bundleFile = BundleFile.createBundleFile(newData.file, newData);
+ }
+ else
+ {
+ try
+ {
+ newData = data.nextGeneration();
+ }
+ catch (IOException e)
+ {
+ throw new BundleException(
+ AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"),
+ e);
+ }
+
+ File bundleGenerationDir = newData.getGenerationDir();
+
+ if (!bundleGenerationDir.exists())
+ {
+ throw new BundleException(
+ AdaptorMsg.formatter.getString(
+ "ADAPTOR_DIRECTORY_CREATE_EXCEPTION",
+ bundleGenerationDir.getPath()));
+ }
+
+ File file = newData.getBundleFile();
+
+ readFile(in, file);
+ newData.bundleFile = BundleFile.createBundleFile(file, newData);
+ }
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+ newData.loadFromManifest();
+ }
+ catch (IOException e)
+ {
+ throw new BundleException(AdaptorMsg.formatter.getString("BUNDLE_READ_EXCEPTION"), e);
+ }
+
+ return (newData);
+ }
+ /**
+ * Commit the change to persistent storage.
+ *
+ * @param postpone If true, the bundle's persistent
+ * storage cannot be immediately reclaimed.
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+
+ public void commit(boolean postpone) throws BundleException
+ {
+ try
+ {
+ newData.save();
+ }
+ catch (IOException e)
+ {
+ throw new BundleException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"), e);
+ }
+
+ stateManager.update(newData.getManifest(),newData.getLocation(),newData.getBundleID());
+
+ File originalGenerationDir = data.getGenerationDir();
+
+ if (postpone || !rm(originalGenerationDir))
+ {
+ /* mark this bundle to be deleted to ensure it is fully cleaned up
+ * on next restart.
+ */
+
+ File delete = new File(originalGenerationDir, ".delete");
+
+ if (!delete.exists())
+ {
+ try
+ {
+ /* create .delete */
+ FileOutputStream out = new FileOutputStream(delete);
+ out.close();
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage());
+ }
+
+ eventPublisher.publishFrameworkEvent(FrameworkEvent.ERROR, data.getBundle(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Undo the change to persistent storage.
+ *
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+ public void undo() throws BundleException
+ {
+ /*if (bundleFile != null)
+ {
+ bundleFile.close();
+ } */
+
+ if (newData != null)
+ {
+ File nextGenerationDir = newData.getGenerationDir();
+
+ if (!rm(nextGenerationDir)) /* delete downloaded bundle */
+ {
+ /* mark this bundle to be deleted to ensure it is fully cleaned up
+ * on next restart.
+ */
+
+ File delete = new File(nextGenerationDir, ".delete");
+
+ if (!delete.exists())
+ {
+ try
+ {
+ /* create .delete */
+ FileOutputStream out = new FileOutputStream(delete);
+ out.close();
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Prepare to uninstall a bundle.
+ * <p>To complete the uninstall,
+ * modify and then commit
+ * will be called on the returned BundleStorage object.
+ * If either of these methods throw a BundleException
+ * or some other error occurs,
+ * then undo will be called on the BundleStorage object
+ * to undo the change to persistent storage.
+ *
+ * @param bundle BundleData to uninstall.
+ * @return BundleOperation object to be used to complete the uninstall.
+ */
+ public BundleOperation uninstallBundle(final org.eclipse.osgi.framework.adaptor.BundleData bundledata)
+ {
+ return (new BundleOperation()
+ {
+ private DefaultBundleData data;
+
+ /**
+ * Perform the change to persistent storage.
+ *
+ * @return Bundle object for the target bundle.
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+ public org.eclipse.osgi.framework.adaptor.BundleData begin() throws BundleException
+ {
+ this.data = (DefaultBundleData) bundledata;
+ return (bundledata);
+ }
+ /**
+ * Commit the change to persistent storage.
+ *
+ * @param postpone If true, the bundle's persistent
+ * storage cannot be immediately reclaimed.
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+ public void commit(boolean postpone) throws BundleException
+ {
+ File bundleDir = data.getBundleDir();
+
+ if (postpone || !rm(bundleDir))
+ {
+ /* mark this bundle to be deleted to ensure it is fully cleaned up
+ * on next restart.
+ */
+
+ File delete = new File(bundleDir, ".delete");
+
+ if (!delete.exists())
+ {
+ try
+ {
+ /* create .delete */
+ FileOutputStream out = new FileOutputStream(delete);
+ out.close();
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ stateManager.uninstall(data);
+ }
+ /**
+ * Undo the change to persistent storage.
+ *
+ * @throws BundleException If a failure occured modifiying peristent storage.
+ */
+ public void undo() throws BundleException
+ {
+ }
+ });
+ }
+
+ /**
+ * Returns the PermissionStorage object which will be used to
+ * to manage the permission data.
+ *
+ * <p>The PermissionStorage object will store permission data
+ * in the "permdata" subdirectory of the bundle storage directory
+ * assigned by <tt>initializeStorage</tt>.
+ *
+ * @return The PermissionStorage object for the DefaultAdaptor.
+ */
+ public org.eclipse.osgi.framework.adaptor.PermissionStorage getPermissionStorage() throws IOException
+ {
+ if (permissionStore == null)
+ {
+ synchronized (this)
+ {
+ if (permissionStore == null)
+ {
+ permissionStore = new PermissionStorage(this);
+ }
+ }
+ }
+
+ return permissionStore;
+ }
+
+ public void frameworkStart(BundleContext context) throws BundleException{
+ super.frameworkStart(context);
+
+ // Check the osgi.dev property to see if dev classpath entries have been defined.
+ String osgiDev = context.getProperty("osgi.dev");
+ if (osgiDev != null) {
+ // Add each dev classpath entry
+ Vector devClassPath = new Vector(6);
+ StringTokenizer st = new StringTokenizer(osgiDev,",");
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ if (!tok.equals("")) {
+ devClassPath.addElement(tok);
+ }
+ }
+ devCP = new String[devClassPath.size()];
+ devClassPath.toArray(devCP);
+ }
+
+ State state = stateManager.getSystemState();
+ BundleDescription systemBundle = state.getBundle(0);
+ if (systemBundle == null || !systemBundle.isResolved())
+ // this would be a bug in the framework
+ throw new IllegalStateException();
+ }
+
+ public void frameworkStop(BundleContext context) throws BundleException {
+ try {
+ stateManager.shutdown();
+ } catch (IOException e) {
+ throw new BundleException(null, e);
+ }
+ super.frameworkStop(context);
+ }
+ /**
+ * Register a service object.
+ *
+ */
+ protected ServiceRegistration register(String name, Object service, Bundle bundle)
+ {
+ Hashtable properties = new Hashtable(7);
+
+ Dictionary headers = bundle.getHeaders();
+
+ properties.put(Constants.SERVICE_VENDOR, headers.get(Constants.BUNDLE_VENDOR));
+
+ properties.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+
+ properties.put(Constants.SERVICE_PID, bundle.getBundleId() + "." + service.getClass().getName());
+
+ return context.registerService(name, service, properties);
+ }
+
+ /**
+ * This function performs the equivalent of "rm -r" on a file or directory.
+ *
+ * @param file file or directory to delete
+ * @return false is the specified files still exists, true otherwise.
+ */
+ protected boolean rm(File file)
+ {
+ if (file.exists())
+ {
+ if (file.isDirectory())
+ {
+ String list[] = file.list();
+ int len = list.length;
+ for (int i = 0; i < len; i++)
+ {
+ // we are doing a lot of garbage collecting here
+ rm(new File(file, list[i]));
+ }
+
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if (file.isDirectory())
+ {
+ Debug.println("rmdir " + file.getPath());
+ }
+ else
+ {
+ Debug.println("rm " + file.getPath());
+ }
+ }
+
+ boolean success = file.delete();
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ if (!success)
+ {
+ Debug.println(" rm failed!!");
+ }
+ }
+
+ return (success);
+ }
+ else
+ {
+ return (true);
+ }
+ }
+
+ public void setInitialBundleStartLevel(int value) {
+ super.setInitialBundleStartLevel(value);
+ metadata.setInt(METADATA_ADAPTOR_IBSL,value);
+ try {
+ metadata.save();
+ } catch (IOException e) {
+ eventPublisher.publishFrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e);
+ }
+ }
+
+ /**
+ * Map a location string into a bundle name.
+ * This methods treats the location string as a URL.
+ *
+ * @param location bundle location string.
+ * @return bundle name.
+ */
+ public String mapLocationToName(String location)
+ {
+ int end = location.indexOf('?', 0); /* "?" query */
+
+ if (end == -1)
+ {
+ end = location.indexOf('#', 0); /* "#" fragment */
+
+ if (end == -1)
+ {
+ end = location.length();
+ }
+ }
+
+ int begin = location.replace('\\', '/').lastIndexOf('/', end);
+ int colon = location.lastIndexOf(':', end);
+
+ if (colon > begin)
+ {
+ begin = colon;
+ }
+
+ return (location.substring(begin + 1, end));
+ }
+
+ /**
+ * Return the next valid, unused bundle id.
+ *
+ * @return Next valid, unused bundle id.
+ * @throws IOException If there are no more unused bundle ids.
+ */
+ protected synchronized long getNextBundleId() throws IOException
+ {
+ while (nextId < Long.MAX_VALUE)
+ {
+ long id = nextId;
+
+ nextId++;
+
+ File bundleDir = new File(bundleRootDir, String.valueOf(id));
+
+ if (bundleDir.exists())
+ {
+ continue;
+ }
+
+ return (id);
+ }
+
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+
+ /**
+ * Read a file from an InputStream and write it to the file system.
+ *
+ * @param is InputStream from which to read.
+ * @param file output file to create.
+ * @exception IOException
+ */
+ public static void readFile(InputStream in, File file) throws IOException
+ {
+ FileOutputStream fos = null;
+ try
+ {
+ fos = new FileOutputStream(file);
+
+ byte buffer[] = new byte[1024];
+ int count;
+ while ((count = in.read(buffer, 0, buffer.length)) > 0)
+ {
+ fos.write(buffer, 0, count);
+ }
+
+ fos.close();
+ fos = null;
+
+ in.close();
+ in = null;
+ }
+ catch (IOException e)
+ {
+ // close open streams
+ if (fos != null)
+ {
+ try
+ {
+ fos.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+
+ if (in != null)
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to read file");
+ Debug.printStackTrace(e);
+ }
+
+ throw e;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getExportPackages()
+ */
+ public String getExportPackages() {
+ if (manifest == null)
+ return null;
+ return (String)manifest.get(Constants.EXPORT_PACKAGE);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getExportServices()
+ */
+ public String getExportServices() {
+ if (manifest == null)
+ return null;
+ return (String)manifest.get(Constants.EXPORT_SERVICE);
+ }
+
+ public AdaptorElementFactory getElementFactory() {
+ if (elementFactory == null)
+ elementFactory = new AdaptorElementFactory();
+ return elementFactory;
+ }
+
+ public IBundleStats getBundleStats(){
+ return null;
+ }
+
+ public State getState() {
+ return stateManager.getSystemState();
+ }
+ public PlatformAdmin getPlatformAdmin() {
+ return stateManager;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultBundleData.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultBundleData.java
new file mode 100644
index 000000000..15dd30474
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultBundleData.java
@@ -0,0 +1,713 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.*;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.framework.adaptor.Version;
+import org.eclipse.osgi.framework.adaptor.core.AbstractBundleData;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+
+/**
+ * The <code>BundleData</code> represents a single bundle that is persistently
+ * stored by a <code>FrameworkAdaptor</code>. A <code>BundleData</code> creates
+ * the ClassLoader for a bundle, finds native libraries installed in the
+ * FrameworkAdaptor for the bundle, creates data files for the bundle,
+ * used to access bundle entries, manifest information, and getting and saving
+ * metadata.
+ *
+ */
+public class DefaultBundleData extends AbstractBundleData implements Cloneable {
+
+ public static final String METADATA_BUNDLE_GEN = "METADATA_BUNDLE_GEN";
+ public static final String METADATA_BUNDLE_LOC = "METADATA_BUNDLE_LOC";
+ public static final String METADATA_BUNDLE_REF = "METADATA_BUNDLE_REF";
+ public static final String METADATA_BUNDLE_NAME = "METADATA_BUNDLE_NAME";
+ public static final String METADATA_BUNDLE_NCP = "METADATA_BUNDLE_NCP";
+ public static final String METADATA_BUNDLE_ABSL = "METADATA_BUNDLE_ABSL";
+ public static final String METADATA_BUNDLE_STATUS = "METADATA_BUNDLE_STATUS";
+ public static final String METADATA_BUNDLE_METADATA = "METADATA_BUNDLE_METADATA";
+
+ /** top level bundle directory */
+ protected File dir;
+
+ /** bundle data directory */
+ protected File dirData;
+
+ /** current generation directory */
+ protected File dirGeneration;
+
+ /** bundle id */
+ protected long id;
+
+ /** bundle location */
+ protected String location;
+
+ /** Is bundle a reference */
+ protected boolean reference;
+
+ /** Bundle's metadata **/
+ protected MetaData metadata;
+
+ /** bundle file */
+ protected File file;
+
+ /** BundleFile object for this BundleData */
+ protected BundleFile bundleFile;
+
+ /** bundle's name */
+ protected String name;
+
+ /** theses values are loaded from the manifest */
+ protected String uniqueId;
+ protected Version version;
+ protected boolean isFragment = false;
+ protected String classpath;
+ protected String activator;
+ protected String executionEnvironment;
+ protected String dynamicImports;
+
+ /** bundle generation */
+ protected int generation = 1;
+
+ /** native code paths for this BundleData */
+ protected String[] nativepaths;
+
+ /** the DefaultAdaptor for this BundleData */
+ protected DefaultAdaptor adaptor;
+
+ protected int startLevel = -1;
+
+ protected int status = 0;
+
+ /**
+ * Read data from an existing directory.
+ * This constructor is used by getInstalledBundles.
+ *
+ * @param directory The top level bundle directory.
+ * @throws NumberFormatException if the directory is not a
+ * number, the directory contains a ".delete" file or
+ * the directory does not contain a ".bundle" file.
+ * @throws IOException If an error occurs initializing the bundle data.
+ */
+ public DefaultBundleData(DefaultAdaptor adaptor){
+ this.adaptor = adaptor;
+ }
+
+ /**
+ * Read data from an existing directory.
+ * This constructor is used by getInstalledBundles.
+ *
+ * @param directory The top level bundle directory.
+ * @throws NumberFormatException if the directory is not a
+ * number, the directory contains a ".delete" file or
+ * the directory does not contain a ".bundle" file.
+ * @throws IOException If an error occurs initializing the bundle data.
+ */
+ public void initializeExistingBundle(String directory) throws IOException {
+ id = Long.parseLong(directory);
+ dir = new File(adaptor.bundleRootDir, directory);
+
+ /* if the file is not a directory */
+ if (!dir.exists() || !dir.isDirectory())
+ throw new NumberFormatException();
+ File delete = new File(dir, ".delete");
+
+ /* and the directory is not marked for delete */
+ if (delete.exists())
+ {
+ throw new NumberFormatException();
+ }
+
+ try
+ {
+ loadFromMetaData();
+ }
+ catch (IOException ioe)
+ {
+ throw new NumberFormatException();
+ }
+
+ dirData = new File(dir, adaptor.dataDirName);
+ dirGeneration = new File(dir, String.valueOf(generation));
+
+ if (reference)
+ {
+ file = new File(name);
+ }
+ else
+ {
+ file = new File(dirGeneration, name);
+ }
+ bundleFile = BundleFile.createBundleFile(file,this);
+ loadFromManifest();
+ }
+
+
+ /**
+ * Create data for a new directory.
+ * This constructor is used by installBundle.
+ *
+ * @param id The id of the new bundle.
+ * @param location The location string of the new bundle.
+ */
+ public void initializeReferencedBundle(long id, String location, String name) throws IOException {
+ this.id = id;
+ this.location = location;
+ this.name = name;
+ this.reference = true;
+
+ String directory = String.valueOf(id);
+
+ dir = new File(adaptor.bundleRootDir, directory);
+
+ dirData = new File(dir, adaptor.dataDirName);
+ dirGeneration = new File(dir, String.valueOf(generation));
+
+ this.file = new File(name);
+
+ loadMetaData();
+ setStartLevel(adaptor.getInitialBundleStartLevel());
+
+ if (!getGenerationDir().exists())
+ {
+ throw new IOException(
+ AdaptorMsg.formatter.getString(
+ "ADAPTOR_DIRECTORY_CREATE_EXCEPTION",
+ getGenerationDir().getPath()));
+ }
+
+ this.bundleFile = BundleFile.createBundleFile(this.file, this);
+ loadFromManifest();
+ }
+
+ /**
+ * Create data for a new directory.
+ * This constructor is used by installBundle.
+ *
+ * @param id The id of the new bundle.
+ * @param location The location string of the new bundle.
+ */
+ public void initializeNewBundle(long id, String location, InputStream in) throws IOException {
+ this.id = id;
+ this.location = location;
+ this.name = adaptor.mapLocationToName(location);
+ this.reference = false;
+
+ String directory = String.valueOf(id);
+
+ dir = new File(adaptor.bundleRootDir, directory);
+
+ dirData = new File(dir, adaptor.dataDirName);
+ dirGeneration = new File(dir, String.valueOf(generation));
+
+ file = new File(dirGeneration, name);
+
+ loadMetaData();
+ setStartLevel(adaptor.getInitialBundleStartLevel());
+
+ if (!getGenerationDir().exists())
+ {
+ throw new IOException(
+ AdaptorMsg.formatter.getString(
+ "ADAPTOR_DIRECTORY_CREATE_EXCEPTION",
+ dirGeneration.getPath()));
+ }
+
+ DefaultAdaptor.readFile(in, file);
+ bundleFile = BundleFile.createBundleFile(file,this);
+ loadFromManifest();
+ }
+
+ /**
+ * Creates the ClassLoader for the BundleData. The ClassLoader created
+ * must use the <code>ClassLoaderDelegate</code> to delegate class, resource
+ * and library loading. The delegate is responsible for finding any resource
+ * or classes imported by the bundle or provided by bundle fragments or
+ * bundle hosts. The <code>ProtectionDomain</code> domain must be used
+ * by the Classloader when defining a class.
+ * @param delegate The <code>ClassLoaderDelegate</code> to delegate to.
+ * @param domain The <code>ProtectionDomain</code> to use when defining a class.
+ * @param bundleclasspath An array of bundle classpaths to use to create this
+ * classloader. This is specified by the Bundle-ClassPath manifest entry.
+ * @return The new ClassLoader for the BundleData.
+ */
+ public org.eclipse.osgi.framework.adaptor.BundleClassLoader createClassLoader(
+ ClassLoaderDelegate delegate,
+ ProtectionDomain domain,
+ String[] bundleclasspath) {
+ return adaptor.getElementFactory().createClassLoader(delegate,domain,bundleclasspath,this);
+ }
+
+ /**
+ * Gets a <code>URL</code> to the bundle entry specified by path.
+ * This method must not use the BundleClassLoader to find the
+ * bundle entry since the ClassLoader will delegate to find the resource.
+ * @param path The bundle entry path.
+ * @return A URL used to access the entry or null if the entry
+ * does not exist.
+ */
+ public URL getEntry(String path) {
+ return bundleFile.getURL(path,0);
+ }
+
+ /**
+ * Gets all of the bundle entries that exist under the specified path.
+ * For example: <p>
+ * <code>getEntryPaths("/META-INF")</code> <p>
+ * This will return all entries from the /META-INF directory of the bundle.
+ * @param path The path to a directory in the bundle.
+ * @return An Enumeration of the entry paths or null if the specified path
+ * does not exist.
+ */
+ public Enumeration getEntryPaths(String path) {
+ return bundleFile.getEntryPaths(path);
+ }
+
+ /**
+ * Returns the absolute path name of a native library. The BundleData
+ * ClassLoader invokes this method to locate the native libraries that
+ * belong to classes loaded from this BundleData. Returns
+ * null if the library does not exist in this BundleData.
+ * @param libname The name of the library to find the absolute path to.
+ * @return The absolute path name of the native library or null if
+ * the library does not exist.
+ */
+ public String findLibrary(String libname) {
+ String mappedName = System.mapLibraryName(libname);
+ String path = null;
+
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println(" mapped library name: "+mappedName);
+ }
+
+ path = findNativePath(mappedName);
+
+ if (path == null){
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println(" library does not exist: "+mappedName);
+ }
+ path = findNativePath(libname);
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println(" returning library: "+path);
+ }
+ return path;
+ }
+
+ protected String findNativePath(String libname) {
+ String path = null;
+ if (!libname.startsWith("/")) {
+ libname = '/' + libname;
+ }
+ if (nativepaths != null) {
+ for (int i=0; i<nativepaths.length; i++){
+ if(nativepaths[i].endsWith(libname)){
+ File nativeFile = bundleFile.getFile(nativepaths[i]);
+ path = nativeFile.getAbsolutePath();
+ }
+ }
+ }
+ return path;
+ }
+
+ /**
+ * Installs the native code paths for this BundleData. Each
+ * element of nativepaths must be installed for lookup when findLibrary
+ * is called.
+ * @param nativepaths The array of native code paths to install for
+ * the bundle.
+ * @throws BundleException If any error occurs during install.
+ */
+ public void installNativeCode(String[] nativepaths)
+ throws BundleException {
+ this.nativepaths = nativepaths;
+ StringBuffer sb = new StringBuffer();
+ for (int i=0; i<nativepaths.length; i++){
+ // extract the native code
+ File nativeFile = bundleFile.getFile(nativepaths[i]);
+ if (nativeFile == null) {
+ throw new BundleException(AdaptorMsg.formatter.getString("BUNDLE_NATIVECODE_EXCEPTION", nativepaths[i]));
+ }
+ sb.append(nativepaths[i]);
+ if (i<nativepaths.length-1) {
+ sb.append(",");
+ }
+ }
+ metadata.set(METADATA_BUNDLE_NCP, sb.toString());
+ }
+
+ /**
+ * Return the bundle data directory.
+ * Attempt to create the directory if it does not exist.
+ *
+ * @return Bundle data directory.
+ */
+ public File getDataFile(String path) {
+ if (!dirData.exists() && !dirData.mkdirs())
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to create bundle data directory: "+dirData.getPath());
+ }
+ }
+
+ return (new File(dirData,path));
+ }
+
+ /**
+ * Get the BundleData bundle ID. This will be used as the bundle
+ * ID by the framework.
+ * @return The BundleData ID.
+ */
+ public long getBundleID() {
+ return (id);
+ }
+
+ /**
+ * Get the BundleData Location. This will be used as the bundle
+ * location by the framework.
+ * @return the BundleData location.
+ */
+ public String getLocation() {
+ return (location);
+ }
+
+ /**
+ * Close all resources for this BundleData
+ */
+ public void close() throws IOException {
+ if (bundleFile != null) {
+ bundleFile.close();
+ }
+ }
+
+ /**
+ * Opens all resource for this BundleData. Reopens the BundleData if
+ * it was previosly closed.
+ */
+ public void open() throws IOException {
+ bundleFile.open();
+ }
+
+ /**
+ * Load the bundle data from the metadata.
+ *
+ * @throws IOException if an read error occurs.
+ * @throws FileNotFoundException if the metadata file does not exist.
+ */
+ protected synchronized void loadFromMetaData() throws IOException {
+ loadMetaData();
+ status = metadata.getInt(METADATA_BUNDLE_STATUS, 0);
+ startLevel = metadata.getInt(METADATA_BUNDLE_ABSL, 1);
+ generation = metadata.getInt(METADATA_BUNDLE_GEN, -1);
+ name = metadata.get(METADATA_BUNDLE_NAME, null);
+ location = metadata.get(METADATA_BUNDLE_LOC, null);
+ reference = metadata.getBoolean(METADATA_BUNDLE_REF, false);
+
+ String npString = metadata.get(METADATA_BUNDLE_NCP, null);
+ if (npString != null)
+ setNativeCodePath(npString);
+
+ if( generation == -1 || name == null || location == null ) {
+ throw new IOException (AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+ }
+
+ protected void loadMetaData() throws IOException
+ {
+ metadata = (new MetaData(new File(dir,".bundle"),"Bundle metadata"));
+ metadata.load();
+ }
+
+ protected void loadFromManifest() throws IOException{
+ try {
+ getManifest();
+ } catch (BundleException e) {
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_ERROR_GETTING_MANIFEST",location));
+ }
+
+ if (manifest == null) {
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_ERROR_GETTING_MANIFEST",location));
+ }
+ version = new Version((String)manifest.get(Constants.BUNDLE_VERSION));
+ uniqueId = (String)manifest.get(Constants.BUNDLE_GLOBALNAME);
+ if (uniqueId == null)
+ uniqueId = (String)manifest.get(Constants.BUNDLE_NAME);
+ classpath = (String)manifest.get(Constants.BUNDLE_CLASSPATH);
+ activator = (String)manifest.get(Constants.BUNDLE_ACTIVATOR);
+ String host = (String)manifest.get(Constants.HOST_BUNDLE);
+ isFragment = host != null;
+ executionEnvironment = (String)manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
+ dynamicImports = (String)manifest.get(Constants.DYNAMICIMPORT_PACKAGE);
+ }
+
+ /**
+ * Return the bundle generation directory.
+ * Attempt to create the directory if it does not exist.
+ *
+ * @return Bundle generation directory.
+ */
+ protected File getGenerationDir()
+ {
+ if (!dirGeneration.exists() && !dirGeneration.mkdirs())
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to create bundle jar directory: "+dirGeneration.getPath());
+ }
+ }
+
+ return(dirGeneration);
+ }
+
+ /**
+ * Return the bundle file.
+ * Attempt to create the bundle generation directory if it does not exist.
+ *
+ * @return Bundle file.
+ */
+ protected File getBundleFile()
+ {
+ getGenerationDir(); /* create the generation dir if necessary */
+
+ return(file);
+ }
+
+ /**
+ * Return the top level bundle directory.
+ *
+ * @return Top level bundle directory.
+ */
+ protected File getBundleDir()
+ {
+ return (dir);
+ }
+
+ /**
+ * Save the bundle data in the data file.
+ *
+ * @throws IOException if a write error occurs.
+ */
+ public synchronized void save() throws IOException {
+ metadata.setInt(METADATA_BUNDLE_STATUS, status);
+ metadata.setInt(METADATA_BUNDLE_ABSL, startLevel);
+ metadata.setInt(METADATA_BUNDLE_GEN, generation);
+ metadata.set(METADATA_BUNDLE_NAME, name);
+ metadata.set(METADATA_BUNDLE_LOC, location);
+ metadata.setBoolean(METADATA_BUNDLE_REF, reference);
+ metadata.save();
+ }
+
+/**
+ * Return a copy of this object with the
+ * generation dependent fields updated to
+ * the next free generation level.
+ *
+ * @throws IOException If there are no more available generation levels.
+ */
+ protected DefaultBundleData nextGeneration(String name) throws IOException
+ {
+ int nextGeneration = generation;
+
+ while (nextGeneration < Integer.MAX_VALUE)
+ {
+ nextGeneration++;
+
+ File nextDirGeneration = new File(dir, String.valueOf(nextGeneration));
+
+ if (nextDirGeneration.exists())
+ {
+ continue;
+ }
+
+ DefaultBundleData next;
+ try
+ {
+ next = (DefaultBundleData)clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
+
+ next.generation = nextGeneration;
+ next.dirGeneration = nextDirGeneration;
+ next.name = name;
+ next.file = new File(name);
+ next.reference = true;
+ // null out the manifest to force it to be re-read.
+ next.manifest = null;
+ return(next);
+ }
+
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+
+/**
+ * Return a copy of this object with the
+ * generation dependent fields updated to
+ * the next free generation level.
+ *
+ * @throws IOException If there are no more available generation levels.
+ */
+ public DefaultBundleData nextGeneration() throws IOException
+ {
+ int nextGeneration = generation;
+
+ while (nextGeneration < Integer.MAX_VALUE)
+ {
+ nextGeneration++;
+
+ File nextDirGeneration = new File(dir, String.valueOf(nextGeneration));
+
+ if (nextDirGeneration.exists())
+ {
+ continue;
+ }
+
+ DefaultBundleData next;
+ try
+ {
+ next = (DefaultBundleData)clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
+
+ next.generation = nextGeneration;
+ next.dirGeneration = nextDirGeneration;
+ if (next.reference) {
+ next.reference = false;
+ next.name = adaptor.mapLocationToName(location);
+ }
+ next.file = new File(nextDirGeneration, next.name);
+ // null out the manifest to force it to be re-read.
+ next.manifest = null;
+ return(next);
+ }
+
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+
+ public Bundle getBundle(){
+ return bundle;
+ }
+
+ public String toString(){
+ return location;
+ }
+
+ public int getStartLevel() {
+ return startLevel;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStartLevel(int value) {
+ startLevel = value;
+ }
+
+ public void setStatus(int value) {
+ status = value;
+ }
+
+ public boolean isReference() {
+ return reference;
+ }
+ public void setReference(boolean value) {
+ reference = value;
+ }
+ public int getGeneration() {
+ return generation;
+ }
+ public void setGeneration(int value) {
+ generation = value;
+ }
+
+ public String getNativeCodePath() {
+ if (nativepaths == null || nativepaths.length == 0)
+ return null;
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < nativepaths.length; i++) {
+ File nativeFile = bundleFile.getFile(nativepaths[i]);
+ if (nativeFile != null) {
+ sb.append(nativepaths[i]);
+ if (i < nativepaths.length - 1)
+ sb.append(",");
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String[] parseNativeCodePath(String value) {
+ if (value == null)
+ return null;
+ ArrayList result = new ArrayList(5);
+ StringTokenizer st = new StringTokenizer(value, ",");
+ while (st.hasMoreTokens()) {
+ String path = st.nextToken();
+ result.add(path);
+ }
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ public void setNativeCodePath(String value) {
+ nativepaths = parseNativeCodePath(value);
+ }
+
+ public String getName() {
+ return name;
+ }
+ public String getUniqueId() {
+ return uniqueId;
+ }
+ public String getClassPath() {
+ return classpath;
+ }
+ public String getActivator() {
+ return activator;
+ }
+ public Version getVersion() {
+ return version;
+ }
+ public boolean isFragment() {
+ return isFragment;
+ }
+ public String getExecutionEnvironment(){
+ return executionEnvironment;
+ }
+ public String getDynamicImports(){
+ return dynamicImports;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultClassLoader.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultClassLoader.java
new file mode 100644
index 000000000..85f0ba64f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/DefaultClassLoader.java
@@ -0,0 +1,567 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+
+import java.io.*;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.core.Msg;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+
+/**
+ * A concrete implementation of BundleClassLoader. This implementation
+ * consolidates all Bundle-ClassPath entries into a single ClassLoader.
+ */
+public class DefaultClassLoader
+ extends org.eclipse.osgi.framework.adaptor.BundleClassLoader {
+
+ /**
+ * The BundleData object for this BundleClassLoader
+ */
+ protected DefaultBundleData hostdata;
+
+ /**
+ * The BundleFiles for this BundleClassLoader. Each BundleFile object
+ * represents on Bundle-ClassPath entry.
+ */
+ protected BundleFile[] bundleFiles;
+
+ protected Vector fragClasspaths;
+
+ /**
+ * The buffer size to use when loading classes. This value is used
+ * only if we cannot determine the size of the class we are loading.
+ */
+ protected int buffersize = 8*1024;
+
+ /**
+ * BundleClassLoader constructor.
+ * @param delegate The ClassLoaderDelegate for this ClassLoader.
+ * @param domain The ProtectionDomain for this ClassLoader.
+ * @param bundleclasspath An array of Bundle-ClassPath entries to
+ * use for loading classes and resources. This is specified by the
+ * Bundle-ClassPath manifest entry.
+ * @param bundledata The BundleData for this ClassLoader
+ */
+ public DefaultClassLoader(ClassLoaderDelegate delegate, ProtectionDomain domain, String[] classpath, DefaultBundleData bundledata)
+ {
+ this(delegate,domain,classpath,null,bundledata);
+ }
+
+ /**
+ * BundleClassLoader constructor.
+ * @param delegate The ClassLoaderDelegate for this ClassLoader.
+ * @param domain The ProtectionDomain for this ClassLoader.
+ * @param bundleclasspath An array of Bundle-ClassPath entries to
+ * use for loading classes and resources. This is specified by the
+ * Bundle-ClassPath manifest entry.
+ * @param parent The parent ClassLoader.
+ * @param bundledata The BundleData for this ClassLoader
+ */
+ public DefaultClassLoader(ClassLoaderDelegate delegate, ProtectionDomain domain, String[] classpath, ClassLoader parent, DefaultBundleData bundledata)
+ {
+ super(delegate,domain,classpath,parent);
+ this.hostdata = bundledata;
+
+ try
+ {
+ hostdata.open(); /* make sure the BundleData is open */
+ } catch (IOException e)
+ {
+ hostdata.adaptor.getEventPublisher().publishFrameworkEvent(
+ FrameworkEvent.ERROR,hostdata.getBundle(),e);
+ }
+
+ bundleFiles = buildClasspath(classpath, hostdata);
+ }
+
+ /**
+ * Attaches the BundleData for a fragment to this BundleClassLoader.
+ * The Fragment BundleData resources must be appended to the end of
+ * this BundleClassLoader's classpath. Fragment BundleData resources
+ * must be searched ordered by Bundle ID's.
+ * @param bundledata The BundleData of the fragment.
+ * @param domain The ProtectionDomain of the resources of the fragment.
+ * Any classes loaded from the fragment's BundleData must belong to this
+ * ProtectionDomain.
+ * @param classpath An array of Bundle-ClassPath entries to
+ * use for loading classes and resources. This is specified by the
+ * Bundle-ClassPath manifest entry of the fragment.
+ */
+ public void attachFragment(org.eclipse.osgi.framework.adaptor.BundleData bundledata,ProtectionDomain domain,String[] classpath) {
+ DefaultBundleData defaultBundledata = (DefaultBundleData) bundledata;
+ try
+ {
+ bundledata.open(); /* make sure the BundleData is open */
+ } catch (IOException e)
+ {
+
+ defaultBundledata.adaptor.getEventPublisher().publishFrameworkEvent(
+ FrameworkEvent.ERROR,defaultBundledata.getBundle(),e);
+ }
+ BundleFile[] fragFiles = buildClasspath(classpath,defaultBundledata);
+ FragmentClasspath fragClasspath = new FragmentClasspath(fragFiles,defaultBundledata,domain);
+ insertFragment(fragClasspath);
+ }
+
+ /**
+ * Inserts a fragment classpath to into the list of fragments for this host.
+ * Fragments are inserted into the list according to the fragment's
+ * Bundle ID.
+ * @param fragClasspath The FragmentClasspath to insert.
+ */
+ protected synchronized void insertFragment(FragmentClasspath fragClasspath) {
+ if (fragClasspaths == null) {
+ // First fragment to attach. Simply create the list and add the fragment.
+ fragClasspaths = new Vector(10);
+ fragClasspaths.addElement(fragClasspath);
+ return;
+ }
+
+ // Find a place in the fragment list to insert this fragment.
+ int size = fragClasspaths.size();
+ long fragID = fragClasspath.bundledata.id;
+ for (int i=0; i<size; i++) {
+ long otherID = ((FragmentClasspath)fragClasspaths.elementAt(i)).bundledata.id;
+ if (fragID < otherID) {
+ fragClasspaths.insertElementAt(fragClasspath,i);
+ return;
+ }
+ }
+ // This fragment has the highest ID; put it at the end of the list.
+ fragClasspaths.addElement(fragClasspath);
+ }
+
+ /**
+ * Gets a BundleFile object for the specified ClassPath entry.
+ * @param cp The ClassPath entry to get the BundleFile for.
+ * @return The BundleFile object for the ClassPath entry.
+ */
+ protected BundleFile getClasspath(String cp, DefaultBundleData bundledata){
+ BundleFile bundlefile = null;
+ File file = bundledata.bundleFile.getFile(cp);
+ if (file != null && file.exists()) {
+ try
+ {
+ bundlefile = BundleFile.createBundleFile(file, bundledata);
+ }
+ catch (IOException e)
+ {
+ bundledata.adaptor.getEventPublisher().publishFrameworkEvent(
+ FrameworkEvent.ERROR,bundledata.getBundle(),e);
+ }
+ }
+ else {
+ if (bundledata.bundleFile instanceof BundleFile.ZipBundleFile)
+ {
+ // the classpath entry may be a directory in the bundle jar file.
+ Enumeration entries = bundledata.bundleFile.getEntryPaths(cp);
+ if (entries.hasMoreElements())
+ {
+ bundlefile = BundleFile.createBundleFile(
+ (BundleFile.ZipBundleFile)bundledata.bundleFile, cp);
+ }
+ }
+
+ if (bundlefile == null)
+ {
+ BundleException be = new BundleException(Msg.formatter.getString("BUNDLE_CLASSPATH_ENTRY_NOT_FOUND_EXCEPTION", cp));
+ bundledata.adaptor.getEventPublisher().publishFrameworkEvent(
+ FrameworkEvent.ERROR,bundledata.getBundle(),be);
+ }
+
+ }
+ return bundlefile;
+ }
+
+ protected synchronized Class findClass(String name) throws ClassNotFoundException {
+ Class result = findLoadedClass(name);
+ if (result != null)
+ return result;
+ for(int i=0; i<bundleFiles.length; i++) {
+ if (bundleFiles[i] != null){
+ result = findClassImpl(name,bundleFiles[i],domain);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ // look in fragments.
+ if (fragClasspaths != null){
+ int size = fragClasspaths.size();
+ for (int i=0; i<size; i++) {
+ FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
+ for (int j=0; j<fragCP.bundlefiles.length; j++) {
+ result = findClassImpl(name,fragCP.bundlefiles[j],fragCP.domain);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ }
+ throw new ClassNotFoundException(name);
+ }
+
+ /**
+ * Finds a class in the BundleFile. If a class is found then the class
+ * is defined using the ProtectionDomain bundledomain.
+ * @param name The name of the class to find.
+ * @param bundleFile The BundleFile to find the class in.
+ * @param bundledomain The ProtectionDomain to use to defind the class if
+ * it is found.
+ * @return The loaded class object or null if the class is not found.
+ */
+ protected Class findClassImpl(String name,BundleFile bundleFile,ProtectionDomain bundledomain){
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println("BundleClassLoader["+hostdata+"].findClass("+name+")");
+ }
+
+ String filename = name.replace('.', '/').concat(".class");
+
+ BundleEntry entry = bundleFile.getEntry(filename);
+
+ if (entry == null)
+ {
+ return null;
+ }
+
+ InputStream in;
+ try
+ {
+ in = entry.getInputStream();
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+
+ int length = (int)entry.getSize();
+ byte[] classbytes;
+ int bytesread = 0;
+ int readcount;
+
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println(" about to read "+length+" bytes from "+filename);
+ }
+
+ try
+ {
+ try
+ {
+ if (length > 0)
+ {
+ classbytes = new byte[length];
+
+ readloop:
+ for (; bytesread < length; bytesread += readcount)
+ {
+ readcount = in.read(classbytes, bytesread, length-bytesread);
+
+ if (readcount <= 0) /* if we didn't read anything */
+ {
+ break readloop; /* leave the loop */
+ }
+ }
+ }
+ else /* BundleEntry does not know its own length! */
+ {
+ length = buffersize;
+ classbytes = new byte[length];
+
+ readloop:
+ while (true)
+ {
+ for (; bytesread < length; bytesread += readcount)
+ {
+ readcount = in.read(classbytes, bytesread, length-bytesread);
+
+ if (readcount <= 0) /* if we didn't read anything */
+ {
+ break readloop; /* leave the loop */
+ }
+ }
+
+ byte[] oldbytes = classbytes;
+ length += buffersize;
+ classbytes = new byte[length];
+ System.arraycopy(oldbytes, 0, classbytes, 0, bytesread);
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println(" IOException reading "+filename+" from "+hostdata);
+ }
+
+ return null;
+ }
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println(" read "+bytesread+" bytes from "+filename);
+ Debug.println(" defining class "+name);
+ }
+
+ try
+ {
+ return(defineClass(name, classbytes, 0, bytesread, bundledomain));
+ }
+ catch (Error e)
+ {
+ if (Debug.DEBUG && Debug.DEBUG_LOADER)
+ {
+ Debug.println(" error defining class "+name);
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.BundleClassLoader#findResource(String)
+ */
+ protected URL findResource(String name) {
+ URL result = null;
+ for(int i=0; i<bundleFiles.length; i++) {
+ if (bundleFiles[i] != null){
+ result = findResourceImpl(name,bundleFiles[i],i);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ // look in fragments
+ if (fragClasspaths != null){
+ int size = fragClasspaths.size();
+ for (int i=0; i<size; i++) {
+ FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
+ for (int j=0; j<fragCP.bundlefiles.length; j++) {
+ result = findResourceImpl(name,fragCP.bundlefiles[j],j);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Looks in the specified BundleFile for the resource.
+ * @param name The name of the resource to find,.
+ * @param bundlefile The BundleFile to look in.
+ * @param cpEntry The ClassPath entry index of the BundleFile.
+ * @return A URL to the resource or null if the resource does not exist.
+ */
+ protected URL findResourceImpl(String name,BundleFile bundlefile,int cpEntry) {
+ return bundlefile.getURL(name,cpEntry);
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.BundleClassLoader#findLocalResources(String)
+ */
+ public Enumeration findLocalResources(String resource) {
+ Vector resources = new Vector(6);
+ for(int i=0; i<bundleFiles.length; i++) {
+ if (bundleFiles[i] != null){
+ URL url = findResourceImpl(resource,bundleFiles[i],i);
+ if (url != null) {
+ resources.addElement(url);
+ }
+ }
+ }
+ // look in fragments
+ if (fragClasspaths != null){
+ int size = fragClasspaths.size();
+ for (int i=0; i<size; i++) {
+ FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
+ for (int j=0; j<fragCP.bundlefiles.length; j++) {
+ URL url = findResourceImpl(resource,fragCP.bundlefiles[j],j);
+ if (url != null) {
+ resources.addElement(url);
+ }
+ }
+ }
+ }
+ if (resources.size()>0){
+ return resources.elements();
+ }
+ return null;
+ }
+
+ /**
+ * Closes all the BundleFile objects for this BundleClassLoader.
+ */
+ public void close(){
+ if (!closed) {
+ super.close();
+ for (int i=0; i<bundleFiles.length; i++) {
+ if (bundleFiles[i] != null) {
+ try
+ {
+ if (bundleFiles[i]!=hostdata.bundleFile) {
+ bundleFiles[i].close();
+ }
+ }
+ catch (IOException e)
+ {
+ hostdata.adaptor.getEventPublisher().publishFrameworkEvent(
+ FrameworkEvent.ERROR,hostdata.getBundle(),e);
+ }
+ }
+ }
+ if (fragClasspaths != null) {
+ int size = fragClasspaths.size();
+ for (int i=0; i<size; i++) {
+ FragmentClasspath fragCP = (FragmentClasspath) fragClasspaths.elementAt(i);
+ fragCP.close();
+ }
+ }
+ }
+ }
+
+ protected BundleFile[] buildClasspath(String[] classpath, DefaultBundleData bundledata) {
+ ArrayList result = new ArrayList(10);
+
+ // If not in dev mode then just add the regular classpath entries and return
+ if (System.getProperty("osgi.dev") == null) {
+ for (int i = 0; i < classpath.length; i++)
+ addClassPathEntry(result, classpath[i],bundledata);
+ return (BundleFile[])result.toArray(new BundleFile[result.size()]);
+ }
+
+ // Otherwise, add the legacy entries for backwards compatibility and
+ // then for each classpath entry add the dev entries as spec'd in the
+ // corresponding properties file. If none are spec'd, add the
+ // classpath entry itself
+ addDefaultDevEntries(result, bundledata);
+ for (int i = 0; i < classpath.length; i++) {
+ String[] devEntries = getDevEntries(classpath[i], bundledata);
+ if (devEntries != null && devEntries.length > 0) {
+ for (int j = 0; j < devEntries.length; j++)
+ addClassPathEntry(result, devEntries[j], bundledata);
+ } else
+ addClassPathEntry(result, classpath[i], bundledata);
+ }
+ return (BundleFile[])result.toArray(new BundleFile[result.size()]);
+ }
+
+ private void addDefaultDevEntries(ArrayList result, DefaultBundleData bundledata) {
+ if (System.getProperty("osgi.dev") == null)
+ return;
+ String[] defaultDevEntries = bundledata.adaptor.devCP;
+ if (defaultDevEntries != null)
+ for (int i = 0; i < defaultDevEntries.length; i++)
+ addClassPathEntry(result, defaultDevEntries[i], bundledata);
+ }
+
+ private void addClassPathEntry(ArrayList result, String entry, DefaultBundleData bundledata) {
+ if (entry.equals("."))
+ result.add(bundledata.bundleFile);
+ else {
+ Object element = getClasspath(entry, bundledata);
+ if (element != null)
+ result.add(element);
+ }
+ }
+
+ private String[] getDevEntries(String classpathEntry, DefaultBundleData bundledata) {
+ Properties devProps = null;
+ File propLocation = bundledata.bundleFile.getFile(classpathEntry + ".properties");
+ if (propLocation == null)
+ return null;
+ try {
+ InputStream in = new FileInputStream(propLocation);
+ try {
+ devProps = new Properties();
+ devProps.load(in);
+ return getArrayFromList(devProps.getProperty("bin"));
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ // TODO log the failures but ignore and try to keep going
+ }
+ return null;
+ }
+
+ /**
+ * Returns the result of converting a list of comma-separated tokens into an array
+ *
+ * @return the array of string tokens
+ * @param prop the initial comma-separated string
+ */
+ private String[] getArrayFromList(String prop) {
+ if (prop == null || prop.trim().equals("")) //$NON-NLS-1$
+ return new String[0];
+ Vector list = new Vector();
+ StringTokenizer tokens = new StringTokenizer(prop, ","); //$NON-NLS-1$
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken().trim();
+ if (!token.equals("")) //$NON-NLS-1$
+ list.addElement(token);
+ }
+ return list.isEmpty() ? new String[0] : (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * A data structure to hold information about a fragment classpath.
+ */
+ protected class FragmentClasspath {
+ /** The BundleFiles of the fragments Bundle-Classpath */
+ protected BundleFile[] bundlefiles;
+ /** The BundleData of the fragment */
+ protected DefaultBundleData bundledata;
+ /** The ProtectionDomain of the fragment */
+ protected ProtectionDomain domain;
+
+ protected FragmentClasspath(BundleFile[] bundlefiles, DefaultBundleData bundledata, ProtectionDomain domain){
+ this.bundlefiles = bundlefiles;
+ this.bundledata = bundledata;
+ this.domain = domain;
+ }
+
+ protected void close(){
+ for (int i=0; i<bundlefiles.length; i++) {
+ try {
+ if (bundlefiles[i] != bundledata.bundleFile) {
+ bundlefiles[i].close();
+ }
+ }
+ catch (IOException e)
+ {
+ bundledata.adaptor.getEventPublisher().publishFrameworkEvent(
+ FrameworkEvent.ERROR,bundledata.getBundle(),e);
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/ExternalMessages.properties b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/ExternalMessages.properties
new file mode 100644
index 000000000..021464bc5
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/ExternalMessages.properties
@@ -0,0 +1,25 @@
+######################################################################
+# Copyright (c) 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+######################################################################
+
+#External Messages for EN locale
+ADAPTOR_STORAGE_EXCEPTION=The FrameworkAdaptor object could not perform the operation
+ADAPTOR_DIRECTORY_EXCEPTION=\"{0}\" is not a directory
+ADAPTOR_URL_CREATE_EXCEPTION=\"{0}\" is an invalid URL
+ADAPTOR_DIRECTORY_CREATE_EXCEPTION=The directory \"{0}\" could not be created
+ADAPTOR_ERROR_GETTING_MANIFEST=An error occured trying to read the manifest in bundle {0}
+ADAPTOR_SAME_REF_UPDATE=The update URL references the same location for update: {0}
+RESOURCE_NOT_FOUND_EXCEPTION=The resource {0} could not be found
+BUNDLE_READ_EXCEPTION=An error occured trying to read the bundle
+BUNDLE_NATIVECODE_EXCEPTION=The Bundle-NativeCode file {0} could not be not found
+RELIABLEFILE_FILE_IS_DIRECTORY=file is directory
+RELIABLEFILE_FILE_LOCKED=file locked
+RELIABLEFILE_RENAME_FAILED=rename failed
+RELIABLEFILE_DELETE_FAILED=delete failed
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/MetaData.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/MetaData.java
new file mode 100644
index 000000000..733723719
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/MetaData.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * A Default implementation of the MetaData interface. This class uses a
+ * Properties object to store and get MetaData information. All data
+ * converted into String data before saving.
+ */
+public class MetaData {
+
+ /**
+ * The Properties file to store the data.
+ */
+ Properties properties;
+
+ /**
+ * The File object to store and load the Properties object.
+ */
+ File datafile;
+
+ /**
+ * The header string to use when storing data to the datafile.
+ */
+ String header;
+
+ /**
+ * Constructs a MetaData object that uses the datafile to persistently
+ * store data.
+ * @param datafile The File object used to persistently load and store data.
+ * @param header The header to use when storing data persistently.
+ */
+ public MetaData(File datafile, String header) {
+ this.datafile = datafile;
+ this.header = header;
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#get(java.lang.String, java.lang.String)
+ */
+ public String get(String key, String def) {
+ return properties.getProperty(key,def);
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#getInt(String, int)
+ */
+ public int getInt(String key, int def) {
+ String result = get(key,null);
+ if (result == null) {
+ return def;
+ }
+ try{
+ return Integer.parseInt(result);
+ }
+ catch (NumberFormatException nfe) {
+ return def;
+ }
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#getLong(String, long)
+ */
+ public long getLong(String key, long def) {
+ String result = get(key,null);
+ if (result == null) {
+ return def;
+ }
+ try{
+ return Long.parseLong(result);
+ }
+ catch (NumberFormatException nfe) {
+ return def;
+ }
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#getBoolean(String, boolean)
+ */
+ public boolean getBoolean(String key, boolean def) {
+ String result = get(key,null);
+ if (result == null) {
+ return def;
+ }
+ return Boolean.valueOf(result).booleanValue();
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#set(String, String)
+ */
+ public void set(String key, String val) {
+ properties.put(key,val);
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#setInt(String, int)
+ */
+ public void setInt(String key, int val) {
+ properties.put(key,Integer.toString(val));
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#setLong(String, long)
+ */
+ public void setLong(String key, long val) {
+ properties.put(key,Long.toString(val));
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#setBoolean(String, boolean)
+ */
+ public void setBoolean(String key, boolean val) {
+ properties.put(key,new Boolean(val).toString());
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#remove(String)
+ */
+ public void remove(String key){
+ properties.remove(key);
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#save()
+ */
+ public void save() throws IOException {
+ FileOutputStream fos = new FileOutputStream(datafile);
+ properties.store(fos,header);
+ }
+
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.MetaData#load()
+ */
+ public void load() throws IOException {
+ properties = new Properties();
+ if (datafile.exists()) {
+ FileInputStream fis = new FileInputStream(datafile);
+ properties.load(fis);
+ fis.close();
+ }
+ }
+
+ /**
+ * Returns the result of toString on the Properties object.
+ */
+ public String toString(){
+ return properties.toString();
+ }
+
+
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/PermissionStorage.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/PermissionStorage.java
new file mode 100644
index 000000000..0e99e2250
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/PermissionStorage.java
@@ -0,0 +1,380 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.reliablefile.ReliableFile;
+import org.eclipse.osgi.framework.internal.reliablefile.ReliableFileInputStream;
+import org.eclipse.osgi.framework.internal.reliablefile.ReliableFileOutputStream;
+
+
+/**
+ * Class to model permission data storage.
+ */
+
+//TODO switch this class over to use MetaData instead of ReliableFile
+class PermissionStorage implements org.eclipse.osgi.framework.adaptor.PermissionStorage
+{
+ /** Directory into which permission data files are stored. */
+ protected File permissionDir;
+
+ /** List of permission files: String location => File permission file */
+ protected Hashtable permissionFiles;
+
+ /** Default permission data. */
+ protected File defaultData;
+
+ /** First permission data format version */
+ protected static final int PERMISSIONDATA_VERSION_1 = 1;
+
+ /** Current permission data format version */
+ protected static final int PERMISSIONDATA_VERSION = PERMISSIONDATA_VERSION_1;
+
+ /**
+ * Constructor.
+ *
+ * @throws IOException If an error occurs initializing the object.
+ */
+ protected PermissionStorage(DefaultAdaptor adaptor) throws IOException
+ {
+ permissionDir = new File(adaptor.bundleRootDir, "permdata");
+ permissionFiles = new Hashtable();
+
+ if (!permissionDir.exists() && !permissionDir.mkdirs())
+ {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ {
+ Debug.println("Unable to create directory: "+permissionDir.getPath());
+ }
+
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+
+ defaultData = new File(permissionDir, ".default");
+
+ loadLocations();
+ }
+
+ /**
+ * Returns the locations that have permission data assigned to them,
+ * that is, locations for which permission data
+ * exists in persistent storage.
+ *
+ * @return The locations that have permission data in
+ * persistent storage, or <tt>null</tt> if there is no permission data
+ * in persistent storage.
+ * @throws IOException If a failure occurs accessing peristent storage.
+ */
+ public synchronized String[] getLocations() throws IOException
+ {
+ int size = permissionFiles.size();
+
+ if (size == 0)
+ {
+ return null;
+ }
+
+ String[] locations = new String[size];
+
+ Enumeration enum = permissionFiles.keys();
+
+ for (int i = 0; i < size; i++)
+ {
+ locations[i] = (String)enum.nextElement();
+ }
+
+ return locations;
+ }
+
+ /**
+ * Gets the permission data assigned to the specified
+ * location.
+ *
+ * @param location The location whose permission data is to
+ * be returned.
+ *
+ * @return The permission data assigned to the specified
+ * location, or <tt>null</tt> if that location has not been assigned any
+ * permission data.
+ * @throws IOException If a failure occurs accessing peristent storage.
+ */
+ public synchronized String[] getPermissionData(String location) throws IOException
+ {
+ File file;
+
+ if (location == null)
+ {
+ file = defaultData;
+ }
+ else
+ {
+ file = (File) permissionFiles.get(location);
+
+ if (file == null)
+ {
+ return null;
+ }
+ }
+
+ try
+ {
+ return readData(file);
+ }
+ catch (FileNotFoundException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Assigns the specified permission data to the specified
+ * location.
+ *
+ * @param location The location that will be assigned the
+ * permissions.
+ * @param data The permission data to be assigned, or <tt>null</tt>
+ * if the specified location is to be removed from persistent storaqe.
+ * @throws IOException If a failure occurs modifying peristent storage.
+ */
+ public synchronized void setPermissionData(String location, String[] data) throws IOException
+ {
+ File file;
+
+ if (location == null)
+ {
+ file = defaultData;
+
+ if (data == null)
+ {
+ ReliableFile.delete(defaultData);
+ }
+ else
+ {
+ save(defaultData, null, data); /* Save the value in persistent storage */
+ }
+ }
+ else
+ {
+ file = (File) permissionFiles.get(location);
+
+ if (data == null)
+ {
+ if (file == null)
+ {
+ return;
+ }
+
+ permissionFiles.remove(location);
+
+ ReliableFile.delete(file);
+ }
+ else
+ {
+ file = save(file, location, data); /* Save the value in persistent storage */
+
+ permissionFiles.put(location, file);
+ }
+ }
+ }
+
+ /**
+ * Load the locations for which permission data exists.
+ *
+ * @throws IOException If an error occurs reading the files.
+ */
+ protected void loadLocations() throws IOException
+ {
+ String list[] = permissionDir.list();
+
+ int len = list.length;
+
+ for (int i = 0; i < len; i++)
+ {
+ String name = list[i];
+
+ if (name.endsWith(ReliableFile.newExt))
+ {
+ continue;
+ }
+
+ if (name.endsWith(ReliableFile.oldExt))
+ {
+ continue;
+ }
+
+ if (name.endsWith(ReliableFile.tmpExt))
+ {
+ continue;
+ }
+
+ File file = new File(permissionDir, name);
+
+ try
+ {
+ String location = readLocation(file);
+
+ if (location != null)
+ {
+ permissionFiles.put(location, file);
+ }
+ }
+ catch (FileNotFoundException e)
+ {
+ /* the file should have been there */
+ }
+ }
+ }
+
+ /**
+ * Read the location from the specified file.
+ *
+ * @param file File to read the location from.
+ * @return Location from the file or null if the file is unknown.
+ * @throws IOException If an error occurs reading the file.
+ * @throws FileNotFoundException if the data file does not exist.
+ */
+ private String readLocation(File file) throws IOException
+ {
+ DataInputStream in = new DataInputStream(new ReliableFileInputStream(file));
+ try
+ {
+ int version = in.readInt();
+
+ switch (version)
+ {
+ case PERMISSIONDATA_VERSION_1:
+ {
+ boolean locationPresent = in.readBoolean();
+
+ if (locationPresent)
+ {
+ String location = in.readUTF();
+
+ return location;
+ }
+ break;
+ }
+ default:
+ {
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+ }
+ }
+ finally
+ {
+ in.close();
+ }
+
+ return null;
+ }
+
+ /**
+ * Read the permission data from the specified file.
+ *
+ * @param file File to read the permission data from.
+ * @throws IOException If an error occurs reading the file.
+ * @throws FileNotFoundException if the data file does not exist.
+ */
+ private String[] readData(File file) throws IOException
+ {
+ DataInputStream in = new DataInputStream(new ReliableFileInputStream(file));
+ try
+ {
+ int version = in.readInt();
+
+ switch (version)
+ {
+ case PERMISSIONDATA_VERSION_1:
+ {
+ boolean locationPresent = in.readBoolean();
+
+ if (locationPresent)
+ {
+ String location = in.readUTF();
+ }
+
+ int size = in.readInt();
+ String[] data = new String[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ data[i] = in.readUTF();
+ }
+
+ return data;
+ }
+ default:
+ {
+ throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION"));
+ }
+ }
+ }
+ finally
+ {
+ in.close();
+ }
+ }
+
+ /**
+ * Save the permission data for the specified key.
+ * This assumes an attempt has been made to load
+ * the specified key just prior to calling save.
+ *
+ * @param key Key to save the permission data for.
+ */
+ protected File save(File file, String location, String[] data) throws IOException
+ {
+ if (file == null) /* we need to create a filename */
+ {
+ file = File.createTempFile("perm", "", permissionDir);
+ file.delete(); /* delete the empty file */
+ }
+
+ int size = data.length;
+
+ DataOutputStream out = new DataOutputStream(new ReliableFileOutputStream(file));
+
+ try
+ {
+ out.writeInt(PERMISSIONDATA_VERSION);
+ if (location == null)
+ {
+ out.writeBoolean(false);
+ }
+ else
+ {
+ out.writeBoolean(true);
+ out.writeUTF(location);
+ }
+ out.writeInt(size);
+
+ for (int i = 0; i < size; i++)
+ {
+ out.writeUTF(data[i]);
+ }
+
+ }
+ finally
+ {
+ out.close();
+ }
+
+ return file;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/ReferenceInputStream.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/ReferenceInputStream.java
new file mode 100644
index 000000000..06698fc55
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/defaultadaptor/ReferenceInputStream.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.defaultadaptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * InputStream subclass which provides a reference (via URL) to the data
+ * rather than allowing the input stream to be directly read.
+ */
+public class ReferenceInputStream extends InputStream
+{
+ protected URL reference;
+
+ public ReferenceInputStream(URL reference)
+ {
+ this.reference = reference;
+ }
+
+ /* This method should not be called.
+ */
+ public int read() throws IOException
+ {
+ throw new IOException();
+ }
+
+ public URL getReference()
+ {
+ return reference;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/bundle/BundleURLConnection.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/bundle/BundleURLConnection.java
new file mode 100644
index 000000000..1f4df569e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/bundle/BundleURLConnection.java
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol.bundle;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.eclipse.osgi.framework.internal.core.Msg;
+import org.eclipse.osgi.framework.internal.defaultadaptor.BundleEntry;
+
+/**
+ * URLConnection for BundleClassLoader resources.
+ */
+
+public class BundleURLConnection extends URLConnection
+{
+ /** BundleEntry that the URL is associated. */
+ protected final BundleEntry bundleEntry;
+
+ /** InputStream for this URLConnection. */
+ protected InputStream in;
+
+ /** content type for this URLConnection */
+ protected String contentType;
+
+ /**
+ * Constructor for a BundleClassLoader resource URLConnection.
+ *
+ * @param url URL for this URLConnection.
+ * @param bundleEntry BundleEntry that the URLConnection is associated.
+ */
+ protected BundleURLConnection(URL url, BundleEntry bundleEntry)
+ {
+ super(url);
+
+ this.bundleEntry = bundleEntry;
+ this.in = null;
+ this.contentType = null;
+ }
+
+ /**
+ * Establishes the connection to the resource specified by this <code>URL</code>
+ * with this <code>method</code>, along with other options that can only be set before
+ * this connection is made.
+ *
+ * @see connected
+ * @see java.io.IOException
+ * @see URLStreamHandler
+ */
+ public synchronized void connect() throws IOException
+ {
+ if (!connected)
+ {
+ if (bundleEntry != null)
+ {
+ in = bundleEntry.getInputStream();
+ connected = true;
+ }
+ else
+ {
+ throw new IOException(Msg.formatter.getString("RESOURCE_NOT_FOUND_EXCEPTION", url));
+ }
+ }
+ }
+
+ /**
+ * Answers the length of the content or body in the response header in bytes.
+ * Answer -1 if <code> Content-Length </code> cannot be found in the response header.
+ *
+ * @return int The length of the content
+ *
+ * @see getContentType()
+ */
+ public int getContentLength()
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException e)
+ {
+ return(-1);
+ }
+ }
+
+ return((int)bundleEntry.getSize());
+ }
+
+ /**
+ * Answers the type of the content.
+ * Answers <code> null </code> if there's no such field.
+ *
+ * @return java.lang.String The type of the content
+ *
+ * @see guessContentTypeFromName()
+ * @see guessContentTypeFromStream()
+ */
+ public String getContentType()
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException e)
+ {
+ return(null);
+ }
+ }
+
+ if (contentType == null)
+ {
+ contentType = guessContentTypeFromName(bundleEntry.getName());
+
+ if (contentType == null)
+ {
+ try
+ {
+ InputStream in = bundleEntry.getInputStream();
+
+ try
+ {
+ contentType = guessContentTypeFromStream(in);
+ }
+ finally
+ {
+ if (in != null)
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+
+ return(contentType);
+ }
+
+ /**
+ * Answers whether this connection supports input.
+ *
+ * @return boolean true if this connection supports input, false otherwise
+ *
+ * @see setDoInput()
+ * @see doInput
+ */
+ public boolean getDoInput()
+ {
+ return(true);
+ }
+
+ /**
+ * Answers whether this connection supports output.
+ *
+ * @return boolean true if this connection supports output, false otherwise
+ *
+ * @see setDoOutput()
+ * @see doOutput
+ */
+ public boolean getDoOutput()
+ {
+ return(false);
+ }
+
+ /**
+ * Creates an InputStream for reading from this URL Connection.
+ * It throws UnknownServiceException by default.
+ * This method should be overridden by its subclasses
+ *
+ * @return InputStream The InputStream to read from
+ * @exception IOException If an InputStream could not be created
+ *
+ * @see getContent()
+ * @see getOutputStream()
+ * @see java.io.InputStream
+ * @see java.io.IOException
+ *
+ */
+ public InputStream getInputStream() throws IOException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ return(in);
+ }
+
+ /**
+ * Answers the value of the field <code>Last-Modified</code> in the response header,
+ * 0 if no such field exists
+ *
+ * @return The last modified time.
+ */
+ public long getLastModified()
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException e)
+ {
+ return(0);
+ }
+ }
+
+ long lastModified = bundleEntry.getTime();
+
+ if (lastModified == -1)
+ {
+ return(0);
+ }
+
+ return(lastModified);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/bundle/Handler.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/bundle/Handler.java
new file mode 100644
index 000000000..91d5354d7
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/bundle/Handler.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol.bundle;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+import org.eclipse.osgi.framework.internal.defaultadaptor.BundleEntry;
+
+/**
+ * URLStreamHandler for BundleClassLoader resources.
+ */
+
+public class Handler extends URLStreamHandler
+{
+ /**
+ * BundleEntry this handler is associated with.
+ */
+ protected final BundleEntry entry;
+
+ /**
+ * Constructor for a BundleClassLoader resource URLStreamHandler.
+ *
+ * @param entry BundleEntry this handler represents.
+ */
+ public Handler(BundleEntry entry)
+ {
+ this.entry = entry;
+ }
+
+ /**
+ * Establishes a connection to the resource specified by <code>URL</code>.
+ * Since different protocols may have unique ways of connecting, it must be
+ * overridden by the subclass.
+ *
+ * @return java.net.URLConnection
+ * @param url java.net.URL
+ *
+ * @exception IOException thrown if an IO error occurs during connection establishment
+ */
+ protected URLConnection openConnection(URL url) throws IOException
+ {
+ return(new BundleURLConnection(url, entry));
+ }
+
+ /**
+ * Converts a bundle URL to a String.
+ *
+ * @param url the URL.
+ * @return a string representation of the URL.
+ */
+ protected String toExternalForm(URL url)
+ {
+ StringBuffer result = new StringBuffer(url.getProtocol());
+ result.append(":");
+
+ String host = url.getHost();
+ if ((host != null) && (host.length() > 0))
+ {
+ result.append("//");
+ result.append(host);
+ }
+
+ int port = url.getPort();
+
+ if (port != -1)
+ {
+ result.append(":");
+ result.append(port);
+ }
+
+ String file = url.getFile();
+ if (file != null)
+ {
+ if ((file.length() > 0) && (file.charAt(0) != '/')) /* if name doesn't have a leading slash */
+ {
+ result.append("/");
+ }
+
+ result.append(file);
+ }
+
+ String ref = url.getRef();
+ if (ref != null)
+ {
+ result.append("#");
+ result.append(ref);
+ }
+
+ return (result.toString());
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/reference/Handler.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/reference/Handler.java
new file mode 100644
index 000000000..7ed522a4d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/reference/Handler.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol.reference;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+/**
+ * URLStreamHandler for ReferenceClassLoader resources.
+ */
+
+public class Handler extends URLStreamHandler
+{
+ /**
+ * Constructor for a BundleClassLoader resource URLStreamHandler.
+ *
+ * @param entry BundleEntry this handler represents.
+ */
+ public Handler()
+ {
+ }
+
+ /**
+ * Establishes a connection to the resource specified by <code>URL</code>.
+ * Since different protocols may have unique ways of connecting, it must be
+ * overridden by the subclass.
+ *
+ * @return java.net.URLConnection
+ * @param url java.net.URL
+ *
+ * @exception IOException thrown if an IO error occurs during connection establishment
+ */
+ protected URLConnection openConnection(URL url) throws IOException
+ {
+ return new ReferenceURLConnection(url);
+ }
+
+ /**
+ * Parse reference URL.
+ */
+ protected void parseURL(URL url, String str, int start, int end)
+ {
+ if (end < start)
+ {
+ return;
+ }
+ String reference = (start < end) ? str.substring(start, end) : url.getPath();
+
+ setURL(url, url.getProtocol(), null, -1, null, null, reference, null, null);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/reference/ReferenceURLConnection.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/reference/ReferenceURLConnection.java
new file mode 100644
index 000000000..130b9bfac
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/protocol/reference/ReferenceURLConnection.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.protocol.reference;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.eclipse.osgi.framework.internal.defaultadaptor.ReferenceInputStream;
+
+/**
+ * URLConnection for BundleClassLoader resources.
+ */
+
+public class ReferenceURLConnection extends URLConnection
+{
+ protected URL reference;
+
+ /**
+ * Constructor for a BundleClassLoader resource URLConnection.
+ *
+ * @param url URL for this URLConnection.
+ * @param bundleEntry BundleEntry that the URLConnection is associated.
+ */
+ protected ReferenceURLConnection(URL url)
+ {
+ super(url);
+ }
+
+ /**
+ * Establishes the connection to the resource specified by this <code>URL</code>
+ * with this <code>method</code>, along with other options that can only be set before
+ * this connection is made.
+ *
+ * @see connected
+ * @see java.io.IOException
+ * @see URLStreamHandler
+ */
+ public synchronized void connect() throws IOException
+ {
+ if (!connected)
+ {
+ URL ref = new URL(url.getPath());
+ if (!new File(ref.getFile()).exists())
+ throw new FileNotFoundException();
+ reference = ref;
+ }
+ }
+
+ /**
+ * Answers whether this connection supports input.
+ *
+ * @return boolean true if this connection supports input, false otherwise
+ *
+ * @see setDoInput()
+ * @see doInput
+ */
+ public boolean getDoInput()
+ {
+ return true;
+ }
+
+ /**
+ * Answers whether this connection supports output.
+ *
+ * @return boolean true if this connection supports output, false otherwise
+ *
+ * @see setDoOutput()
+ * @see doOutput
+ */
+ public boolean getDoOutput()
+ {
+ return false;
+ }
+
+ /**
+ * Creates an InputStream for reading from this URL Connection.
+ * It throws UnknownServiceException by default.
+ * This method should be overridden by its subclasses
+ *
+ * @return InputStream The InputStream to read from
+ * @exception IOException If an InputStream could not be created
+ *
+ * @see getContent()
+ * @see getOutputStream()
+ * @see java.io.InputStream
+ * @see java.io.IOException
+ *
+ */
+ public InputStream getInputStream() throws IOException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+
+ return new ReferenceInputStream(reference);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFile.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFile.java
new file mode 100644
index 000000000..f6c4635b8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFile.java
@@ -0,0 +1,536 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.reliablefile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import org.eclipse.osgi.framework.internal.defaultadaptor.AdaptorMsg;
+
+/**
+ * ReliableFile class used by ReliableFileInputStream and ReliableOutputStream.
+ * This class encapsulates all the logic for reliable file support.
+ */
+
+public class ReliableFile
+{
+ /**
+ * Extension of tmp file used during writing.
+ * A reliable file with this extension should
+ * never be directly used.
+ */
+ public static final String tmpExt = ".tmp";
+
+ /**
+ * Extension of previous generation of the reliable file.
+ * A reliable file with this extension should
+ * never be directly used.
+ */
+ public static final String oldExt = ".bak";
+
+ /**
+ * Extension of next generation of the reliable file.
+ * A reliable file with this extension should
+ * never be directly used.
+ */
+ public static final String newExt = ".new";
+
+ /** List of active ReliableFile objects: File => ReliableFile */
+ private static Hashtable files;
+
+ static
+ {
+ files = new Hashtable(30); /* initialize files */
+ }
+
+ /** File object for original file */
+ private File orgFile;
+
+ /** File object for the temporary output file */
+ private File tmpFile;
+
+ /** File object for old data file */
+ private File oldFile;
+
+ /** File object for file containing new data */
+ private File newFile;
+
+
+ /** True if this object is open for read or write */
+ private boolean locked;
+
+ /** Use code of this object. When zero this object must be removed from files */
+ private int use;
+
+ /**
+ * ReliableFile object factory. This method is called by ReliableFileInputStream
+ * and ReliableFileOutputStream to get a ReliableFile object for a target file.
+ * If the object is in the cache, the cached copy is returned.
+ * Otherwise a new ReliableFile object is created and returned.
+ * The use count of the returned ReliableFile object is incremented.
+ *
+ * @param name Name of the target file.
+ * @return A ReliableFile object for the target file.
+ * @throws IOException If the target file is a directory.
+ */
+ static ReliableFile getReliableFile(String name) throws IOException
+ {
+ return getReliableFile(new File(name));
+ }
+
+ /**
+ * ReliableFile object factory. This method is called by ReliableFileInputStream
+ * and ReliableFileOutputStream to get a ReliableFile object for a target file.
+ * If the object is in the cache, the cached copy is returned.
+ * Otherwise a new ReliableFile object is created and returned.
+ * The use count of the returned ReliableFile object is incremented.
+ *
+ * @param file File object for the target file.
+ * @return A ReliableFile object for the target file.
+ * @throws IOException If the target file is a directory.
+ */
+ static ReliableFile getReliableFile(File file) throws IOException
+ {
+ if (file.isDirectory())
+ {
+ throw new FileNotFoundException(AdaptorMsg.formatter.getString("RELIABLEFILE_FILE_IS_DIRECTORY"));
+ }
+
+ synchronized (files)
+ {
+ ReliableFile reliable = (ReliableFile)files.get(file);
+
+ if (reliable == null)
+ {
+ reliable = new ReliableFile(file);
+
+ files.put(file, reliable);
+ }
+
+ reliable.use++;
+
+ return reliable;
+ }
+ }
+
+ /**
+ * Decrement this object's use count. If the use count
+ * drops to zero, remove this object from the cache.
+ *
+ */
+ private void release()
+ {
+ synchronized (files)
+ {
+ use--;
+
+ if (use <= 0)
+ {
+ files.remove(orgFile);
+ }
+ }
+ }
+
+ /**
+ * Private constructor used by the static getReliableFile factory methods.
+ *
+ * @param file File object for the target file.
+ */
+ private ReliableFile(File file)
+ {
+ String name = file.getPath();
+
+ orgFile = file;
+ tmpFile = new File(name + tmpExt);
+ oldFile = new File(name + oldExt);
+ newFile = new File(name + newExt);
+ use = 0;
+ locked = false;
+ }
+
+ /**
+ * Recovers the target file, if necessary, and returns an InputStream
+ * object for reading the target file.
+ *
+ * @return An InputStream object which can be used to read the target file.
+ * @throws IOException If an error occurs preparing the file.
+ */
+ synchronized InputStream getInputStream() throws IOException
+ {
+ try
+ {
+ lock();
+ }
+ catch (IOException e)
+ {
+ /* the lock request failed; decrement the use count */
+ release();
+
+ throw e;
+ }
+
+ try
+ {
+ recoverFile();
+
+ return new FileInputStream(orgFile.getPath());
+ }
+ catch (IOException e)
+ {
+ unlock();
+
+ release();
+
+ throw e;
+ }
+ }
+
+ /**
+ * Close the target file for reading.
+ *
+ * @throws IOException If an error occurs closing the file.
+ */
+ /* This method does not need to be synchronized if it only calls release. */
+ void closeInputFile() throws IOException
+ {
+ unlock();
+
+ release();
+ }
+
+ /**
+ * Recovers the target file, if necessary, and returns an OutputStream
+ * object for writing the target file.
+ *
+ * @return An OutputStream object which can be used to write the target file.
+ * @throws IOException If an error occurs preparing the file.
+ */
+ synchronized OutputStream getOutputStream(boolean append) throws IOException
+ {
+ try
+ {
+ lock();
+ }
+ catch (IOException e)
+ {
+ /* the lock request failed; decrement the use count */
+ release();
+
+ throw e;
+ }
+
+ try
+ {
+ if (append)
+ {
+ recoverFile();
+
+ if (orgFile.exists())
+ {
+ cp(orgFile, tmpFile);
+ }
+ }
+
+ return new FileOutputStream(tmpFile.getPath(), append);
+ }
+ catch (IOException e)
+ {
+ unlock();
+
+ release();
+
+ throw e;
+ }
+ }
+
+ /**
+ * Close the target file for reading.
+ *
+ * @throws IOException If an error occurs closing the file.
+ */
+ synchronized void closeOutputFile() throws IOException
+ {
+ try
+ {
+ boolean orgExists = orgFile.exists();
+ boolean newExists = newFile.exists();
+
+ if (newExists)
+ {
+ rm(oldFile);
+ mv(newFile, oldFile);
+ }
+
+ mv(tmpFile, newFile);
+
+ if (orgExists)
+ {
+ if (newExists)
+ {
+ rm(orgFile);
+ }
+ else
+ {
+ rm(oldFile);
+ mv(orgFile, oldFile);
+ }
+ }
+
+ mv(newFile, orgFile);
+ }
+ finally
+ {
+ unlock();
+
+ release();
+ }
+ }
+
+ /**
+ * This method recovers the reliable file if necessary.
+ *
+ * @throws IOException If an error occurs recovering the file.
+ */
+ private void recoverFile() throws IOException
+ {
+ boolean orgExists = orgFile.exists();
+ boolean newExists = newFile.exists();
+ boolean oldExists = oldFile.exists();
+
+ if (newExists)
+ {
+ if (orgExists && !oldExists)
+ {
+ mv(orgFile, oldFile);
+ }
+
+ cp(newFile, orgFile);
+
+ if (orgExists || oldExists)
+ {
+ rm(newFile);
+ }
+ else
+ {
+ mv(newFile, oldFile);
+ }
+ }
+ else
+ {
+ if (oldExists && !orgExists)
+ {
+ cp(oldFile, orgFile);
+ }
+ }
+ }
+
+ /**
+ * Lock the target file.
+ *
+ * @throws IOException If the file is already locked.
+ */
+ private void lock() throws IOException
+ {
+ if (locked)
+ {
+ throw new FileNotFoundException(AdaptorMsg.formatter.getString("RELIABLEFILE_FILE_LOCKED"));
+ }
+
+ locked = true;
+ }
+
+ /**
+ * Unlock the target file.
+ */
+ private void unlock()
+ {
+ locked = false;
+ }
+
+ /**
+ * Rename a file.
+ *
+ * @param from The original file.
+ * @param to The new file name.
+ * @throws IOException If the rename failed.
+ */
+ private static void mv(File from, File to) throws IOException
+ {
+ if (!from.renameTo(to))
+ {
+ throw new IOException(AdaptorMsg.formatter.getString("RELIABLEFILE_RENAME_FAILED"));
+ }
+ }
+
+ /**
+ * Copy a file.
+ *
+ * @param from The original file.
+ * @param to The target file.
+ * @throws IOException If the copy failed.
+ */
+ private static final int CP_BUF_SIZE = 4096;
+ private static void cp(File from, File to) throws IOException
+ {
+ FileInputStream in = null;
+ FileOutputStream out = null;
+
+ try
+ {
+ out = new FileOutputStream(to);
+
+ int length = (int)from.length();
+ if (length > 0)
+ {
+ if (length > CP_BUF_SIZE)
+ {
+ length = CP_BUF_SIZE;
+ }
+
+ in = new FileInputStream(from);
+
+ byte buffer[] = new byte[length];
+ int count;
+ while ((count = in.read(buffer, 0, length)) > 0)
+ {
+ out.write(buffer, 0, count);
+ }
+
+ in.close();
+ in = null;
+ }
+
+ out.close();
+ out = null;
+ }
+ catch (IOException e)
+ {
+ // close open streams
+ if (out != null)
+ {
+ try
+ {
+ out.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+
+ if (in != null)
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ * Delete a file.
+ *
+ * @param file The file to delete.
+ * @throws IOException If the delete failed.
+ */
+ private static void rm(File file) throws IOException
+ {
+ if (file.exists() && !file.delete())
+ {
+ throw new IOException(AdaptorMsg.formatter.getString("RELIABLEFILE_DELETE_FAILED"));
+ }
+ }
+
+ /**
+ * Answers a boolean indicating whether or not the specified reliable file
+ * exists on the underlying file system.
+ *
+ * @return <code>true</code> if the specified reliable file exists,
+ * <code>false</code> otherwise.
+ */
+ public static boolean exists(File file)
+ {
+ if (file.exists()) /* quick test */
+ {
+ return true;
+ }
+
+ String name = file.getPath();
+
+ return new File(name + oldExt).exists() || new File(name + newExt).exists();
+ }
+
+ /**
+ * Delete this reliable file on the underlying file system.
+ *
+ * @throws IOException If the delete failed.
+ */
+ private synchronized void delete() throws IOException
+ {
+ try
+ {
+ lock();
+ }
+ catch (IOException e)
+ {
+ /* the lock request failed; decrement the use count */
+ release();
+
+ throw e;
+ }
+
+ try
+ {
+ rm(oldFile);
+ rm(orgFile);
+ rm(newFile);
+ rm(tmpFile);
+ }
+ finally
+ {
+ unlock();
+
+ release();
+ }
+ }
+
+ /**
+ * Delete the specified reliable file
+ * on the underlying file system.
+ *
+ * @return <code>true</code> if the specified reliable file was deleted,
+ * <code>false</code> otherwise.
+ */
+ public static boolean delete(File file)
+ {
+ try
+ {
+ getReliableFile(file).delete();
+
+ return true;
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileInputStream.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileInputStream.java
new file mode 100644
index 000000000..c29095d32
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileInputStream.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.reliablefile;
+
+import java.io.File;
+import java.io.FilterInputStream;
+import java.io.IOException;
+
+/**
+ * A ReliableFile FileInputStream replacement class.
+ * This class can be used just like FileInputStream. The class
+ * is in partnership with ReliableFileOutputStream to avoid losing
+ * file data by using multiple files.
+ *
+ * @see ReliableFileOutputStream
+ */
+public class ReliableFileInputStream extends FilterInputStream
+{
+ /**
+ * ReliableFile object for this file.
+ */
+ private ReliableFile reliable;
+
+ /**
+ * Constructs a new ReliableFileInputStream on the file named <code>name</code>. If the
+ * file does not exist, the <code>FileNotFoundException</code> is thrown.
+ * The <code>name</code> may be absolute or relative
+ * to the System property <code>"user.dir"</code>.
+ *
+ * @param name the file on which to stream reads.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileInputStream(String name) throws IOException
+ {
+ this(ReliableFile.getReliableFile(name));
+ }
+
+ /**
+ * Constructs a new ReliableFileInputStream on the File <code>file</code>. If the
+ * file does not exist, the <code>FileNotFoundException</code> is thrown.
+ *
+ * @param file the File on which to stream reads.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileInputStream(File file) throws IOException
+ {
+ this(ReliableFile.getReliableFile(file));
+ }
+
+ /**
+ * Private constructor used by other constructors.
+ *
+ * @param reliable the ReliableFile on which to read.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ private ReliableFileInputStream(ReliableFile reliable) throws IOException
+ {
+ super(reliable.getInputStream());
+
+ this.reliable = reliable;
+ }
+
+ /**
+ * Closes this input stream and releases any system resources associated
+ * with the stream.
+ *
+ * @exception java.io.IOException If an error occurs closing the file.
+ */
+ public synchronized void close() throws IOException
+ {
+ if (reliable != null)
+ {
+ try
+ {
+ super.close();
+ }
+ finally
+ {
+ reliable.closeInputFile();
+ reliable = null;
+ }
+ }
+ }
+
+ /**
+ * Call close to finalize the underlying ReliableFile.
+ */
+ protected void finalize() throws IOException
+ {
+ close();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileOutputStream.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileOutputStream.java
new file mode 100644
index 000000000..ee6fdeb34
--- /dev/null
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileOutputStream.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.reliablefile;
+
+import java.io.File;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+
+/**
+ * A ReliableFile FileOutputStream replacement class.
+ * This class can be used just like FileOutputStream. The class
+ * is in partnership with ReliableFileInputStream to avoid losing
+ * file data by using multiple files.
+ *
+ * @see ReliableFileInputStream
+ */
+public class ReliableFileOutputStream extends FilterOutputStream
+{
+ /**
+ * ReliableFile object for the file.
+ */
+ private ReliableFile reliable;
+
+ /**
+ * Constructs a new ReliableFileOutputStream on the File <code>file</code>. If the
+ * file exists, it is written over. See the constructor which can append to
+ * the file if so desired.
+ *
+ * @param file the File on which to stream reads.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileOutputStream(File file) throws IOException
+ {
+ this(ReliableFile.getReliableFile(file), false);
+ }
+
+ /**
+ * Constructs a new ReliableFileOutputStream on the File <code>file</code>.
+ *
+ * @param file the File on which to stream reads.
+ * @param append a boolean indicating whether or not to append to an existing file.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileOutputStream(File file, boolean append) throws IOException
+ {
+ this(ReliableFile.getReliableFile(file), append);
+ }
+
+ /**
+ * Constructs a new ReliableFileOutputStream on the file named <code>name</code>. If
+ * the file exists, it is written over. See the constructor which can append to
+ * the file if so desired.
+ * The <code>name</code> may be absolute or relative
+ * to the System property <code>"user.dir"</code>.
+ *
+ * @param name the file on which to stream writes.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileOutputStream(String name) throws IOException
+ {
+ this(ReliableFile.getReliableFile(name), false);
+ }
+
+ /**
+ * Constructs a new ReliableFileOutputStream on the file named <code>name</code>.
+ * The <code>name</code> may be absolute or relative
+ * to the System property <code>"user.dir"</code>.
+ *
+ * @param name the file on which to stream writes.
+ * @param append a boolean indicating whether or not to append to an existing file.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileOutputStream(String name, boolean append) throws IOException
+ {
+ this(ReliableFile.getReliableFile(name), append);
+ }
+
+ /**
+ * Private constructor used by other constructors.
+ *
+ * @param reliable the ReliableFile on which to read.
+ * @param append a boolean indicating whether or not to append to an existing file.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ private ReliableFileOutputStream(ReliableFile reliable, boolean append) throws IOException
+ {
+ super(reliable.getOutputStream(append));
+
+ this.reliable = reliable;
+ }
+
+ /**
+ * Closes this output stream and releases any system resources
+ * associated with this stream. The general contract of <code>close</code>
+ * is that it closes the output stream. A closed stream cannot perform
+ * output operations and cannot be reopened.
+ *
+ * @exception java.io.IOException If an error occurs closing the file.
+ */
+ public synchronized void close() throws IOException
+ {
+ if (reliable != null)
+ {
+ try
+ {
+ super.close();
+ }
+ finally
+ {
+ reliable.closeOutputFile();
+ reliable = null;
+ }
+ }
+ }
+
+ /**
+ * Call close to finalize the underlying ReliableFile.
+ */
+ protected void finalize() throws IOException
+ {
+ close();
+ }
+
+
+ /**
+ * Override default FilterOutputStream method.
+ */
+ public void write(byte b[], int off, int len) throws IOException
+ {
+ out.write(b, off, len);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/META-INF/ADAPTOR.MF b/bundles/org.eclipse.osgi/eclipseAdaptor/src/META-INF/ADAPTOR.MF
new file mode 100644
index 000000000..4fe568229
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/META-INF/ADAPTOR.MF
@@ -0,0 +1,3 @@
+Export-Package:
+ org.eclipse.osgi.framework.stats
+
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/CachedManifest.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/CachedManifest.java
new file mode 100644
index 000000000..1decf58ac
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/CachedManifest.java
@@ -0,0 +1,64 @@
+package org.eclipse.core.runtime.adaptor;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import org.eclipse.osgi.framework.adaptor.Version;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.osgi.framework.BundleException;
+
+public class CachedManifest extends Dictionary {
+
+ Dictionary manifest = null;
+ EclipseBundleData bundledata;
+
+ public CachedManifest(EclipseBundleData bundledata) {
+ this.bundledata = bundledata;
+ }
+
+ protected Dictionary getManifest() {
+ if (manifest == null)
+ try {
+ manifest = bundledata.loadManifest();
+ } catch (BundleException e) {
+ return null;
+ }
+ return manifest;
+ }
+
+ public int size() {
+ return getManifest().size();
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public Enumeration elements() {
+ return getManifest().elements();
+ }
+
+ public Enumeration keys() {
+ return getManifest().keys();
+ }
+
+ public Object get(Object key) {
+ if (Constants.BUNDLE_VERSION.equalsIgnoreCase((String)key)) {
+ Version result = bundledata.getVersion();
+ return result == null ? null : result.toString();
+ }
+ if ("plugin-class".equalsIgnoreCase((String)key))
+ return bundledata.getPluginClass();
+ if ("legacy".equalsIgnoreCase((String)key))
+ return bundledata.isLegacy();
+ return getManifest().get(key);
+ }
+
+ public Object remove(Object key) {
+ return getManifest().remove(key);
+ }
+
+ public Object put(Object key, Object value) {
+ return getManifest().put(key, value);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseAdaptor.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseAdaptor.java
new file mode 100644
index 000000000..a9c5781e0
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseAdaptor.java
@@ -0,0 +1,318 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.adaptor;
+
+import java.io.*;
+import java.util.*;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.debug.DebugOptions;
+import org.eclipse.osgi.framework.internal.defaultadaptor.*;
+import org.eclipse.osgi.framework.stats.StatsManager;
+import org.eclipse.osgi.internal.resolver.StateImpl;
+import org.eclipse.osgi.internal.resolver.StateManager;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.osgi.framework.*;
+
+public class EclipseAdaptor extends DefaultAdaptor {
+ public static boolean MONITOR_BUNDLES = false;
+ public static boolean MONITOR_CLASSES = false;
+ public static boolean MONITOR_RESOURCE_BUNDLES = false;
+ public static String TRACE_FILENAME = "runtime.traces"; //$NON-NLS-1$
+ public static String TRACE_FILTERS = "trace.properties"; //$NON-NLS-1$
+ public static boolean TRACE_CLASSES = false;
+ public static boolean TRACE_BUNDLES = false;
+
+ private static final String RUNTIME_ADAPTOR = "org.eclipse.core.runtime.adaptor";
+
+ //Option names for spies
+ private static final String OPTION_MONITOR_BUNDLES = RUNTIME_ADAPTOR + "/monitor/bundles"; //$NON-NLS-1$
+ private static final String OPTION_MONITOR_CLASSES = RUNTIME_ADAPTOR + "/monitor/classes"; //$NON-NLS-1$
+ private static final String OPTION_MONITOR_RESOURCEBUNDLES = RUNTIME_ADAPTOR + "/monitor/resourcebundles"; //$NON-NLS-1$
+ private static final String OPTION_TRACE_BUNDLES = RUNTIME_ADAPTOR + "/trace/bundleActivation"; //$NON-NLS-1$
+ private static final String OPTION_TRACE_CLASSES = RUNTIME_ADAPTOR + "/trace/classLoading"; //$NON-NLS-1$
+ private static final String OPTION_TRACE_FILENAME = RUNTIME_ADAPTOR + "/trace/filename"; //$NON-NLS-1$
+ private static final String OPTION_TRACE_FILTERS = RUNTIME_ADAPTOR + "/trace/filters"; //$NON-NLS-1$
+ private static final String OPTION_STATE_READER = RUNTIME_ADAPTOR + "/state/reader";//$NON-NLS-1$
+ private static final String OPTION_RESOLVER = RUNTIME_ADAPTOR + "/resolver/timing"; //$NON-NLS-1$
+ private static final String OPTION_RESOLVER_READER = RUNTIME_ADAPTOR + "/resolver/reader/timing"; //$NON-NLS-1$
+ public static final byte BUNDLEDATA_VERSION = 1;
+ public static final byte NULL = 0;
+ public static final byte OBJECT = 1;
+
+ int startLevel = 1;
+
+ public EclipseAdaptor(String[] args) {
+ super(args);
+ setDebugOptions();
+ }
+
+ public AdaptorElementFactory getElementFactory() {
+ if (elementFactory == null)
+ elementFactory = new EclipseElementFactory();
+ return elementFactory;
+ }
+
+ public void frameworkStart(BundleContext context) throws BundleException {
+ super.frameworkStart(context);
+ Bundle bundle = context.getBundle();
+ register(org.eclipse.osgi.service.environment.EnvironmentInfo.class.getName(), EnvironmentInfo.getDefault(), bundle);
+ register(PlatformAdmin.class.getName(), stateManager, bundle);
+ registerEndorsedXMLParser();
+ }
+
+ private void setDebugOptions() {
+ DebugOptions options = DebugOptions.getDefault();
+ // may be null if debugging is not enabled
+ if (options == null)
+ return;
+ MONITOR_BUNDLES = options.getBooleanOption(OPTION_MONITOR_BUNDLES, false);
+ org.eclipse.osgi.framework.internal.core.Bundle.MONITOR_BUNDLES = MONITOR_BUNDLES;
+ MONITOR_CLASSES = options.getBooleanOption(OPTION_MONITOR_CLASSES, false);
+ MONITOR_RESOURCE_BUNDLES = options.getBooleanOption(OPTION_MONITOR_RESOURCEBUNDLES, false);
+ TRACE_CLASSES = options.getBooleanOption(OPTION_TRACE_CLASSES, false);
+ TRACE_BUNDLES = options.getBooleanOption(OPTION_TRACE_BUNDLES, false);
+ TRACE_FILENAME = options.getOption(OPTION_TRACE_FILENAME);
+ TRACE_FILTERS = options.getOption(OPTION_TRACE_FILTERS);
+ StateManager.DEBUG_READER = options.getBooleanOption(OPTION_RESOLVER_READER, false);
+ StateImpl.DEBUG_RESOLVER = options.getBooleanOption(OPTION_RESOLVER, false);
+ }
+
+ private void registerEndorsedXMLParser() {
+ if (!is14VMorGreater())
+ return;
+ new ParsingService();
+ }
+ private static boolean is14VMorGreater() {
+ final String DELIM = ".";
+ String vmVersionString = System.getProperty("java.version"); //$NON-NLS-1$
+ StringTokenizer tokenizer = new StringTokenizer(vmVersionString, DELIM);
+ int major, minor;
+ // major
+ if (tokenizer.hasMoreTokens()) {
+ major = Integer.parseInt(tokenizer.nextToken());
+ if (major > 1)
+ return true;
+ }
+
+ // minor
+ if (tokenizer.hasMoreTokens()) {
+ minor = Integer.parseInt(tokenizer.nextToken());
+ if (minor > 3)
+ return true;
+ }
+ return false;
+ }
+ private class ParsingService implements ServiceFactory {
+ public static final String SAXFACTORYNAME = "javax.xml.parsers.SAXParserFactory";
+
+ public Object getService(Bundle bundle, ServiceRegistration registration) {
+ return SAXParserFactory.newInstance();
+ }
+
+ public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+ }
+
+ public ParsingService() {
+ context.registerService(SAXFACTORYNAME, this, new Hashtable());
+ }
+ }
+ public void frameworkStop(BundleContext context) throws BundleException {
+ saveMetaData();
+ super.frameworkStop(context);
+ if (DebugOptions.getDefault() != null) {
+ System.out.println("Time spent in registry parsing: " + DebugOptions.getDefault().getOption("org.eclipse.core.runtime/registry/parsing/timing/value"));
+ System.out.println("Time spent in package admin resolve: " + DebugOptions.getDefault().getOption("debug.packageadmin/timing/value"));
+ System.out.println("Time spent resolving the dependency system: " + DebugOptions.getDefault().getOption("org.eclipse.core.runtime.adaptor/resolver/timing/value"));
+ }
+ }
+
+ private File getMetaDataLocation() {
+ String configArea = (String) System.getProperty("osgi.configuration.area");
+ return new File(configArea, ".bundledata");
+ }
+ /**
+ * @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getInstalledBundles()
+ */
+ public Vector getInstalledBundles() {
+ File metadata = getMetaDataLocation();
+ Vector result = null;
+ try {
+ DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(metadata)));
+ try {
+ if (in.readByte() != BUNDLEDATA_VERSION)
+ return result;
+ startLevel = in.readInt();
+ nextId = in.readLong();
+ int bundleCount = in.readInt();
+ result = new Vector(bundleCount);
+ for (int i = 0; i < bundleCount; i++) {
+ try {
+ try {
+ long id = in.readLong();
+ if (id != 0) {
+ EclipseBundleData data = (EclipseBundleData) getElementFactory().getBundleData(this);
+ loadMetaDataFor(data, in);
+ data.initializeExistingBundle(Long.toString(id));
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL)
+ Debug.println("BundleData created: " + data);
+ result.addElement(data);
+ }
+ } catch (NumberFormatException e) {
+ // should never happen
+ }
+ } catch (IOException e) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Error reading framework metadata: " + e.getMessage());
+ Debug.printStackTrace(e);
+ }
+ }
+ }
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Error reading framework metadata: " + e.getMessage());
+ Debug.printStackTrace(e);
+ }
+ }
+ return result;
+ }
+
+ protected void loadMetaDataFor(EclipseBundleData data, DataInputStream in) throws IOException {
+ byte flag = in.readByte();
+ if (flag == NULL)
+ return;
+ data.setLocation(readString(in, false));
+ data.setName(readString(in, false));
+ data.setUniqueId(readString(in, false));
+ data.setVersion(new Version(readString(in, false)));
+ data.setActivator(readString(in, false));
+ data.setPluginClass(readString(in, false));
+ data.setLegacy(readString(in, false));
+ data.setClassPath(readString(in, false));
+ data.setNativeCodePath(readString(in, false));
+ data.setExecutionEnvironment(readString(in, false));
+ data.setDynamicImports(readString(in, false));
+ data.setGeneration(in.readInt());
+ data.setStartLevel(in.readInt());
+ data.setStatus(in.readInt());
+ data.setReference(in.readBoolean());
+ data.setFragment(in.readBoolean());
+ }
+ protected void saveMetaDataFor(BundleData data, DataOutputStream out) throws IOException {
+ if (data.getBundleID() == 0 || !(data instanceof DefaultBundleData)) {
+ out.writeByte(NULL);
+ return;
+ }
+ EclipseBundleData bundleData = (EclipseBundleData) data;
+ out.writeByte(OBJECT);
+ writeStringOrNull(out, bundleData.getLocation());
+ writeStringOrNull(out, bundleData.getName());
+ writeStringOrNull(out, bundleData.getUniqueId());
+ writeStringOrNull(out, bundleData.getVersion().toString());
+ writeStringOrNull(out, bundleData.getActivator());
+ writeStringOrNull(out, bundleData.getPluginClass());
+ writeStringOrNull(out, bundleData.isLegacy());
+ writeStringOrNull(out, bundleData.getClassPath());
+ writeStringOrNull(out, bundleData.getNativeCodePath());
+ writeStringOrNull(out, bundleData.getExecutionEnvironment());
+ writeStringOrNull(out, bundleData.getDynamicImports());
+ out.writeInt(bundleData.getGeneration());
+ out.writeInt(bundleData.getStartLevel());
+ out.writeInt(bundleData.getStatus());
+ out.writeBoolean(bundleData.isReference());
+ out.writeBoolean(bundleData.isFragment());
+ }
+
+ private String readString(DataInputStream in, boolean intern) throws IOException {
+ byte type = in.readByte();
+ if (type == NULL)
+ return null;
+ if (intern)
+ return in.readUTF().intern();
+ else
+ return in.readUTF();
+ }
+ private void writeStringOrNull(DataOutputStream out, String string) throws IOException {
+ if (string == null)
+ out.writeByte(NULL);
+ else {
+ out.writeByte(OBJECT);
+ out.writeUTF(string);
+ }
+ }
+
+ public File getBundleRootDir() {
+ super.getBundleDir();
+ return bundleRootDir;
+ }
+
+ public String getDataDirName() {
+ return dataDirName;
+ }
+
+ public void saveMetaData() {
+ File metadata = getMetaDataLocation();
+ try {
+ DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(metadata)));
+ try {
+ out.write(BUNDLEDATA_VERSION);
+ out.writeInt(startLevel);
+ out.writeLong(nextId);
+ Bundle[] bundles = context.getBundles();
+ out.writeInt(bundles.length);
+ for (int i = 0; i < bundles.length; i++) {
+ long id = bundles[i].getBundleId();
+ out.writeLong(id);
+ if (id != 0) {
+ BundleData data = ((org.eclipse.osgi.framework.internal.core.Bundle) bundles[i]).getBundleData();
+ saveMetaDataFor(data, out);
+ }
+ }
+ } finally {
+ out.close();
+ }
+ } catch (IOException e) {
+ if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
+ Debug.println("Error writing framework metadata: " + e.getMessage());
+ Debug.printStackTrace(e);
+ }
+ }
+ }
+
+ public int getStartLevel() {
+ return startLevel;
+ }
+
+ public void setStartLevel(int value) {
+ startLevel = value;
+ }
+
+ public IBundleStats getBundleStats() {
+ return StatsManager.getDefault();
+ }
+
+ protected StateManager createStateManager() {
+ return new StateManager(new File(System.getProperty("osgi.configuration.area")));
+ }
+
+ protected File getMetaDataFile() {
+ File configAreaDirectory = new File(System.getProperty("osgi.configuration.area"));
+ if (!configAreaDirectory.exists())
+ configAreaDirectory.mkdirs();
+ return new File(configAreaDirectory, ".framework");
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseBundleData.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseBundleData.java
new file mode 100644
index 000000000..65973b348
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseBundleData.java
@@ -0,0 +1,353 @@
+package org.eclipse.core.runtime.adaptor;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.Version;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.defaultadaptor.*;
+import org.eclipse.osgi.framework.util.Headers;
+import org.osgi.framework.BundleException;
+
+public class EclipseBundleData extends DefaultBundleData {
+
+ private URL base;
+ private static String[] libraryVariants = null;
+
+ /**
+ * At the first time will try to find the manifest in an alternative
+ * location if needed.
+ */
+ private boolean firstManifestLookup = true;
+
+ // URL protocol designations
+ public static final String PROTOCOL = "platform"; //$NON-NLS-1$
+ public static final String FILE = "file"; //$NON-NLS-1$
+ protected String isLegacy = null;
+ protected String pluginClass = null;
+
+ private static String[] buildLibraryVariants() {
+ ArrayList result = new ArrayList();
+ EnvironmentInfo info = EnvironmentInfo.getDefault();
+ result.add("ws/" + info.getWS() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ result.add("os/" + info.getOS() + "/" + info.getOSArch() + "/"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ result.add("os/" + info.getOS() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ String nl = info.getNL();
+ nl = nl.replace('_', '/');
+ while (nl.length() > 0) {
+ result.add("nl/" + nl + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ int i = nl.lastIndexOf('/');
+ nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$
+ }
+ result.add(""); //$NON-NLS-1$
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ public EclipseBundleData(DefaultAdaptor adaptor) throws IOException {
+ super(adaptor);
+ }
+
+ public void initializeNewBundle(long id, String location, InputStream in) throws IOException {
+ this.id = id;
+ this.location = location;
+ this.name = adaptor.mapLocationToName(location);
+ this.reference = false;
+
+ dir = new File(((EclipseAdaptor)adaptor).getBundleRootDir(), String.valueOf(id));
+ dirData = new File(dir, ((EclipseAdaptor)adaptor).getDataDirName());
+ dirGeneration = new File(dir, String.valueOf(generation));
+ file = new File(dirGeneration, name);
+ setStartLevel(adaptor.getInitialBundleStartLevel());
+
+ if (!getGenerationDir().exists())
+ {
+ throw new IOException(
+ AdaptorMsg.formatter.getString(
+ "ADAPTOR_DIRECTORY_CREATE_EXCEPTION",
+ dirGeneration.getPath()));
+ }
+ DefaultAdaptor.readFile(in, file);
+ bundleFile = BundleFile.createBundleFile(file, this);
+ loadFromManifest();
+ initializeBase(location);
+ }
+
+ public void initializeReferencedBundle(long id, String location, String name) throws IOException {
+ this.id = id;
+ this.location = location;
+ this.name = name;
+ this.reference = true;
+
+ dir = new File(((EclipseAdaptor)adaptor).getBundleRootDir(), String.valueOf(id));
+ dirData = new File(dir, ((EclipseAdaptor)adaptor).getDataDirName());
+ dirGeneration = new File(dir, String.valueOf(generation));
+ file = new File(name);
+ setStartLevel(adaptor.getInitialBundleStartLevel());
+ bundleFile = BundleFile.createBundleFile(file, this);
+ loadFromManifest();
+ // TODO it is unclear where the generation dir should be and what exactly will go in it
+// if (!getGenerationDir().exists())
+// {
+// throw new IOException(
+// AdaptorMsg.formatter.getString(
+// "ADAPTOR_DIRECTORY_CREATE_EXCEPTION",
+// dirGeneration.getPath()));
+// }
+ initializeBase(location);
+ }
+
+ public void initializeExistingBundle(String directory) throws IOException {
+ id = Long.parseLong(directory);
+ dir = new File(((EclipseAdaptor)adaptor).getBundleRootDir(), String.valueOf(id));
+ dirData = new File(dir, ((EclipseAdaptor)adaptor).getDataDirName());
+ dirGeneration = new File(dir, String.valueOf(generation));
+ file = reference ? new File(name) : new File(dirGeneration, name);
+ bundleFile = BundleFile.createBundleFile(file,this);
+ initializeBase(location);
+ }
+
+ void initialize() throws IOException {
+ initializeBase(location);
+ dir = new File(((EclipseAdaptor)adaptor).getBundleRootDir(), Long.toString(id));
+ File delete = new File(dir, ".delete");
+ if (delete.exists())
+ throw new NumberFormatException();
+
+ dirData = new File(dir, ((EclipseAdaptor)adaptor).getDataDirName());
+ dirGeneration = new File(dir, String.valueOf(generation));
+
+ if (reference)
+ file = new File(name);
+ else
+ file = new File(dirGeneration, name);
+ bundleFile = BundleFile.createBundleFile(file, this);
+ }
+
+ private void initializeBase(String location) throws IOException {
+ if (!location.endsWith("/"))
+ location += "/";
+ try {
+ base = new URL(location);
+ if (base.getProtocol().equals("reference"))
+ base = new URL(base.getFile());
+ }
+ catch (MalformedURLException e) {
+ base = null;
+ }
+
+ }
+
+ /**
+ * Returns the absolute path name of a native library. The VM invokes this
+ * method to locate the native libraries that belong to classes loaded with
+ * this class loader. If this method returns <code>null</code>, the VM
+ * searches the library along the path specified as the <code>java.library.path</code>
+ * property.
+ *
+ * @param libname
+ * the library name
+ * @return the absolute path of the native library
+ */
+ public String findLibrary(String libName) {
+ // first do the standard OSGi lookup using the native clauses
+ // in the manifest. If that fails, do the legacy Eclipse lookup.
+ String result = super.findLibrary(libName);
+ if (result != null)
+ return result;
+ if (libraryVariants == null)
+ libraryVariants = buildLibraryVariants();
+ if (libName.length() == 0)
+ return null;
+ if (libName.charAt(0) == '/' || libName.charAt(0) == '\\')
+ libName = libName.substring(1);
+ libName = System.mapLibraryName(libName);
+
+ // if (DEBUG && DEBUG_SHOW_ACTIONS && debugNative(libName))
+ // debug("findLibrary(" + libName + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (base == null)
+ return null;
+ String libFileName = null;
+ if (base.getProtocol().equals(FILE)) {
+ // directly access library
+ URL foundPath = searchVariants(new URL[] { base }, libraryVariants, libName);
+ if (foundPath != null)
+ libFileName = foundPath.getFile();
+ } else {
+ if (base.getProtocol().equals(PROTOCOL)) {
+ URL[] searchList = getSearchURLs(base);
+ if ((searchList != null) && (searchList.length != 0)) {
+ URL foundPath = searchVariants(searchList, libraryVariants, libName);
+ if (foundPath != null)
+ libFileName = foundPath.getFile();
+ }
+ }
+ }
+
+ if (libFileName == null)
+ return null;
+ return new File(libFileName).getAbsolutePath();
+ }
+
+ private URL searchVariants(URL[] basePaths, String[] variants, String path) {
+ // This method assumed basePaths are 'resolved' URLs
+ for (int i = 0; i < variants.length; i++) {
+ for (int j = 0; j < basePaths.length; j++) {
+ String fileName = basePaths[j].getFile() + variants[i] + path;
+ File file = new File(fileName);
+ if (!file.exists()) {
+ // if (DEBUG && DEBUG_SHOW_FAILURE)
+ // debug("not found " + file.getAbsolutePath());
+ // //$NON-NLS-1$
+ } else {
+ // if (DEBUG && DEBUG_SHOW_SUCCESS)
+ // debug("found " + path + " as " +
+ // file.getAbsolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
+ try {
+ return new URL("file:" + fileName); //$NON-NLS-1$
+ } catch (MalformedURLException e) {
+ // Intentionally ignore this exception
+ // so we continue looking for a matching
+ // URL.
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private URL[] getSearchURLs(URL target) {
+ return new URL[] { target };
+ }
+
+ public synchronized Dictionary getManifest() {
+ try {
+ return getManifest(false);
+ } catch (BundleException e) {
+ // TODO: handle exception
+ return null;
+ }
+ }
+ public synchronized Dictionary getManifest(boolean first) throws BundleException {
+ if (manifest == null)
+ manifest = first ? loadManifest() : new CachedManifest(this);
+ return manifest;
+ }
+
+ public synchronized Dictionary loadManifest() throws BundleException {
+ Dictionary result = null;
+ try {
+ URL url = getEntry(Constants.OSGI_BUNDLE_MANIFEST);
+ if (url == null)
+ throw new BundleException("Error reading manifest " + getLocation());
+ try {
+ result = Headers.parseManifest(url.openStream());
+ } catch (IOException e) {
+ throw new BundleException("Error reading manifest " + getLocation(), e);
+ }
+ return result;
+ } catch (BundleException e) {
+ if (firstManifestLookup)
+ result = loadManifestFromAlternativeLocation();
+ if (result == null)
+ // TODO log the error somewhere
+ throw new BundleException("Manifest not found: " + getLocation(), e);
+ } finally {
+ firstManifestLookup = false;
+ }
+ return result;
+ }
+
+ private Dictionary loadManifestFromAlternativeLocation() throws BundleException {
+ String cacheLocation = (String) System.getProperties().get("osgi.manifest.cache");
+ File generationLocation = new File(cacheLocation + '/' + computeFileName(this.file.toString()) + ".MF");
+ if (!generationLocation.isFile())
+ return null;
+ URL url = null;
+ try {
+ url = generationLocation.toURL();
+ } catch (MalformedURLException e) {
+ //ignore. because the url format is correct
+ }
+ try {
+ return Headers.parseManifest(url.openStream());
+ } catch (IOException e) {
+ throw new BundleException("Manifest not found: " + getLocation(), e);
+ }
+ }
+ /*
+ * Derives a file name from a a path string. Example: c:\autoexec.bat ->
+ * c__autoexec.bat
+ */
+ private String computeFileName(String filePath) {
+ StringBuffer newName = new StringBuffer(filePath);
+ for (int i = 0; i < filePath.length(); i++) {
+ char c = newName.charAt(i);
+ if (c == ':' || c == '/' || c == '\\')
+ newName.setCharAt(i, '_');
+ }
+ return newName.toString();
+ }
+
+ public void save() {
+ // do nothing. This method is here to override one in the superclass.
+ }
+
+ protected void loadMetaData() {
+ // do nothing. This method is here to override one in the superclass.
+ }
+
+ protected void loadFromMetaData() {
+ // do nothing. This method is here to override one in the superclass.
+ }
+ protected void loadFromManifest() throws IOException{
+ try {
+ getManifest(true);
+ } catch (BundleException e) {
+ throw new IOException("Unable to properly read manifest for: " + location);
+ }
+ super.loadFromManifest();
+ pluginClass = (String)manifest.get("Plugin-Class");
+ isLegacy = (String)manifest.get("Legacy");
+ }
+ public String isLegacy() {
+ return isLegacy;
+ }
+ public void setLegacy(String value) {
+ isLegacy = value;
+ }
+
+ public String getPluginClass() {
+ return pluginClass;
+ }
+ public void setPluginClass(String value) {
+ pluginClass = value;
+ }
+ public void setLocation(String value) {
+ this.location = value;
+ }
+ public void setName(String value) {
+ this.name = value;
+ }
+ public void setUniqueId(String value){
+ this.uniqueId = value;
+ }
+ public void setVersion(Version value){
+ this.version = value;
+ }
+ public void setActivator(String value){
+ this.activator = value;
+ }
+ public void setClassPath(String value){
+ this.classpath = value;
+ }
+ public void setFragment(boolean value){
+ this.isFragment = value;
+ }
+ public void setExecutionEnvironment(String value) {
+ this.executionEnvironment = value;
+ }
+ public void setDynamicImports(String value) {
+ this.dynamicImports = value;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseClassLoader.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseClassLoader.java
new file mode 100644
index 000000000..b1bdd1c1c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseClassLoader.java
@@ -0,0 +1,76 @@
+package org.eclipse.core.runtime.adaptor;
+
+import java.net.URL;
+import java.security.ProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.defaultadaptor.DefaultClassLoader;
+import org.eclipse.osgi.framework.stats.ClassloaderStats;
+import org.eclipse.osgi.framework.stats.ResourceBundleStats;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+public class EclipseClassLoader extends DefaultClassLoader {
+
+ // TODO do we want to have autoActivate on all the time or just for Legacy plugins?
+ private boolean autoActivate = true;
+
+ public EclipseClassLoader(ClassLoaderDelegate delegate, ProtectionDomain domain, String[] classpath, BundleData bundledata) {
+ super(delegate, domain, classpath, (org.eclipse.osgi.framework.internal.defaultadaptor.DefaultBundleData) bundledata);
+ }
+
+ public Class findLocalClass(String name) throws ClassNotFoundException {
+ // See if we need to do autoactivation. We don't if autoActivation is turned off
+ // or if we have already activated this bundle.
+ if (EclipseAdaptor.MONITOR_CLASSES)
+ ClassloaderStats.startLoadingClass(getClassloaderId(), name);
+
+ boolean found = true;
+
+ try {
+ if (autoActivate) {
+ int state = hostdata.getBundle().getState();
+ // Assume that if we get to this point the bundle is installed, resolved, ... so
+ // we just need to check that it is not already started or being started. There is a
+ // small window where two thread race to start. One will make it first, the other will
+ // throw an exception. Below we catch the exception and ignore it if it is
+ // of this nature.
+ // Ensure that we do the activation outside of any synchronized blocks to avoid deadlock.
+ if (state != Bundle.STARTING && state != Bundle.ACTIVE)
+ try {
+ hostdata.getBundle().start();
+ } catch (BundleException e) {
+ // TODO do nothing for now but we need to filter the type of exception here and
+ // sort the bad from the ok. Basically, failing to start the bundle should not be damning.
+ // Automagic activation is currently a best effort deal.
+ }
+ // once we have tried, there is no need to try again.
+ // TODO revisit this when considering what happens when a bundle is stopped
+ // and then subsequently accessed. That is, should it be restarted?
+ autoActivate = false;
+ }
+ return super.findLocalClass(name);
+ } catch (ClassNotFoundException e) {
+ found = false;
+ throw e;
+ } finally {
+ if (EclipseAdaptor.MONITOR_CLASSES)
+ ClassloaderStats.endLoadingClass(getClassloaderId(), name, found);
+ }
+ }
+
+ private String getClassloaderId() {
+ return hostdata.getBundle().getGlobalName();
+ }
+
+ public URL getResouce(String name) {
+ URL result = super.getResource(name);
+ if (EclipseAdaptor.MONITOR_RESOURCE_BUNDLES) {
+ if (result != null && name.endsWith(".properties")) { //$NON-NLS-1$
+ ClassloaderStats.loadedBundle(getClassloaderId(), new ResourceBundleStats(getClassloaderId(), name, result));
+ }
+ }
+ return result;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseElementFactory.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseElementFactory.java
new file mode 100644
index 000000000..84a3cb140
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseElementFactory.java
@@ -0,0 +1,20 @@
+package org.eclipse.core.runtime.adaptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.ProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.framework.adaptor.core.AbstractBundleData;
+import org.eclipse.osgi.framework.internal.defaultadaptor.*;
+
+public class EclipseElementFactory extends AdaptorElementFactory {
+
+ public AbstractBundleData getBundleData(DefaultAdaptor adaptor) throws IOException {
+ return new EclipseBundleData(adaptor);
+ }
+
+ public org.eclipse.osgi.framework.adaptor.BundleClassLoader createClassLoader(ClassLoaderDelegate delegate, ProtectionDomain domain, String[] bundleclasspath, DefaultBundleData data) {
+ return new EclipseClassLoader(delegate, domain, bundleclasspath, data);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseStarter.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseStarter.java
new file mode 100644
index 000000000..397f57324
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EclipseStarter.java
@@ -0,0 +1,535 @@
+package org.eclipse.core.runtime.adaptor;
+
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.net.*;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.OSGi;
+import org.osgi.framework.*;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+import org.eclipse.osgi.framework.tracker.ServiceTracker;
+
+public class EclipseStarter {
+
+ private static BundleContext context;
+ private static String dataLocation = null;
+ private static String configLocation = null;
+
+ // command line arguments
+ private static final String CONSOLE = "-console"; //$NON-NLS-1$
+ private static final String DEBUG = "-debug"; //$NON-NLS-1$
+ private static final String DEV = "-dev"; //$NON-NLS-1$
+ private static final String WS = "-ws"; //$NON-NLS-1$
+ private static final String OS = "-os"; //$NON-NLS-1$
+ private static final String ARCH = "-arch"; //$NON-NLS-1$
+ private static final String NL = "-nl"; //$NON-NLS-1$
+ private static final String CONFIGURATION = "-configuration"; //$NON-NLS-1$
+ // this is more of an Eclipse argument but this OSGi implementation stores its
+ // metadata alongside Eclipse's.
+ private static final String DATA = "-data"; //$NON-NLS-1$
+
+
+ // Constants for configuration location discovery
+ private static final String ECLIPSE = "eclipse"; //$NON-NLS-1$
+ private static final String PRODUCT_SITE_MARKER = ".eclipseproduct"; //$NON-NLS-1$
+ private static final String PRODUCT_SITE_ID = "id"; //$NON-NLS-1$
+ private static final String PRODUCT_SITE_VERSION = "version"; //$NON-NLS-1$
+
+ /** string containing the classname of the adaptor to be used in this framework instance */
+ protected static String adaptorClassName = "org.eclipse.core.runtime.adaptor.EclipseAdaptor";
+
+ // Console information
+ protected static final String consoleClassName = "org.eclipse.osgi.framework.internal.core.FrameworkConsole";
+ private static final String CONSOLE_NAME = "OSGi Console";
+ private static String consolePort = "";
+ private static boolean console = false;
+ private static ServiceTracker applicationTracker;
+
+ public static Object run(String[] args,Runnable endSplashHandler) throws Exception {
+ processCommandLine(args);
+ if (System.getProperty("osgi.bundles") == null)
+ System.getProperties().put("osgi.bundles", "org.eclipse.osgi.services_1.0.0@1,org.eclipse.osgi.util_1.0.0@1,org.eclipse.core.runtime.osgi_3.0.0@2,org.eclipse.update.configurator_3.0.0@3,org.eclipse.core.applicationrunner_1.0.0@5");
+ setInstanceLocation();
+ setConfigurationLocation();
+ FrameworkAdaptor adaptor = null;
+ adaptor = createAdaptor();
+ OSGi osgi = new OSGi(adaptor);
+ if (osgi == null)
+ throw new IllegalStateException("OSGi framework could not be started");
+ osgi.launch();
+
+ try {
+ if (console)
+ startConsole(osgi, new String[0]);
+
+ context = osgi.getBundleContext();
+ publishSplashScreen(endSplashHandler);
+
+ initializeApplicationTracker();
+ loadBundles();
+ // TODO determine how long to wait here
+ Runnable application = (Runnable)applicationTracker.waitForService(9999999999L);
+ applicationTracker.close();
+ if (application == null)
+ throw new IllegalStateException("Unable to acquire application service");
+ application.run();
+ } finally {
+ Bundle systemBundle = context.getBundle(0);
+ if (systemBundle.getState() == Bundle.ACTIVE) {
+ final Semaphore semaphore = new Semaphore(0);
+ FrameworkListener listener = new FrameworkListener() {
+ public void frameworkEvent(FrameworkEvent event) {
+ if (event.getType() == FrameworkEvent.STARTLEVEL_CHANGED)
+ semaphore.release();
+ }
+
+ };
+ context.addFrameworkListener(listener);
+ systemBundle.stop();
+ semaphore.acquire();
+ context.removeFrameworkListener(listener);
+ }
+ }
+ // TODO for now, if an exception is not thrown from this method, we have to do
+ // the System.exit. In the future we will update startup.jar to do the System.exit all
+ // the time.
+ String exitCode = System.getProperty("eclipse.exitcode");
+ if (exitCode == null)
+ System.exit(0);
+ try {
+ System.exit(Integer.parseInt(exitCode));
+ } catch (NumberFormatException e) {
+ System.exit(13);
+ }
+ return null;
+ }
+
+ private static void publishSplashScreen(Runnable endSplashHandler) {
+ // InternalPlatform now how to retrieve this later
+ Dictionary properties = new Hashtable();
+ properties.put("name","splashscreen");
+ context.registerService(Runnable.class.getName(),endSplashHandler,properties);
+ }
+
+ private static String searchForBundle(String name, String syspath) {
+ String location = "reference:file:"+ syspath +"/"+name;
+ try {
+ URLConnection result = new URL(location).openConnection();
+ result.connect();
+ return location;
+ } catch (IOException e) {
+ int i = location.indexOf('_');
+ return i == -1? location : location.substring(0, i);
+ }
+ }
+
+ private static String[] loadBundles() {
+ long startTime = System.currentTimeMillis();
+ ServiceReference reference = context.getServiceReference(StartLevel.class.getName());
+ StartLevel start = null;
+ if (reference != null)
+ start = (StartLevel)context.getService(reference);
+ String[] bundles = getArrayFromList(System.getProperty("osgi.bundles"));
+
+ String syspath = getSysPath();
+ Bundle bundle;
+ Vector installed = new Vector();
+ Vector ignored = new Vector();
+ for (int i = 0; i < bundles.length; i++) {
+ String name = bundles[i];
+ if (name == null)
+ continue;
+ try {
+ int level = -1;
+ int index = name.indexOf('@');
+ if (index >= 0) {
+ String levelString = name.substring(index + 1, name.length());
+ level = Integer.parseInt(levelString);
+ name = name.substring(0, index);
+ }
+ String location = searchForBundle(name, syspath);
+ if (!isInstalled(location)) {
+ bundle = context.installBundle(location);
+ if (level >= 0 && start != null)
+ start.setBundleStartLevel(bundle, level);
+ installed.addElement(bundle);
+ } else
+ ignored.addElement(name);
+ } catch (Exception ex) {
+ System.err.println("Ignoring " + name);
+ ex.printStackTrace();
+ ignored.addElement(name);
+ continue;
+ }
+ }
+ refreshPackages((Bundle[])installed.toArray(new Bundle[installed.size()]));
+
+ Enumeration e = installed.elements();
+ while (e.hasMoreElements()) {
+ bundle = (Bundle)e.nextElement();
+ try {
+ bundle.start();
+ } catch (BundleException ex) {
+ System.out.println("Error starting " + bundle.getLocation());
+ ex.printStackTrace();
+ }
+ }
+ // TODO remove this constant. At least set it to the max of 6 and the current value...
+ start.setStartLevel(6);
+ context.ungetService(reference);
+ System.out.println("Time (re)start the framework: " + (System.currentTimeMillis() - startTime));
+ return (String[])ignored.toArray(new String[ignored.size()]);
+ }
+
+ private static void refreshPackages(Bundle[] bundles) {
+ if (bundles.length == 0)
+ return;
+ ServiceReference packageAdminRef = context.getServiceReference(PackageAdmin.class.getName());
+ PackageAdmin packageAdmin = null;
+ if (packageAdminRef != null) {
+ packageAdmin = (PackageAdmin)context.getService(packageAdminRef);
+ if (packageAdmin == null)
+ return;
+ }
+ // TODO this is such a hack it is silly. There are still cases for race conditions etc
+ // but this should allow for some progress...
+ final Semaphore semaphore = new Semaphore(0);
+ FrameworkListener listener = new FrameworkListener() {
+ public void frameworkEvent(FrameworkEvent event) {
+ if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED)
+ semaphore.release();
+ }
+ };
+ context.addFrameworkListener(listener);
+ packageAdmin.refreshPackages(bundles);
+ semaphore.acquire();
+ context.removeFrameworkListener(listener);
+ context.ungetService(packageAdminRef);
+ }
+
+ /**
+ * Invokes the OSGi Console on another thread
+ *
+ * @param osgi The current OSGi instance for the console to attach to
+ * @param consoleArgs An String array containing commands from the command line
+ * for the console to execute
+ */
+ private static void startConsole(OSGi osgi, String[] consoleArgs) {
+ try {
+ Class consoleClass = Class.forName(consoleClassName);
+ Class[] parameterTypes;
+ Object[] parameters;
+ if (consolePort.length() == 0) {
+ parameterTypes = new Class[] { OSGi.class, String[].class };
+ parameters = new Object[] { osgi, consoleArgs };
+ } else {
+ parameterTypes = new Class[] { OSGi.class, int.class, String[].class };
+ parameters = new Object[] { osgi, new Integer(consolePort), consoleArgs };
+ }
+ Constructor constructor = consoleClass.getConstructor(parameterTypes);
+ Object console = constructor.newInstance(parameters);
+ Thread t = new Thread(((Runnable) console), CONSOLE_NAME);
+ t.start();
+ } catch (NumberFormatException nfe) {
+ System.err.println("Invalid console port: " + consolePort);
+ } catch (Exception ex) {
+ System.out.println("Failed to find/start: " + CONSOLE_NAME);
+ }
+
+ }
+
+ /**
+ * Creates and returns the adaptor
+ *
+ * @return a FrameworkAdaptor object
+ */
+ private static FrameworkAdaptor createAdaptor() throws Exception {
+ Class adaptorClass = Class.forName(adaptorClassName);
+ Class[] constructorArgs = new Class[] { String[].class };
+ Constructor constructor = adaptorClass.getConstructor(constructorArgs);
+ return (FrameworkAdaptor) constructor.newInstance(new Object[] { new String[0] });
+ }
+
+ private static String[] processCommandLine(String[] args) throws Exception {
+ EnvironmentInfo.allArgs = args;
+ int[] configArgs = new int[100];
+ configArgs[0] = -1; // need to initialize the first element to something that could not be an index.
+ int configArgIndex = 0;
+ for (int i = 0; i < args.length; i++) {
+ boolean found = false;
+ // check for args without parameters (i.e., a flag arg)
+
+ // check if development mode should be enabled for the entire platform
+ // If this is the last arg or there is a following arg (i.e., arg+1 has a leading -),
+ // simply enable development mode. Otherwise, assume that that the following arg is
+ // actually some additional development time class path entries. This will be processed below.
+ if (args[i].equalsIgnoreCase(DEV) && ((i + 1 == args.length) || ((i + 1 < args.length) && (args[i + 1].startsWith("-"))))) { //$NON-NLS-1$
+ System.getProperties().put("osgi.dev", "");
+ found = true;
+ continue;
+ }
+
+ // look for the console with no port.
+ if (args[i].equalsIgnoreCase(CONSOLE)) {
+ console = true;
+ found = true;
+ continue;
+ }
+
+ if (found) {
+ configArgs[configArgIndex++] = i;
+ continue;
+ }
+ // check for args with parameters. If we are at the last argument or if the next one
+ // has a '-' as the first character, then we can't have an arg with a parm so continue.
+ if (i == args.length - 1 || args[i + 1].startsWith("-")) { //$NON-NLS-1$
+ continue;
+ }
+ String arg = args[++i];
+
+ // look for the console and port.
+ if (args[i - 1].equalsIgnoreCase(CONSOLE)) {
+ console = true;
+ consolePort = arg;
+ found = true;
+ continue;
+ }
+
+ // look for the configuraiton location .
+ if (args[i - 1].equalsIgnoreCase(CONFIGURATION)) {
+ configLocation = arg;
+ continue;
+ }
+
+ // look for the development mode and class path entries.
+ if (args[i - 1].equalsIgnoreCase(DEV)) {
+ System.getProperties().put("osgi.dev", arg);
+ found = true;
+ continue;
+ }
+
+ // look for the data location for this instance.
+ if (args[i - 1].equalsIgnoreCase(DATA)) {
+ dataLocation = arg;
+ continue;
+ }
+
+ // look for the debug mode and option file location.
+ if (args[i - 1].equalsIgnoreCase(DEBUG)) {
+ System.getProperties().put("osgi.debug", arg);
+ found = true;
+ continue;
+ }
+
+ // look for the window system.
+ if (args[i - 1].equalsIgnoreCase(WS)) {
+ found = true;
+ System.getProperties().put("osgi.ws", arg);
+ }
+
+ // look for the operating system
+ if (args[i - 1].equalsIgnoreCase(OS)) {
+ found = true;
+ System.getProperties().put("osgi.os", arg);
+ }
+
+ // look for the system architecture
+ if (args[i - 1].equalsIgnoreCase(ARCH)) {
+ found = true;
+ System.getProperties().put("osgi.arch", arg);
+ }
+
+ // look for the nationality/language
+ if (args[i - 1].equalsIgnoreCase(NL)) {
+ found = true;
+ System.getProperties().put("osgi.nl", arg);
+ }
+
+ // done checking for args. Remember where an arg was found
+ if (found) {
+ configArgs[configArgIndex++] = i - 1;
+ configArgs[configArgIndex++] = i;
+ }
+ }
+
+ // remove all the arguments consumed by this argument parsing
+ if (configArgIndex == 0) {
+ EnvironmentInfo.frameworkArgs = new String[0];
+ EnvironmentInfo.appArgs = args;
+ return args;
+ }
+ EnvironmentInfo.appArgs = new String[args.length - configArgIndex];
+ EnvironmentInfo.frameworkArgs = new String[configArgIndex];
+ configArgIndex = 0;
+ int j = 0;
+ int k = 0;
+ for (int i = 0; i < args.length; i++) {
+ if (i == configArgs[configArgIndex]) {
+ EnvironmentInfo.frameworkArgs[k++] = args[i];
+ configArgIndex++;
+ } else
+ EnvironmentInfo.appArgs[j++] = args[i];
+ }
+ return EnvironmentInfo.appArgs;
+ }
+
+ /**
+ * Returns the result of converting a list of comma-separated tokens into an array
+ *
+ * @return the array of string tokens
+ * @param prop the initial comma-separated string
+ */
+ private static String[] getArrayFromList(String prop) {
+ if (prop == null || prop.trim().equals("")) //$NON-NLS-1$
+ return new String[0];
+ Vector list = new Vector();
+ StringTokenizer tokens = new StringTokenizer(prop, ","); //$NON-NLS-1$
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken().trim();
+ if (!token.equals("")) //$NON-NLS-1$
+ list.addElement(token);
+ }
+ return list.isEmpty() ? new String[0] : (String[]) list.toArray(new String[list.size()]);
+ }
+
+ private static String getSysPath() {
+ String result = System.getProperty("osgi.syspath");
+ if (result != null)
+ return result;
+
+ URL url = EclipseStarter.class.getProtectionDomain().getCodeSource().getLocation();
+ result = url.getFile();
+ if (result.endsWith("/"))
+ result = result.substring(0, result.length() - 1);
+ result = result.substring(0, result.lastIndexOf('/'));
+ result = result.substring(0, result.lastIndexOf('/'));
+ return result;
+ }
+
+ private static void setInstanceLocation() {
+ File result = null;
+ String location = System.getProperty("osgi.instance.area");
+ // if the instance location is not set, predict where the workspace will be and
+ // put the instance area inside the workspace meta area.
+ if (location == null) {
+ if (dataLocation == null)
+ result = new File(System.getProperty("user.dir"), "workspace");//$NON-NLS-1$ //$NON-NLS-2$
+ else
+ result = new File(dataLocation);
+ result = new File(result, ".metadata/bundles");
+ } else {
+ result = new File(location);
+ }
+ System.getProperties().put("osgi.instance.area", result.getAbsolutePath());
+ System.getProperties().put("org.eclipse.osgi.framework.defaultadaptor.bundledir", result.getAbsolutePath());
+ }
+
+ private static void setConfigurationLocation() {
+ String location = System.getProperty("osgi.configuration.area");
+ if (location != null) {
+ configLocation = location;
+ if (System.getProperty("osgi.manifest.cache") == null)
+ System.getProperties().put("osgi.manifest.cache", configLocation + "/manifests");
+ return;
+ }
+ // -configuration was not specified so compute a configLocation based on the
+ // install location. If it is read/write then use it. Otherwise use the user.dir
+ if (configLocation == null) {
+ configLocation = getDefaultStateLocation() + "/.config";
+ // TODO handle the case where the install location is read only. Find out
+ // how they do it in the update code and do the same here.
+ } else {
+ // if -configuration was specified, then interpret the config location from the
+ // value given. Allow for the specification of a config file (.cfg) or a dir.
+ try {
+ configLocation = new URL(configLocation).getFile();
+ } catch (MalformedURLException e) {
+ // TODO do something in the error case
+ }
+ configLocation = configLocation.replace('\\', '/');
+ int index = configLocation.lastIndexOf('/');
+ if (configLocation.endsWith(".cfg") || configLocation.endsWith("/"))
+ configLocation = configLocation.substring(0, index);
+ }
+ System.getProperties().put("osgi.configuration.area", configLocation);
+ if (System.getProperty("osgi.manifest.cache") == null) {
+ System.getProperties().put("osgi.manifest.cache", configLocation + "/manifests");
+ }
+ }
+
+ private static boolean isInstalled(String location) {
+ Bundle[] installed = context.getBundles();
+ for (int i = 0; i < installed.length; i++) {
+ Bundle bundle = installed[i];
+ if (location.equalsIgnoreCase(bundle.getLocation()))
+ return true;
+ }
+ return false;
+ }
+
+ private static String getDefaultStateLocation() {
+ // 1) We store the config state relative to the 'eclipse' directory if possible
+ // 2) If this directory is read-only
+ // we store the state in <user.home>/.eclipse/<application-id>_<version> where <user.home>
+ // is unique for each local user, and <application-id> is the one
+ // defined in .eclipseproduct marker file. If .eclipseproduct does not
+ // exist, use "eclipse" as the application-id.
+
+ String installProperty = System.getProperty("eclipse.installURL");
+ URL installURL = null;
+ try {
+ installURL = new URL(installProperty);
+ } catch (MalformedURLException e) {
+ // do nothgin here since it is basically impossible to get a bogus url
+ }
+ File installDir = new File(installURL.getFile());
+ if ("file".equals(installURL.getProtocol()) && installDir.canWrite()) { //$NON-NLS-1$
+// if (DEBUG)
+// debug("Using the installation directory."); //$NON-NLS-1$
+ return installDir.getAbsolutePath();
+ }
+
+ // We can't write in the eclipse install dir so try for some place in the user's home dir
+// if (DEBUG)
+// debug("Using the user.home location."); //$NON-NLS-1$
+ String appName = "." + ECLIPSE; //$NON-NLS-1$
+ File eclipseProduct = new File(installDir, PRODUCT_SITE_MARKER );
+ if (eclipseProduct.exists()) {
+ Properties props = new Properties();
+ try {
+ props.load(new FileInputStream(eclipseProduct));
+ String appId = props.getProperty(PRODUCT_SITE_ID);
+ if (appId == null || appId.trim().length() == 0)
+ appId = ECLIPSE;
+ String appVersion = props.getProperty(PRODUCT_SITE_VERSION);
+ if (appVersion == null || appVersion.trim().length() == 0)
+ appVersion = ""; //$NON-NLS-1$
+ appName += File.separator + appId + "_" + appVersion; //$NON-NLS-1$
+ } catch (IOException e) {
+ // Do nothing if we get an exception. We will default to a standard location
+ // in the user's home dir.
+ }
+ }
+
+ String userHome = System.getProperty("user.home"); //$NON-NLS-1$
+ File configDir = new File(userHome, appName);
+ configDir.mkdirs();
+ return configDir.getAbsolutePath();
+ }
+
+ private static void initializeApplicationTracker() {
+ Filter filter = null;
+ try {
+ filter = context.createFilter("(&(objectClass=java.lang.Runnable)(eclipse.application=*))");
+ } catch (InvalidSyntaxException e) {
+ // ignore this. It should never happen as we have tested the above format.
+ e.printStackTrace();
+ }
+ applicationTracker = new ServiceTracker(context, filter, null);
+ applicationTracker.open();
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EnvironmentInfo.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EnvironmentInfo.java
new file mode 100644
index 000000000..5f2c19952
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/EnvironmentInfo.java
@@ -0,0 +1,152 @@
+package org.eclipse.core.runtime.adaptor;
+
+import java.util.*;
+import org.eclipse.osgi.service.environment.Constants;
+
+public class EnvironmentInfo implements org.eclipse.osgi.service.environment.EnvironmentInfo {
+ private static EnvironmentInfo singleton;
+ private static String nl;
+ private static String os;
+ private static String ws;
+ private static String arch;
+ static String[] allArgs;
+ static String[] frameworkArgs;
+ static String[] appArgs;
+
+ // While we recognize the SunOS operating system, we change
+ // this internally to be Solaris.
+ private static final String INTERNAL_OS_SUNOS = "SunOS"; //$NON-NLS-1$
+ // While we recognize the i386 architecture, we change
+ // this internally to be x86.
+ private static final String INTERNAL_ARCH_I386 = "i386"; //$NON-NLS-1$
+
+
+ private EnvironmentInfo() {
+ super();
+ setupSystemContext();
+ }
+
+ public static EnvironmentInfo getDefault() {
+ if (singleton == null)
+ singleton = new EnvironmentInfo();
+ return singleton;
+ }
+
+ public boolean inDevelopmentMode() {
+ return System.getProperty("osgi.dev") != null;
+ }
+
+ public boolean inDebugMode() {
+ return System.getProperty("osgi.debug") != null;
+ }
+
+ public String[] getAllArgs() {
+ return allArgs;
+ }
+
+ public String[] getFrameworkArgs() {
+ return frameworkArgs;
+ }
+
+ public String[] getApplicationArgs() {
+ return appArgs;
+ }
+
+ public String getOSArch() {
+ return arch;
+ }
+
+ public String getNL() {
+ return nl;
+ }
+
+ public String getOS() {
+ return os;
+ }
+
+ public String getWS() {
+ return ws;
+ }
+
+ /**
+ * Initializes the execution context for this run of the platform. The context
+ * includes information about the locale, operating system and window system.
+ *
+ * NOTE: The OS, WS, and ARCH values should never be null. The executable should
+ * be setting these values and therefore this code path is obsolete for Eclipse
+ * when run from the executable.
+ */
+ private void setupSystemContext() {
+ // if the user didn't set the locale with a command line argument then
+ // use the default.
+ nl = System.getProperty("osgi.nl");
+ if (nl != null) {
+ StringTokenizer tokenizer = new StringTokenizer(nl, "_"); //$NON-NLS-1$
+ int segments = tokenizer.countTokens();
+ try {
+ switch (segments) {
+ case 2:
+ Locale userLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken());
+ Locale.setDefault(userLocale);
+ break;
+ case 3:
+ userLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken());
+ Locale.setDefault(userLocale);
+ break;
+ }
+ } catch (NoSuchElementException e) {
+ // fall through and use the default
+ }
+ }
+ nl = Locale.getDefault().toString();
+ System.getProperties().put("osgi.nl", nl);
+
+ // if the user didn't set the operating system with a command line
+ // argument then use the default.
+ os = System.getProperty("osgi.os");
+ if (os == null) {
+ String name = System.getProperty("os.name");//$NON-NLS-1$
+ // check to see if the VM returned "Windows 98" or some other
+ // flavour which should be converted to win32.
+ if (name.regionMatches(true, 0, Constants.OS_WIN32, 0, 3))
+ os = Constants.OS_WIN32;
+ // EXCEPTION: All mappings of SunOS convert to Solaris
+ if (os == null)
+ os = name.equalsIgnoreCase(INTERNAL_OS_SUNOS) ? Constants.OS_SOLARIS : Constants.OS_UNKNOWN;
+ }
+ System.getProperties().put("osgi.os", os);
+
+ // if the user didn't set the window system with a command line
+ // argument then use the default.
+ ws = System.getProperty("osgi.ws");
+ if (ws == null) {
+ // setup default values for known OSes if nothing was specified
+ if (os.equals(Constants.OS_WIN32))
+ ws = Constants.WS_WIN32;
+ else if (os.equals(Constants.OS_LINUX))
+ ws = Constants.WS_MOTIF;
+ else if (os.equals(Constants.OS_MACOSX))
+ ws = Constants.WS_CARBON;
+ else if (os.equals(Constants.OS_HPUX))
+ ws = Constants.WS_MOTIF;
+ else if (os.equals(Constants.OS_AIX))
+ ws = Constants.WS_MOTIF;
+ else if (os.equals(Constants.OS_SOLARIS))
+ ws = Constants.WS_MOTIF;
+ else
+ ws = Constants.WS_UNKNOWN;
+ }
+ System.getProperties().put("osgi.ws", ws);
+
+ // if the user didn't set the system architecture with a command line
+ // argument then use the default.
+ arch = System.getProperty("osgi.arch");
+ if (arch == null) {
+ String name = System.getProperty("os.arch");//$NON-NLS-1$
+ // Map i386 architecture to x86
+ arch = name.equalsIgnoreCase(INTERNAL_ARCH_I386) ? Constants.ARCH_X86 : name;
+ }
+ System.getProperties().put("osgi.arch", arch);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/Semaphore.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/Semaphore.java
new file mode 100644
index 000000000..7d40935f0
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/adaptor/Semaphore.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.adaptor;
+
+public class Semaphore {
+ protected long notifications;
+ public Semaphore(int count) {
+ notifications = count;
+ }
+ /**
+ * Attempts to acquire this semaphore. Returns only when the semaphore has been acquired.
+ */
+ public synchronized void acquire() {
+ while (true) {
+ if (notifications > 0) {
+ notifications--;
+ return;
+ }
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ /**
+ * Attempts to acquire this semaphore. Returns true if it was successfully acquired,
+ * and false otherwise.
+ */
+ public synchronized boolean acquire(long delay) {
+ long start = System.currentTimeMillis();
+ long timeLeft = delay;
+ while (true) {
+ if (notifications > 0) {
+ notifications--;
+ return true;
+ }
+ if (timeLeft < 0)
+ return false;
+ try {
+ wait(timeLeft);
+ } catch (InterruptedException e) {
+ }
+ timeLeft = start + delay - System.currentTimeMillis();
+ }
+ }
+ public synchronized void release() {
+ notifications++;
+ notifyAll();
+ }
+ // for debug only
+ public String toString() {
+ return "Semaphore(" + notifications + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/BundleStats.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/BundleStats.java
new file mode 100644
index 000000000..4a02d211c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/BundleStats.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.stats;
+
+import java.util.ArrayList;
+import org.eclipse.core.runtime.adaptor.EclipseAdaptor;
+
+/**
+ * Contains information about activated bundles and acts as the main
+ * entry point for logging plugin activity.
+ */
+
+public class BundleStats {
+ public String pluginId;
+ public int activationOrder;
+ private long timestamp; //timeStamp at which this plugin has been activated
+ private boolean duringStartup; // indicate if the plugin has been activated during startup
+ private long startupTime; // the time took by the plugin to startup
+ private long startupMethodTime; // the time took to run the startup method
+
+ // Indicate the position of the activation trace in the file
+ private long traceStart=-1;
+ private long traceEnd =-1;
+
+ //To keep plugins parentage
+ private ArrayList pluginsActivated = new ArrayList(3); // TODO create lazily
+ private BundleStats activatedBy=null;
+
+// static {
+// // activate the boot plugin manually since we do not control the classloader
+//// activateBootPlugin();
+// }
+
+ // hard code the starting of the boot plugin as it does not go through the normal sequence
+// private static void activateBootPlugin() {
+// BundleStats plugin = findPlugin(BootLoader.PI_BOOT);
+// plugin.setTimestamp(System.currentTimeMillis());
+// plugin.setActivationOrder(plugins.size());
+// }
+
+
+
+
+
+ // Get the pluginInfo if available, or create it.
+ public BundleStats(String pluginId) {
+ this.pluginId = pluginId;
+// duringStartup = booting;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public int getActivationOrder() {
+ return activationOrder;
+ }
+
+ protected void activated(BundleStats plugin) {
+ pluginsActivated.add(plugin);
+ }
+
+ public BundleStats getActivatedBy() {
+ return activatedBy;
+ }
+
+ public String getPluginId() {
+ return pluginId;
+ }
+
+ public long getStartupTime() {
+ return startupTime;
+ }
+
+ public long getStartupMethodTime() {
+ return startupMethodTime;
+ }
+
+ public boolean isStartupPlugin() {
+ return duringStartup;
+ }
+
+ public int getClassLoadCount() {
+ if (!EclipseAdaptor.MONITOR_CLASSES)
+ return 0;
+ ClassloaderStats loader = ClassloaderStats.getLoader(pluginId);
+ return loader == null ? 0 : loader.getClassLoadCount();
+ }
+
+ public long getClassLoadTime() {
+ if (!EclipseAdaptor.MONITOR_CLASSES)
+ return 0;
+ ClassloaderStats loader = ClassloaderStats.getLoader(pluginId);
+ return loader == null ? 0 : loader.getClassLoadTime();
+ }
+
+ public ArrayList getPluginsActivated() {
+ return pluginsActivated;
+ }
+
+ public long getTraceStart() {
+ return traceStart;
+ }
+
+ public long getTraceEnd() {
+ return traceEnd;
+ }
+
+ protected void setTimestamp(long value) {
+ timestamp = value;
+ }
+
+ protected void setActivationOrder(int value) {
+ activationOrder = value;
+ }
+
+ protected void setTraceStart(long time) {
+ traceStart = time;
+ }
+
+ protected static void setBooting(boolean boot) {
+// booting = boot;
+ }
+
+ protected void endActivation() {
+ startupTime = System.currentTimeMillis() - timestamp;
+ }
+
+ protected void setTraceEnd(long position) {
+ traceEnd = position;
+ }
+
+
+ protected void setActivatedBy(BundleStats value) {
+ activatedBy = value;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ClassStats.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ClassStats.java
new file mode 100644
index 000000000..166aa6794
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ClassStats.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.stats;
+
+import java.util.ArrayList;
+
+/**
+ * Maintain statistics about a loaded class.
+ */
+public class ClassStats {
+ private String className; // fully qualified name of this class
+ private ClassloaderStats classloader; // the classloader that loaded this class
+ private int loadOrder = -1;
+
+ private long timestamp; // time at which this class was loaded
+ private long timeLoading; // time to load the class
+ private long timeLoadingOthers = 0; // time spent loading classes which has been triggered by this class
+
+ // parentage of classes loaded
+ private ClassStats loadedBy = null; // a reference to the class that loaded this class
+ private ArrayList loaded = new ArrayList(2); // a reference to the classes that this class loaded
+
+ private boolean duringStartup; // indicate if the class was loaded during platform startup
+
+ //information to retrieve the stacktrace from the file
+ private long traceStart = -1;
+ private long traceEnd = -1;
+
+ static private boolean booting = true; // whether or not the platform is starting up.
+
+ public static void setBooting(boolean value) {
+ booting = value;
+ }
+
+ public ClassStats(String name, ClassloaderStats classloader) {
+ className = name;
+ timestamp = System.currentTimeMillis();
+ duringStartup = booting;
+ this.classloader = classloader;
+ }
+
+ public void setLoadOrder(int order) {
+ loadOrder = order;
+ }
+
+ public void loadingDone() {
+ timeLoading = System.currentTimeMillis() - timestamp;
+ }
+
+ public long getTimeLoading() {
+ return timeLoading;
+ }
+
+ public long getLocalTimeLoading() {
+ return timeLoading - timeLoadingOthers;
+ }
+
+ public void addTimeLoadingOthers(long time) {
+ timeLoadingOthers = timeLoadingOthers + time;
+ }
+
+ public long getTraceStart() {
+ return traceStart;
+ }
+
+ public long getTraceEnd() {
+ return traceEnd;
+ }
+
+ public void setTraceStart(long position) {
+ traceStart = position;
+ }
+
+ public void setTraceEnd(long position) {
+ traceEnd = position;
+ }
+
+ public void loaded(ClassStats child) {
+ loaded.add(child);
+ }
+
+ public void setLoadedBy(ClassStats parent) {
+ loadedBy = parent;
+ }
+
+ public ClassStats getLoadedBy() {
+ return loadedBy;
+ }
+
+ public ArrayList getLoadedClasses() {
+ return loaded;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public boolean isStartupClass() {
+ return duringStartup;
+ }
+
+ public ClassloaderStats getClassloader() {
+ return classloader;
+ }
+
+ public int getLoadOrder() {
+ return loadOrder;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void toBaseClass() {
+ duringStartup = true;
+ loadOrder = -2;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ClassloaderStats.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ClassloaderStats.java
new file mode 100644
index 000000000..df6fcc021
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ClassloaderStats.java
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.stats;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Contains information about the classes and the bundles loaded by a given classloader. Typically there is one classloader per plugin so at levels above boot, this equates to information about
+ * classes and bundles in a plugin.
+ */
+public class ClassloaderStats {
+ private String id;
+ private long loadingTime; // time spent loading classes
+ private int failureCount = 0; // number of classes requested but that we fail to provide
+ /**
+ * classes loaded by the plugin (key: class name, value: ClassStats)
+ */
+ private Map classes = Collections.synchronizedMap(new HashMap(20));
+ private ArrayList bundles = new ArrayList(2); // bundles loaded
+
+ private boolean keepTraces = false; // indicate whether or not the traces of classes loaded are kept
+
+ // filters to indicate which classes we want to keep the traces
+ private static ArrayList packageFilters = new ArrayList(4); // filters on a package basis
+ private static Set pluginFilters = new HashSet(5); // filters on a plugin basis
+
+ private static Stack classStack = new Stack(); // represents the classes that are currently being loaded
+ /**
+ * a dictionary of the classloaderStats (key: pluginId, value: ClassloaderStats)
+ */
+ private static Map loaders = Collections.synchronizedMap(new HashMap(20));
+ public static File traceFile;
+
+ private static boolean TRACE_PLUGINS = true; //TODO make a debug option
+ private static boolean TRACE_CLASSES = true; //TODO make a debug option
+ private static String TRACE_FILENAME = "c:/trace"; //TODO make a debug option
+ private static String TRACE_FILTERS = "c:/traceFilters"; //TODO make a debug option
+
+ static {
+ if (TRACE_CLASSES || TRACE_PLUGINS)
+ initializeTraceOptions();
+ }
+ private static void initializeTraceOptions() {
+ // create the trace file
+ String filename = TRACE_FILENAME = "c:/trace";
+ traceFile = new File(filename);
+ traceFile.delete();
+
+ //load the filters
+ if (!TRACE_CLASSES)
+ return;
+// filename = TRACE_FILTERS;
+// if (filename.length() == 0)
+// return;
+// try {
+// File filterFile = new File(filename);
+//// if (!filterFile.isAbsolute())
+//// filterFile = new File(InternalBootLoader.getBootDir() + filename);
+// System.out.print("Runtime tracing elements defined in: " + filterFile.getAbsolutePath() + "..."); //$NON-NLS-1$ //$NON-NLS-2$
+// InputStream input = new FileInputStream(filterFile);
+// System.out.println(" Loaded."); //$NON-NLS-1$
+// Properties filters = new Properties() {
+// public Object put(Object key, Object value) {
+// addFilters((String) key, (String) value);
+// return null;
+// }
+// };
+// try {
+// filters.load(input);
+// } finally {
+// input.close();
+// }
+// } catch (IOException e) {
+// System.out.println(" No trace filters loaded."); //$NON-NLS-1$
+// }
+ }
+// protected static void addFilters(String key, String value) {
+// String[] filters = DelegatingURLClassLoader.getArrayFromList(value);
+// if ("plugins".equals(key)) //$NON-NLS-1$
+// pluginFilters.addAll(Arrays.asList(filters));
+// if ("packages".equals(key)) //$NON-NLS-1$
+// packageFilters.addAll(Arrays.asList(filters));
+// }
+ public static void startLoadingClass(String id, String className) {
+ findLoader(id).startLoadClass(className);
+ }
+ // get and create if does not exist
+ private static ClassloaderStats findLoader(String id) {
+ ClassloaderStats result = (ClassloaderStats) loaders.get(id);
+ if (result == null) {
+ result = new ClassloaderStats(id);
+ loaders.put(id, result);
+ }
+ return result;
+ }
+ public static Stack getClassStack() {
+ return classStack;
+ }
+ public static ClassloaderStats[] getLoaders() {
+ //the parameter to toArray is of size zero for thread safety, otherwise this
+ //could return an array with null entries if the map shrinks concurrently
+ return (ClassloaderStats[]) loaders.values().toArray(new ClassloaderStats[0]);
+ }
+ public static void endLoadingClass(String id, String className, boolean success) {
+ // must be called from a synchronized location to protect against
+ // concurrent updates
+ findLoader(id).endLoadClass(className, success);
+ }
+ public static void loadedBundle(String id, ResourceBundleStats info) {
+ findLoader(id).loadedBundle(info);
+ }
+ public static ClassloaderStats getLoader(String id) {
+ return (ClassloaderStats) loaders.get(id);
+ }
+ public ClassloaderStats(String id) {
+ this.id = id;
+ keepTraces = pluginFilters.contains(id);
+ }
+ public void addBaseClasses(String[] baseClasses) {
+// if (!id.equals(BootLoader.PI_BOOT))
+// return;
+ for (int i = 0; i < baseClasses.length; i++) {
+ String name = baseClasses[i];
+ if (classes.get(name) == null) {
+ ClassStats value = new ClassStats(name, this);
+ value.toBaseClass();
+ classes.put(name, value);
+ }
+ }
+ }
+ private void loadedBundle(ResourceBundleStats bundle) {
+ bundles.add(bundle);
+ }
+ public ArrayList getBundles() {
+ return bundles;
+ }
+ private synchronized void startLoadClass(String name) {
+ classStack.push(findClass(name));
+ }
+ // internal method that return the existing classStats or creates one
+ private ClassStats findClass(String name) {
+ ClassStats result = (ClassStats) classes.get(name);
+ return result == null ? new ClassStats(name, this) : result;
+ }
+ private synchronized void endLoadClass(String name, boolean success) {
+ ClassStats current = (ClassStats) classStack.pop();
+ if (!success) {
+ failureCount++;
+ return;
+ }
+ if (current.getLoadOrder() >= 0)
+ return;
+
+ classes.put(name, current);
+ current.setLoadOrder(classes.size());
+ current.loadingDone();
+ traceLoad(name, current);
+
+ // is there something on the load stack. if so, link them together...
+ if (classStack.size() != 0) {
+ // get the time spent loading cli and subtract its load time from the class that requires loading
+ ClassStats previous = ((ClassStats) classStack.peek());
+ previous.addTimeLoadingOthers(current.getTimeLoading());
+ current.setLoadedBy(previous);
+ previous.loaded(current);
+ } else {
+ loadingTime = loadingTime + current.getTimeLoading();
+ }
+ }
+ private void traceLoad(String name, ClassStats target) {
+ // Stack trace code
+ if (!keepTraces) {
+ boolean found = false;
+ for (int i = 0; !found && i < packageFilters.size(); i++)
+ if (name.startsWith((String) packageFilters.get(i)))
+ found = true;
+ if (!found)
+ return;
+ }
+
+ // Write the stack trace. The position in the file are set to the corresponding classStat object
+ try {
+ target.setTraceStart(traceFile.length());
+ PrintWriter output = new PrintWriter(new FileOutputStream(traceFile.getAbsolutePath(), true));
+ try {
+ output.println("Loading class: " + name); //$NON-NLS-1$
+ output.println("Class loading stack:"); //$NON-NLS-1$
+ output.println("\t" + name); //$NON-NLS-1$
+ for (int i = classStack.size() - 1; i >= 0; i--)
+ output.println("\t" + ((ClassStats) classStack.get(i)).getClassName()); //$NON-NLS-1$
+ output.println("Stack trace:"); //$NON-NLS-1$
+ new Throwable().printStackTrace(output);
+ } finally {
+ output.close();
+ }
+ target.setTraceEnd(traceFile.length());
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ public int getClassLoadCount() {
+ return classes.size();
+ }
+ public long getClassLoadTime() {
+ return loadingTime;
+ }
+ public ClassStats[] getClasses() {
+ //the parameter to toArray is of size zero for thread safety, otherwise this
+ //could return an array with null entries if the map shrinks concurrently
+ return (ClassStats[]) classes.values().toArray(new ClassStats[0]);
+ }
+ public String getId() {
+ return id;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ResourceBundleStats.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ResourceBundleStats.java
new file mode 100644
index 000000000..17d24de47
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/ResourceBundleStats.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.stats;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * BundleStats is used to represent information about loaded bundle. A
+ * bundlestats instance represents only one bundle.
+ */
+
+public class ResourceBundleStats {
+ private String pluginId; // the plugin loading this bundle
+ private String fileName; // the filename of the bundle
+ private int keyCount = 0; // number of keys in the bundle
+ private int keySize = 0; // size of the keys in the bundle
+ private int valueSize = 0; // size of the values in the bundle
+ private long hashSize = 0; // size of the hashtable
+ private long fileSize = 0;
+
+ private static int sizeOf(String value) {
+ return 44 + (2 * value.length());
+ }
+
+ private static int sizeOf(Properties value) {
+ return (int)Math.round(44 + (16 + (value.size() * 1.25 * 4)) + (24 * value.size()));
+ }
+
+ public ResourceBundleStats(String pluginId, String fileName, URL input) {
+ this.pluginId = pluginId;
+ this.fileName = fileName;
+ initialize(input);
+ }
+
+ public ResourceBundleStats(String pluginId, String fileName, ResourceBundle bundle) {
+ this.pluginId = pluginId;
+ this.fileName = fileName;
+ initialize(bundle);
+ }
+
+ /**
+ * Compute the size of bundle
+ */
+ private void initialize(ResourceBundle bundle) {
+ for (Enumeration enum = bundle.getKeys(); enum.hasMoreElements();) {
+ String key = (String) enum.nextElement();
+ keySize += sizeOf(key);
+ valueSize += sizeOf(bundle.getString(key));
+ keyCount++;
+ }
+ }
+
+ /**
+ * Compute the size of stream which represents a property file
+ */
+ private void initialize(URL url) {
+ InputStream stream = null;
+ Properties props = new Properties();
+ try {
+ try {
+ stream = url.openStream();
+ fileSize = stream.available();
+ props.load(stream);
+ for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
+ String key = (String) iter.next();
+ keySize += sizeOf(key);
+ valueSize += sizeOf(props.getProperty(key));
+ keyCount++;
+ }
+ hashSize = sizeOf(props);
+ } finally {
+ if (stream!=null)
+ stream.close();
+ }
+ } catch (IOException e) {
+ // ignore exceptions as they will be handled when the stream
+ // is loaded for real. See callers.
+ }
+ }
+
+ public long getHashSize() {
+ return hashSize;
+ }
+
+ public int getKeyCount() {
+ return keyCount;
+ }
+
+ public String getPluginId() {
+ return pluginId;
+ }
+
+ public int getKeySize() {
+ return keySize;
+ }
+
+ public int getValueSize() {
+ return valueSize;
+ }
+
+ public long getTotalSize() {
+ return keySize + valueSize + hashSize;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public long getFileSize() {
+ return fileSize;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/StatsManager.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/StatsManager.java
new file mode 100644
index 000000000..f9a8f5941
--- /dev/null
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/osgi/framework/stats/StatsManager.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.framework.stats;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.runtime.adaptor.EclipseAdaptor;
+import org.eclipse.osgi.framework.adaptor.IBundleStats;
+
+public class StatsManager implements IBundleStats {
+ // This connect plugins and their info, and so allows to access the info without running through
+ // the plugin registry. This map only contains activated plugins. The key is the plugin Id
+ private static Map plugins = new HashMap(20);
+ private static Stack activationStack = new Stack(); // a stack of the plugins being activated
+ private static boolean booting = true; // the state of the platform. This value is changed by the InternalPlatform itself.
+
+ private static StatsManager defaultInstance;
+
+ public static StatsManager getDefault() {
+ if (defaultInstance == null)
+ defaultInstance = new StatsManager();
+ return defaultInstance;
+ }
+
+ public void startActivation(String bundle) {
+ // should be called from a synchronized location to protect against concurrent updates
+ BundleStats plugin = findPlugin(bundle);
+ plugin.setTimestamp(System.currentTimeMillis());
+ plugin.setActivationOrder(plugins.size());
+
+ // set the parentage of activation
+ if (activationStack.size() != 0) {
+ BundleStats activatedBy = (BundleStats) activationStack.peek();
+ activatedBy.activated(plugin);
+ plugin.setActivatedBy(activatedBy);
+ }
+ activationStack.push(plugin);
+
+ if (EclipseAdaptor.TRACE_BUNDLES = true) {
+ traceActivate(bundle, plugin);
+ }
+ }
+
+ public void endActivation(String pluginId) {
+ // should be called from a synchronized location to protect against concurrent updates
+ if (!EclipseAdaptor.MONITOR_BUNDLES)
+ return;
+ BundleStats plugin = (BundleStats) activationStack.pop();
+ plugin.endActivation();
+ }
+
+ private void traceActivate(String id, BundleStats plugin) {
+ try {
+ PrintWriter output = new PrintWriter(new FileOutputStream(ClassloaderStats.traceFile.getAbsolutePath(), true));
+ try {
+ long startPosition = ClassloaderStats.traceFile.length();
+ output.println("Activating plugin: " + id); //$NON-NLS-1$
+ output.println("Plugin activation stack:"); //$NON-NLS-1$
+ for (int i = activationStack.size() - 1; i >= 0 ; i--)
+ output.println("\t" + ((BundleStats)activationStack.get(i)).getPluginId()); //$NON-NLS-1$
+ output.println("Class loading stack:"); //$NON-NLS-1$
+ Stack classStack = ClassloaderStats.getClassStack();
+ for (int i = classStack.size() - 1; i >= 0 ; i--)
+ output.println("\t" + ((ClassStats) classStack.get(i)).getClassName()); //$NON-NLS-1$
+ output.println("Stack trace:"); //$NON-NLS-1$
+ new Throwable().printStackTrace(output);
+ plugin.setTraceStart(startPosition);
+ } finally {
+ output.close();
+ plugin.setTraceEnd(ClassloaderStats.traceFile.length());
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static BundleStats findPlugin(String id) {
+ BundleStats result = (BundleStats)plugins.get(id);
+ ClassLoader cl = BundleStats.class.getClassLoader();
+ System.out.println(cl);
+ System.out.println(StatsManager.class.getClassLoader());
+ try {
+ if (result == null) {
+ result = new BundleStats(id);
+ plugins.put(id, result);
+ }} catch(IllegalAccessError e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ public BundleStats[] getPlugins() {
+ return (BundleStats[])plugins.values().toArray(new BundleStats[plugins.size()]);
+ }
+
+ public BundleStats getPlugin(String id) {
+ return (BundleStats) plugins.get(id);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/launch/.cvsignore b/bundles/org.eclipse.osgi/launch/.cvsignore
new file mode 100644
index 000000000..09b92bb00
--- /dev/null
+++ b/bundles/org.eclipse.osgi/launch/.cvsignore
@@ -0,0 +1,4 @@
+bundles
+workspace
+.bundledata
+eclipsebundles
diff --git a/bundles/org.eclipse.osgi/launch/.options b/bundles/org.eclipse.osgi/launch/.options
new file mode 100644
index 000000000..ae0979c35
--- /dev/null
+++ b/bundles/org.eclipse.osgi/launch/.options
@@ -0,0 +1,11 @@
+debug=false
+debug.loader=false
+debug.events=false
+debug.services=false
+debug.packages=false
+debug.manifest=false
+debug.filter=false
+debug.security=false
+debug.startlevel=false
+debug.packageadmin=false
+debug.bundleTime=false
diff --git a/bundles/org.eclipse.osgi/launch/DevFramework.launch b/bundles/org.eclipse.osgi/launch/DevFramework.launch
new file mode 100644
index 000000000..4c5837fb8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/launch/DevFramework.launch
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.osgi.framework.launcher.Launcher"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-console &quot;launch&quot;"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;4&quot; path=&quot;2&quot; containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;1&quot; path=&quot;3&quot; projectName=&quot;org.eclipse.osgi&quot;/&gt;&#10;"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.osgi"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dorg.eclipse.osgi.framework.internal.core.properties=osgi.properties -Dcom.ibm.osg.webcontainer.defaultports=&quot;&quot; -Dosgi.autoExportSystemPackages=&quot;&quot;"/>
+<stringAttribute key="org.eclipse.debug.ui.target_debug_perspective" value="perspective_default"/>
+<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="org.eclipse.osgi/launch"/>
+<stringAttribute key="org.eclipse.debug.ui.target_run_perspective" value="perspective_default"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.debug.ui.javaSourceLocator"/>
+</launchConfiguration>
diff --git a/bundles/org.eclipse.osgi/launch/EclipseFramework.launch b/bundles/org.eclipse.osgi/launch/EclipseFramework.launch
new file mode 100644
index 000000000..577df6839
--- /dev/null
+++ b/bundles/org.eclipse.osgi/launch/EclipseFramework.launch
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.osgi.framework.launcher.Launcher"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-adaptor:org.eclipse.core.runtime.adaptor.EclipseAdaptor:bundledir=eclipsebundles -console &quot;launch&quot;"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;4&quot; path=&quot;2&quot; containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry type=&quot;1&quot; path=&quot;3&quot; projectName=&quot;org.eclipse.osgi&quot;/&gt;&#10;"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.osgi"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dorg.eclipse.osgi.framework.internal.core.properties=osgi.properties -Dcom.ibm.osg.webcontainer.defaultports=&quot;&quot; -Dosgi.configuration.area=eclipsebundles"/>
+<stringAttribute key="org.eclipse.debug.ui.target_debug_perspective" value="perspective_default"/>
+<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="org.eclipse.osgi/launch"/>
+<stringAttribute key="org.eclipse.debug.ui.target_run_perspective" value="perspective_default"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.debug.ui.javaSourceLocator"/>
+</launchConfiguration>
diff --git a/bundles/org.eclipse.osgi/launch/osgi.properties b/bundles/org.eclipse.osgi/launch/osgi.properties
new file mode 100644
index 000000000..0188008ff
--- /dev/null
+++ b/bundles/org.eclipse.osgi/launch/osgi.properties
@@ -0,0 +1,116 @@
+# Eclipse OSGi Framework properties file
+#
+# This file contains properties used by the Eclipse
+# OSGi framework. The property values are
+# set by the framework when it is started. Any value set in
+# this file will override the default value derived
+# by the framework.
+
+# The setting of the default values will be done in
+# org.eclipse.osgi.framework.internal.core.Framework.java. Only
+# uncomment the value if overriding the default.
+
+# The location of this file can be specified by the
+# property org.eclipse.osgi.framework.internal.core.properties on the command
+# line when invoking the framework. The default is equivalent to:
+# -Dorg.eclipse.osgi.framework.internal.core.properties=osgi.properties
+
+# The Framework Beginning StartLevel. This is the
+# startlevel that the framework will launch with.
+# Valid values are any positive integers.
+# Default value is compatibility mode where startlevel=1
+#org.eclipse.osgi.framework.internal.core.FrameworkBeginningStartLevel=1
+
+# This is a list of package descriptions (see Export-Package
+# manifest header description) of additional packages available
+# on the system classpath. Packages described here will
+# allow bundles which imports a listed package to resolve.
+#org.eclipse.osgi.framework.internal.core.systempackages=<package-description>
+
+# This is the directory in which the DefaultAdaptor
+# persistently stores the installed bundles and permission
+# information. The default is "bundles"
+# NOTE: The contents of this directory will be entirely
+# owned by the DefaultAdapter and may be changed or deleted.
+#org.eclipse.osgi.framework.defaultadaptor.bundledir=bundles
+
+# This is the size in bytes of the buffers used by
+# bundle ClassLoaders when they need to create a buffer
+# to read a class or resource from a bundle.
+# The default value is 8024 bytes.
+#org.eclipse.osgi.framework.internal.core.loader.buffersize=8024
+
+# The org.osgi.framework.* properties can be modified for
+# the specifics of the deployment to override the default
+# values from the JVM system properties.
+
+# Default value is the value of the system property os.arch
+#org.osgi.framework.processor=x86
+
+# Default value is the value of the system property os.name
+#org.osgi.framework.os.name=Windows 2000
+
+# Default value is the value of the system property os.version
+# (up to the first space, if any)
+#org.osgi.framework.os.version=5.0
+
+# Default value is the value of the system property user.language
+#org.osgi.framework.language=en
+
+# Default value is the value of the windowing system
+#org.osgi.framework.windowing.system=win32
+
+# Default value is the system properties
+# microedition.configuration/microedition.profiles if ether of the
+# microedition properties do not exist then the default value is empty.
+# Any values set here will be merged with the default value.
+org.osgi.framework.executionenvironment=OSGi/Minimum-1.0
+
+# Default hardware type
+#org.eclipse.osgi.framework.internal.core.hardware.type=pc
+
+# Default hardware model
+#org.eclipse.osgi.framework.internal.core.hardware.model=clone
+
+# Default hardware version
+#org.eclipse.osgi.framework.internal.core.hardware.version=unknown
+
+# Endianness of the platform.
+# Possible values are "le" (little) & "be" (big).
+# Default value is "le".
+#org.eclipse.osgi.framework.internal.core.endian=le
+
+# Default address length.
+# Possible values are 32 and 64.
+# Default value is 32
+#org.eclipse.osgi.framework.internal.core.addresslength=32
+
+# Java Virtual Machine name
+# Default value from the system property java.vm.name
+#org.eclipse.osgi.framework.internal.core.vm=
+
+# The ImplType states that the framework is running on the corresponding
+# J9 class library.
+# Possible values:
+# JCL_GatewayPlus_1_3 (maps to com.ibm.oti.configuration values "rm" & "gwp")
+# JCL_Foundation_1_3 (maps to com.ibm.oti.configuration value "foun")
+# JCL_Max_1_3 (maps to com.ibm.oti.configuration value "max")
+# Value is empty for any other configuration or if not J9 VM.
+#org.eclipse.osgi.framework.internal.core.impltype=
+
+# Country
+# Default value from the system property user.region
+#org.eclipse.osgi.framework.internal.core.country=US
+
+# Flag that indicates whether or not the framework should publish
+# FrameworkEvent errors when an element in the Bundle-Classpath
+# can not be found in the bundle.
+# Default is true - publish these errors.
+#org.eclipse.osgi.framework.internal.core.publishBundleClassPathErrors=true
+
+# Flag that indicates whether or not the framework should validate
+# that a bundle's Bundle-Activator class is actually loaded from the bundle
+# and not from another bundle or the classpath.
+# Default is false - do not validate the Bundle-Activator class is loaded
+# from the bundle.
+#org.eclipse.osgi.framework.internal.core.validateBundleActivatorFromBundle=true
diff --git a/bundles/org.eclipse.osgi/osgi/ee.foundation.jar b/bundles/org.eclipse.osgi/osgi/ee.foundation.jar
new file mode 100644
index 000000000..0e67586f8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/ee.foundation.jar
Binary files differ
diff --git a/bundles/org.eclipse.osgi/osgi/ee.minimum.jar b/bundles/org.eclipse.osgi/osgi/ee.minimum.jar
new file mode 100644
index 000000000..4fbfc19c6
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/ee.minimum.jar
Binary files differ
diff --git a/bundles/org.eclipse.osgi/osgi/osgi-r3-jdk131-notsigned.jar b/bundles/org.eclipse.osgi/osgi/osgi-r3-jdk131-notsigned.jar
new file mode 100644
index 000000000..b68b32802
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/osgi-r3-jdk131-notsigned.jar
Binary files differ
diff --git a/bundles/org.eclipse.osgi/osgi/src/copyright.txt b/bundles/org.eclipse.osgi/osgi/src/copyright.txt
new file mode 100644
index 000000000..62cda3b11
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/copyright.txt
@@ -0,0 +1,64 @@
+Copyright (c) 2000, 2003
+
+The Open Services Gateway Initiative
+Bishop Ranch 2
+2694 Bishop Drive
+Suite 275
+San Ramon
+CA 94583 USA
+
+All Rights Reserved.
+
+LEGAL TERMS AND CONDITIONS REGARDING SPECIFICATION
+
+Implementation of certain elements of the Open Services Gateway
+Initiative (OSGi) Specification may be subject to third party
+intellectual property rights, including without limitation, patent
+rights (such a third party may or may not be a member of OSGi). OSGi is
+not responsible and shall not be held responsible in any manner for
+identifying or failing to identify any or all such third party
+intellectual property rights.
+
+THE RECIPIENT ACKNOWLEDGES AND AGREES THAT THE SPECIFICATION IS PROVIDED
+"AS IS" AND WITH NO WARRANTIES WHATSOEVER, WHETHER EXPRESS, IMPLIED OR
+STATUTORY, INCLUDING, BUT NOT LIMITED TO ANY WARRANTY OF
+MERCHANTABILITY, NONINFRINGEMENT, FITNESS OF ANY PARTICULAR PURPOSE, OR
+ANY WARRANTY OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION, OR
+SAMPLE. THE RECIPIENT'S USE OF THE SPECIFICATION IS SOLELY AT THE
+RECIPIENT'S OWN RISK. THE RECIPIENT'S USE OF THE SPECIFICATION IS
+SUBJECT TO THE RECIPIENT'S OSGi MEMBER AGREEMENT, IN THE EVENT THAT THE
+RECIPIENT IS AN OSGi MEMBER. IN NO EVENT SHALL OSGi BE LIABLE OR
+OBLIGATED TO THE RECIPIENT OR ANY THIRD PARTY IN ANY MANNER FOR ANY
+SPECIAL, NON-COMPENSATORY, CONSEQUENTIAL, INDIRECT, INCIDENTAL,
+STATUTORY OR PUNITIVE DAMAGES OF ANY KIND, INCLUDING, WITHOUT
+LIMITATION, LOST PROFITS AND LOST REVENUE, REGARDLESS OF THE FORM OF
+ACTION, WHETHER IN CONTRACT, TORT, NEGLIGENCE, STRICT PRODUCT LIABILITY,
+OR OTHERWISE, EVEN IF OSGi HAS BEEN INFORMED OF OR IS AWARE OF THE
+POSSIBILITY OF ANY SUCH DAMAGES IN ADVANCE.
+
+THE LIMITATIONS SET FORTH ABOVE SHALL BE DEEMED TO APPLY TO THE MAXIMUM
+EXTENT PERMITTED BY APPLICABLE LAW AND NOTWITHSTANDING THE FAILURE OF
+THE ESSENTIAL PURPOSE OF ANY LIMITED REMEDIES AVAILABLE TO THE
+RECIPIENT. THE RECIPIENT ACKNOWLEDGES AND AGREES THAT THE RECIPIENT HAS
+FULLY CONSIDERED THE FOREGOING ALLOCATION OF RISK AND FINDS IT
+REASONABLE, AND THAT THE FOREGOING LIMITATIONS ARE AN ESSENTIAL BASIS OF
+THE BARGAIN BETWEEN THE RECIPIENT AND OSGi. IF THE RECIPIENT USES THE
+SPECIFICATION, THE RECIPIENT AGREES TO ALL OF THE FOREGOING TERMS AND
+CONDITIONS. IF THE RECIPIENT DOES NOT AGREE TO THESE TERMS AND
+CONDITIONS, THE RECIPIENT SHOULD NOT USE THE SPECIFICATION AND SHOULD
+CONTACT OSGi IMMEDIATELY.
+
+Trademarks
+
+OSGi(TM) is a trademark, registered trademark, or service mark of The
+Open Services Gateway Initiative in the US and other countries. Java is
+a trademark, registered trademark, or service mark of Sun Microsystems,
+Inc. in the US and other countries. All other trademarks, registered
+trademarks, or service marks used in this document are the property of
+their respective owners and are hereby recognized.
+
+Feedback
+
+This specification can be downloaded from the OSGi web site:
+http://www.osgi.org. Comments about this specification can be mailed
+to: speccomments@mail.osgi.org
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/Constants.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/Constants.java
new file mode 100644
index 000000000..700e1eda5
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/Constants.java
@@ -0,0 +1,122 @@
+package org.eclipse.osgi.service.environment;
+
+public interface Constants {
+ //TODO These constants need to be aligned with the OSGi ones. See page 64-588 of the spec
+
+ /**
+ * Constant string (value "win32") indicating the platform is running on a
+ * Window 32-bit operating system (e.g., Windows 98, NT, 2000).
+ */
+ public static final String OS_WIN32 = "win32";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "linux") indicating the platform is running on a
+ * Linux-based operating system.
+ */
+ public static final String OS_LINUX = "linux";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "aix") indicating the platform is running on an
+ * AIX-based operating system.
+ */
+ public static final String OS_AIX = "aix";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "solaris") indicating the platform is running on a
+ * Solaris-based operating system.
+ */
+ public static final String OS_SOLARIS = "solaris";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "hpux") indicating the platform is running on an
+ * HP/UX-based operating system.
+ */
+ public static final String OS_HPUX = "hpux";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "qnx") indicating the platform is running on a
+ * QNX-based operating system.
+ */
+ public static final String OS_QNX = "qnx";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "macosx") indicating the platform is running on a
+ * Mac OS X operating system.
+ *
+ * @since 2.0
+ */
+ public static final String OS_MACOSX = "macosx";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "unknown") indicating the platform is running on a
+ * machine running an unknown operating system.
+ */
+ public static final String OS_UNKNOWN = "unknown";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "x86") indicating the platform is running on an
+ * x86-based architecture.
+ */
+ public static final String ARCH_X86 = "x86";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "PA_RISC") indicating the platform is running on an
+ * PA_RISC-based architecture.
+ */
+ public static final String ARCH_PA_RISC = "PA_RISC";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "ppc") indicating the platform is running on an
+ * PowerPC-based architecture.
+ *
+ * @since 2.0
+ */
+ public static final String ARCH_PPC = "ppc";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "sparc") indicating the platform is running on an
+ * Sparc-based architecture.
+ *
+ * @since 2.0
+ */
+ public static final String ARCH_SPARC = "sparc";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "win32") indicating the platform is running on a
+ * machine using the Windows windowing system.
+ */
+ public static final String WS_WIN32 = "win32";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "motif") indicating the platform is running on a
+ * machine using the Motif windowing system.
+ */
+ public static final String WS_MOTIF = "motif";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "gtk") indicating the platform is running on a
+ * machine using the GTK windowing system.
+ */
+ public static final String WS_GTK = "gtk";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "photon") indicating the platform is running on a
+ * machine using the Photon windowing system.
+ */
+ public static final String WS_PHOTON = "photon";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "carbon") indicating the platform is running on a
+ * machine using the Carbon windowing system (Mac OS X).
+ *
+ * @since 2.0
+ */
+ public static final String WS_CARBON = "carbon";//$NON-NLS-1$
+
+ /**
+ * Constant string (value "unknown") indicating the platform is running on a
+ * machine running an unknown windowing system.
+ */
+ public static final String WS_UNKNOWN = "unknown";//$NON-NLS-1$
+
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/DebugOptions.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/DebugOptions.java
new file mode 100644
index 000000000..713147098
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/DebugOptions.java
@@ -0,0 +1,8 @@
+package org.eclipse.osgi.service.environment;
+
+public interface DebugOptions {
+ public abstract boolean getBooleanOption(String option, boolean defaultValue);
+ public abstract String getOption(String option);
+ public abstract int getIntegerOption(String option, int defaultValue);
+ public abstract void setOption(String option, String value);
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/EnvironmentInfo.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/EnvironmentInfo.java
new file mode 100644
index 000000000..1b3ee8e59
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/environment/EnvironmentInfo.java
@@ -0,0 +1,79 @@
+package org.eclipse.osgi.service.environment;
+
+//TODO There is basically two kinds of info that are required from the running OSGi : some are related to the args that have been passed to the system,
+// some others are relative the state in which the osgi platform is running (debug, dev?, etc...). I temporarily put them here
+
+
+/**
+ * A Framework service which gives access to the command line used to start
+ * this running framework.
+ */
+public interface EnvironmentInfo {
+
+ /**
+ * Returns all command line arguments specified when the running framework was started.
+ * @return the array of command line arguments.
+ */
+ public String[] getAllArgs();
+
+ /**
+ * Returns the arguments consumed by the framework implementation itself. Which
+ * arguments are consumed is implementation specific.
+ * @return the array of command line arguments consumed by the framework.
+ */
+ public String[] getFrameworkArgs();
+
+ /**
+ * Returns the arguments not consumed by the framework implementation itself. Which
+ * arguments are consumed is implementation specific. These arguments are available
+ * for use by the application.
+ * @return the array of command line arguments not consumed by the framework.
+ */
+ public String[] getApplicationArgs();
+
+ /**
+ * Returns the string name of the current system architecture.
+ * The value is a user-defined string if the architecture is
+ * specified on the command line, otherwise it is the value
+ * returned by <code>java.lang.System.getProperty("os.arch")</code>.
+ *
+ * @return the string name of the current system architecture
+ * @since 2.0
+ */
+ public String getOSArch();
+
+ /**
+ * Returns the string name of the current locale for use in finding files
+ * whose path starts with <code>$nl$</code>.
+ *
+ * @return the string name of the current locale
+ */
+ public String getNL();
+
+ /**
+ * Returns the string name of the current operating system for use in finding
+ * files whose path starts with <code>$os$</code>. <code>OS_UNKNOWN</code> is
+ * returned if the operating system cannot be determined.
+ * The value may indicate one of the operating systems known to the platform
+ * (as specified in <code>knownOSValues</code>) or a user-defined string if
+ * the operating system name is specified on the command line.
+ *
+ * @return the string name of the current operating system
+ * @see #knownOSValues
+ *
+ */
+ public String getOS();
+
+ /**
+ * Returns the string name of the current window system for use in finding files
+ * whose path starts with <code>$ws$</code>. <code>null</code> is returned
+ * if the window system cannot be determined.
+ *
+ * @return the string name of the current window system or <code>null</code>
+ */
+ public String getWS();
+
+
+ public boolean inDebugMode(); //The same API could exists at the Eclipse level (why should we debug OSGi when we want to debug eclipse and vice-versa)
+
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleDelta.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleDelta.java
new file mode 100644
index 000000000..5683218d1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleDelta.java
@@ -0,0 +1,72 @@
+package org.eclipse.osgi.service.resolver;
+
+/**
+ * BundleDeltas represent the changes related to an individual bundle between two
+ * states.
+ */
+public interface BundleDelta {
+
+ /**
+ * Delta type constant (bit mask) indicating that the bundle has been added
+ * to the new state.
+ * @see BundleDelta#getType
+ */
+ public static final int ADDED = 0x1;
+ /**
+ * Delta type constant (bit mask) indicating that the bundle is no longer present in
+ * the new state.
+ * @see BundleDelta#getType
+ */
+ public static final int REMOVED = 0x2;
+ /**
+ * Delta type constant (bit mask) indicating that the bundle has been updated
+ * between the old and new state. Note that an update delta may in fact represent
+ * a downgrading of the bundle to a previous version.
+ * @see BundleDelta#getType
+ */
+ public static final int UPDATED = 0x4;
+ /**
+ * Delta type constant (bit mask) indicating that the bundle has become resolved
+ * in the new state.
+ * @see BundleDelta#getType
+ */
+ public static final int RESOLVED = 0x8;
+ /**
+ * Delta type constant (bit mask) indicating that the bundle has become unresolved
+ * in the new state. Note that newly added bundles are unresolved by default and
+ * as such, do not transition to unresolved state so this flag is not set.
+ * @see BundleDelta#getType
+ */
+ public static final int UNRESOLVED = 0x10;
+ /**
+ * Delta type constant (bit mask) indicating that the bundles and packages which this
+ * bundle requires/imports (respectively) have changed in the new state.
+ * @see BundleDelta#getType
+ */
+ public static final int LINKAGE_CHANGED = 0x20;
+
+ /**
+ * Delta type constant (bit mask) indicating that the bundles which this
+ * bundle optionally requires have changed in the new state.
+ * @see BundleDelta#getType
+ */
+ public static final int OPTIONAL_LINKAGE_CHANGED = 0x40;
+
+ /**
+ *
+ * @return
+ */
+ public BundleDescription getBundle();
+
+ /**
+ * Returns the type of change which occured. The return value is composed
+ * of by bit-wise masking the relevant flags from the set ADDED, REMOVED,
+ * CHANGED, RESOLVED, UNRESOLVED, LINKAGE_CHANGED.
+ * Note that bundle start and stop state changes are not captured in the
+ * delta as they do not represent structural changes but rather transient
+ * runtime states.
+ * @return the type of change which occured
+ */
+ public int getType();
+
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleDescription.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleDescription.java
new file mode 100644
index 000000000..835fc9da4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleDescription.java
@@ -0,0 +1,146 @@
+package org.eclipse.osgi.service.resolver;
+
+import java.util.Dictionary;
+
+/**
+ * This class represents a specific version of a bundle in the system.
+ */
+
+public interface BundleDescription {
+
+ /**
+ * The location string for this bundle
+ */
+ public String getLocation();
+
+ /**
+ * Returns an array of package specifications defined by the Import-Package
+ * and Export-Package clauses. Exported and imported packages can be told
+ * apart by checking the isExport flag.
+ *
+ * @see PackageSpecification#isExport
+ * @return an array of package specifications
+ */
+ public PackageSpecification[] getPackages();
+ /**
+ * Returns an array of package specifications defined by the
+ * Provide-Package clause.
+ *
+ * @return an array of package names
+ */
+ public String[] getProvidedPackages();
+ /**
+ * Returns the package specification whose package name matches the given
+ * name. The packages detailed in getPackages() are searched for the given
+ * name.
+ *
+ * @param name the name of the package to look for
+ * @return the package description for the discovered bundle or null if
+ * none could be found.
+ */
+ public PackageSpecification getPackage(String name);
+
+ /**
+ * Returns an array of bundle specifications defined by the Require-Bundle
+ * clause in this bundle.
+ *
+ * @return an array of bundle specifications
+ */
+ public BundleSpecification[] getRequiredBundles();
+
+ /**
+ * Returns the bundle specification for the bundle with the given unique id
+ * in this bundle.
+ *
+ * @param uniqueId the id of the required bundle to look for.
+ * @return the discovered bunde specification or null if none could be
+ * found.
+ */
+ public BundleSpecification getRequiredBundle(String name);
+
+ /**
+ * Return unique id of the bundle.
+ *
+ * @return the unique id of this bundle.
+ */
+ public String getUniqueId();
+
+ /**
+ * Returns true if this bundle is resolved in its host state.
+ *
+ * @return
+ */
+ public boolean isResolved();
+
+ /**
+ * Returns the state object which hosts this bundle. null is returned if
+ * this bundle is not currently in a state.
+ *
+ * @return the state object which hosts this bundle.
+ */
+ public State getContainingState();
+
+ /**
+ * Returns the framework state of this bundle. The return value can be one
+ * of the valid states as defined in org.osgi.framwork.Bundle or NONE as
+ * defined in BundleDescription.
+ *
+ * @return @see org.osgi.framework.Bundle
+ */
+ public int getState();
+
+ /**
+ * Returns the version specification of for this bundle.
+ *
+ * @return the version specification for this bundle.
+ */
+ public Version getVersion();
+ /**
+ * Returns an array containing all constraints specified by the given
+ * bundle that could not be satisfied. If all constraints could be
+ * satisfied, returns an empty array. This does not relate to the fact that
+ * the bundle became resolved or not. A resolved bundle may have
+ * unsatisfied constraints (if they are optional), as well as an unresolved
+ * bundle may not have any unsatisfied constraints (which means that it has
+ * not been picked - for instance, if only one version is allowed and there
+ * is a "better" version).
+ * @return an array of <code>VersionConstraint</code> objects containing
+ * all constraints that could not be satisfied.
+ */
+ public VersionConstraint[] getUnsatisfiedConstraints();
+
+ /**
+ * Returns a read-only dictionary for the Manifest of this bundle.
+ */
+ public Dictionary getManifest();
+
+ /**
+ * Returns the string representation of this bundle.
+ *
+ * @return String representation of this bundle.
+ */
+ public String toString();
+
+ /**
+ * Returns the host for this bundle. null is returned if this bundle is not
+ * a fragment.
+ *
+ * @return
+ */
+ public HostSpecification getHost();
+
+ /**
+ * Returns the numeric id of this bundle. Typically a bundle description
+ * will only have a numeric id if it represents a bundle that is installed in a
+ * framework as the framework assigns the ids. -1 is returned if the id is not known.
+ *
+ * @return the numeric id of this bundle description
+ */
+ public long getBundleId();
+
+ /**
+ * Returns all fragments known to this bundle (regardless resolution status).
+ * @return an array of BundleDescriptions containing all known fragments
+ */
+ public BundleDescription[] getFragments();
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleSpecification.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleSpecification.java
new file mode 100644
index 000000000..b7c9dae4f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/BundleSpecification.java
@@ -0,0 +1,14 @@
+package org.eclipse.osgi.service.resolver;
+
+
+/**
+ * A representation of one bundle import constraint as seen in a
+ * bundle manifest and managed by a state and resolver.
+ */
+public interface BundleSpecification extends VersionConstraint {
+
+ public boolean isExported();
+
+ public boolean isOptional();
+
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/HostSpecification.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/HostSpecification.java
new file mode 100644
index 000000000..f31ee2489
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/HostSpecification.java
@@ -0,0 +1,11 @@
+package org.eclipse.osgi.service.resolver;
+
+/**
+ * A representation of one host bundle constraint as seen in a
+ * bundle manifest and managed by a state and resolver.
+ */
+public interface HostSpecification extends VersionConstraint {
+
+ public boolean reloadHost();
+
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/PackageSpecification.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/PackageSpecification.java
new file mode 100644
index 000000000..ea5aaa54c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/PackageSpecification.java
@@ -0,0 +1,11 @@
+package org.eclipse.osgi.service.resolver;
+
+
+/**
+ * A representation of one package import constraint as seen in a
+ * bundle manifest and managed by a state and resolver.
+ */
+public interface PackageSpecification extends VersionConstraint {
+
+ public boolean isExported();
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/PlatformAdmin.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/PlatformAdmin.java
new file mode 100644
index 000000000..034cd6c2f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/PlatformAdmin.java
@@ -0,0 +1,46 @@
+package org.eclipse.osgi.service.resolver;
+
+import org.osgi.framework.BundleException;
+/**
+ * Framework service which allows bundle programmers to inspect the bundles and
+ * packages known to the Framework. The PlatformAdmin service also allows bundles
+ * with sufficient privileges to update the state of the framework by committing a new
+ * configuration of bundles and packages.
+ *
+ * If present, there will only be a single instance of this service
+ * registered with the Framework.
+ */
+public interface PlatformAdmin {
+
+ /**
+ * Returns a state representing the current system. The returned state
+ * will not be associated with any resolver.
+ * @return a state representing the current framework.
+ */
+ public State getState();
+
+ /**
+ * Commit the differences between the current state and the given state.
+ * The given state must return true from State.isResolved() or an exception
+ * is thrown. The resolved state is committed verbatim, as-is.
+ *
+ * @param state the future state of the framework
+ * @throws BundleException if the id of the given state does not match that of the
+ * current state or if the given state is not resolved.
+ */
+ public void commit(State state) throws BundleException;
+
+ /**
+ * Returns a resolver supplied by the system. The returned resolver
+ * will not be associated with any state.
+ * @return a system resolver
+ */
+ public Resolver getResolver();
+
+ /**
+ * Returns a factory that knows how to create state objects, such as bundle
+ * descriptions and the different types of version constraints.
+ * @return a state object factory
+ */
+ public StateObjectFactory getFactory();
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/Resolver.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/Resolver.java
new file mode 100644
index 000000000..92b7f7fd4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/Resolver.java
@@ -0,0 +1,70 @@
+package org.eclipse.osgi.service.resolver;
+
+public interface Resolver {
+
+ /**
+ * Resolves the state associated with this resolver and returns an array of
+ * bundle deltas describing the changes.. The state and version bindings
+ * for the various bundles and packages in this state are updated and a
+ * array containing bundle deltas describing the changes returned.
+ * <p>
+ * This method is intended to be called only by State objects in response
+ * to a user invocation of State.resolve(). States will typically refuse to
+ * update their constituents (see State.resolveBundle() and
+ * State.resolveConstraint()) if their resolve method is not currently
+ * being invoked.
+ * </p>
+ * <p>
+ * Note the given state is destructively modified to reflect the results of
+ * resolution.
+ * </p>
+ */
+ public void resolve();
+ /**
+ *
+ */
+ public void resolve(BundleDescription[] discard);
+
+ /**
+ * Flushes this resolver of any stored/cached data it may be keeping to
+ * facilitate incremental processing on its associated state. This is
+ * typicaly used when switching the resolver's state object.
+ */
+ public void flush();
+
+ /**
+ * Returns the state associated with this resolver. A state can work with
+ * at most one resolver at any given time. Similarly, a resolver can work
+ * with at most one state at a time.
+ *
+ * @return the state for this resolver. null is returned if the resolver
+ * does not have a state
+ */
+ public State getState();
+
+ /**
+ * Sets the state associated with this resolver. A state can work with at
+ * most one resolver at any given time. Similarly, a resolver can work with
+ * at most one state at a time.
+ * <p>
+ * To ensure that this resolver and the given state are properly linked,
+ * the following expression must be included in this method if the given
+ * state (value) is not identical to the result of this.getState().
+ * </p>
+ *
+ * <pre>
+ * if (this.getState() != value) value.setResolver(this);
+ * </pre>
+ */
+ public void setState(State value);
+ /**
+ * Notifies the resolver a bundle has been added to the state.
+ * @param bundle
+ */
+ public void bundleAdded(BundleDescription bundle);
+ /**
+ * Notifies the resolver a bundle has been removed from the state.
+ * @param bundle
+ */
+ public void bundleRemoved(BundleDescription bundle);
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/State.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/State.java
new file mode 100644
index 000000000..9070ae5b4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/State.java
@@ -0,0 +1,265 @@
+package org.eclipse.osgi.service.resolver;
+
+import java.util.Dictionary;
+
+import org.osgi.framework.BundleException;
+
+/**
+ * The state of a system as reported by a resolver. This includes all bundles
+ * presented to the resolver relative to this state (i.e., both resolved and
+ * unresolved).
+ */
+public interface State {
+
+ /**
+ * Adds the given bundle to this state.
+ *
+ * @param description the description to add
+ */
+ public void addBundle(BundleDescription description);
+
+ /**
+ * Returns a delta describing the differences between this state and the
+ * given state. This state is taken as the base so the absence of a bundle
+ * in the given state is reported as a deletion, etc.
+ *
+ * @param state
+ * @return
+ */
+ public StateChangeEvent compare(State state);
+
+ /**
+ * Removes a bundle description with the given bundle id.
+ *
+ * @param bundleId the id of the bundle description to be removed
+ * @return the removed bundle description, or <code>null</code>, if a bundle
+ * with the given id does not exist in this state
+ */
+ public BundleDescription removeBundle(long bundleId);
+ /**
+ * Removes the given bundle description.
+ *
+ * @param bundle the bundle description to be removed
+ * @return <code>true</code>, if if the bundle description was removed,
+ * <code>false</code> otherwise
+ */
+ public boolean removeBundle(BundleDescription bundle);
+
+ /**
+ * Returns the delta representing the changes from the time this state was
+ * first captured until now.
+ *
+ * @return
+ */
+ public StateDelta getChanges();
+
+ /**
+ * Returns descriptions for all bundles known to this state.
+ *
+ * @return the descriptions for all bundles known to this state.
+ */
+ public BundleDescription[] getBundles();
+
+ /**
+ * Returns descriptor for all bundles known to this state.
+ *
+ * @return the descriptors for all bundles known to this state.
+ * @see BundleDescription#getBundleId()
+ */
+ public BundleDescription getBundle(long id);
+
+ /**
+ * Returns the bundle descriptor for the bundle with the given name and
+ * version. null is returned if no such bundle is found in this state. If
+ * the version argument is null then the bundle with the given name which
+ * is resolve and/or has the highest version number is returned.
+ *
+ * @param name name of the bundle to query
+ * @param version version of the bundle to query. null matches any bundle
+ * @return the descriptor for the identified bundle
+ */
+ public BundleDescription getBundle(String uniqueId, Version version);
+
+ /**
+ * Returns the id of the state on which this state is based. This
+ * correlates this state to the system state. For example, if
+ * Resolver.getState() returns state 4 but then some bundles are installed,
+ * the system state id is updated. By comparing 4 to the current system
+ * state id it is possible to detect if the the states are out of sync.
+ *
+ * @return the id of the state on which this state is based
+ */
+ public long getTimeStamp();
+
+ /**
+ * Returns true if there have been no modifications to this state since the
+ * last time resolve() was called.
+ *
+ * @return whether or not this state has changed since last resolved.
+ */
+ public boolean isResolved();
+
+ /**
+ * Resolves the given version constraint with the given values. The given
+ * constraint object is destructively modified to reflect its new resolved
+ * state. Note that a constraint can be unresolved by passing null for both
+ * the actual version and the supplier.
+ * <p>
+ * This method is intended to be used by resolvers in the process of
+ * determining which constraints are satisfied by which components.
+ * </p>
+ *
+ * @param constraint the version constraint to update
+ * @param actualVersion the version to which the constraint will be bound.
+ * May be null if the constraint is to be unresolved.
+ * @param supplier the bundle that supplies the given version which
+ * satisfies the constraint. May be null if the constraint is to be
+ * unresolved.
+ * @throws IllegalStateException if this is not done during a call to
+ * <code>resolve</code>
+ */
+ public void resolveConstraint(VersionConstraint constraint, Version actualVersion, BundleDescription supplier);
+
+ /**
+ * Sets whether or not the given bundle is selected in this state..
+ * <p>
+ * This method is intended to be used by resolvers in the process of
+ * determining which constraints are satisfied by which components.
+ * </p>
+ *
+ * @param bundle the bundle to update
+ * @param status whether or not the given bundle is selected
+ */
+ public void resolveBundle(BundleDescription bundle, int status);
+
+ /**
+ * Returns the resolver associated with this state. A state can work with
+ * at most one resolver at any given time. Similarly, a resolver can work
+ * with at most one state at a time.
+ *
+ * @return the resolver for this state. null is returned if the state does
+ * not have a resolver
+ */
+ public Resolver getResolver();
+
+ /**
+ * Sets the resolver associated with this state. A state can work with at
+ * most one resolver at any given time. Similarly, a resolver can work with
+ * at most one state at a time.
+ * <p>
+ * To ensure that this state and the given resovler are properly linked,
+ * the following expression must be included in this method if the given
+ * resolver (value) is not identical to the result of this.getResolver().
+ *
+ * <pre>
+ * if (this.getResolver() != value) value.setState(this);
+ * </pre>
+ *
+ * </p>
+ */
+ public void setResolver(Resolver value);
+
+ /**
+ * Resolves the constraints contained in this state using the resolver
+ * currently associated with the state and returns a delta describing the
+ * changes in resolved states and dependencies in the state.
+ * <p>
+ * Note that this method is typically implemented using
+ *
+ * <pre>
+ * this.getResolver().resolve();
+ * </pre>
+ *
+ * and is the preferred path for invoking resolution. In particular, states
+ * should refuse to perform updates (@see #select() and
+ * #resolveConstraint()) if they are not currently involved in a resolution
+ * cycle.
+ * <p>
+ * Note the given state is destructively modified to reflect the results of
+ * resolution.
+ * </p>
+ *
+ * @param incremental a flag controlling whether resolution should be incremental
+ * @return a delta describing the changes in resolved state and
+ * interconnections
+ */
+ public StateDelta resolve(boolean incremental);
+
+ /**
+ * Same as State.resolve(true);
+ */
+ public StateDelta resolve();
+
+ /**
+ * Resolves the constraints contained in this state using the resolver
+ * currently associated with the state in a incremental, "least-perturbing"
+ * mode, and returns a delta describing the changes in resolved states and
+ * dependencies in the state.
+ *
+ * @param discard an array containing descriptions for bundles whose
+ * current resolution state should be forgotten
+ * @return a delta describing the changes in resolved state and
+ * interconnections
+ */
+ public StateDelta resolve(BundleDescription[] discard);
+
+ /**
+ * Sets the version overrides which are to be applied during the resolutoin
+ * of this state. Version overrides allow external forces to
+ * refine/override the version constraints setup by the components in the
+ * state.
+ *
+ * @param value
+ */
+ // TODO the exact form of this is not defined as yet.
+ public void setOverrides(Object value);
+ /**
+ * Returns descriptions for all bundles currently resolved in this state.
+ *
+ * @return the descriptions for all bundles currently resolved in this
+ * state.
+ */
+ public BundleDescription[] getResolvedBundles();
+ /**
+ * Adds the given listener for change events to this state.
+ * Has no effect if an identical listener is already registered.
+ * <p>
+ * The resolver associated with the state, if implements StateChangeListener,
+ * receives all notifications for added/removed bundlers for free.
+ * </p>
+ *
+ * @param listener the listener
+ * @param flags the bit-wise OR of all event types of interest to the listener
+ *
+ * @see BundleDelta for all available event types
+ */
+ public void addStateChangeListener(StateChangeListener listener, int flags);
+ /**
+ * Removes the given listener from the list of listeners.
+ * Has no effect if an identical listener is not registered.
+ *
+ * @param listener the listener
+ */
+ public void removeStateChangeListener(StateChangeListener listener);
+
+ /**
+ * Returns whether this state is empty.
+ * @return <code>true</code> if this state is empty, <code>false</code>
+ * otherwise
+ */
+ public boolean isEmpty();
+
+ /**
+ * Returns all exported packages in this state, according to the OSGi rules for resolution.
+ *
+ * @return
+ * @see org.osgi.service.packageadmin.PackageAdmin#getExportedPackages(Bundle)
+ */
+ public PackageSpecification[] getExportedPackages();
+
+ /**
+ * Returns all bundle descriptions with the given bundle global name.
+ */
+ public BundleDescription[] getBundles(String globalName);
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateChangeEvent.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateChangeEvent.java
new file mode 100644
index 000000000..7ae88aeea
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateChangeEvent.java
@@ -0,0 +1,19 @@
+package org.eclipse.osgi.service.resolver;
+
+import java.util.EventObject;
+
+public class StateChangeEvent extends EventObject {
+ private StateDelta delta;
+ public StateChangeEvent(StateDelta delta) {
+ super(delta.getState());
+ this.delta = delta;
+ }
+ /**
+ * Returns a delta detailing changes to a state object.
+ *
+ * @return a state delta
+ */
+ public StateDelta getDelta() {
+ return delta;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateChangeListener.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateChangeListener.java
new file mode 100644
index 000000000..46ea645b7
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateChangeListener.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.service.resolver;
+
+import java.util.EventListener;
+
+public interface StateChangeListener extends EventListener {
+ public void stateChanged(StateChangeEvent event);
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateDelta.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateDelta.java
new file mode 100644
index 000000000..7791afdd7
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateDelta.java
@@ -0,0 +1,28 @@
+package org.eclipse.osgi.service.resolver;
+
+public interface StateDelta {
+ /**
+ * Returns an array of all the bundle deltas in this delta regardless of type.
+ * @return an array of bundle deltas
+ */
+ public BundleDelta[] getChanges();
+
+ /**
+ * Returns an array of all the members
+ * of this delta which match the given flags. If an exact match is requested
+ * then only delta members whose type exactly matches the given mask are
+ * included. Otherwise, all bundle deltas whose type's bit-wise and with the
+ * mask is non-zero are included.
+ *
+ * @param mask
+ * @param exact
+ * @return an array of bundle deltas matching the given match criteria.
+ */
+ public BundleDelta[] getChanges(int mask, boolean exact);
+
+ /**
+ * Returns the state whose changes are represented by this delta.
+ * @return
+ */
+ public State getState();
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateObjectFactory.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateObjectFactory.java
new file mode 100644
index 000000000..88e311c20
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/StateObjectFactory.java
@@ -0,0 +1,28 @@
+package org.eclipse.osgi.service.resolver;
+
+import java.util.Dictionary;
+
+public interface StateObjectFactory {
+ public State createState();
+ public State createState(State state);
+ public BundleDescription createBundleDescription(long id, String globalName, Version version, String location, BundleSpecification[] required, HostSpecification host, PackageSpecification[] packages, String[] providedPackages);
+ /**
+ * Returns a bundle description based on the information in the supplied manifest dictionary.
+ * The manifest should contain String keys and String values which correspond to
+ * proper OSGi manifest headers and values.
+ *
+ * @param manifest a collection of OSGi manifest headers and values
+ * @param location the URL location of the bundle
+ * @param id the id of the bundle
+ * @return a bundle description derived from the given information
+ */
+ public BundleDescription createBundleDescription(Dictionary manifest, String location, long id);
+ public BundleDescription createBundleDescription(BundleDescription original);
+ public BundleSpecification createBundleSpecification(BundleDescription parentBundle, String hostGlobalName, Version hostVersion, byte matchRule, boolean export, boolean optional);
+ public BundleSpecification createBundleSpecification(BundleSpecification original);
+ public HostSpecification createHostSpecification(BundleDescription parentBundle, String hostGlobalName, Version hostVersion, byte matchRule, boolean reloadHost);
+ public HostSpecification createHostSpecification(HostSpecification original);
+ public PackageSpecification createPackageSpecification(BundleDescription parentBundle, String packageName, Version packageVersion, boolean exported);
+ public PackageSpecification createPackageSpecification(PackageSpecification original);
+
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/Version.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/Version.java
new file mode 100644
index 000000000..a9003e903
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/Version.java
@@ -0,0 +1,520 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.service.resolver;
+
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * <p>
+ * Version identifier for a plug-in. In its string representation,
+ * it consists of up to 4 tokens separated by a decimal point.
+ * The first 3 tokens are positive integer numbers, the last token
+ * is an uninterpreted string (no whitespace characters allowed).
+ * For example, the following are valid version identifiers
+ * (as strings):
+ * <ul>
+ * <li><code>0.0.0</code></li>
+ * <li><code>1.0.127564</code></li>
+ * <li><code>3.7.2.build-127J</code></li>
+ * <li><code>1.9</code> (interpreted as <code>1.9.0</code>)</li>
+ * <li><code>3</code> (interpreted as <code>3.0.0</code>)</li>
+ * </ul>
+ * </p>
+ * <p>
+ * The version identifier can be decomposed into a major, minor,
+ * service level component and qualifier components. A difference
+ * in the major component is interpreted as an incompatible version
+ * change. A difference in the minor (and not the major) component
+ * is interpreted as a compatible version change. The service
+ * level component is interpreted as a cumulative and compatible
+ * service update of the minor version component. The qualifier is
+ * not interpreted, other than in version comparisons. The
+ * qualifiers are compared using lexicographical string comparison.
+ * </p>
+ * <p>
+ * Version identifiers can be matched as perfectly equal, equivalent,
+ * compatible or greaterOrEqual.
+ * </p>
+ * <p>
+ * Clients may instantiate; not intended to be subclassed by clients.
+ * </p>
+ * @see IPluginDescriptor#getVersionIdentifier
+ * @see java.lang.String#compareTo
+ */
+public final class Version implements Comparable {
+
+ private int major = 0;
+ private int minor = 0;
+ private int service = 0;
+ private String qualifier = ""; //$NON-NLS-1$
+
+ private static final String SEPARATOR = "."; //$NON-NLS-1$
+
+ public static final Version EMPTY_VERSION = new Version(0, 0, 0);
+
+ /**
+ * Creates a plug-in version identifier from a given Version.
+ *
+ * @param version
+ */
+ public Version(Version version) {
+ this(version.major, version.minor, version.service, version.qualifier);
+ }
+
+ /**
+ * Creates a plug-in version identifier from its components.
+ *
+ * @param major major component of the version identifier
+ * @param minor minor component of the version identifier
+ * @param service service update component of the version identifier
+ */
+ public Version(int major, int minor, int service) {
+ this(major, minor, service, null);
+ }
+ /**
+ * Creates a plug-in version identifier from its components.
+ *
+ * @param major major component of the version identifier
+ * @param minor minor component of the version identifier
+ * @param service service update component of the version identifier
+ * @param qualifier qualifier component of the version identifier.
+ * Qualifier characters that are not a letter or a digit are replaced.
+ */
+ public Version(int major, int minor, int service, String qualifier)
+ throws IllegalArgumentException {
+
+ // Do the test outside of the assert so that they 'Policy.bind'
+ // will not be evaluated each time (including cases when we would
+ // have passed by the assert).
+
+ if (major < 0)
+ throw new IllegalArgumentException("Negative major");
+ if (minor < 0)
+ throw new IllegalArgumentException("Negative minor");
+ if (service < 0)
+ throw new IllegalArgumentException("Negative service");
+ if (qualifier == null)
+ qualifier = ""; //$NON-NLS-1$
+
+ this.major = major;
+ this.minor = minor;
+ this.service = service;
+ this.qualifier = verifyQualifier(qualifier);
+ }
+ /**
+ * Creates a plug-in version identifier from the given string.
+ * The string represenation consists of up to 4 tokens
+ * separated by decimal point.
+ * For example, the following are valid version identifiers
+ * (as strings):
+ * <ul>
+ * <li><code>0.0.0</code></li>
+ * <li><code>1.0.127564</code></li>
+ * <li><code>3.7.2.build-127J</code></li>
+ * <li><code>1.9</code> (interpreted as <code>1.9.0</code>)</li>
+ * <li><code>3</code> (interpreted as <code>3.0.0</code>)</li>
+ * </ul>
+ * </p>
+ *
+ * @param versionId string representation of the version identifier.
+ * Qualifier characters that are not a letter or a digit are replaced.
+ */
+ public Version(String versionId) {
+ Object[] parts = parseVersion(versionId);
+ this.major = ((Integer) parts[0]).intValue();
+ this.minor = ((Integer) parts[1]).intValue();
+ this.service = ((Integer) parts[2]).intValue();
+ this.qualifier = (String) parts[3];
+ }
+ /**
+ * Validates the given string as a plug-in version identifier.
+ *
+ * @param version the string to validate
+ * @return a status object with code <code>IStatus.OK</code> if
+ * the given string is valid as a plug-in version identifier, otherwise a status
+ * object indicating what is wrong with the string
+ * @since 2.0
+ */
+ public static RuntimeException validateVersion(String version) {
+ try {
+ parseVersion(version);
+ } catch (RuntimeException e) {
+ return e;
+ // OG...
+ // return new Status(IStatus.ERROR, Platform.PI_RUNTIME, IStatus.ERROR, e.getMessage(), e);
+ }
+ return null;
+ // OG...
+ // return new Status(IStatus.OK, Platform.PI_RUNTIME, IStatus.OK, Policy.bind("ok"), null); //$NON-NLS-1$
+ }
+ private static Object[] parseVersion(String versionId) {
+
+ // Do the test outside of the assert so that they 'Policy.bind'
+ // will not be evaluated each time (including cases when we would
+ // have passed by the assert).
+ if (versionId == null)
+ throw new IllegalArgumentException("Null version string");
+
+ String s = versionId.trim();
+ if (s.equals("")) //$NON-NLS-1$
+ throw new IllegalArgumentException("Empty version string");
+ if (s.startsWith(SEPARATOR))
+ throw new IllegalArgumentException("Invalid version format");
+ if (s.endsWith(SEPARATOR))
+ throw new IllegalArgumentException("Invalid version format");
+ if (s.indexOf(SEPARATOR + SEPARATOR) != -1)
+ throw new IllegalArgumentException("Invalid version format");
+
+ StringTokenizer st = new StringTokenizer(s, SEPARATOR);
+ Vector elements = new Vector(4);
+
+ while (st.hasMoreTokens())
+ elements.addElement(st.nextToken());
+
+ int elementSize = elements.size();
+
+ if (elementSize <= 0)
+ throw new IllegalArgumentException("Invalid version format (no token)");
+ if (elementSize > 4)
+ throw new IllegalArgumentException("Invalid version format (more than 4 tokens)");
+
+ int[] numbers = new int[3];
+ try {
+ numbers[0] = Integer.parseInt((String) elements.elementAt(0));
+ if (numbers[0] < 0)
+ throw new IllegalArgumentException("Negative major");
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Invalid major");
+ }
+
+ try {
+ if (elementSize >= 2) {
+ numbers[1] = Integer.parseInt((String) elements.elementAt(1));
+ if (numbers[1] < 0)
+ throw new IllegalArgumentException("Negative minor");
+ } else
+ numbers[1] = 0;
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Invalid minor");
+ }
+
+ try {
+ if (elementSize >= 3) {
+ numbers[2] = Integer.parseInt((String) elements.elementAt(2));
+ if (numbers[2] < 0)
+ throw new IllegalArgumentException("Invalid service");
+ } else
+ numbers[2] = 0;
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Invalid service");
+ }
+
+ // "result" is a 4-element array with the major, minor, service, and qualifier
+ Object[] result = new Object[4];
+ result[0] = new Integer(numbers[0]);
+ result[1] = new Integer(numbers[1]);
+ result[2] = new Integer(numbers[2]);
+ if (elementSize >= 4)
+ result[3] = verifyQualifier((String) elements.elementAt(3));
+ else
+ result[3] = ""; //$NON-NLS-1$
+ return result;
+ }
+ /**
+ * Compare version identifiers for equality. Identifiers are
+ * equal if all of their components are equal.
+ *
+ * @param object an object to compare
+ * @return whehter or not the two objects are equal
+ */
+ public boolean equals(Object object) {
+ if (!(object instanceof Version))
+ return false;
+ Version v = (Version) object;
+ return v.getMajorComponent() == major
+ && v.getMinorComponent() == minor
+ && v.getServiceComponent() == service
+ && v.getQualifierComponent().equals(qualifier);
+ }
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return an integer which is a hash code value for this object.
+ */
+ public int hashCode() {
+ int code = major + minor + service; // R1.0 result
+ if (qualifier.equals("")) //$NON-NLS-1$
+ return code;
+ else
+ return code + qualifier.hashCode();
+ }
+ /**
+ * Returns the major (incompatible) component of this
+ * version identifier.
+ *
+ * @return the major version
+ */
+ public int getMajorComponent() {
+ return major;
+ }
+ /**
+ * Returns the minor (compatible) component of this
+ * version identifier.
+ *
+ * @return the minor version
+ */
+ public int getMinorComponent() {
+ return minor;
+ }
+ /**
+ * Returns the service level component of this
+ * version identifier.
+ *
+ * @return the service level
+ */
+ public int getServiceComponent() {
+ return service;
+ }
+ /**
+ * Returns the qualifier component of this
+ * version identifier.
+ *
+ * @return the qualifier
+ */
+ public String getQualifierComponent() {
+ return qualifier;
+ }
+ /**
+ * Compares two version identifiers to see if this one is
+ * greater than or equal to the argument.
+ * <p>
+ * A version identifier is considered to be greater than or equal
+ * if its major component is greater than the argument major
+ * component, or the major components are equal and its minor component
+ * is greater than the argument minor component, or the
+ * major and minor components are equal and its service component is
+ * greater than the argument service component, or the major, minor and
+ * service components are equal and the qualifier component is
+ * greated than the argument qualifier component (using lexicographic
+ * string comparison), or all components are equal.
+ * </p>
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is compatible with the given version identifier, and
+ * <code>false</code> otherwise
+ * @since 2.0
+ */
+ public boolean isGreaterOrEqualTo(Version id) {
+ if (id == null)
+ return false;
+ if (major > id.getMajorComponent())
+ return true;
+ if ((major == id.getMajorComponent()) && (minor > id.getMinorComponent()))
+ return true;
+ if ((major == id.getMajorComponent())
+ && (minor == id.getMinorComponent())
+ && (service > id.getServiceComponent()))
+ return true;
+ if ((major == id.getMajorComponent())
+ && (minor == id.getMinorComponent())
+ && (service == id.getServiceComponent())
+ && (qualifier.compareTo(id.getQualifierComponent()) >= 0))
+ return true;
+ else
+ return false;
+ }
+ /**
+ * Compares two version identifiers for compatibility.
+ * <p>
+ * A version identifier is considered to be compatible if its major
+ * component equals to the argument major component, and its minor component
+ * is greater than or equal to the argument minor component.
+ * If the minor components are equal, than the service level of the
+ * version identifier must be greater than or equal to the service level
+ * of the argument identifier. If the service levels are equal, the two
+ * version identifiers are considered to be equivalent if this qualifier is
+ * greated or equal to the qualifier of the argument (using lexicographic
+ * string comparison).
+ * </p>
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is compatible with the given version identifier, and
+ * <code>false</code> otherwise
+ */
+ public boolean isCompatibleWith(Version id) {
+ if (id == null)
+ return false;
+ if (major != id.getMajorComponent())
+ return false;
+ if (minor > id.getMinorComponent())
+ return true;
+ if (minor < id.getMinorComponent())
+ return false;
+ if (service > id.getServiceComponent())
+ return true;
+ if (service < id.getServiceComponent())
+ return false;
+ if (qualifier.compareTo(id.getQualifierComponent()) >= 0)
+ return true;
+ else
+ return false;
+ }
+ /**
+ * Compares two version identifiers for equivalency.
+ * <p>
+ * Two version identifiers are considered to be equivalent if their major
+ * and minor component equal and are at least at the same service level
+ * as the argument. If the service levels are equal, the two version
+ * identifiers are considered to be equivalent if this qualifier is
+ * greated or equal to the qualifier of the argument (using lexicographic
+ * string comparison).
+ *
+ * </p>
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is equivalent to the given version identifier, and
+ * <code>false</code> otherwise
+ */
+ public boolean isEquivalentTo(Version id) {
+ if (id == null)
+ return false;
+ if (major != id.getMajorComponent())
+ return false;
+ if (minor != id.getMinorComponent())
+ return false;
+ if (service > id.getServiceComponent())
+ return true;
+ if (service < id.getServiceComponent())
+ return false;
+ if (qualifier.compareTo(id.getQualifierComponent()) >= 0)
+ return true;
+ else
+ return false;
+ }
+ /**
+ * Compares two version identifiers for perfect equality.
+ * <p>
+ * Two version identifiers are considered to be perfectly equal if their
+ * major, minor, service and qualifier components are equal
+ * </p>
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is perfectly equal to the given version identifier, and
+ * <code>false</code> otherwise
+ * @since 2.0
+ */
+ public boolean isPerfect(Version id) {
+ if (id == null)
+ return false;
+ if ((major != id.getMajorComponent())
+ || (minor != id.getMinorComponent())
+ || (service != id.getServiceComponent())
+ || (!qualifier.equals(id.getQualifierComponent())))
+ return false;
+ else
+ return true;
+ }
+ /**
+ * Compares two version identifiers for order using multi-decimal
+ * comparison.
+ *
+ * @param versionId the other version identifier
+ * @return <code>true</code> is this version identifier
+ * is greater than the given version identifier, and
+ * <code>false</code> otherwise
+ */
+ public boolean isGreaterThan(Version id) {
+
+ if (id == null) {
+ if (major == 0 && minor == 0 && service == 0 && qualifier.equals(""))
+ return false; //$NON-NLS-1$
+ else
+ return true;
+ }
+
+ if (major > id.getMajorComponent())
+ return true;
+ if (major < id.getMajorComponent())
+ return false;
+ if (minor > id.getMinorComponent())
+ return true;
+ if (minor < id.getMinorComponent())
+ return false;
+ if (service > id.getServiceComponent())
+ return true;
+ if (service < id.getServiceComponent())
+ return false;
+ if (qualifier.compareTo(id.getQualifierComponent()) > 0)
+ return true;
+ else
+ return false;
+
+ }
+ /**
+ * Returns the string representation of this version identifier.
+ * The result satisfies
+ * <code>vi.equals(new PluginVersionIdentifier(vi.toString()))</code>.
+ *
+ * @return the string representation of this plug-in version identifier
+ */
+ public String toString() {
+ String base = major + SEPARATOR + minor + SEPARATOR + service;
+ // R1.0 result
+ if (qualifier.equals("")) //$NON-NLS-1$
+ return base;
+ else
+ return base + SEPARATOR + qualifier;
+ }
+
+ private static String verifyQualifier(String s) {
+ char[] chars = s.trim().toCharArray();
+ boolean whitespace = false;
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (!(Character.isLetter(c) || Character.isDigit(c))) {
+ chars[i] = '-';
+ whitespace = true;
+ }
+ }
+ return whitespace ? new String(chars) : s;
+ }
+
+ /**
+ * Compares this Version object with the specified Version object for order.
+ * Returns a negative integer, zero, or a positive integer as this object is less
+ * than, equal to, or greater than the specified object.<p>
+ *
+ * @param obj the Version object to be compared.
+ * @return a negative integer, zero, or a positive integer as this object
+ * is less than, equal to, or greater than the specified Version object.
+ *
+ * @throws ClassCastException if the specified object's type
+ * is not Version.
+ */
+ public int compareTo(Object o) {
+ if (!(o instanceof Version))
+ throw new ClassCastException();
+
+ Version version = (Version) o;
+ if (equals(o))
+ return 0;
+
+ if (isGreaterThan((Version) o))
+ return 1;
+
+ return -1;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/VersionConstraint.java b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/VersionConstraint.java
new file mode 100644
index 000000000..8c735716b
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/eclipse/osgi/service/resolver/VersionConstraint.java
@@ -0,0 +1,31 @@
+package org.eclipse.osgi.service.resolver;
+
+
+/**
+ * VersionConstraints represent the relationship between two bundles (in the
+ * case of bundle requires) or a bundle and a package (in the case of import/export).
+ */
+public interface VersionConstraint extends Cloneable {
+
+ public byte NO_MATCH = 0;
+ public byte PERFECT_MATCH = 1;
+ public byte EQUIVALENT_MATCH = 2;
+ public byte COMPATIBLE_MATCH = 3;
+ public byte GREATER_EQUAL_MATCH = 4;
+
+ public String getName();
+
+ public Version getVersionSpecification();
+
+ public Version getActualVersion();
+
+ public byte getMatchingRule();
+
+ public BundleDescription getBundle();
+
+ public BundleDescription getSupplier();
+
+ public boolean isResolved();
+
+ boolean isSatisfiedBy(Version version);
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java
new file mode 100644
index 000000000..f7cf909c4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java
@@ -0,0 +1,213 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java,v 1.1 2003/11/10 17:49:33 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+import java.security.Permission;
+import java.security.BasicPermission;
+import java.security.PermissionCollection;
+
+/**
+ * Indicates the caller's authority to perform lifecycle operations on
+ * or to get sensitive information about a bundle.
+ * <p><tt>AdminPermission</tt> has no actions or target.
+ * <p>The <tt>hashCode()</tt> method of <tt>AdminPermission</tt> is
+ * inherited from <tt>java.security.BasicPermission</tt>. The hash code it
+ * returns is the hash code of the name "AdminPermission", which is
+ * always the same for all instances of <tt>AdminPermission</tt>. <p>
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+public final class AdminPermission extends BasicPermission
+{
+ /**
+ * Creates a new <tt>AdminPermission</tt> object with its name set to
+ * "AdminPermission".
+ */
+ public AdminPermission()
+ {
+ super("AdminPermission");
+ }
+
+ /**
+ * Creates a new <tt>AdminPermission</tt> object for use by the <code>Policy</code>
+ * object to instantiate new <tt>Permission</tt> objects.
+ *
+ * @param name Ignored; always set to "AdminPermission".
+ * @param actions Ignored.
+ */
+ public AdminPermission(String name, String actions)
+ {
+ this();
+ }
+
+ /**
+ * Determines if the specified permission is implied by this object.
+ * <p>This method returns <tt>true</tt> if the specified
+ * permission is an instance of <tt>AdminPermission</tt>.
+ *
+ * @param p The permission to interrogate.
+ *
+ * @return <tt>true</tt> if the permission is an
+ * instance of this class; <tt>false</tt> otherwise.
+ */
+ public boolean implies(Permission p)
+ {
+ return(p instanceof AdminPermission);
+ }
+
+ /**
+ * Determines the equality of two <tt>AdminPermission</tt> objects. <p>Two <tt>AdminPermission</tt>
+ * objects are always equal.
+ *
+ * @param obj The object being compared for equality with this object.
+ * @return <tt>true</tt> if <tt>obj</tt> is an <tt>AdminPermission</tt>; <tt>false</tt> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ return(obj instanceof AdminPermission);
+ }
+
+ /**
+ * Returns a new <tt>PermissionCollection</tt> object suitable for storing
+ * <tt>AdminPermission</tt>s.
+ * <p>
+ *
+ * @return A new <tt>PermissionCollection</tt> object.
+ */
+
+ public PermissionCollection newPermissionCollection()
+ {
+ return(new AdminPermissionCollection());
+ }
+}
+
+/**
+ * Stores a collection of <tt>AdminPermission</tt>s.
+ *
+ */
+final class AdminPermissionCollection extends PermissionCollection
+{
+ /**
+ * True if collection is non-empty.
+ *
+ * @serial
+ */
+ private boolean hasElement;
+
+ /**
+ * Creates an empty <tt>AdminPermission</tt> object.
+ *
+ */
+ public AdminPermissionCollection()
+ {
+ hasElement = false;
+ }
+
+ /**
+ * Adds the specified permission to the <tt>AdminPermissionCollection</tt>.
+ * The key for the hash is the interface name of the service.
+ *
+ * @param permission The <tt>Permission</tt> object to add.
+ *
+ * @exception IllegalArgumentException If the permission is not an
+ * <tt>AdminPermission</tt>.
+ *
+ * @exception SecurityException If this AdminPermissionCollection
+ * object has been marked read-only.
+ */
+
+ public void add(Permission permission)
+ {
+ if (!(permission instanceof AdminPermission))
+ {
+ throw new IllegalArgumentException("invalid permission: "+
+ permission);
+ }
+
+ if (isReadOnly())
+ throw new SecurityException("attempt to add a Permission to a " +
+ "readonly PermissionCollection");
+
+ hasElement = true;
+ }
+
+ /**
+ * Determines if the specified set of permissions implies the
+ * permissions expressed in the parameter <tt>permission</tt>.
+ *
+ * @param p The Permission object to compare.
+ *
+ * @return true if permission is a proper subset of a permission in
+ * the set; false otherwise.
+ */
+
+ public boolean implies(Permission p)
+ {
+ return(hasElement && (p instanceof AdminPermission));
+ }
+
+ /**
+ * Returns an enumeration of an <tt>AdminPermission</tt> object.
+ *
+ * @return Enumeration of an <tt>AdminPermission</tt> object.
+ */
+
+ public Enumeration elements()
+ {
+ return (new Enumeration()
+ {
+ private boolean more = hasElement;
+
+ public boolean hasMoreElements()
+ {
+ return(more);
+ }
+
+ public Object nextElement()
+ {
+ if (more)
+ {
+ more = false;
+
+ return (new AdminPermission());
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+ });
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java
new file mode 100644
index 000000000..afb8656c5
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java
@@ -0,0 +1,693 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java,v 1.4 2003/11/25 16:46:04 prapicau Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.net.URL;
+
+/**
+ * An installed bundle in the Framework.
+ *
+ * <p>A <tt>Bundle</tt> object is the access point to define the life cycle
+ * of an installed bundle. Each bundle installed in the OSGi environment
+ * will have an associated <tt>Bundle</tt> object.
+ *
+ * <p>A bundle will have a unique identity, a <tt>long</tt>, chosen by the
+ * Framework. This identity will not change during the life cycle of a bundle, even when
+ * the bundle is updated. Uninstalling and then reinstalling the bundle will create
+ * a new unique identity.
+ *
+ * <p>A bundle can be in one of six states:
+ * <ul>
+ * <li>{@link #UNINSTALLED}
+ * <li>{@link #INSTALLED}
+ * <li>{@link #RESOLVED}
+ * <li>{@link #STARTING}
+ * <li>{@link #STOPPING}
+ * <li>{@link #ACTIVE}
+ * </ul>
+ * <p>Values assigned to these states have no specified ordering;
+ * they represent bit values that may be ORed together to determine if
+ * a bundle is in one of the valid states.
+ *
+ * <p>A bundle should only execute code when its state is one of
+ * <tt>STARTING</tt>, <tt>ACTIVE</tt>, or <tt>STOPPING</tt>.
+ * An <tt>UNINSTALLED</tt> bundle can not be set to another state; it
+ * is a zombie and can only be reached because invalid references are kept somewhere.
+ *
+ * <p>The Framework is the only entity that is allowed to
+ * create <tt>Bundle</tt> objects, and these objects are only valid
+ * within the Framework that created them.
+ *
+ * @version $Revision: 1.4 $
+ * @author Open Services Gateway Initiative
+ */
+public abstract interface Bundle
+{
+ /**
+ * This bundle is uninstalled and may not be used.
+ *
+ * <p>The <tt>UNINSTALLED</tt> state is only visible after a bundle
+ * is uninstalled; the bundle is in an unusable state
+ * and all references to the <tt>Bundle</tt> object should be released
+ * immediately.
+ * <p>The value of <tt>UNINSTALLED</tt> is 0x00000001.
+ */
+ public static final int UNINSTALLED = 0x00000001;
+
+ /**
+ * This bundle is installed but not yet resolved.
+ *
+ * <p>A bundle is in the <tt>INSTALLED</tt> state when it has been installed
+ * in the Framework but cannot run.
+ * <p>This state is visible if the bundle's code dependencies are not resolved.
+ * The Framework may attempt to resolve an <tt>INSTALLED</tt> bundle's
+ * code dependencies and move the bundle to the <tt>RESOLVED</tt> state.
+ * <p>The value of <tt>INSTALLED</tt> is 0x00000002.
+ */
+ public static final int INSTALLED = 0x00000002;
+
+ /**
+ * This bundle is resolved and is able to be started.
+ *
+ * <p>A bundle is in the <tt>RESOLVED</tt> state when the Framework has successfully
+ * resolved the bundle's dependencies. These dependencies include:
+ * <ul>
+ * <li>The bundle's class path from its {@link Constants#BUNDLE_CLASSPATH} Manifest header.
+ * <li>The bundle's native language code from its {@link Constants#BUNDLE_NATIVECODE} Manifest header.
+ * <li>The bundle's package dependencies from
+ * its {@link Constants#EXPORT_PACKAGE}and {@link Constants#IMPORT_PACKAGE} Manifest headers.
+ * </ul>
+ * <p>Note that the bundle is not active yet. A bundle must be put in the
+ * <tt>RESOLVED</tt> state before it can be started. The Framework may attempt to
+ * resolve a bundle at any time.
+ * <p>The value of <tt>RESOLVED</tt> is 0x00000004.
+ */
+ public static final int RESOLVED = 0x00000004;
+
+ /**
+ * This bundle is in the process of starting.
+ *
+ * <p>A bundle is in the <tt>STARTING</tt> state when the {@link #start}method
+ * is active. A bundle will be in this state when the bundle's
+ * {@link BundleActivator#start}is called. If this method completes
+ * without exception, then the bundle has successfully started and will move to the
+ * <tt>ACTIVE</tt> state.
+ * <p>The value of <tt>STARTING</tt> is 0x00000008.
+ */
+ public static final int STARTING = 0x00000008;
+
+ /**
+ * This bundle is in the process of stopping.
+ *
+ * <p>A bundle is in the <tt>STOPPING</tt> state when the {@link #stop}method
+ * is active. A bundle will be in this state when the bundle's
+ * {@link BundleActivator#stop}method is called. When this method completes
+ * the bundle is stopped and will move to the <tt>RESOLVED</tt> state.
+ * <p>The value of <tt>STOPPING</tt> is 0x00000010.
+ */
+ public static final int STOPPING = 0x00000010;
+
+ /**
+ * This bundle is now running.
+ *
+ * <p>A bundle is in the <tt>ACTIVE</tt> state when it has been successfully started.
+ * <p>The value of <tt>ACTIVE</tt> is 0x00000020.
+ */
+ public static final int ACTIVE = 0x00000020;
+
+ /**
+ * Returns this bundle's current state.
+ *
+ * <p>A bundle can be in only one state at any time.
+ *
+ * @return An element of <tt>UNINSTALLED</tt>, <tt>INSTALLED</tt>,
+ * <tt>RESOLVED</tt>, <tt>STARTING</tt>, <tt>STOPPING</tt>,
+ * <tt>ACTIVE</tt>.
+ */
+ public abstract int getState();
+
+ /**
+ * Starts this bundle.
+ *
+ * <p>The following steps are required to start a bundle:
+ * <ol>
+ * <li>If this bundle's state is <tt>UNINSTALLED</tt> then
+ * an <tt>IllegalStateException</tt> is thrown.
+ *
+ * <li>If this bundle's state is <tt>STARTING</tt> or <tt>STOPPING</tt>
+ * then this method will wait for this bundle to
+ * change state before continuing. If this does not occur
+ * in a reasonable time, a <tt>BundleException</tt> is thrown to indicate
+ * this bundle was unable to be started.
+ *
+ * <li>If this bundle's state is <tt>ACTIVE</tt> then this method returns immediately.
+ *
+ * <li>If this bundle's state is not <tt>RESOLVED</tt>,
+ * an attempt is made to resolve this bundle's package dependencies.
+ * If the Framework cannot resolve this bundle, a <tt>BundleException</tt> is thrown.
+ *
+ * <li>This bundle's state is set to <tt>STARTING</tt>.
+ *
+ * <li>The {@link BundleActivator#start}method of this
+ * bundle's <tt>BundleActivator</tt>, if one is specified, is called.
+ * If the <tt>BundleActivator</tt> is invalid or throws an exception, this bundle's state
+ * is set back to <tt>RESOLVED</tt>.
+ * <br>Any services registered by the bundle will be unregistered.
+ * <br>Any services used by the bundle will be released.
+ * <br>Any listeners registered by the bundle will be removed.
+ * <br>A <tt>BundleException</tt> is then thrown.
+ *
+ * <li> If this bundle's state is <tt>UNINSTALLED</tt>,
+ * because the bundle was uninstalled while the <tt>BundleActivator.start</tt>
+ * method was running, a <tt>BundleException</tt> is thrown.
+ *
+ * <li>Since it is recorded that this bundle has been started, when
+ * the Framework is restarted this bundle will be automatically started.
+ *
+ * <li>This bundle's state is set to <tt>ACTIVE</tt>.
+ *
+ * <li>A bundle event of type {@link BundleEvent#STARTED}is broadcast.
+ * </ol>
+ *
+ * <h5>Preconditions</h5>
+ * <ul>
+ * <li><tt>getState()</tt> in {<tt>INSTALLED</tt>}, {<tt>RESOLVED</tt>}.
+ * </ul>
+ * <h5>Postconditions, no exceptions thrown</h5>
+ * <ul>
+ * <li><tt>getState()</tt> in {<tt>ACTIVE</tt>}.
+ * <li><tt>BundleActivator.start()</tt> has been called and did not throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li><tt>getState()</tt> not in {<tt>STARTING</tt>}, {<tt>ACTIVE</tt>}.
+ * </ul>
+ *
+ * @exception BundleException If this bundle couldn't be started.
+ * This could be because a code dependency could not be resolved or
+ * the specified <tt>BundleActivator</tt> could not be loaded or threw an exception.
+ * @exception java.lang.IllegalStateException If this
+ * bundle has been uninstalled or this bundle tries to change its own state.
+ * @exception java.lang.SecurityException If the caller does not have
+ * the appropriate <tt>AdminPermisson</tt>, and the Java Runtime Environment
+ * supports permissions.
+ */
+ public abstract void start() throws BundleException;
+
+ /**
+ * Stops this bundle.
+ *
+ * </ul>
+ * <p> The following steps are required to stop a bundle:
+ * <ol>
+ * <li>If this bundle's state is <tt>UNINSTALLED</tt> then
+ * an <tt>IllegalStateException</tt> is thrown.
+ *
+ * <li>If this bundle's state is <tt>STARTING</tt> or <tt>STOPPING</tt>
+ * then this method will wait for this bundle to
+ * change state before continuing. If this does not occur
+ * in a reasonable time, a <tt>BundleException</tt> is thrown to indicate
+ * this bundle was unable to be stopped.
+ *
+ * <li>If this bundle's state is not <tt>ACTIVE</tt> then this method returns immediately.
+ *
+ * <li> This bundle's state is set to <tt>STOPPING</tt>.
+ *
+ * <li>Since it is recorded that this bundle has been stopped,
+ * Framework is restarted this bundle will not be automatically started.
+ *
+ * <li>The {@link BundleActivator#stop}method of this
+ * bundle's <tt>BundleActivator</tt>, if one is specified, is called.
+ * If this method throws an exception, it will continue to stop this bundle.
+ * A <tt>BundleException</tt> will be thrown after completion of the
+ * remaining steps.
+ *
+ * <li>Any services registered by this bundle will be unregistered.
+ * <li>Any services used by this bundle will be released.
+ * <li>Any listeners registered by this bundle will be removed.
+ *
+ * <li> If this bundle's state is <tt>UNINSTALLED</tt>,
+ * because the bundle was uninstalled while the <tt>BundleActivator.stop</tt>
+ * method was running, a <tt>BundleException</tt> is thrown.
+ *
+ * <li>This bundle's state is set to <tt>RESOLVED</tt>.
+ *
+ * <li>A bundle event of type {@link BundleEvent#STOPPED}is broadcast.
+ * </ol>
+ *
+ * <h5>Preconditions</h5>
+ * <ul>
+ * <li><tt>getState()</tt> in {<tt>ACTIVE</tt>}.
+ * </ul>
+ * <h5>Postconditions, no exceptions thrown</h5>
+ * <ul>
+ * <li><tt>getState()</tt> not in {<tt>ACTIVE</tt>, <tt>STOPPING</tt>}.
+ * <li><tt>BundleActivator.stop</tt> has been called and did not throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>None.
+ * </ul>
+ *
+ * @exception BundleException If this bundle's
+ * <tt>BundleActivator</tt> could not be loaded or threw an exception.
+ * @exception java.lang.IllegalStateException If this
+ * bundle has been uninstalled or this bundle tries to change its own state.
+ * @exception java.lang.SecurityException If the caller does not have
+ * the appropriate <tt>AdminPermission</tt>, and the Java Runtime Environment
+ * supports permissions.
+ */
+ public abstract void stop() throws BundleException;
+
+ /**
+ * Updates this bundle.
+ *
+ * <p>If this bundle's state is <tt>ACTIVE</tt>, it will be stopped
+ * before the update and started after the update successfully completes.
+ *
+ * <p>If the bundle being updated has exported any packages, these
+ * packages will not be updated. Instead, the previous package version will remain
+ * exported until the <tt>PackageAdmin.refreshPackages</tt> method has been
+ * has been called or the Framework is relaunched.
+ *
+ * <p> The following steps are required to update a bundle:
+ * <ol>
+ * <li>If this bundle's state is <tt>UNINSTALLED</tt> then
+ * an <tt>IllegalStateException</tt> is thrown.
+ *
+ * <li>If this bundle's state is <tt>ACTIVE</tt>, <tt>STARTING</tt> or <tt>STOPPING</tt>,
+ * the bundle is stopped as described in the <tt>Bundle.stop</tt> method.
+ * If <tt>Bundle.stop</tt> throws an exception, the exception is rethrown
+ * terminating the update.
+ *
+ * <li>The download location of the new version of this bundle
+ * is determined from either the bundle's {@link Constants#BUNDLE_UPDATELOCATION} Manifest
+ * header (if available) or the bundle's original location.
+ *
+ * <li>The location is interpreted in an implementation dependent manner,
+ * typically as a URL, and the new version of this bundle is obtained from
+ * this location.
+ *
+ * <li>The new version of this bundle is installed. If the Framework is
+ * unable to install the new version of this bundle, the original version
+ * of this bundle will be restored and a <tt>BundleException</tt> will be thrown
+ * after completion of the remaining steps.
+ *
+ * <li>This bundle's state is set to <tt>INSTALLED</tt>.
+ *
+ * <li> If this bundle has not declared an <tt>Import-Package</tt> header
+ * in its Manifest file (specifically, this bundle does not depend on any packages from
+ * other bundles), this bundle's state may be set to <tt>RESOLVED</tt>.
+ *
+ * <li>If the new version of this bundle was successfully installed,
+ * a bundle event of type {@link BundleEvent#UPDATED}is broadcast.
+ *
+ * <li>If this bundle's state was originally <tt>ACTIVE</tt>,
+ * the updated bundle is started as described in the <tt>Bundle.start</tt> method.
+ * If <tt>Bundle.start</tt> throws an exception, a Framework event of
+ * type {@link FrameworkEvent#ERROR}is broadcast containing the exception.
+ * </ol>
+ *
+ * <h5>Preconditions</h5>
+ * <ul>
+ * <li><tt>getState()</tt> not in {<tt>UNINSTALLED</tt>}.
+ * </ul>
+ * <h5>Postconditions, no exceptions thrown</h5>
+ * <ul>
+ * <li><tt>getState()</tt> in
+ * {<tt>INSTALLED</tt>, <tt>RESOLVED</tt>, <tt>ACTIVE</tt>}.
+ * <li>This bundle has been updated.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li><tt>getState()</tt> in {<tt>INSTALLED</tt>, <tt>RESOLVED</tt>, <tt>ACTIVE</tt>}.
+ * <li>Original bundle is still used; no update occurred.
+ * </ul>
+ *
+ * @exception BundleException If the update fails.
+ * @exception java.lang.IllegalStateException If this
+ * bundle has been uninstalled or this bundle tries to change its own state.
+ * @exception java.lang.SecurityException If the caller does not have
+ * the appropriate <tt>AdminPermission</tt>, and the Java Runtime Environment
+ * supports permissions.
+ * @see #stop()
+ * @see #start()
+ */
+ public abstract void update() throws BundleException;
+
+ /**
+ * Updates this bundle from an <tt>InputStream</tt>.
+ *
+ * <p>This method performs all the steps listed in <tt>Bundle.update()</tt>, except the bundle
+ * will be read from the supplied <tt>InputStream</tt>, rather than a <tt>URL</tt>.
+ * <p>This method will always close the <tt>InputStream</tt>
+ * when it is done, even if an exception is thrown.
+ *
+ * @param in The <tt>InputStream</tt> from which to read the new bundle.
+ * @see #update()
+ */
+ public abstract void update(InputStream in) throws BundleException;
+
+ /**
+ * Uninstalls this bundle.
+ *
+ * <p>This method causes the Framework to notify other bundles that this bundle
+ * is being uninstalled, and then puts this bundle into the <tt>UNINSTALLED</tt>
+ * state. The Framework will remove any resources related to this
+ * bundle that it is able to remove.
+ *
+ * <p>If this bundle has exported any packages, the Framework will
+ * continue to make these packages available to their importing bundles
+ * until the <tt>PackageAdmin.refreshPackages</tt> method has been called
+ * or the Framework is relaunched.
+ *
+ * <p>The following steps are required to uninstall a bundle:
+ * <ol>
+ * <li>If this bundle's state is <tt>UNINSTALLED</tt> then
+ * an <tt>IllegalStateException</tt> is thrown.
+ *
+ * <li>If this bundle's state is <tt>ACTIVE</tt>, <tt>STARTING</tt> or <tt>STOPPING</tt>,
+ * this bundle is stopped as described in the <tt>Bundle.stop</tt> method.
+ * If <tt>Bundle.stop</tt> throws an exception, a Framework event of type
+ * {@link FrameworkEvent#ERROR}is broadcast containing the exception.
+ *
+ * <li>This bundle's state is set to <tt>UNINSTALLED</tt>.
+ *
+ * <li>A bundle event of type {@link BundleEvent#UNINSTALLED}is broadcast.
+ *
+ * <li>This bundle and any persistent storage area provided for this bundle
+ * by the Framework are removed.
+ * </ol>
+ *
+ * <h5>Preconditions</h5>
+ * <ul>
+ * <li><tt>getState()</tt> not in {<tt>UNINSTALLED</tt>}.
+ * </ul>
+ * <h5>Postconditions, no exceptions thrown</h5>
+ * <ul>
+ * <li><tt>getState()</tt> in {<tt>UNINSTALLED</tt>}.
+ * <li>This bundle has been uninstalled.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li><tt>getState()</tt> not in {<tt>UNINSTALLED</tt>}.
+ * <li>This Bundle has not been uninstalled.
+ * </ul>
+ *
+ * @exception BundleException If the uninstall failed.
+ * @exception java.lang.IllegalStateException If this
+ * bundle has been uninstalled or this bundle tries to change its own state.
+ * @exception java.lang.SecurityException If the caller does not have
+ * the appropriate <tt>AdminPermission</tt>, and the Java Runtime Environment
+ * supports permissions.
+ * @see #stop()
+ */
+ public abstract void uninstall() throws BundleException;
+
+ /**
+ * Returns this bundle's Manifest headers and values.
+ * This method returns all the Manifest headers and values
+ * from the main section of the bundle's Manifest file; that is, all lines prior
+ * to the first blank line.
+ *
+ * <p>Manifest header names are case-insensitive. The methods of the returned
+ * <tt>Dictionary</tt> object will operate on header names in a case-insensitive manner.
+ *
+ * <p>For example, the following Manifest headers and values are included
+ * if they are present in the Manifest file:
+ * <pre>
+ * Bundle-Name
+ * Bundle-Vendor
+ * Bundle-Version
+ * Bundle-Description
+ * Bundle-DocURL
+ * Bundle-ContactAddress
+ * </pre>
+ * <p>This method will continue to return Manifest header information
+ * while this bundle is in the <tt>UNINSTALLED</tt> state.
+ *
+ * @return A <tt>Dictionary</tt> object containing this bundle's Manifest headers and values.
+ *
+ * @exception java.lang.SecurityException If the caller does not have
+ * the <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ */
+ public abstract Dictionary getHeaders();
+
+ /**
+ * Returns this bundle's identifier. The bundle is assigned a unique identifier by the Framework
+ * when it is installed in the OSGi environment.
+ *
+ * <p>A bundle's unique identifier has the following attributes:
+ * <ul>
+ * <li>Is unique and persistent.
+ * <li>Is a <tt>long</tt>.
+ * <li>Its value is not reused for another bundle, even after the bundle is uninstalled.
+ * <li>Does not change while the bundle remains installed.
+ * <li>Does not change when the bundle is updated.
+ * </ul>
+ *
+ * <p>This method will continue to return this bundle's unique identifier
+ * while this bundle is in the <tt>UNINSTALLED</tt> state.
+ *
+ * @return The unique identifier of this bundle.
+ */
+ public abstract long getBundleId();
+
+ /**
+ * Returns this bundle's location identifier.
+ *
+ * <p>The bundle location identifier is the location passed to
+ * {@link BundleContext#installBundle}when a bundle is installed.
+ *
+ * <p>This method will continue to return this bundle's location
+ * identifier while this bundle is in the <tt>UNINSTALLED</tt> state.
+ *
+ * @return The string representation of this bundle's location identifier.
+ * @exception java.lang.SecurityException If the caller does not have
+ * the appropriate <tt>AdminPermission</tt>, and the Java Runtime Environment
+ * supports permissions.
+ */
+ public abstract String getLocation();
+
+ /**
+ * Returns this bundle's <tt>ServiceReference</tt> list for all
+ * services it has registered or <tt>null</tt> if this bundle
+ * has no registered services.
+ *
+ * <p>If the Java runtime supports permissions, a <tt>ServiceReference</tt> object to a
+ * service is included in the returned list only if the
+ * caller has the <tt>ServicePermission</tt> to get the service
+ * using at least one of the named classes the service was registered under.
+ *
+ * <p>The list is valid at the time of the call to this method, however, as the Framework is a very dynamic
+ * environment, services can be modified or unregistered at anytime.
+ *
+ * @return An array of <tt>ServiceReference</tt> objects or <tt>null</tt>.
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ * @see ServiceRegistration
+ * @see ServiceReference
+ * @see ServicePermission
+ */
+ public abstract ServiceReference[] getRegisteredServices();
+
+ /**
+ * Returns this bundle's <tt>ServiceReference</tt> list for all
+ * services it is using or returns <tt>null</tt> if this bundle is not using any services.
+ * A bundle is considered to be using a service if its use count for that service is greater than zero.
+ *
+ * <p>If the Java Runtime Environment supports permissions,
+ * a <tt>ServiceReference</tt> object to a service is included in the returned list only if the
+ * caller has the <tt>ServicePermission</tt> to get the service
+ * using at least one of the named classes the service was registered under.
+ * <p>The list is valid at the time of the call to this method, however, as the Framework is a very dynamic
+ * environment, services can be modified or unregistered at anytime.
+ *
+ * @return An array of <tt>ServiceReference</tt>s or <tt>null</tt>.
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ * @see ServiceReference
+ * @see ServicePermission
+ */
+ public abstract ServiceReference[] getServicesInUse();
+
+ /**
+ * Determines if this bundle has the specified permissions.
+ *
+ * <p>If the Java Runtime Environment does not support permissions,
+ * this method always returns <tt>true</tt>.
+ * <p><tt>permission</tt> is of type <tt>Object</tt> to
+ * avoid referencing the <tt>java.security.Permission</tt> class
+ * directly. This is to allow the Framework to be implemented in Java
+ * environments which do not support permissions.
+ *
+ * <p>If the Java Runtime Environment does support permissions, this
+ * bundle and all its resources including nested JAR files, belong
+ * to the same <tt>java.security.ProtectionDomain</tt>; that is, they will share
+ * the same set of permissions.
+ *
+ * @param permission The permission to verify.
+ *
+ * @return <tt>true</tt> if this bundle has the specified permission or
+ * the permissions possessed by this bundle imply the specified permission;
+ * <tt>false</tt> if this bundle does not have the specified permission or <tt>permission</tt>
+ * is not an <tt>instanceof</tt> <tt>java.security.Permission</tt>.
+ *
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ */
+ public abstract boolean hasPermission(Object permission);
+
+ /**
+ * Find the specified resource in this bundle.
+ *
+ * This bundle's class loader is called to search for the named resource.
+ * If this bundle's state is <tt>INSTALLED</tt>, then only this bundle will
+ * be searched for the specified resource. Imported packages cannot be searched
+ * when a bundle has not been resolved.
+ *
+ * @param name The name of the resource.
+ * See <tt>java.lang.ClassLoader.getResource</tt> for a description of
+ * the format of a resource name.
+ * @return a URL to the named resource, or <tt>null</tt> if the resource could
+ * not be found or if the caller does not have
+ * the <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ *
+ * @since 1.1
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ */
+ public abstract URL getResource(String name);
+
+ //================================================================
+ // Equinox Addenda
+ //================================================================
+
+ /**
+ * This method returns the global name of this bundle as specified
+ * by its Bundle-GlobalName manifest header. The name must be unique,
+ * it is recommended to use the same naming convention as Java Packages.
+ * This method does not require any security permission to access a
+ * bundle’s global name. If the bundle does not have a specified
+ * global name then null is returned.
+ *
+ * @return The global name of this bundle.
+ */
+ public String getGlobalName();
+
+ /**
+ * This method returns the list of attached fragment bundles. If the
+ * bundle is a fragment then null is returned. If no fragments are
+ * attached then null is returned. This method does not require any
+ * security permission to access.
+ *
+ * @return an array of fragment bundles or null if the bundle does not
+ * have any fragments.
+ */
+ public abstract org.osgi.framework.Bundle[] getFragments();
+
+ /**
+ * This method indicates that this bundle is a fragment bundle or not.
+ * True is returned if the bundle is a fragment; otherwise false is returned.
+ * This method does not require any security permission to access.
+ * @return true if the bundle is a fragment; otherwise false is returned.
+ */
+ public boolean isFragment();
+
+ /**
+ * This method returns the host bundle for this bundle or null if the
+ * bundle is not attached to a host. If the bundle is not a fragment
+ * bundle then null is returned. This method does not require any
+ * security permission to access.
+ * @return the host bundle or null if the bundle does not have a host.
+ */
+ public org.osgi.framework.Bundle getHost();
+
+ /**
+ *
+ * This method loads the specified class using this bundle’s classloader.
+ * If this bundle's state is INSTALLED, this method will attempt to
+ * resolve the bundle before attempting to load the class.
+ * <p>
+ * If the bundle cannot be resolved, the Framework must publish a
+ * Framework Event of type ERROR containing a BundleException with
+ * details of the reason the bundle could not be resolved.
+ * This method must then throw a ClassNotFoundException.
+ * <p>
+ * If this bundle's state is UNINSTALLED, then an
+ * IllegalStateException is thrown.
+ *
+ * @param classname name of the class to find.
+ * @return the loaded Class.
+ * @throws ClassNotFoundException if no such class can be found or
+ * if the caller does not have the <tt>AdminPermission</tt>, and the Java
+ * Runtime Environment supports permissions.
+ */
+ public Class loadClass(String classname) throws ClassNotFoundException;
+
+ /**
+ * This method returns enumeration of all the paths to entries within the
+ * bundle whose longest sub-path matches the supplied path argument.
+ * Returned paths indicating subdirectory paths end with a ’/’.
+ * The returned paths are all relative to the root of the bundle and
+ * have a leading ’/’.
+ * <p>
+ * This method returns an empty enumeration if no entries could not be
+ * found matching the specified path or if the caller does not have
+ * the AdminPermission or BundlePermission[READ, <target bundle global name>],
+ * and the Java Runtime Environment supports permissions.
+ *
+ * @param path the path name to get the entry path names for.
+ * @return An Enumeration of the entry paths that are contained in the
+ * specified path.
+ */
+ public Enumeration getEntryPaths(java.lang.String path);
+
+ /**
+ * This method returns a URL to the specified entry in this bundle.
+ * The bundle's classloader is not used to search for the named entry.
+ * Only the content of the bundle is searched for the named entry.
+ * This method returns a URL to the named entry, or null if the entry
+ * could not be found or if the caller does not have the AdminPermission
+ * or BundlePermission[READ, <target bundle global name>],
+ * and the Java Runtime Environment supports permissions.
+ *
+ * @param name The name of the resource.
+ * See <tt>java.lang.ClassLoader.getResource</tt> for a description of
+ * the format of a resource name.
+ * @return a URL to the named resource, or <tt>null</tt> if the resource could
+ * not be found or if the caller does not have
+ * the <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ *
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ */
+ public URL getEntry(String name) throws IOException;
+
+}
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleActivator.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleActivator.java
new file mode 100644
index 000000000..e96ffe6b4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleActivator.java
@@ -0,0 +1,92 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleActivator.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+/**
+ * Customizes the starting and stopping of this bundle.
+ * <p><tt>BundleActivator</tt> is an interface that may be implemented
+ * when this bundle is started or stopped. The Framework can create instances of this bundle's
+ * <tt>BundleActivator</tt> as required. If an instance's <tt>BundleActivator.start</tt>
+ * method executes successfully, it is guaranteed that the same instance's <tt>BundleActivator.stop</tt>
+ * method will be called when this bundle is to be stopped.
+ *
+ * <p><tt>BundleActivator</tt> is specified through the
+ * <tt>Bundle-Activator</tt> Manifest header. A bundle can only specify
+ * a single <tt>BundleActivator</tt> in the Manifest file.
+ * The form of the Manifest header is:
+ * <pre>
+ * Bundle-Activator: <i>class-name</i>
+ * </pre>
+ * where <code>class-name</code> is a fully qualified Java classname.
+ * <p>The specified <tt>BundleActivator</tt> class must have a public constructor
+ * that takes no parameters so that a <tt>BundleActivator</tt> object can
+ * be created by <tt>Class.newInstance()</tt>.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+public abstract interface BundleActivator
+{
+ /**
+ * Called when this bundle is started so the Framework can perform the bundle-specific
+ * activities necessary to start this bundle. This method can be used to register services or to
+ * allocate any resources that this bundle needs.
+ *
+ * <p>This method must complete and return to its caller in a timely manner.
+ *
+ * @param context The execution context of the bundle being started.
+ * @exception java.lang.Exception If this method throws an exception, this bundle is marked
+ * as stopped and the Framework will remove this bundle's listeners, unregister all services
+ * registered by this bundle, and release all services used by this bundle.
+ * @see Bundle#start
+ */
+ public abstract void start(BundleContext context) throws Exception;
+
+ /**
+ * Called when this bundle is stopped so the Framework can perform the bundle-specific
+ * activities necessary to stop the bundle.
+ * In general, this method should undo the work that the <tt>BundleActivator.start</tt> method started.
+ * There should be no active threads that were started by this bundle when this bundle returns.
+ * A stopped bundle should be stopped and should not call any Framework objects.
+ *
+ * <p>This method must complete and return to its caller in a timely manner.
+ *
+ * @param context The execution context of the bundle being stopped.
+ * @exception java.lang.Exception If this method throws an exception,
+ * the bundle is still marked as stopped, and the Framework will
+ * remove the bundle's listeners, unregister all services
+ * registered by the bundle, and release all services
+ * used by the bundle.
+ * @see Bundle#stop
+ */
+ public abstract void stop(BundleContext context) throws Exception;
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleContext.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleContext.java
new file mode 100644
index 000000000..ca25d5ede
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleContext.java
@@ -0,0 +1,643 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleContext.java,v 1.3 2003/11/25 15:44:22 twatson Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Dictionary;
+
+/**
+ * A bundle's execution context within the Framework.
+ * The context is used to grant access to other methods so that this bundle can interact
+ * with the Framework.
+ *
+ * <p><tt>BundleContext</tt> methods allow a bundle to:
+ * <ul>
+ * <li>Subscribe to events published by the Framework.
+ * <li>Register services in the Framework service registry.
+ * <li>Retrieve <tt>ServiceReferences</tt> from the Framework service registry.
+ * <li>Get and release service objects for a referenced service.
+ * <li>Install new bundles in the Framework.
+ * <li>Get the list of bundles installed in the Framework.
+ * <li>Get the {@link Bundle}object for a bundle.
+ * <li>Create <code>File</code> objects for files in a
+ * persistent storage area provided for the bundle by the Framework.
+ * </ul>
+ *
+ * <p>A <tt>BundleContext</tt> object will be created and provided
+ * to this bundle when it is started using the {@link BundleActivator#start}method.
+ * The same <tt>BundleContext</tt> object will be passed to this bundle when it is
+ * stopped using the {@link BundleActivator#stop}method.
+ * <tt>BundleContext</tt> is generally for the private use of this bundle and
+ * is not meant to be shared with other bundles in the OSGi environment. <tt>BundleContext</tt> is used
+ * when resolving <tt>ServiceListener</tt>s and <tt>EventListener</tt>s.
+ *
+ * <p> The <tt>BundleContext</tt> object is only valid during an execution instance of
+ * this bundle; that is, during the period from when this bundle is called by
+ * <tt>BundleActivator.start</tt> until after this bundle is called and returns from
+ * <tt>BundleActivator.stop</tt> (or if <tt>BundleActivator.start</tt> terminates with an exception).
+ * If the <tt>BundleContext</tt> object is used subsequently,
+ * an <tt>IllegalStateException</tt> may be thrown.
+ * When this bundle is restarted, a new <tt>BundleContext</tt> object will be created.
+ *
+ * <p>The Framework is the only entity that can create <tt>BundleContext</tt>
+ * objects and they are only valid within the Framework that created them.
+ *
+ * <p>Note: A single virtual machine may host multiple Framework instances at any
+ * given time, but objects created by one Framework instance cannot be used by bundles
+ * running in the execution context of another Framework instance.
+ *
+ * @version $Revision: 1.3 $
+ * @author Open Services Gateway Initiative
+ */
+
+public abstract interface BundleContext {
+ /**
+ * Returns the value of the specified environment property.
+ * <p>The following standard property keys are valid:
+ * <p>
+ * <dl>
+ * <dt>{@link Constants#FRAMEWORK_VERSION}
+ * <dd>The OSGi Framework version.
+ * <dt>{@link Constants#FRAMEWORK_VENDOR}
+ * <dd>The Framework implementation vendor.
+ * <dt>{@link Constants#FRAMEWORK_LANGUAGE}
+ * <dd>The language being used.
+ * See ISO 639 for possible values.
+ * <dt>{@link Constants#FRAMEWORK_OS_NAME}
+ * <dd>The host computer operating system.
+ * <dt>{@link Constants#FRAMEWORK_OS_VERSION}
+ * <dd>The host computer operating system version number.
+ * <dt>{@link Constants#FRAMEWORK_PROCESSOR}
+ * <dd>The host computer processor name.
+ * </dl>
+ *
+ * <p>Note: The last four standard properties are used by the
+ * {@link Constants#BUNDLE_NATIVECODE}<tt>Manifest</tt> header's matching
+ * algorithm for selecting native language code.
+ *
+ * @param key The name of the requested property.
+ * @return The value of the requested property, or <code>null</code> if the property is undefined.
+ */
+ public abstract String getProperty(String key);
+
+ /**
+ * Returns the <tt>Bundle</tt> object for this context bundle.
+ *
+ * <p>The context bundle is defined as the bundle that was assigned
+ * this <tt>BundleContext</tt> in its <tt>BundleActivator</tt>.
+ *
+ * @return The context bundle's <tt>Bundle</tt> object.
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ */
+ public abstract Bundle getBundle();
+
+ /**
+ * Installs the bundle from the specified location string. A bundle is obtained
+ * from <tt>location</tt> as interpreted by the Framework in an
+ * implementation dependent manner.
+ * <p>Every installed bundle is uniquely identified by its location string, typically
+ * in the form of a URL.
+ *
+ * <p>The following steps are required to install a bundle:
+ * <ol>
+ * <li>If a bundle containing the same location string is already installed,
+ * the <tt>Bundle</tt> object for that bundle is returned.
+ *
+ * <li>The bundle's content is read from the location string. If this fails,
+ * a {@link BundleException}is thrown.
+ *
+ * <li>The bundle's <tt>Bundle-ClassPath</tt> and <tt>Bundle-NativeCode</tt>
+ * dependencies are resolved. If this fails, a <tt>BundleException</tt> is thrown.
+ *
+ * <li>The bundle's associated resources are allocated. The associated resources minimally
+ * consist of a unique identifier, and a persistent storage area if the platform has file
+ * system support. If this step fails, a <tt>BundleException</tt> is thrown.
+ *
+ * <li>The bundle's state is set to <tt>INSTALLED</tt>.
+ *
+ * <li>If the bundle has not declared an Import-Package Manifest header
+ * (that is, the bundle does not depend on any packages from other OSGi bundles), the
+ * bundle's state may be set to <tt>RESOLVED</tt>.
+ *
+ * <li>A bundle event of type {@link BundleEvent#INSTALLED}is broadcast.
+ *
+ * <li>The <tt>Bundle</tt> object for the newly installed bundle is returned.
+ * </ol>
+ *
+ * <h5>Postconditions, no exceptions thrown</h5>
+ * <ul>
+ * <li><tt>getState()</tt> in {<tt>INSTALLED</tt>}, <tt>RESOLVED</tt>}.
+ * <li>Bundle has a unique ID.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>Bundle is not installed and no trace of the bundle exists.
+ * </ul>
+ *
+ * @param location The location identifier of the bundle to install.
+ * @return The <tt>Bundle</tt> object of the installed bundle.
+ * @exception BundleException If the installation failed.
+ * @exception java.lang.SecurityException If the caller does not have
+ * the appropriate <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ */
+ public abstract Bundle installBundle(String location)
+ throws BundleException;
+
+ /**
+ * Installs the bundle from the specified <tt>InputStream</tt> object.
+ *
+ * <p>This method performs all of the steps listed in <tt>BundleContext.installBundle(String location)</tt>,
+ * except that the bundle's content will be read from the <tt>InputStream</tt> object.
+ * The location identifier string specified will be used as the identity of the bundle.
+ *
+ * <p>This method will always close the <tt>InputStream</tt> object, even if an exception is thrown.
+ * @param location The location identifier of the bundle to install.
+ * @param in The <tt>InputStream</tt> object from which this bundle will be read.
+ * @return The <tt>Bundle</tt> object of the installed bundle.
+ * @exception BundleException If the provided stream cannot be read.
+ * @see #installBundle(java.lang.String)
+ */
+ public abstract Bundle installBundle(String location, InputStream in)
+ throws BundleException;
+
+ /**
+ * Returns the bundle with the specified identifier.
+ *
+ * @param id The identifier of the bundle to retrieve.
+ * @return A <tt>Bundle</tt> object, or <tt>null</tt> if the identifier does not match any installed bundle.
+ */
+ public abstract Bundle getBundle(long id);
+
+ /**
+ * Returns a list of all installed bundles. <p>This method returns a list
+ * of all bundles installed in the OSGi environment at the time
+ * of the call to this method. However, as the Framework is a very dynamic
+ * environment, bundles can be installed or uninstalled at anytime.
+ *
+ * @return An array of <tt>Bundle</tt> objects; one object per installed bundle.
+ */
+ public abstract Bundle[] getBundles();
+
+ /**
+ * Adds the specified <tt>ServiceListener</tt> object with the specified
+ * <tt>filter</tt> to this context bundle's list of listeners. <p>See
+ * {@link #getBundle}for a definition of context bundle, and
+ * {@link Filter}for a description of the filter syntax.
+ * <tt>ServiceListener</tt> objects are notified when a service has a lifecycle state
+ * change.
+ *
+ * <p>If this context bundle's list of listeners already contains a
+ * listener <tt>l</tt> such that <tt>(l==listener)</tt>, this method
+ * replaces that listener's filter (which may be <tt>null</tt>) with the
+ * specified one (which may be <tt>null</tt>).
+ *
+ * <p>The listener is called if the filter criteria is met.
+ * To filter based upon the class of the service, the filter
+ * should reference the {@link Constants#OBJECTCLASS}property.
+ * If <tt>filter</tt> is <tt>null</tt>, all services
+ * are considered to match the filter.
+ *
+ * <p>If the Java Runtime Environment supports permissions, the
+ * <tt>ServiceListener</tt> object will be notified of a service event only
+ * if the bundle that is registering it has the <tt>ServicePermission</tt>
+ * to get the service using at least one of the named classes the service was registered under.
+ *
+ * @param listener The <tt>ServiceListener</tt> object to be added.
+ * @param filter The filter criteria.
+ *
+ * @exception InvalidSyntaxException If </tt>filter</tt> contains
+ * an invalid filter string which cannot be parsed.
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ *
+ * @see ServiceEvent
+ * @see ServiceListener
+ * @see ServicePermission
+ */
+ public abstract void addServiceListener(
+ ServiceListener listener,
+ String filter)
+ throws InvalidSyntaxException;
+
+ /**
+ * Adds the specified <tt>ServiceListener</tt> object to this context bundle's list of
+ * listeners.
+ *
+ * <p> This method is the same as calling <tt>BundleContext.addServiceListener(ServiceListener listener,
+ * String filter)</tt> with <tt>filter</tt> set to <tt>null</tt>.
+ *
+ * @param listener The <tt>ServiceListener</tt> object to be added.
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ *
+ * @see #addServiceListener(ServiceListener, String)
+ */
+ public abstract void addServiceListener(ServiceListener listener);
+
+ /**
+ * Removes the specified <tt>ServiceListener</tt> object from this context bundle's list of listeners.
+ * See {@link #getBundle}for a definition of context bundle.
+ *
+ * <p>If <tt>listener</tt> is not contained in this context bundle's list
+ * of listeners, this method does nothing.
+ *
+ * @param listener The <tt>ServiceListener</tt> to be removed.
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ */
+ public abstract void removeServiceListener(ServiceListener listener);
+
+ /**
+ * Adds the specified <tt>BundleListener</tt> object to this context bundle's
+ * list of listeners if not already present.
+ * See {@link #getBundle}for a definition of context bundle.
+ * BundleListener objects are notified when a bundle has a lifecycle state change.
+ *
+ * <p>If this context bundle's list of listeners already contains a
+ * listener <tt>l</tt> such that <tt>(l==listener)</tt>, this method does
+ * nothing.
+ *
+ * @param listener The <tt>BundleListener</tt> to be added.
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ *
+ * @see BundleEvent
+ * @see BundleListener
+ */
+ public abstract void addBundleListener(BundleListener listener);
+
+ /**
+ * Removes the specified <tt>BundleListener</tt> object from this context bundle's list of listeners.
+ * See {@link #getBundle}for a definition of context bundle.
+ *
+ * <p> If <tt>listener</tt> is not contained in this context bundle's list
+ * of listeners, this method does nothing.
+ *
+ * @param listener The <tt>BundleListener</tt> object to be removed.
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ */
+ public abstract void removeBundleListener(BundleListener listener);
+
+ /**
+ * Adds the specified <tt>FrameworkListener</tt> object to this context bundle's
+ * list of listeners if not already present.
+ * See {@link #getBundle}for a definition of context bundle.
+ * FrameworkListeners are notified of general Framework events.
+ *
+ * <p> If this context bundle's list of listeners already contains a
+ * listener <tt>l</tt> such that <tt>(l==listener)</tt>, this method does
+ * nothing.
+ *
+ * @param listener The <tt>FrameworkListener</tt> object to be added.
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ *
+ * @see FrameworkEvent
+ * @see FrameworkListener
+ */
+ public abstract void addFrameworkListener(FrameworkListener listener);
+
+ /**
+ * Removes the specified <tt>FrameworkListener</tt> object from this context
+ * bundle's list of listeners.
+ * See {@link #getBundle}for a definition of context bundle.
+ *
+ * <p> If <tt>listener</tt> is not contained in this context bundle's list
+ * of listeners, this method does nothing.
+ *
+ * @param listener The <tt>FrameworkListener</tt> object to be removed.
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ */
+ public abstract void removeFrameworkListener(FrameworkListener listener);
+
+ /**
+ * Registers the specified service object with the specified properties
+ * under the specified class names into the Framework.
+ * A <tt>ServiceRegistration</tt> object is returned.
+ * The <tt>ServiceRegistration</tt> object is for the private use of the
+ * bundle registering the service and should not be shared with other
+ * bundles.
+ * The registering bundle is defined to be the context bundle.
+ * See {@link #getBundle}for a definition of context bundle.
+ * Other bundles can locate the service by using either the
+ * {@link #getServiceReferences}or {@link #getServiceReference}method.
+ *
+ * <p>A bundle can register a service object that implements the
+ * {@link ServiceFactory}interface to have more flexibility in providing service objects to other
+ * bundles.
+ *
+ * <p>The following steps are required to register a service:
+ * <ol>
+ * <li>If </tt>service</tt> is not a <tt>ServiceFactory</tt>,
+ * an <tt>IllegalArgumentException</tt> is thrown if <tt>service</tt> is not an
+ * <code>instanceof</code> all the classes named.
+ * <li>The Framework adds these service properties to the specified
+ * <tt>Dictionary</tt> (which may be <tt>null</tt>):
+ * a property named {@link Constants#SERVICE_ID}identifying the
+ * registration number of the service, and a property named
+ * {@link Constants#OBJECTCLASS}containing all the specified
+ * classes. If any of these properties have already been specified by the
+ * registering bundle, their values will be overwritten by the Framework.
+ * <li>The service is added to the Framework service registry and may now be used by other bundles.
+ * <li>A service event of type {@link ServiceEvent#REGISTERED}is synchronously sent.
+ * <li>A <tt>ServiceRegistration</tt> object for this registration is returned.
+ * </ol>
+ *
+ * @param clazzes The class names under which the service can be located.
+ * The class names in this array will be stored in the service's properties under the key
+ * {@link Constants#OBJECTCLASS}.
+ * @param service The service object or a <tt>ServiceFactory</tt> object.
+ * @param properties The properties for this service. The keys in the properties object must
+ * all be Strings. See {@link Constants}for a list of standard service property keys.
+ * Changes should not be made to this object after calling this method.
+ * To update the service's properties the {@link ServiceRegistration#setProperties}method must be called.
+ * <tt>properties</tt> may be <code>null</code> if the service has no properties.
+ *
+ * @return A <tt>ServiceRegistration</tt> object for use by the bundle
+ * registering the service to update the service's properties or to unregister the service.
+ *
+ * @exception java.lang.IllegalArgumentException If one of the following is true:
+ * <ul>
+ * <li><tt>service</tt> is null.
+ * <li><tt>service</tt> is not a <tt>ServiceFactory</tt> object and is not an
+ * <tt>instanceof</tt> all the named classes in <tt>clazzes</tt>.
+ * <li><tt>properties</tt> contains case variants of the same key name.
+ * </ul>
+ *
+ * @exception java.lang.SecurityException If the caller does not have the
+ * <tt>ServicePermission</tt> to register the service for all the named classes and
+ * the Java Runtime Environment supports permissions.
+ *
+ * @exception java.lang.IllegalStateException If this context bundle has stopped.
+ *
+ * @see ServiceRegistration
+ * @see ServiceFactory
+ */
+ public abstract ServiceRegistration registerService(
+ String[] clazzes,
+ Object service,
+ Dictionary properties);
+
+ /**
+ * Registers the specified service object with the specified properties
+ * under the specified class name into the Framework.
+ *
+ * <p>This method is otherwise identical to
+ * {@link #registerService(java.lang.String[], java.lang.Object,
+ * java.util.Dictionary)}and is provided as a convenience when <tt>service</tt> will only
+ * be registered under a single class name. Note that even in this case the value of the service's
+ * {@link Constants#OBJECTCLASS}property will be an array of strings, rather than just a single string.
+ *
+ * @see #registerService(java.lang.String[], java.lang.Object,
+ * java.util.Dictionary)
+ */
+ public abstract ServiceRegistration registerService(
+ String clazz,
+ Object service,
+ Dictionary properties);
+
+ /**
+ * Returns a list of <tt>ServiceReference</tt> objects. This method returns a list of
+ * <tt>ServiceReference</tt> objects for services which implement and were registered under
+ * the specified class and match the specified filter criteria.
+ *
+ * <p>The list is valid at the time of the call to this method, however as the Framework is
+ * a very dynamic environment, services can be modified or unregistered at anytime.
+ *
+ * <p><tt>filter</tt> is used to select the registered service whose
+ * properties objects contain keys and values which satisfy the filter.
+ * See {@link Filter}for a description of the filter string syntax.
+ *
+ * <p>If <tt>filter</tt> is <tt>null</tt>, all registered services
+ * are considered to match the filter.
+ * <p>If <tt>filter</tt> cannot be parsed, an {@link InvalidSyntaxException}will
+ * be thrown with a human readable message where the filter became unparsable.
+ *
+ * <p>The following steps are required to select a service:
+ * <ol>
+ * <li>If the Java Runtime Environment supports permissions, the caller is checked for the
+ * <tt>ServicePermission</tt> to get the service with the specified class.
+ * If the caller does not have the correct permission, <tt>null</tt> is returned.
+ * <li>If the filter string is not <tt>null</tt>, the filter string is
+ * parsed and the set of registered services which satisfy the filter is
+ * produced.
+ * If the filter string is <tt>null</tt>, then all registered services
+ * are considered to satisfy the filter.
+ * <li>If <code>clazz</code> is not <tt>null</tt>, the set is further reduced to
+ * those services which are an <tt>instanceof</tt> and were registered under the specified class.
+ * The complete list of classes of which a service is an instance and which
+ * were specified when the service was registered is available from the
+ * service's {@link Constants#OBJECTCLASS}property.
+ * <li>An array of <tt>ServiceReference</tt> to the selected services is returned.
+ * </ol>
+ *
+ * @param clazz The class name with which the service was registered, or
+ * <tt>null</tt> for all services.
+ * @param filter The filter criteria.
+ * @return An array of <tt>ServiceReference</tt> objects, or
+ * <tt>null</tt> if no services are registered which satisfy the search.
+ * @exception InvalidSyntaxException If <tt>filter</tt> contains
+ * an invalid filter string which cannot be parsed.
+ */
+ public abstract ServiceReference[] getServiceReferences(
+ String clazz,
+ String filter)
+ throws InvalidSyntaxException;
+
+ /**
+ * Returns a <tt>ServiceReference</tt> object for a service that implements, and
+ * was registered under, the specified class.
+ *
+ * <p>This <tt>ServiceReference</tt> object is valid at the time
+ * of the call to this method, however as the Framework is a very dynamic
+ * environment, services can be modified or unregistered at anytime.
+ *
+ * <p> This method is the same as calling {@link #getServiceReferences}with a
+ * <code>null</code> filter string.
+ * It is provided as a convenience for when the caller is interested in any service that
+ * implements the specified class. <p>If multiple such services exist, the service
+ * with the highest ranking (as specified in its {@link Constants#SERVICE_RANKING}property) is
+ * returned.
+ * <p>If there is a tie in ranking, the service with the lowest
+ * service ID (as specified in its {@link Constants#SERVICE_ID}property); that is,
+ * the service that was registered first is returned.
+ *
+ * @param clazz The class name with which the service was registered.
+ * @return A <tt>ServiceReference</tt> object, or <tt>null</tt>
+ * if no services are registered which implement the named class.
+ * @see #getServiceReferences
+ */
+ public abstract ServiceReference getServiceReference(String clazz);
+
+ /**
+ * Returns the specified service object for a service.
+ * <p>A bundle's use of a service is tracked by the bundle's use
+ * count of that service. Each time a service's service object is returned by
+ * {@link #getService}the context bundle's use count for that service
+ * is incremented by one. Each time the service is released by
+ * {@link #ungetService}the context bundle's use count for that service is decremented by one.
+ * <p>When a bundle's use count for a service drops to zero, the bundle should no longer use that service.
+ * See {@link #getBundle}for a definition of context bundle.
+ *
+ * <p>This method will always return <tt>null</tt> when the
+ * service associated with this <tt>reference</tt> has been unregistered.
+ *
+ * <p>The following steps are required to get the service object:
+ * <ol>
+ * <li>If the service has been unregistered, <tt>null</tt> is returned.
+ * <li>The context bundle's use count for this service is incremented by one.
+ * <li>If the context bundle's use count for the service is currently one and
+ * the service was registered with an object implementing the <tt>ServiceFactory</tt> interface,
+ * the {@link ServiceFactory#getService}method is called to create a service object
+ * for the context bundle.
+ * This service object is cached by the Framework.
+ * While the context bundle's use count for the service is greater than
+ * zero, subsequent calls to get the services's service object for the
+ * context bundle will return the cached service object.
+ * <br>If the service object returned by the <tt>ServiceFactory</tt> object
+ * is not an <code>instanceof</code> all the classes named when the service was registered or
+ * the <tt>ServiceFactory</tt> object throws an exception, <tt>null</tt> is returned and a
+ * Framework event of type {@link FrameworkEvent#ERROR}is broadcast.
+ * <li>The service object for the service is returned.
+ * </ol>
+ *
+ * @param reference A reference to target service object's service.
+ * @return A service object for the service associated with <tt>reference</tt>,
+ * or <tt>null</tt> if the service is not registered.
+ * @exception java.lang.SecurityException If the caller does not have
+ * the <tt>ServicePermission</tt> to get the service using at least one of the named classes
+ * the service was registered under, and the Java Runtime Environment supports permissions.
+ * @exception java.lang.IllegalStateException If the context bundle has stopped.
+ * @see #ungetService
+ * @see ServiceFactory
+ */
+ public abstract Object getService(ServiceReference reference);
+
+ /**
+ * Releases the service object referenced by the specified <tt>ServiceReference</tt> object.
+ * If the context bundle's use count for the service is zero, this method
+ * returns <tt>false</tt>. Otherwise, the context bundle's use count
+ * for the service is decremented by one.
+ * See {@link #getBundle}for a definition of context bundle.
+ *
+ * <p>The service's service object should no longer be used and all references to it
+ * should be destroyed when a bundle's use count for the service drops to zero.
+ *
+ * <p>The following steps are required to unget the service object:
+ * <ol>
+ * <li>If the context bundle's use count for the service is zero or
+ * the service has been unregistered, <tt>false</tt> is returned.
+ * <li>The context bundle's use count for this service is decremented by one.
+ * <li>If the context bundle's use count for the service is currently zero and
+ * the service was registered with a <tt>ServiceFactory</tt> object, the
+ * {@link ServiceFactory#ungetService}method is called to release the service object
+ * for the context bundle.
+ * <li><tt>true</tt> is returned.
+ * </ol>
+ *
+ * @param reference A reference to the service to be released.
+ * @return <tt>false</tt> if the context bundle's use count for the
+ * service is zero or if the service has been unregistered; <tt>true</tt> otherwise.
+ * @exception java.lang.IllegalStateException If the context bundle has stopped.
+ * @see #getService
+ * @see ServiceFactory
+ */
+ public abstract boolean ungetService(ServiceReference reference);
+
+ /**
+ * Creates a <tt>File</tt> object for a file in the
+ * persistent storage area provided for the bundle by the Framework.
+ * This method will return <tt>null</tt> if the platform does not
+ * have file system support.
+ *
+ * <p>A <tt>File</tt> object for the base directory of the
+ * persistent storage area provided for the context bundle by the Framework
+ * can be obtained by calling this method with an empty string (" ")
+ * as <tt>filename</tt>.
+ * See {@link #getBundle}for a definition of context bundle.
+ *
+ * <p>If the Java Runtime Environment supports permissions,
+ * the Framework will ensure that the bundle has the <tt>java.io.FilePermission</tt> with actions
+ * <tt>read</tt>, <tt>write</tt>, <tt>execute</tt>, <tt>delete</tt> for all files (recursively) in the
+ * persistent storage area provided for the context bundle.
+ *
+ * @param filename A relative name to the file to be accessed.
+ * @return A <tt>File</tt> object that represents the requested file or
+ * <tt>null</tt> if the platform does not have file system support.
+ * @exception java.lang.IllegalStateException If the context bundle has stopped.
+ */
+ public abstract File getDataFile(String filename);
+
+ /**
+ * Creates a <tt>Filter</tt> object. This <tt>Filter</tt> object may be used
+ * to match a <tt>ServiceReference</tt> object or a <tt>Dictionary</tt> object.
+ * See {@link Filter}for a description of the filter string syntax.
+ *
+ * <p>If the filter cannot be parsed, an {@link InvalidSyntaxException}will be thrown
+ * with a human readable message where the filter became unparsable.
+ *
+ * @param filter The filter string.
+ * @return A <tt>Filter</tt> object encapsulating the filter string.
+ * @exception InvalidSyntaxException If </tt>filter</tt> contains
+ * an invalid filter string that cannot be parsed.
+ *
+ * @since 1.1
+ */
+ public abstract Filter createFilter(String filter)
+ throws InvalidSyntaxException;
+
+ //================================================================
+ // Equinox Addenda
+ //================================================================
+ /**
+ * This method returns an array of bundles with the specified global
+ * name. If no bundles are installed with the specified global name
+ * then null is returned. The returned bundles are ordered in
+ * descending bundle version order.
+ *
+ * @param globalName The global name of the bundle to retrieve.
+ * @return A <tt>Bundle []</tt> containing all install bundles with the
+ * specified global name, or <tt>null</tt> if the global name
+ * does not match any installed bundle.
+ */
+ public abstract Bundle[] getBundles(String globalName);
+
+ /**
+ *
+ * @deprecated to be removed. Use getBundles(String)
+ * @param object
+ * @return
+ */
+ public abstract Bundle getBundle(String globalName);
+
+ // TODO hack alert. This likely is not required in the API. It allows you to find out
+ // what bundle loaded a given object. This is useful for debugging etc but...
+ /**
+ * @deprecated To be removed, no replacement
+ */
+ public abstract Bundle getBundleFor(Object object);
+
+ //================================================================
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleEvent.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleEvent.java
new file mode 100644
index 000000000..408344dd1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleEvent.java
@@ -0,0 +1,159 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleEvent.java,v 1.2 2003/11/10 17:51:50 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventObject;
+
+/**
+ * A Framework event describing a bundle lifecycle change.
+ * <p><tt>BundleEvent</tt> objects are delivered to <tt>BundleListener</tt> objects when a change
+ * occurs in a bundle's lifecycle. A type code is used to identify the event type
+ * for future extendability.
+ *
+ * <p>OSGi reserves the right to extend the set of types.
+ *
+ * @version $Revision: 1.2 $
+ * @author Open Services Gateway Initiative
+ */
+
+public class BundleEvent extends EventObject
+{
+ /**
+ * Bundle that had a change occur in its lifecycle.
+ */
+ private transient Bundle bundle;
+
+ /**
+ * Type of bundle lifecycle change.
+ */
+ private transient int type;
+
+ /**
+ * This bundle has been installed.
+ * <p>The value of <tt>INSTALLED</tt> is 0x00000001.
+ *
+ * @see BundleContext#installBundle
+ */
+ public final static int INSTALLED = 0x00000001;
+
+ /**
+ * This bundle has been started.
+ * <p>The value of <tt>STARTED</tt> is 0x00000002.
+ *
+ * @see Bundle#start
+ */
+ public final static int STARTED = 0x00000002;
+
+ /**
+ * This bundle has been stopped.
+ * <p>The value of <tt>STOPPED</tt> is 0x00000004.
+ *
+ * @see Bundle#stop
+ */
+ public final static int STOPPED = 0x00000004;
+
+ /**
+ * This bundle has been updated.
+ * <p>The value of <tt>UPDATED</tt> is 0x00000008.
+ *
+ * @see Bundle#update
+ */
+ public final static int UPDATED = 0x00000008;
+
+ /**
+ * This bundle has been uninstalled.
+ * <p>The value of <tt>UNINSTALLED</tt> is 0x00000010.
+ *
+ * @see Bundle#uninstall
+ */
+ public final static int UNINSTALLED = 0x00000010;
+
+ /**
+ * This bundle has been resolved.
+ * <p>The value of <tt>RESOLVED</tt> is 0x00000020.
+ *
+ * @see Bundle#uninstall
+ */
+ public final static int RESOLVED = 0x00000020;
+
+ /**
+ * This bundle is no longer resolved.
+ * <p>The value of <tt>UNRESOLVED</tt> is 0x00000040.
+ *
+ * @see Bundle#uninstall
+ */
+ public final static int UNRESOLVED = 0x00000040;
+
+
+ /**
+ * Creates a bundle event of the specified type.
+ *
+ * @param type The event type.
+ * @param bundle The bundle which had a lifecycle change.
+ */
+
+ public BundleEvent(int type, Bundle bundle)
+ {
+ super(bundle);
+ this.bundle = bundle;
+ this.type = type;
+ }
+
+ /**
+ * Returns the bundle which had a lifecycle change.
+ * This bundle is the source of the event.
+ *
+ * @return A bundle that had a change occur in its lifecycle.
+ */
+ public Bundle getBundle()
+ {
+ return bundle;
+ }
+
+ /**
+ * Returns the type of lifecyle event.
+ * The type values are:
+ * <ul>
+ * <li>{@link #INSTALLED}
+ * <li>{@link #STARTED}
+ * <li>{@link #STOPPED}
+ * <li>{@link #UPDATED}
+ * <li>{@link #UNINSTALLED}
+ * </ul>
+ *
+ * @return The type of lifecycle event.
+ */
+
+ public int getType()
+ {
+ return type;
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleException.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleException.java
new file mode 100644
index 000000000..8d640d0b5
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleException.java
@@ -0,0 +1,84 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleException.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A Framework exception used to indicate that a bundle lifecycle problem occurred.
+ *
+ * <p><tt>BundleException</tt> object is created by the Framework to denote an exception condition
+ * in the lifecycle of a bundle.
+ * <tt>BundleException</tt>s should not be created by bundle developers.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+public class BundleException extends Exception
+{
+ /**
+ * Nested exception.
+ */
+ private transient Throwable throwable;
+
+ /**
+ * Creates a <tt>BundleException</tt> that wraps another exception.
+ *
+ * @param msg The associated message.
+ * @param throwable The nested exception.
+ */
+ public BundleException(String msg, Throwable throwable)
+ {
+ super(msg);
+ this.throwable = throwable;
+ }
+
+ /**
+ * Creates a <tt>BundleException</tt> object with the specified message.
+ *
+ * @param msg The message.
+ */
+ public BundleException(String msg)
+ {
+ super(msg);
+ this.throwable = null;
+ }
+
+ /**
+ * Returns any nested exceptions included in this exception.
+ *
+ * @return The nested exception; <tt>null</tt> if there is
+ * no nested exception.
+ */
+ public Throwable getNestedException()
+ {
+ return(throwable);
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleListener.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleListener.java
new file mode 100644
index 000000000..f083d4b71
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleListener.java
@@ -0,0 +1,57 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/BundleListener.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventListener;
+
+/**
+ * A <tt>BundleEvent</tt> listener.
+ *
+ * <p><tt>BundleListener</tt> is a listener interface that may be implemented by a bundle developer.
+ * <p>A <tt>BundleListener</tt> object is registered with the Framework using the
+ * {@link BundleContext#addBundleListener}method.
+ * <tt>BundleListener</tt>s are called with a <tt>BundleEvent</tt> object when a bundle has been
+ * installed, started, stopped, updated, or uninstalled.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @see BundleEvent
+ */
+
+public abstract interface BundleListener extends EventListener
+{
+ /**
+ * Receives notification that a bundle has had a lifecycle change.
+ *
+ * @param event The <tt>BundleEvent</tt>.
+ */
+ public abstract void bundleChanged(BundleEvent event);
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundlePermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundlePermission.java
new file mode 100644
index 000000000..83a2430b6
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/BundlePermission.java
@@ -0,0 +1,596 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/BundlePermission.java,v 1.1 2003/11/10 17:51:50 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.security.Permission;
+import java.security.BasicPermission;
+import java.security.PermissionCollection;
+
+/**
+ * A bundle's authority to require or provide or specify a host bundle.
+ *
+ * <p>A bundle unique ID is a dot-separated string that defines a fully qualified name.
+ * <p>For example:
+ * <pre>
+ * <tt>org.osgi.service.http</tt>
+ * </pre>
+ * <p><tt>BundlePermission</tt> has three actions: <tt>PROVIDE</tt>, <tt>REQUIRE</tt> and
+ * <tt>HOST</tt>.
+ * The <tt>PROVIDE</tt> action implies the <tt>REQUIRE</tt> action.
+ *
+ * @author Open Services Gateway Initiative
+ */
+
+public final class BundlePermission extends BasicPermission
+{
+
+ /**
+ * The action string <tt>provide</tt>.
+ */
+ public final static String PROVIDE = "provide";
+
+ /**
+ * The action string <tt>require</tt>.
+ */
+ public final static String REQUIRE = "require";
+
+ /**
+ * The action string <tt>host</tt>.
+ */
+ public final static String HOST = "host";
+
+ private final static int ACTION_PROVIDE = 0x00000001;
+ private final static int ACTION_REQUIRE = 0x00000002;
+ private final static int ACTION_HOST = 0x00000004;
+ private final static int ACTION_ALL = ACTION_PROVIDE | ACTION_REQUIRE | ACTION_HOST;
+ private final static int ACTION_NONE = 0;
+ private final static int ACTION_ERROR = 0x80000000;
+
+ /**
+ * The actions mask.
+ */
+ private transient int action_mask = ACTION_NONE;
+
+ /**
+ * The actions in canonical form.
+ *
+ * @serial
+ */
+ private String actions = null;
+
+ /**
+ * Defines the authority to provide and/or require and or specify a host bundle
+ * unique ID within the OSGi environment.
+ * <p>The uniqueId is specified as a bundles Unique ID: a dot-separated string. Wildcards may be used.
+ * For example:
+ * <pre>
+ * org.osgi.service.http
+ * javax.servlet.*
+ * *
+ * </pre>
+ * <p>Bundle Permissions are granted over all possible versions of a bundle.
+ *
+ * A bundle that needs to provide a bundle must have the appropriate <tt>BundlePermission</tt>
+ * for the unique ID; a bundle that requires a bundle must have the appropriate
+ * <tt>BundlePermssion</tt> for that unique ID; a bundle that specifies a host bundle
+ * must have teh appropriate <tt>BundlePermission</tt> for that unique ID.
+ * @param uniqueId the bundle unique ID.
+ * @param actions <tt>PROVIDE</tt>, <tt>REQUIRE</tt>, <tt>HOST</tt> (canonical order).
+ */
+
+ public BundlePermission(String uniqueId, String actions)
+ {
+ this(uniqueId, getMask(actions));
+ }
+
+ /**
+ * Bundle private constructor used by BundlePermissionCollection.
+ *
+ * @param uniqueId class name
+ * @param action mask
+ */
+ BundlePermission(String uniqueId, int mask)
+ {
+ super(uniqueId);
+ init(mask);
+ }
+
+ /**
+ * Called by constructors and when deserialized.
+ *
+ * @param action mask
+ */
+ private void init(int mask)
+ {
+ if ((mask == ACTION_NONE) ||
+ ((mask & ACTION_ALL) != mask))
+ {
+ throw new IllegalArgumentException("invalid action string");
+ }
+
+ action_mask = mask;
+ }
+
+ /**
+ * Parse action string into action mask.
+ *
+ * @param actions Action string.
+ * @return action mask.
+ */
+ private static int getMask(String actions)
+ {
+ boolean seencomma = false;
+
+ int mask = ACTION_NONE;
+
+ if (actions == null)
+ {
+ return (mask);
+ }
+
+ char[] a = actions.toCharArray();
+
+ int i = a.length - 1;
+ if (i < 0)
+ return (mask);
+
+ while (i != -1)
+ {
+ char c;
+
+ // skip whitespace
+ while ((i!=-1) && ((c = a[i]) == ' ' ||
+ c == '\r' ||
+ c == '\n' ||
+ c == '\f' ||
+ c == '\t'))
+ i--;
+
+ // check for the known strings
+ int matchlen;
+
+ if (i >= 6 && (a[i-6] == 'p' || a[i-6] == 'P') &&
+ (a[i-5] == 'r' || a[i-5] == 'R') &&
+ (a[i-4] == 'o' || a[i-4] == 'O') &&
+ (a[i-3] == 'v' || a[i-3] == 'V') &&
+ (a[i-2] == 'i' || a[i-2] == 'I') &&
+ (a[i-1] == 'd' || a[i-1] == 'D') &&
+ (a[i] == 'e' || a[i] == 'E'))
+ {
+ matchlen = 7;
+ mask |= ACTION_PROVIDE | ACTION_REQUIRE;
+
+ }
+ else if (i >= 6 && (a[i-6] == 'r' || a[i-6] == 'R') &&
+ (a[i-5] == 'e' || a[i-5] == 'E') &&
+ (a[i-4] == 'q' || a[i-4] == 'Q') &&
+ (a[i-3] == 'u' || a[i-3] == 'U') &&
+ (a[i-2] == 'i' || a[i-2] == 'I') &&
+ (a[i-1] == 'r' || a[i-1] == 'R') &&
+ (a[i] == 'e' || a[i] == 'E'))
+ {
+ matchlen = 7;
+ mask |= ACTION_REQUIRE;
+
+ }
+ else if (i >= 3 && (a[i-3] == 'h' || a[i-3] == 'H') &&
+ (a[i-2] == 'o' || a[i-2] == 'O') &&
+ (a[i-1] == 's' || a[i-1] == 'S') &&
+ (a[i] == 't' || a[i] == 'T'))
+ {
+ matchlen = 4;
+ mask |= ACTION_HOST;
+
+ }
+ else
+ {
+ // parse error
+ throw new IllegalArgumentException("invalid permission: " +
+ actions);
+ }
+
+ // make sure we didn't just match the tail of a word
+ // like "ackbarfrequire". Also, skip to the comma.
+ seencomma = false;
+ while (i >= matchlen && !seencomma)
+ {
+ switch (a[i-matchlen])
+ {
+ case ',':
+ seencomma = true;
+ /*FALLTHROUGH*/
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\f':
+ case '\t':
+ break;
+ default:
+ throw new IllegalArgumentException("invalid permission: " +
+ actions);
+ }
+ i--;
+ }
+
+ // point i at the location of the comma minus one (or -1).
+ i -= matchlen;
+ }
+
+ if (seencomma)
+ {
+ throw new IllegalArgumentException("invalid permission: " +
+ actions);
+ }
+
+ return (mask);
+ }
+
+ /**
+ * Determines if the specified permission is implied by this object.
+ *
+ * <p>This method checks that the unique ID of the target is implied by the unique ID
+ * of this object. The list of <tt>BundlePermission</tt> actions must either match or allow
+ * for the list of the target object to imply the target <tt>BundlePermission</tt> action.
+ * <p>The permission to provide a bundle implies the permission to require the named unique ID.
+ * <pre>
+ * x.y.*,"provide" -> x.y.z,"provice" is true
+ * *,"require" -> x.y, "require" is true
+ * *,"provide" -> x.y, "require" is true
+ * x.y,"provide" -> x.y.z, "provice" is false
+ * </pre>
+ *
+ * @param p The target permission to interrogate.
+ * @return <tt>true</tt> if the specified <tt>BundlePermission</tt> action is implied by
+ * this object; <tt>false</tt> otherwise.
+ */
+
+ public boolean implies(Permission p)
+ {
+ if (p instanceof BundlePermission)
+ {
+ BundlePermission target = (BundlePermission) p;
+
+ return(((action_mask & target.action_mask)==target.action_mask) &&
+ super.implies(p));
+ }
+
+ return(false);
+ }
+
+ /**
+ * Returns the canonical string representation of the <tt>BundlePermission</tt> actions.
+ *
+ * <p>Always returns present <tt>BundlePermission</tt> actions in the following order:
+ * <tt>PROVIDE</tt>, <tt>REQUIRE</tt>, <tt>HOST</tt>.
+ * @return Canonical string representation of the <tt>BundlePermission</tt> actions.
+ */
+
+ public String getActions()
+ {
+ if (actions == null)
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean comma = false;
+
+ if ((action_mask & ACTION_PROVIDE) == ACTION_PROVIDE)
+ {
+ sb.append(PROVIDE);
+ comma = true;
+ }
+
+ if ((action_mask & ACTION_REQUIRE) == ACTION_REQUIRE)
+ {
+ if (comma) sb.append(',');
+ sb.append(REQUIRE);
+ comma = true;
+ }
+
+ if ((action_mask & ACTION_HOST) == ACTION_HOST)
+ {
+ if (comma) sb.append(',');
+ sb.append(HOST);
+ }
+
+ actions = sb.toString();
+ }
+
+ return(actions);
+ }
+
+ /**
+ * Returns a new <tt>PermissionCollection</tt> object suitable for storing
+ * <tt>BundlePermission</tt> objects.
+ *
+ * @return A new <tt>PermissionCollection</tt> object.
+ */
+ public PermissionCollection newPermissionCollection()
+ {
+ return(new BundlePermissionCollection());
+ }
+
+ /**
+ * Determines the equality of two <tt>BundlePermission</tt> objects.
+ *
+ * This method checks that specified bundle has the same bundle unique ID
+ * and <tt>BundlePermission</tt> actions as this <tt>BundlePermission</tt> object.
+ *
+ * @param obj The object to test for equality with this <tt>BundlePermission</tt> object.
+ * @return <tt>true</tt> if <tt>obj</tt> is a <tt>BundlePermission</tt>, and has the
+ * same bundle unique ID and actions as this <tt>BundlePermission</tt> object; <tt>false</tt> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return(true);
+ }
+
+ if (!(obj instanceof BundlePermission))
+ {
+ return(false);
+ }
+
+ BundlePermission p = (BundlePermission) obj;
+
+ return((action_mask == p.action_mask) &&
+ getName().equals(p.getName()));
+ }
+
+ /**
+ * Returns the hash code value for this object.
+ *
+ * @return A hash code value for this object.
+ */
+
+ public int hashCode()
+ {
+ return(getName().hashCode() ^ getActions().hashCode());
+ }
+
+ /**
+ * Returns the current action mask.
+ * <p>Used by the BundlePermissionCollection class.
+ *
+ * @return Current action mask.
+ */
+ int getMask()
+ {
+ return(action_mask);
+ }
+
+ /**
+ * WriteObject is called to save the state of the <tt>BundlePermission</tt>
+ * object to a stream. The actions are serialized, and the superclass
+ * takes care of the name.
+ */
+
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws IOException
+ {
+ // Write out the actions. The superclass takes care of the name
+ // call getActions to make sure actions field is initialized
+ if (actions == null)
+ getActions();
+ s.defaultWriteObject();
+ }
+
+ /**
+ * readObject is called to restore the state of the BundlePermission from
+ * a stream.
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ // Read in the action, then initialize the rest
+ s.defaultReadObject();
+ init(getMask(actions));
+ }
+}
+
+/**
+ * Stores a set of <tt>BundlePermission</tt> permissions.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+
+final class BundlePermissionCollection extends PermissionCollection
+{
+
+ /**
+ * Table of permissions.
+ *
+ * @serial
+ */
+ private Hashtable permissions;
+
+ /**
+ * Boolean saying if "*" is in the collection.
+ *
+ * @serial
+ */
+ private boolean all_allowed;
+
+ /**
+ * Create an empty BundlePermissions object.
+ *
+ */
+
+ public BundlePermissionCollection()
+ {
+ permissions = new Hashtable();
+ all_allowed = false;
+ }
+
+ /**
+ * Adds a permission to the <tt>BundlePermission</tt> objects. The key for the hash is
+ * the unique ID.
+ *
+ * @param permission The <tt>BundlePermission</tt> object to add.
+ *
+ * @exception IllegalArgumentException If the permission is not a
+ * <tt>BundlePermission</tt> instance.
+ *
+ * @exception SecurityException If this <tt>BundlePermissionCollection</tt>
+ * object has been marked read-only.
+ */
+
+ public void add(Permission permission)
+ {
+ if (! (permission instanceof BundlePermission))
+ throw new IllegalArgumentException("invalid permission: "+
+ permission);
+ if (isReadOnly())
+ throw new SecurityException("attempt to add a Permission to a " +
+ "readonly PermissionCollection");
+
+ BundlePermission bp = (BundlePermission) permission;
+ String name = bp.getName();
+
+ BundlePermission existing =
+ (BundlePermission) permissions.get(name);
+
+ if (existing != null)
+ {
+ int oldMask = existing.getMask();
+ int newMask = bp.getMask();
+ if (oldMask != newMask)
+ {
+ permissions.put(name,
+ new BundlePermission(name,
+ oldMask | newMask));
+
+ }
+ }
+ else
+ {
+ permissions.put(name, permission);
+ }
+
+ if (!all_allowed)
+ {
+ if (name.equals("*"))
+ all_allowed = true;
+ }
+ }
+
+ /**
+ * Determines if the specified permissions implies the permissions
+ * expressed in <tt>permission</tt>.
+ *
+ * @param p The Permission object to compare with this <tt>BundlePermission</tt> object.
+ *
+ * @return <tt>true</tt> if <tt>permission</tt> is a proper subset of a permission in
+ * the set; <tt>false</tt> otherwise.
+ */
+
+ public boolean implies(Permission permission)
+ {
+ if (!(permission instanceof BundlePermission))
+ return(false);
+
+ BundlePermission bp = (BundlePermission) permission;
+ BundlePermission x;
+
+ int desired = bp.getMask();
+ int effective = 0;
+
+ // short circuit if the "*" Permission was added
+ if (all_allowed)
+ {
+ x = (BundlePermission) permissions.get("*");
+ if (x != null)
+ {
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return(true);
+ }
+ }
+
+ // strategy:
+ // Check for full match first. Then work our way up the
+ // name looking for matches on a.b.*
+
+ String name = bp.getName();
+
+ x = (BundlePermission) permissions.get(name);
+
+ if (x != null)
+ {
+ // we have a direct hit!
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return(true);
+ }
+
+ // work our way up the tree...
+ int last, offset;
+
+ offset = name.length()-1;
+
+ while ((last = name.lastIndexOf(".", offset)) != -1)
+ {
+
+ name = name.substring(0, last+1) + "*";
+ x = (BundlePermission) permissions.get(name);
+
+ if (x != null)
+ {
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return(true);
+ }
+ offset = last -1;
+ }
+
+ // we don't have to check for "*" as it was already checked
+ // at the top (all_allowed), so we just return false
+ return(false);
+ }
+
+ /**
+ * Returns an enumeration of all <tt>BundlePermission</tt> objects in the
+ * container.
+ *
+ * @return Enumeration of all <tt>BundlePermission</tt> objects.
+ */
+
+ public Enumeration elements()
+ {
+ return(permissions.elements());
+ }
+}
+
+
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Configurable.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Configurable.java
new file mode 100644
index 000000000..3247ae859
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Configurable.java
@@ -0,0 +1,61 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/Configurable.java,v 1.1 2003/11/10 17:49:33 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+/**
+ * Supports a configuration object.
+ *
+ * <p><tt>Configurable</tt> is an interface that should be used by a bundle developer in support
+ * of a configurable service.
+ * Bundles that need to configure a service may test to determine
+ * if the service object is an <tt>instanceof Configurable</tt>.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+public abstract interface Configurable
+{
+ /**
+ * Returns this service's configuration object.
+ *
+ * <p>Services implementing <tt>Configurable</tt> should take care when returning a
+ * service configuration object since this object is probably sensitive.
+ * <p>If the Java Runtime Environment supports permissions, it is recommended that
+ * the caller is checked for the appropriate permission before returning the configuration object.
+ * It is recommended that callers possessing the appropriate
+ * {@link AdminPermission} always be allowed to get the configuration object.
+ *
+ * @return The configuration object for this service.
+ * @exception java.lang.SecurityException If the caller does not have
+ * an appropriate permission and the Java Runtime Environment supports permissions.
+ */
+ public abstract Object getConfigurationObject();
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java
new file mode 100644
index 000000000..5a41fb995
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java
@@ -0,0 +1,564 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java,v 1.3 2003/11/25 15:44:22 twatson Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+/**
+ * Defines standard names for the OSGi environment property, service property,
+ * and Manifest header attribute keys.
+ *
+ * <p>The values associated with these keys are of type <tt>java.lang.String</tt>,
+ * unless otherwise indicated.
+ *
+ * @version $Revision: 1.3 $
+ * @author Open Services Gateway Initiative
+ * @since 1.1
+ * @see Bundle#getHeaders
+ * @see BundleContext#getProperty
+ * @see BundleContext#registerService
+ */
+
+public interface Constants {
+ /**
+ * Location identifier of the OSGi <i>system bundle</i>, which is
+ * defined to be &quot;System Bundle&quot;.
+ */
+ public static final String SYSTEM_BUNDLE_LOCATION = "System Bundle";
+
+ /**
+ * Manifest header (named &quot;Bundle-Category&quot;)
+ * identifying the bundle's category.
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_CATEGORY = "Bundle-Category";
+
+ /**
+ * Manifest header (named &quot;Bundle-ClassPath&quot;)
+ * identifying a list of nested JAR files, which are bundle resources used
+ * to extend the bundle's classpath.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_CLASSPATH = "Bundle-ClassPath";
+
+ /**
+ * Manifest header (named &quot;Bundle-Copyright&quot;)
+ * identifying the bundle's copyright information, which may be retrieved
+ * from the <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_COPYRIGHT = "Bundle-Copyright";
+
+ /**
+ * Manifest header (named &quot;Bundle-Description&quot;)
+ * containing a brief description of the bundle's functionality.
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_DESCRIPTION = "Bundle-Description";
+
+ /**
+ * Manifest header (named &quot;Bundle-Name&quot;)
+ * identifying the bundle's name.
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_NAME = "Bundle-Name";
+
+ /**
+ * Manifest header (named &quot;Bundle-NativeCode&quot;)
+ * identifying a number of hardware environments and the native language code
+ * libraries that the bundle is carrying for each of these environments.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_NATIVECODE = "Bundle-NativeCode";
+
+ /**
+ * Manifest header (named &quot;Export-Package&quot;)
+ * identifying the names (and optionally version numbers) of the packages
+ * that the bundle offers to the Framework for export.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String EXPORT_PACKAGE = "Export-Package";
+
+ /**
+ * Manifest header (named &quot;Export-Service&quot;)
+ * identifying the fully qualified class names of the services that the
+ * bundle may register (used for informational purposes only).
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String EXPORT_SERVICE = "Export-Service";
+
+ /**
+ * Manifest header (named &quot;Import-Package&quot;)
+ * identifying the names (and optionally, version numbers) of the packages
+ * that the bundle is dependent on.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String IMPORT_PACKAGE = "Import-Package";
+
+ /**
+ * Manifest header (named &quot;DynamicImport-Package&quot;)
+ * identifying the names of the packages
+ * that the bundle may dynamically import during execution.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ * @since 1.2
+ */
+ public static final String DYNAMICIMPORT_PACKAGE = "DynamicImport-Package";
+
+ /**
+ * Manifest header (named &quot;Import-Service&quot;)
+ * identifying the fully qualified class names of the services that the
+ * bundle requires (used for informational purposes only).
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String IMPORT_SERVICE = "Import-Service";
+
+ /**
+ * Manifest header (named &quot;Bundle-Vendor&quot;)
+ * identifying the bundle's vendor.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_VENDOR = "Bundle-Vendor";
+
+ /**
+ * Manifest header (named &quot;Bundle-Version&quot;)
+ * identifying the bundle's version.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_VERSION = "Bundle-Version";
+
+ /**
+ * Manifest header (named &quot;Bundle-DocURL&quot;)
+ * identifying the bundle's documentation URL, from which further
+ * information about the bundle may be obtained.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_DOCURL = "Bundle-DocURL";
+
+ /**
+ * Manifest header (named &quot;Bundle-ContactAddress&quot;)
+ * identifying the contact address where problems with the
+ * bundle may be reported; for example, an email address.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_CONTACTADDRESS = "Bundle-ContactAddress";
+
+ /**
+ * Manifest header attribute (named &quot;Bundle-Activator&quot;)
+ * identifying the bundle's activator class.
+ *
+ * <p>If present, this header specifies the name of the bundle resource
+ * class that implements the <tt>BundleActivator</tt> interface and whose
+ * <tt>start</tt> and <tt>stop</tt> methods are called by the Framework
+ * when the bundle is started and stopped, respectively.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_ACTIVATOR = "Bundle-Activator";
+
+ /**
+ * Manifest header (named &quot;Bundle-UpdateLocation&quot;)
+ * identifying the location from which a new bundle version is
+ * obtained during a bundle update operation.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public static final String BUNDLE_UPDATELOCATION = "Bundle-UpdateLocation";
+
+ /**
+ * Manifest header attribute (named &quot;specification-version&quot;)
+ * identifying the version of a package specified in the
+ * Export-Package or Import-Package Manifest header.
+ *
+ * <p>The attribute value is encoded in the Export-Package or
+ * Import-Package Manifest header like:
+ * <pre>
+ * Import-Package: org.osgi.framework ; specification-version="1.1"
+ * </pre>
+ */
+ public static final String PACKAGE_SPECIFICATION_VERSION =
+ "specification-version";
+
+ /**
+ * Manifest header attribute (named &quot;processor&quot;) identifying the processor
+ * required to run native bundle code specified in the Bundle-NativeCode Manifest header).
+ *
+ * <p>The attribute value is encoded in the Bundle-NativeCode Manifest header like:
+ * <pre>
+ * Bundle-NativeCode: http.so ; processor=x86 ...
+ * </pre>
+ */
+ public static final String BUNDLE_NATIVECODE_PROCESSOR = "processor";
+
+ /**
+ * Manifest header attribute (named &quot;osname&quot;) identifying the
+ * operating system required to run native bundle code specified in the Bundle-NativeCode
+ * Manifest header).
+ * <p>The attribute value is encoded in the Bundle-NativeCode Manifest header like:
+ * <pre>
+ * Bundle-NativeCode: http.so ; osname=Linux ...
+ * </pre>
+ */
+ public static final String BUNDLE_NATIVECODE_OSNAME = "osname";
+
+ /**
+ * Manifest header attribute (named &quot;osversion&quot;) identifying the
+ * operating system version required to run native bundle code specified in the Bundle-NativeCode
+ * Manifest header).
+ * <p>The attribute value is encoded in the Bundle-NativeCode Manifest header like:
+ * <pre>
+ * Bundle-NativeCode: http.so ; osversion="2.34" ...
+ * </pre>
+ */
+ public static final String BUNDLE_NATIVECODE_OSVERSION = "osversion";
+
+ /**
+ * Manifest header attribute (named &quot;language&quot;) identifying the
+ * language in which the native bundle code is written specified in the
+ * Bundle-NativeCode Manifest header. See ISO 639 for possible values.
+ * <p>The attribute value is encoded in the Bundle-NativeCode Manifest header like:
+ * <pre>
+ * Bundle-NativeCode: http.so ; language=nl_be ...
+ * </pre>
+ */
+ public static final String BUNDLE_NATIVECODE_LANGUAGE = "language";
+
+
+ /**
+ * Manifest header (named &quot;Bundle-RequiredExecutionEnvironment&quot;)
+ * identifying the required
+ * execution environment for the bundle. The service platform may run this
+ * bundle if any of the execution environments named in this header matches
+ * one of the execution environments it
+ * implements.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ * @since 1.2
+ */
+ public static final String BUNDLE_REQUIREDEXECUTIONENVIRONMENT = "Bundle-RequiredExecutionEnvironment";
+
+
+ /*
+ * Framework environment properties.
+ */
+
+ /**
+ * Framework environment property (named &quot;org.osgi.framework.version&quot;)
+ * identifying the Framework version.
+ *
+ * <p>The value of this property may be retrieved by calling the
+ * <tt>BundleContext.getProperty</tt> method.
+ */
+ public static final String FRAMEWORK_VERSION = "org.osgi.framework.version";
+
+ /**
+ * Framework environment property (named &quot;org.osgi.framework.vendor&quot;)
+ * identifying the Framework implementation vendor.
+ *
+ * <p>The value of this property may be retrieved by calling the
+ * <tt>BundleContext.getProperty</tt> method.
+ */
+ public static final String FRAMEWORK_VENDOR = "org.osgi.framework.vendor";
+
+ /**
+ * Framework environment property (named &quot;org.osgi.framework.language&quot;)
+ * identifying the Framework implementation language (see ISO 639 for possible values).
+ *
+ * <p>The value of this property may be retrieved by calling the
+ * <tt>BundleContext.getProperty</tt> method.
+ */
+ public static final String FRAMEWORK_LANGUAGE =
+ "org.osgi.framework.language";
+
+ /**
+ * Framework environment property (named &quot;org.osgi.framework.os.name&quot;)
+ * identifying the Framework host-computer's operating system.
+ *
+ * <p>The value of this property may be retrieved by calling the <tt>BundleContext.getProperty</tt> method.
+ */
+ public static final String FRAMEWORK_OS_NAME = "org.osgi.framework.os.name";
+
+ /**
+ * Framework environment property (named &quot;org.osgi.framework.os.version&quot;)
+ * identifying the Framework host-computer's operating system version number.
+ *
+ * <p>The value of this property may be retrieved by calling the <tt>BundleContext.getProperty</tt> method.
+ */
+ public static final String FRAMEWORK_OS_VERSION =
+ "org.osgi.framework.os.version";
+
+ /**
+ * Framework environment property (named &quot;org.osgi.framework.processor&quot;)
+ * identifying the Framework host-computer's processor name.
+ * <p>The value of this property may be retrieved by calling the <tt>BundleContext.getProperty</tt> method.
+ */
+ public static final String FRAMEWORK_PROCESSOR =
+ "org.osgi.framework.processor";
+
+ /**
+ * Framework environment property (named &quot;org.osgi.framework.executionenvironment&quot;)
+ * identifying execution environments provided by the Framework.
+ * <p>The value of this property may be retrieved by calling the <tt>BundleContext.getProperty</tt> method.
+ * @since 1.2
+ */
+ public static final String FRAMEWORK_EXECUTIONENVIRONMENT =
+ "org.osgi.framework.executionenvironment";
+
+
+ /*
+ * Service properties.
+ */
+
+ /**
+ * Service property (named &quot;objectClass&quot;)
+ * identifying all of the class names under which a service was registered in the Framework
+ * (of type <tt>java.lang.String[]</tt>).
+ *
+ * <p>This property is set by the Framework when a service is registered.
+ */
+ public static final String OBJECTCLASS = "objectClass";
+
+ /**
+ * Service property (named &quot;service.id&quot;) identifying a service's
+ * registration number (of type <tt>java.lang.Long</tt>).
+ *
+ * <p>The value of this property is assigned by the Framework when a
+ * service is registered. The Framework assigns a unique value that
+ * is larger than all previously assigned values since the Framework was
+ * started.
+ * These values are NOT persistent across restarts of the Framework.
+ */
+ public static final String SERVICE_ID = "service.id";
+
+ /**
+ * Service property (named &quot;service.pid&quot;) identifying a service's
+ * persistent identifier.
+ *
+ * <p>This property may be supplied in the <tt>properties</tt>
+ * <tt>Dictionary</tt> object passed to the <tt>BundleContext.registerService</tt> method.
+ *
+ * <p>A service's persistent identifier uniquely identifies the service
+ * and persists across multiple Framework invocations.
+ *
+ * <p>By convention, every bundle has its own unique namespace,
+ * starting with the bundle's identifier (see {@link Bundle#getBundleId})
+ * and followed by a dot (.). A bundle may use this as the prefix of the
+ * persistent identifiers for the services it registers.
+ */
+ public static final String SERVICE_PID = "service.pid";
+
+ /**
+ * Service property (named &quot;service.ranking&quot;)
+ * identifying a service's ranking number (of type <tt>java.lang.Integer</tt>).
+ *
+ * <p>This property may be supplied in the <tt>properties</tt>
+ * <tt>Dictionary</tt> object passed to the <tt>BundleContext.registerService</tt> method.
+ *
+ * <p>The service ranking is used by the Framework to determine the
+ * <i>default</i> service to be returned from a call to the
+ * {@link BundleContext#getServiceReference}method:
+ * If more than one service implements the specified class, the <tt>ServiceReference</tt> object with
+ * the highest ranking is returned.
+ *
+ * <p>The default ranking is zero (0). A service with a ranking of <tt>Integer.MAX_VALUE</tt>
+ * is very likely to be returned as the default service, whereas a service with a ranking of
+ * <tt>Integer.MIN_VALUE</tt> is very unlikely to be returned.
+ *
+ * <p>If the supplied property value is not of type <tt>java.lang.Integer</tt>,
+ * it is deemed to have a ranking value of zero.
+ */
+ public static final String SERVICE_RANKING = "service.ranking";
+
+ /**
+ * Service property (named &quot;service.vendor&quot;) identifying a service's vendor.
+ *
+ * <p>This property may be supplied in the properties <tt>Dictionary</tt> object passed to
+ * the <tt>BundleContext.registerService</tt> method.
+ */
+ public static final String SERVICE_VENDOR = "service.vendor";
+
+ /**
+ * Service property (named &quot;service.description&quot;)
+ * identifying a service's description.
+ *
+ * <p>This property may be supplied in the properties <tt>Dictionary</tt>
+ * object passed to the <tt>BundleContext.registerService</tt> method.
+ */
+ public static final String SERVICE_DESCRIPTION = "service.description";
+
+ //================================================================
+ // Equinox Addenda
+ //================================================================
+
+ /**
+ * Manifest header (named &quot;Bundle-GlobalName&quot;)
+ * identifying the bundle's unique ID.
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public final static String BUNDLE_GLOBALNAME = "Bundle-GlobalName";
+
+ /**
+ * Manifest header (named &quot;Provide-Package&quot;)
+ * identifying the names of the packages
+ * that the bundle provides to the Framework as a bundle.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public final static String PROVIDE_PACKAGE = "Provide-Package";
+
+ /**
+ * Manifest header (named &quot;Require-Bundle&quot;)
+ * identifying the unique ID's (and optionally, version, match, export,
+ * and optional attributes ) of the bundles
+ * that the bundle is dependent on.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public final static String REQUIRE_BUNDLE = "Require-Bundle";
+
+ /**
+ * Manifest header attribute (named &quot;bundle-version&quot;)
+ * identifying the version of a bundle specified in the
+ * Require-Bundle or Host-Bundle Manifest headers.
+ *
+ * <p>The attribute value is encoded in the Require-Bundle Manifest
+ * header like:
+ * <pre>
+ * Require-Bundle: org.osgi.framework.module.test; bundle-version="1.1"
+ * </pre>
+ */
+ public static final String BUNDLE_VERSION_ATTRIBUTE = "bundle-version";
+
+ /**
+ * This constant represents the Require-Bundle manifest header attribute
+ * named "provide-packages" identifying that any packages that are provided
+ * by the required bundle must be provided by the requiring bundle.
+ * The default value is "false".
+ * <p>
+ * The attribute value is encoded in the Require-Bundle Manifest header like:
+ * <pre>
+ * Require-Bundle: org.osgi.framework.module.test;
+ * provide-packages="true"
+ * </pre>
+ */
+ public final static String PROVIDE_PACKAGES_ATTRIBUTE = "provide-packages";
+
+ /**
+ * Manifest header attribute (named &quot;optional&quot;)
+ * identifying that a required bundle is optional and that
+ * this bundle should be resolved if the required bundle is
+ * not available. The default value is "false".
+ *
+ * <p>The attribute value is encoded in the Require-Bundle
+ * Manifest header like:
+ * <pre>
+ * Require-Bundle: org.osgi.framework.module.test;
+ * bundle-version="1.1";
+ * optional="true"
+ * </pre>
+ */
+ public final static String OPTIONAL_ATTRIBUTE = "optional";
+
+ /**
+ * Manifest header attribute (named &quot;version-match&quot;)
+ * identifying the match type for a required bundle.
+ * The default value is "greaterthan-or-equal".
+ *
+ * <p>The attribute value is encoded in the Require-Bundle
+ * Manifest header like:
+ * <pre>
+ * Require-Bundle: org.osgi.framework.module.test;
+ * bundle-version="1.1";
+ * version-match="perfect"
+ * </pre>
+ */
+ public final static String VERSION_MATCH_ATTRIBUTE = "version-match";
+
+ public final static String VERSION_MATCH_PERFECT = "perfect";
+ public final static String VERSION_MATCH_EQUIVALENT = "equivalent";
+ public final static String VERSION_MATCH_COMPATIBLE = "compatible";
+ public final static String VERSION_MATCH_GREATERTHANOREQUAL = "greaterthan-or-equal";
+
+ /**
+ * Manifest header (named &quot;Host-Bundle&quot;)
+ * identifying the unique ID (and optionally, version and match
+ * attributes ) of the bundle that the bundle is a fragment to.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ */
+ public final static String HOST_BUNDLE = "Host-Bundle";
+
+
+ /**
+ * Framework environment property (named &quot;org.osgi.framework.processor&quot;)
+ * identifying the Framework host-computer's processor name.
+ * <p>The value of this property may be retrieved by calling the <tt>BundleContext.getProperty</tt> method.
+ */
+ public static final String FRAMEWORK_WINDOWING_SYSTEM = "org.osgi.framework.windowing.system";
+
+ /**
+ * Manifest header attribute (named &quot;ws&quot;) identifying the
+ * windowing system in which the native bundle code can operate in.
+ * Bundle-NativeCode Manifest header.
+ * <p>The attribute value is encoded in the Bundle-NativeCode Manifest header like:
+ * <pre>
+ * Bundle-NativeCode: http.so ; ws=win32 ...
+ * </pre>
+ */
+ //TODO define possible values
+ public static final String BUNDLE_NATIVECODE_WINDOWING_SYSTEM = "ws";
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Filter.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Filter.java
new file mode 100644
index 000000000..5c4b91403
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Filter.java
@@ -0,0 +1,109 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/Filter.java,v 1.1 2003/11/10 17:49:33 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.Dictionary;
+
+/**
+ * An RFC 1960-based Filter.
+ * <p><tt>Filter</tt> objects can be created by calling
+ * {@link BundleContext#createFilter} with the chosen filter string.
+ * <p>A <tt>Filter</tt> object can be used numerous times to determine if the
+ * match argument matches the filter string that was used to create the <tt>Filter</tt>
+ * object.
+ * <p>Some examples of LDAP filters are:
+ *
+ * <pre>
+ * &quot;(cn=Babs Jensen)&quot;
+ * &quot;(!(cn=Tim Howes))&quot;
+ * &quot;(&amp;(&quot; + Constants.OBJECTCLASS + &quot;=Person)(|(sn=Jensen)(cn=Babs J*)))&quot;
+ * &quot;(o=univ*of*mich*)&quot;
+ * </pre>
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @since 1.1
+ */
+
+public interface Filter
+{
+ /**
+ * Filter using a service's properties.
+ * <p>The filter is executed using properties of the referenced service.
+ *
+ * @param reference The reference to the service whose properties are used in the match.
+ *
+ * @return <tt>true</tt> if the service's properties match this filter;
+ * <tt>false</tt> otherwise.
+ */
+ public boolean match(ServiceReference reference);
+
+ /**
+ * Filter using a <tt>Dictionary</tt> object.
+ * The Filter is executed using the <tt>Dictionary</tt> object's keys and values.
+ *
+ * @param dictionary The <tt>Dictionary</tt> object whose keys are used in the match.
+ *
+ * @return <tt>true</tt> if the <tt>Dictionary</tt> object's keys and values match this filter;
+ * <tt>false</tt> otherwise.
+ *
+ * @exception IllegalArgumentException If <tt>dictionary</tt> contains case
+ * variants of the same key name.
+ */
+ public boolean match(Dictionary dictionary);
+
+ /**
+ * Returns this <tt>Filter</tt> object's filter string.
+ * <p>The filter string is normalized by removing whitespace which does
+ * not affect the meaning of the filter.
+ *
+ * @return Filter string.
+ */
+ public String toString();
+
+ /**
+ * Compares this <tt>Filter</tt> object to another object.
+ *
+ * @param obj The object to compare against this <tt>Filter</tt> object.
+ *
+ * @return If the other object is a <tt>Filter</tt> object, then
+ * returns <tt>this.toString().equals(obj.toString()</tt>;
+ * <tt>false</tt> otherwise.
+ */
+ public boolean equals(Object obj);
+
+ /**
+ * Returns the hashCode for this <tt>Filter</tt> object.
+ *
+ * @return The hashCode of the filter string; that is, <tt>this.toString().hashCode()</tt>.
+ */
+ public int hashCode();
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkEvent.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkEvent.java
new file mode 100644
index 000000000..95cf51737
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkEvent.java
@@ -0,0 +1,181 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkEvent.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventObject;
+
+/**
+ * A general Framework event.
+ *
+ * <p><tt>FrameworkEvent</tt> is the event class used when notifying listeners of general events occuring
+ * within the OSGI environment. A type code is used to identify the event type for future extendability.
+ *
+ * <p>OSGi reserves the right to extend the set of event types.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+public class FrameworkEvent extends EventObject
+{
+ /**
+ * Bundle related to the event.
+ */
+ private transient Bundle bundle;
+
+ /**
+ * Exception related to the event.
+ */
+ private transient Throwable throwable;
+
+ /**
+ * Type of event.
+ */
+ private transient int type;
+
+ /**
+ * The Framework has started.
+ *
+ * <p>This event is broadcast when the Framework has started after all installed bundles that
+ * are marked to be started have been started
+ * and the Framework has reached the intitial start level.
+ *
+ * <p>The value of <tt>STARTED</tt> is 0x00000001.
+ * @see org.osgi.service.startlevel.StartLevel
+ */
+ public final static int STARTED = 0x00000001;
+
+ /**
+ * An error has occurred.
+ *
+ * <p>There was an error associated with a bundle.
+ *
+ * <p>The value of <tt>ERROR</tt> is 0x00000002.
+ */
+ public final static int ERROR = 0x00000002;
+
+ /**
+ * A PackageAdmin.refreshPackage operation has completed.
+ *
+ * <p>This event is broadcast when the Framework has completed
+ * the refresh packages operation initiated by a call to the
+ * PackageAdmin.refreshPackages method.
+ *
+ * <p>The value of <tt>PACKAGES_REFRESHED</tt> is 0x00000004.
+ * @since 1.2
+ * @see org.osgi.service.packageadmin.PackageAdmin#refreshPackages
+ */
+ public final static int PACKAGES_REFRESHED = 0x00000004;
+
+ /**
+ * A StartLevel.setStartLevel operation has completed.
+ *
+ * <p>This event is broadcast when the Framework has completed
+ * changing the active start level initiated by a call to the
+ * StartLevel.setStartLevel method.
+ *
+ * <p>The value of <tt>STARTLEVEL_CHANGED</tt> is 0x00000008.
+ * @since 1.2
+ * @see org.osgi.service.startlevel.StartLevel
+ */
+ public final static int STARTLEVEL_CHANGED = 0x00000008;
+
+ /**
+ * Creates a Framework event.
+ *
+ * @param type The event type.
+ * @param source The event source object. This may not be <tt>null</tt>.
+ * @deprecated Since 1.2. This constructor is deprecated in favor of using
+ * the other constructor with the System Bundle as the event source.
+ */
+ public FrameworkEvent(int type, Object source)
+ {
+ super(source);
+ this.type = type;
+ this.bundle = null;
+ this.throwable = null;
+ }
+
+ /**
+ * Creates a Framework event regarding the specified bundle.
+ *
+ * @param type The event type.
+ * @param bundle The event source.
+ * @param throwable The related exception. This argument may be <tt>null</tt>
+ * if there is no related exception.
+ */
+ public FrameworkEvent(int type, Bundle bundle, Throwable throwable )
+ {
+ super(bundle);
+ this.type = type;
+ this.bundle = bundle;
+ this.throwable = throwable;
+ }
+
+ /**
+ * Returns the exception associated with the event.
+ * <p>If the event type is <tt>ERROR</tt>, this method returns the exception related to the error.
+ *
+ * @return An exception if an event of type <tt>ERROR</tt> or <tt>null</tt>.
+ */
+ public Throwable getThrowable()
+ {
+ return(throwable);
+ }
+
+ /**
+ * Returns the bundle associated with the event.
+ * This bundle is also the source of the event.
+ *
+ * @return The bundle associated with the event.
+ */
+ public Bundle getBundle()
+ {
+ return(bundle);
+ }
+
+ /**
+ * Returns the type of bundle state change.
+ * <p>The type values are:
+ * <ul>
+ * <li>{@link #STARTED}
+ * <li>{@link #ERROR}
+ * <li>{@link #PACKAGES_REFRESHED}
+ * <li>{@link #STARTLEVEL_CHANGED}
+ * </ul>
+ * @return The type of state change.
+ */
+
+ public int getType()
+ {
+ return(type);
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkListener.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkListener.java
new file mode 100644
index 000000000..9bc58af79
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkListener.java
@@ -0,0 +1,58 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkListener.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventListener;
+
+/**
+ * A <tt>FrameworkEvent</tt> listener.
+ *
+ * <p><tt>FrameworkListener</tt> is a listener interface that may be implemented by a bundle
+ * developer. A <tt>FrameworkListener</tt> object is registered with the Framework using the
+ * {@link BundleContext#addFrameworkListener}method.
+ * <tt>FrameworkListener</tt> objects are called with a <tt>FrameworkEvent</tt> objects when
+ * the Framework starts and when asynchronous errors occur.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @see FrameworkEvent
+ */
+
+public abstract interface FrameworkListener extends EventListener
+{
+
+ /**
+ * Receives notification of a general <tt>FrameworkEvent</tt> object.
+ *
+ * @param event The <tt>FrameworkEvent</tt> object.
+ */
+ public abstract void frameworkEvent(FrameworkEvent event);
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/InvalidSyntaxException.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/InvalidSyntaxException.java
new file mode 100644
index 000000000..b009d0d69
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/InvalidSyntaxException.java
@@ -0,0 +1,78 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/InvalidSyntaxException.java,v 1.1 2003/11/10 17:49:33 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A Framework exception.
+ *
+ * <p>An <tt>InvalidSyntaxException</tt> object indicates that a filter
+ * string parameter has an invalid syntax and cannot be parsed.
+ *
+ * <p> See {@link Filter} for a description of the filter string syntax.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+public class InvalidSyntaxException extends Exception
+{
+ /**
+ * The invalid filter string.
+ */
+ private transient String filter;
+
+ /**
+ * Creates an exception of type <tt>InvalidSyntaxException</tt>.
+ *
+ * <p>This method creates an <tt>InvalidSyntaxException</tt> object with
+ * the specified message and the filter string which generated the exception.
+ *
+ * @param msg The message.
+ * @param filter The invalid filter string.
+ */
+ public InvalidSyntaxException(String msg, String filter)
+ {
+ super(msg);
+ this.filter = filter;
+ }
+
+ /**
+ * Returns the filter string that generated the <tt>InvalidSyntaxException</tt> object.
+ *
+ * @return The invalid filter string.
+ * @see BundleContext#getServiceReferences
+ * @see BundleContext#addServiceListener
+ */
+ public String getFilter()
+ {
+ return(filter);
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java
new file mode 100644
index 000000000..8f2842150
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java
@@ -0,0 +1,572 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java,v 1.1 2003/11/10 17:49:33 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.security.Permission;
+import java.security.BasicPermission;
+import java.security.PermissionCollection;
+
+/**
+ * A bundle's authority to import or export a package.
+ *
+ * <p>A package is a dot-separated string that defines a fully qualified Java
+ * package.
+ * <p>For example:
+ * <pre>
+ * <tt>org.osgi.service.http</tt>
+ * </pre>
+ * <p><tt>PackagePermission</tt> has two actions: <tt>EXPORT</tt> and <tt>IMPORT</tt>.
+ * The <tt>EXPORT</tt> action implies the <tt>IMPORT</tt> action.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+public final class PackagePermission extends BasicPermission
+{
+
+ /**
+ * The action string <tt>export</tt>.
+ */
+ public final static String EXPORT = "export";
+
+ /**
+ * The action string <tt>import</tt>.
+ */
+ public final static String IMPORT = "import";
+
+ private final static int ACTION_EXPORT = 0x00000001;
+ private final static int ACTION_IMPORT = 0x00000002;
+ private final static int ACTION_ALL = ACTION_EXPORT | ACTION_IMPORT;
+ private final static int ACTION_NONE = 0;
+ private final static int ACTION_ERROR = 0x80000000;
+
+ /**
+ * The actions mask.
+ */
+ private transient int action_mask = ACTION_NONE;
+
+ /**
+ * The actions in canonical form.
+ *
+ * @serial
+ */
+ private String actions = null;
+
+ /**
+ * Defines the authority to import and/or export a package within the OSGi environment.
+ * <p>The name is specified as a normal Java package name: a dot-separated string. Wildcards may be used.
+ * For example:
+ * <pre>
+ * org.osgi.service.http
+ * javax.servlet.*
+ * *
+ * </pre>
+ * <p>Package Permissions are granted over all possible versions of a package.
+ *
+ * A bundle that needs to export a package must have the appropriate <tt>PackagePermission</tt>
+ * for that package; similarly, a bundle that needs to import a package must have the appropriate
+ * <tt>PackagePermssion</tt> for that package.
+ * <p>Permission is granted for both classes and resources.
+ * @param name Package name.
+ * @param actions <tt>EXPORT</tt>, <tt>IMPORT</tt> (canonical order).
+ */
+
+ public PackagePermission(String name, String actions)
+ {
+ this(name, getMask(actions));
+ }
+
+ /**
+ * Package private constructor used by PackagePermissionCollection.
+ *
+ * @param name class name
+ * @param action mask
+ */
+ PackagePermission(String name, int mask)
+ {
+ super(name);
+ init(mask);
+ }
+
+ /**
+ * Called by constructors and when deserialized.
+ *
+ * @param action mask
+ */
+ private void init(int mask)
+ {
+ if ((mask == ACTION_NONE) ||
+ ((mask & ACTION_ALL) != mask))
+ {
+ throw new IllegalArgumentException("invalid action string");
+ }
+
+ action_mask = mask;
+ }
+
+ /**
+ * Parse action string into action mask.
+ *
+ * @param actions Action string.
+ * @return action mask.
+ */
+ private static int getMask(String actions)
+ {
+ boolean seencomma = false;
+
+ int mask = ACTION_NONE;
+
+ if (actions == null)
+ {
+ return (mask);
+ }
+
+ char[] a = actions.toCharArray();
+
+ int i = a.length - 1;
+ if (i < 0)
+ return (mask);
+
+ while (i != -1)
+ {
+ char c;
+
+ // skip whitespace
+ while ((i!=-1) && ((c = a[i]) == ' ' ||
+ c == '\r' ||
+ c == '\n' ||
+ c == '\f' ||
+ c == '\t'))
+ i--;
+
+ // check for the known strings
+ int matchlen;
+
+ if (i >= 5 && (a[i-5] == 'i' || a[i-5] == 'I') &&
+ (a[i-4] == 'm' || a[i-4] == 'M') &&
+ (a[i-3] == 'p' || a[i-3] == 'P') &&
+ (a[i-2] == 'o' || a[i-2] == 'O') &&
+ (a[i-1] == 'r' || a[i-1] == 'R') &&
+ (a[i] == 't' || a[i] == 'T'))
+ {
+ matchlen = 6;
+ mask |= ACTION_IMPORT;
+
+ }
+ else if (i >= 5 && (a[i-5] == 'e' || a[i-5] == 'E') &&
+ (a[i-4] == 'x' || a[i-4] == 'X') &&
+ (a[i-3] == 'p' || a[i-3] == 'P') &&
+ (a[i-2] == 'o' || a[i-2] == 'O') &&
+ (a[i-1] == 'r' || a[i-1] == 'R') &&
+ (a[i] == 't' || a[i] == 'T'))
+ {
+ matchlen = 6;
+ mask |= ACTION_EXPORT | ACTION_IMPORT;
+
+ }
+ else
+ {
+ // parse error
+ throw new IllegalArgumentException("invalid permission: " +
+ actions);
+ }
+
+ // make sure we didn't just match the tail of a word
+ // like "ackbarfimport". Also, skip to the comma.
+ seencomma = false;
+ while (i >= matchlen && !seencomma)
+ {
+ switch (a[i-matchlen])
+ {
+ case ',':
+ seencomma = true;
+ /*FALLTHROUGH*/
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\f':
+ case '\t':
+ break;
+ default:
+ throw new IllegalArgumentException("invalid permission: " +
+ actions);
+ }
+ i--;
+ }
+
+ // point i at the location of the comma minus one (or -1).
+ i -= matchlen;
+ }
+
+ if (seencomma)
+ {
+ throw new IllegalArgumentException("invalid permission: " +
+ actions);
+ }
+
+ return (mask);
+ }
+
+ /**
+ * Determines if the specified permission is implied by this object.
+ *
+ * <p>This method checks that the package name of the target is implied by the package name
+ * of this object. The list of <tt>PackagePermission</tt> actions must either match or allow
+ * for the list of the target object to imply the target <tt>PackagePermission</tt> action.
+ * <p>The permission to export a package implies the permission to import the named package.
+ * <pre>
+ * x.y.*,"export" -> x.y.z,"export" is true
+ * *,"import" -> x.y, "import" is true
+ * *,"export" -> x.y, "import" is true
+ * x.y,"export" -> x.y.z, "export" is false
+ * </pre>
+ *
+ * @param p The target permission to interrogate.
+ * @return <tt>true</tt> if the specified <tt>PackagePermission</tt> action is implied by
+ * this object; <tt>false</tt> otherwise.
+ */
+
+ public boolean implies(Permission p)
+ {
+ if (p instanceof PackagePermission)
+ {
+ PackagePermission target = (PackagePermission) p;
+
+ return(((action_mask & target.action_mask)==target.action_mask) &&
+ super.implies(p));
+ }
+
+ return(false);
+ }
+
+ /**
+ * Returns the canonical string representation of the <tt>PackagePermission</tt> actions.
+ *
+ * <p>Always returns present <tt>PackagePermission</tt> actions in the following order:
+ * <tt>EXPORT</tt>, <tt>IMPORT</tt>.
+ * @return Canonical string representation of the <tt>PackagePermission</tt> actions.
+ */
+
+ public String getActions()
+ {
+ if (actions == null)
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean comma = false;
+
+ if ((action_mask & ACTION_EXPORT) == ACTION_EXPORT)
+ {
+ sb.append(EXPORT);
+ comma = true;
+ }
+
+ if ((action_mask & ACTION_IMPORT) == ACTION_IMPORT)
+ {
+ if (comma) sb.append(',');
+ sb.append(IMPORT);
+ }
+
+ actions = sb.toString();
+ }
+
+ return(actions);
+ }
+
+ /**
+ * Returns a new <tt>PermissionCollection</tt> object suitable for storing
+ * <tt>PackagePermission</tt> objects.
+ *
+ * @return A new <tt>PermissionCollection</tt> object.
+ */
+ public PermissionCollection newPermissionCollection()
+ {
+ return(new PackagePermissionCollection());
+ }
+
+ /**
+ * Determines the equality of two <tt>PackagePermission</tt> objects.
+ *
+ * This method checks that specified package has the same package name
+ * and <tt>PackagePermission</tt> actions as this <tt>PackagePermission</tt> object.
+ *
+ * @param obj The object to test for equality with this <tt>PackagePermission</tt> object.
+ * @return <tt>true</tt> if <tt>obj</tt> is a <tt>PackagePermission</tt>, and has the
+ * same package name and actions as this <tt>PackagePermission</tt> object; <tt>false</tt> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return(true);
+ }
+
+ if (!(obj instanceof PackagePermission))
+ {
+ return(false);
+ }
+
+ PackagePermission p = (PackagePermission) obj;
+
+ return((action_mask == p.action_mask) &&
+ getName().equals(p.getName()));
+ }
+
+ /**
+ * Returns the hash code value for this object.
+ *
+ * @return A hash code value for this object.
+ */
+
+ public int hashCode()
+ {
+ return(getName().hashCode() ^ getActions().hashCode());
+ }
+
+ /**
+ * Returns the current action mask.
+ * <p>Used by the PackagePermissionCollection class.
+ *
+ * @return Current action mask.
+ */
+ int getMask()
+ {
+ return(action_mask);
+ }
+
+ /**
+ * WriteObject is called to save the state of the <tt>ServicePermission</tt>
+ * object to a stream. The actions are serialized, and the superclass
+ * takes care of the name.
+ */
+
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws IOException
+ {
+ // Write out the actions. The superclass takes care of the name
+ // call getActions to make sure actions field is initialized
+ if (actions == null)
+ getActions();
+ s.defaultWriteObject();
+ }
+
+ /**
+ * readObject is called to restore the state of the ServicePermission from
+ * a stream.
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ // Read in the action, then initialize the rest
+ s.defaultReadObject();
+ init(getMask(actions));
+ }
+}
+
+/**
+ * Stores a set of <tt>PackagePermission</tt> permissions.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+
+final class PackagePermissionCollection extends PermissionCollection
+{
+
+ /**
+ * Table of permissions.
+ *
+ * @serial
+ */
+ private Hashtable permissions;
+
+ /**
+ * Boolean saying if "*" is in the collection.
+ *
+ * @serial
+ */
+ private boolean all_allowed;
+
+ /**
+ * Create an empty PackagePermissions object.
+ *
+ */
+
+ public PackagePermissionCollection()
+ {
+ permissions = new Hashtable();
+ all_allowed = false;
+ }
+
+ /**
+ * Adds a permission to the <tt>PackagePermission</tt> objects. The key for the hash is
+ * the name.
+ *
+ * @param permission The <tt>PackagePermission</tt> object to add.
+ *
+ * @exception IllegalArgumentException If the permission is not a
+ * <tt>PackagePermission</tt> instance.
+ *
+ * @exception SecurityException If this <tt>PackagePermissionCollection</tt>
+ * object has been marked read-only.
+ */
+
+ public void add(Permission permission)
+ {
+ if (! (permission instanceof PackagePermission))
+ throw new IllegalArgumentException("invalid permission: "+
+ permission);
+ if (isReadOnly())
+ throw new SecurityException("attempt to add a Permission to a " +
+ "readonly PermissionCollection");
+
+ PackagePermission pp = (PackagePermission) permission;
+ String name = pp.getName();
+
+ PackagePermission existing =
+ (PackagePermission) permissions.get(name);
+
+ if (existing != null)
+ {
+ int oldMask = existing.getMask();
+ int newMask = pp.getMask();
+ if (oldMask != newMask)
+ {
+ permissions.put(name,
+ new PackagePermission(name,
+ oldMask | newMask));
+
+ }
+ }
+ else
+ {
+ permissions.put(name, permission);
+ }
+
+ if (!all_allowed)
+ {
+ if (name.equals("*"))
+ all_allowed = true;
+ }
+ }
+
+ /**
+ * Determines if the specified permissions implies the permissions
+ * expressed in <tt>permission</tt>.
+ *
+ * @param p The Permission object to compare with this <tt>PackagePermission</tt> object.
+ *
+ * @return <tt>true</tt> if <tt>permission</tt> is a proper subset of a permission in
+ * the set; <tt>false</tt> otherwise.
+ */
+
+ public boolean implies(Permission permission)
+ {
+ if (!(permission instanceof PackagePermission))
+ return(false);
+
+ PackagePermission pp = (PackagePermission) permission;
+ PackagePermission x;
+
+ int desired = pp.getMask();
+ int effective = 0;
+
+ // short circuit if the "*" Permission was added
+ if (all_allowed)
+ {
+ x = (PackagePermission) permissions.get("*");
+ if (x != null)
+ {
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return(true);
+ }
+ }
+
+ // strategy:
+ // Check for full match first. Then work our way up the
+ // name looking for matches on a.b.*
+
+ String name = pp.getName();
+
+ x = (PackagePermission) permissions.get(name);
+
+ if (x != null)
+ {
+ // we have a direct hit!
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return(true);
+ }
+
+ // work our way up the tree...
+ int last, offset;
+
+ offset = name.length()-1;
+
+ while ((last = name.lastIndexOf(".", offset)) != -1)
+ {
+
+ name = name.substring(0, last+1) + "*";
+ x = (PackagePermission) permissions.get(name);
+
+ if (x != null)
+ {
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return(true);
+ }
+ offset = last -1;
+ }
+
+ // we don't have to check for "*" as it was already checked
+ // at the top (all_allowed), so we just return false
+ return(false);
+ }
+
+ /**
+ * Returns an enumeration of all <tt>PackagePermission</tt> objects in the
+ * container.
+ *
+ * @return Enumeration of all <tt>PackagePermission</tt> objects.
+ */
+
+ public Enumeration elements()
+ {
+ return(permissions.elements());
+ }
+}
+
+
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceEvent.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceEvent.java
new file mode 100644
index 000000000..4ed92907a
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceEvent.java
@@ -0,0 +1,139 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceEvent.java,v 1.1 2003/11/10 17:49:33 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventObject;
+
+/**
+ * A service lifecycle change event.
+ * <p><tt>ServiceEvent</tt> objects are delivered to a <tt>ServiceListener</tt> objects when a change
+ * occurs in this service's lifecycle. A type code is used to identify the event type for
+ * future extendability.
+ *
+ * <p>OSGi reserves the right to extend the set of types.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @see ServiceListener
+ */
+
+public class ServiceEvent extends EventObject
+{
+ /**
+ * Reference to the service that had a change occur in its lifecycle.
+ */
+ private transient ServiceReference reference;
+
+ /**
+ * Type of service lifecycle change.
+ */
+ private transient int type;
+
+ /**
+ * This service has been registered.
+ * <p>This event is synchronously delivered <strong>after</strong> the service has been registered with the
+ * Framework.
+ *
+ * <p>The value of <tt>REGISTERED</tt> is 0x00000001.
+ *
+ * @see BundleContext#registerService
+ */
+ public final static int REGISTERED = 0x00000001;
+
+ /**
+ * The properties of a registered service have been modified.
+ * <p>This event is synchronously delivered <strong>after</strong> the service properties
+ * have been modified.
+ *
+ * <p>The value of <tt>MODIFIED</tt> is 0x00000002.
+ *
+ * @see ServiceRegistration#setProperties
+ */
+ public final static int MODIFIED = 0x00000002;
+
+ /**
+ * This service is in the process of being unregistered.
+ * <p>This event is synchronously delivered <strong>before</strong> the service has completed
+ * unregistering.
+ *
+ * <p>If a bundle is using a service that is <tt>UNREGISTERING</tt>, the bundle should release
+ * its use of the service when it receives this event. If the bundle does not release its use of
+ * the service when it receives this event, the Framework will automatically release the bundle's
+ * use of the service while completing the service unregistration operation.
+ *
+ * <p>The value of UNREGISTERING is 0x00000004.
+ *
+ * @see ServiceRegistration#unregister
+ * @see BundleContext#ungetService
+ */
+ public final static int UNREGISTERING = 0x00000004;
+
+
+ /**
+ * Creates a new service event object.
+ *
+ * @param type The event type.
+ * @param reference A <tt>ServiceReference</tt> object to the service that had a lifecycle change.
+ */
+ public ServiceEvent(int type, ServiceReference reference)
+ {
+ super(reference);
+ this.reference = reference;
+ this.type = type;
+ }
+
+ /**
+ * Returns a reference to the service that had a change occur in its lifecycle.
+ * <p>This reference is the source of the event.
+ *
+ * @return Reference to the service that had a lifecycle change.
+ */
+ public ServiceReference getServiceReference()
+ {
+ return(reference);
+ }
+
+ /**
+ * Returns the type of event.
+ * The event type values are:
+ * <ul>
+ * <li>{@link #REGISTERED}
+ * <li>{@link #MODIFIED}
+ * <li>{@link #UNREGISTERING}
+ * </ul>
+ * @return Type of service lifecycle change.
+ */
+
+ public int getType()
+ {
+ return(type);
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceFactory.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceFactory.java
new file mode 100644
index 000000000..ad08af6db
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceFactory.java
@@ -0,0 +1,96 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceFactory.java,v 1.1 2003/11/10 17:49:33 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+/**
+ * Allows services to provide customized service objects in the OSGi environment.
+ *
+ * <p>When registering a service, a <tt>ServiceFactory</tt> object can
+ * be used instead of a service object, so that the bundle developer can gain control
+ * of the specific service object granted to a bundle that is using the service.
+ *
+ * <p>When this happens, the <tt>BundleContext.getService(ServiceReference)</tt> method calls the
+ * <tt>ServiceFactory.getService</tt> method to create a service object specifically for the
+ * requesting bundle. The service object returned by the <tt>ServiceFactory</tt> object
+ * is cached by the Framework until the bundle releases its use of the service.
+ *
+ * <p>When the bundle's use count for the service equals zero (including the bundle stopping
+ * or the service being unregistered), the <tt>ServiceFactory.ungetService</tt> method is called.
+ *
+ * <p><tt>ServiceFactory</tt> objects are only used by the Framework and are not
+ * made available to other bundles in the OSGi environment.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @see BundleContext#getService
+ */
+
+public abstract interface ServiceFactory
+{
+ /**
+ * Creates a new service object.
+ *
+ * <p>The Framework invokes this method the first time the specified <tt>bundle</tt> requests
+ * a service object using the <tt>BundleContext.getService(ServiceReference)</tt> method.
+ * The service factory can then return a specific service object for each bundle.
+ *
+ * <p>The Framework caches the value returned (unless it is <tt>null</tt>), and
+ * will return the same service object on any future call to <tt>BundleContext.getService</tt>
+ * from the same bundle.
+ *
+ * <p>The Framework will check if the returned service object is an instance of
+ * all the classes named when the service was registered. If not, then <tt>null</tt> is
+ * returned to the bundle.
+ *
+ * @param bundle The bundle using the service.
+ * @param registration The <tt>ServiceRegistration</tt> object for the service.
+ * @return A service object that <strong>must</strong> be an instance of
+ * all the classes named when the service was registered.
+ * @see BundleContext#getService
+ */
+ public abstract Object getService(Bundle bundle,
+ ServiceRegistration registration);
+
+ /**
+ * Releases a service object.
+ *
+ * <p>The Framework invokes this method when a service has been released by a bundle.
+ * The service object may then be destroyed.
+ *
+ * @param bundle The bundle releasing the service.
+ * @param registration The <tt>ServiceRegistration</tt> object for the service.
+ * @param service The service object returned by a previous call to the <tt>ServiceFactory.getService</tt> method.
+ * @see BundleContext#ungetService
+ */
+ public abstract void ungetService(Bundle bundle,
+ ServiceRegistration registration,
+ Object service);
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceListener.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceListener.java
new file mode 100644
index 000000000..51abc9671
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceListener.java
@@ -0,0 +1,66 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceListener.java,v 1.1 2003/11/10 17:49:33 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.EventListener;
+
+/**
+ * A <tt>ServiceEvent</tt> listener.
+ *
+ * <p><tt>ServiceListener</tt> is a listener interface that may be implemented by a bundle
+ * developer.
+ * <p>A <tt>ServiceListener</tt> object is registered with the Framework using the
+ * <tt>BundleContext.addServiceListener</tt> method.
+ * <tt>ServiceListener</tt> objects are called with a <tt>ServiceEvent</tt> object when
+ * a service has been registered or modified, or is in the process of unregistering.
+ *
+ * <p><tt>ServiceEvent</tt> object delivery to <tt>ServiceListener</tt> objects is filtered by the
+ * filter specified when the listener was registered. If the Java Runtime Environment
+ * supports permissions, then additional filtering is done.
+ * <tt>ServiceEvent</tt> objects are only delivered to the listener if the bundle which defines
+ * the listener object's class has the appropriate <tt>ServicePermission</tt> to get the service
+ * using at least one of the named classes the service was registered under.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @see ServiceEvent
+ * @see ServicePermission
+ */
+
+public abstract interface ServiceListener extends EventListener
+{
+ /**
+ * Receives notification that a service has had a lifecycle change.
+ *
+ * @param event The <tt>ServiceEvent</tt> object.
+ */
+ public abstract void serviceChanged(ServiceEvent event);
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java
new file mode 100644
index 000000000..1dc2de4e4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java
@@ -0,0 +1,548 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.security.Permission;
+import java.security.BasicPermission;
+import java.security.PermissionCollection;
+
+/**
+ * Indicates a bundle's authority to register or get a service.
+ * <ul>
+ * <li>The <tt>ServicePermission.REGISTER</tt> action allows a bundle
+ * to register a service on the specified names.
+ * <li>The <tt>ServicePermission.GET</tt> action allows a bundle to detect a service and
+ * get it.
+ * </ul>
+ * Permission to get a service is required in order to detect events regarding the service.
+ * Untrusted bundles should not be able to detect the presence of certain services unless they have
+ * the appropriate <tt>ServicePermission</tt> to get the specific service.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+final public class ServicePermission extends BasicPermission
+{
+
+ /**
+ * The action string <tt>get</tt> (Value is "get").
+ */
+ public final static String GET = "get";
+ /**
+ * The action string <tt>register</tt> (Value is "register").
+ */
+ public final static String REGISTER = "register";
+
+ private final static int ACTION_GET = 0x00000001;
+ private final static int ACTION_REGISTER = 0x00000002;
+ private final static int ACTION_ALL = ACTION_GET | ACTION_REGISTER;
+ private final static int ACTION_NONE = 0;
+ private final static int ACTION_ERROR = 0x80000000;
+
+ /**
+ * The actions mask.
+ */
+ private transient int action_mask = ACTION_NONE;
+
+ /**
+ * The actions in canonical form.
+ *
+ * @serial
+ */
+ private String actions = null;
+
+ /**
+ * Create a new ServicePermission.
+ *
+ * <p>The name of the service is specified as a fully qualified class name.
+ * <pre>
+ * ClassName ::= &lt;class name&gt; | &lt;class name ending in ".*"&gt;
+ * </pre>
+ * Examples:
+ * <pre>
+ * org.osgi.service.http.HttpService
+ * org.osgi.service.http.*
+ * org.osgi.service.snmp.*
+ * </pre>
+ *
+ * <p>There are two possible actions: <tt>get</tt> and <tt>register</tt>.
+ * The <tt>get</tt> permission allows the owner of this permission
+ * to obtain a service with this name. The <tt>register</tt> permission
+ * allows the bundle to register a service under that name.
+ *
+ * @param name class name
+ * @param actions <tt>get</tt>, <tt>register</tt> (canonical order)
+ */
+
+ public ServicePermission(String name, String actions)
+ {
+ this(name, getMask(actions));
+ }
+
+ /**
+ * Package private constructor used by ServicePermissionCollection.
+ *
+ * @param name class name
+ * @param action mask
+ */
+ ServicePermission(String name, int mask)
+ {
+ super(name);
+
+ init(mask);
+ }
+
+ /**
+ * Called by constructors and when deserialized.
+ *
+ * @param action mask
+ */
+ private void init(int mask)
+ {
+ if ((mask == ACTION_NONE) ||
+ ((mask & ACTION_ALL) != mask))
+ {
+ throw new IllegalArgumentException("invalid action string");
+ }
+
+ action_mask = mask;
+ }
+
+ /**
+ * Parse action string into action mask.
+ *
+ * @param actions Action string.
+ * @return action mask.
+ */
+ private static int getMask(String actions)
+ {
+ boolean seencomma = false;
+
+ int mask = ACTION_NONE;
+
+ if (actions == null) {
+ return mask;
+ }
+
+ char[] a = actions.toCharArray();
+
+ int i = a.length - 1;
+ if (i < 0)
+ return mask;
+
+ while (i != -1) {
+ char c;
+
+ // skip whitespace
+ while ((i!=-1) && ((c = a[i]) == ' ' ||
+ c == '\r' ||
+ c == '\n' ||
+ c == '\f' ||
+ c == '\t'))
+ i--;
+
+ // check for the known strings
+ int matchlen;
+
+ if (i >= 2 && (a[i-2] == 'g' || a[i-2] == 'G') &&
+ (a[i-1] == 'e' || a[i-1] == 'E') &&
+ (a[i] == 't' || a[i] == 'T'))
+ {
+ matchlen = 3;
+ mask |= ACTION_GET;
+
+ } else if (i >= 7 && (a[i-7] == 'r' || a[i-7] == 'R') &&
+ (a[i-6] == 'e' || a[i-6] == 'E') &&
+ (a[i-5] == 'g' || a[i-5] == 'G') &&
+ (a[i-4] == 'i' || a[i-4] == 'I') &&
+ (a[i-3] == 's' || a[i-3] == 'S') &&
+ (a[i-2] == 't' || a[i-2] == 'T') &&
+ (a[i-1] == 'e' || a[i-1] == 'E') &&
+ (a[i] == 'r' || a[i] == 'R'))
+ {
+ matchlen = 8;
+ mask |= ACTION_REGISTER;
+
+ } else {
+ // parse error
+ throw new IllegalArgumentException(
+ "invalid permission: " + actions);
+ }
+
+ // make sure we didn't just match the tail of a word
+ // like "ackbarfregister". Also, skip to the comma.
+ seencomma = false;
+ while (i >= matchlen && !seencomma) {
+ switch(a[i-matchlen]) {
+ case ',':
+ seencomma = true;
+ /*FALLTHROUGH*/
+ case ' ': case '\r': case '\n':
+ case '\f': case '\t':
+ break;
+ default:
+ throw new IllegalArgumentException("invalid permission: " +
+ actions);
+ }
+ i--;
+ }
+
+ // point i at the location of the comma minus one (or -1).
+ i -= matchlen;
+ }
+
+ if (seencomma) {
+ throw new IllegalArgumentException("invalid permission: " +
+ actions);
+ }
+
+ return mask;
+ }
+
+ /**
+ * Determines if a <tt>ServicePermission</tt> object "implies" the
+ * specified permission.
+ *
+ * @param p The target permission to check.
+ * @return <tt>true</tt> if the specified permission is implied by
+ * this object; <tt>false</tt> otherwise.
+ */
+
+ public boolean implies(Permission p)
+ {
+ if (p instanceof ServicePermission)
+ {
+ ServicePermission target = (ServicePermission) p;
+
+ return(((action_mask & target.action_mask)==target.action_mask) &&
+ super.implies(p));
+ }
+
+ return(false);
+ }
+
+ /**
+ * Returns the canonical string representation of the actions.
+ * Always returns present actions in the following order:
+ * <tt>get</tt>, <tt>register</tt>.
+ * @return The canonical string representation of the actions.
+ */
+ public String getActions()
+ {
+ if (actions == null)
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean comma = false;
+
+ if ((action_mask & ACTION_GET) == ACTION_GET)
+ {
+ sb.append(GET);
+ comma = true;
+ }
+
+ if ((action_mask & ACTION_REGISTER) == ACTION_REGISTER)
+ {
+ if (comma) sb.append(',');
+ sb.append(REGISTER);
+ }
+
+ actions = sb.toString();
+ }
+
+ return(actions);
+ }
+
+ /**
+ * Returns a new <tt>PermissionCollection</tt> object for storing
+ * <tt>ServicePermission<tt> objects.
+ *
+ * @return A new <tt>PermissionCollection</tt> object suitable for storing
+ * <tt>ServicePermission</tt> objects.
+ */
+ public PermissionCollection newPermissionCollection()
+ {
+ return (new ServicePermissionCollection());
+ }
+
+ /**
+ * Determines the equalty of two ServicePermission objects.
+ *
+ * Checks that specified object has the same class name
+ * and action as this <tt>ServicePermission</tt>.
+ *
+ * @param obj The object to test for equality.
+ * @return true if obj is a <tt>ServicePermission</tt>, and has the
+ * same class name and actions as this <tt>ServicePermission</tt> object; <tt>false</tt> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return(true);
+ }
+
+ if (!(obj instanceof ServicePermission))
+ {
+ return(false);
+ }
+
+ ServicePermission p = (ServicePermission) obj;
+
+ return((action_mask == p.action_mask) &&
+ getName().equals(p.getName()));
+ }
+
+ /**
+ * Returns the hash code value for this object.
+ *
+ * @return Hash code value for this object.
+ */
+
+ public int hashCode()
+ {
+ return(getName().hashCode() ^ getActions().hashCode());
+ }
+
+ /**
+ * Returns the current action mask.
+ * Used by the ServicePermissionCollection object.
+ *
+ * @return The actions mask.
+ */
+ int getMask()
+ {
+ return (action_mask);
+ }
+
+ /**
+ * WriteObject is called to save the state of the ServicePermission
+ * to a stream. The actions are serialized, and the superclass
+ * takes care of the name.
+ */
+
+ private synchronized void writeObject(java.io.ObjectOutputStream s)
+ throws IOException
+ {
+ // Write out the actions. The superclass takes care of the name
+ // call getActions to make sure actions field is initialized
+ if (actions == null)
+ getActions();
+ s.defaultWriteObject();
+ }
+
+ /**
+ * readObject is called to restore the state of the ServicePermission from
+ * a stream.
+ */
+ private synchronized void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ // Read in the action, then initialize the rest
+ s.defaultReadObject();
+ init(getMask(actions));
+ }
+}
+
+/**
+ * Stores a set of ServicePermission permissions.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+
+final class ServicePermissionCollection extends PermissionCollection
+{
+
+ /**
+ * Table of permissions.
+ *
+ * @serial
+ */
+ private Hashtable permissions;
+
+ /**
+ * Boolean saying if "*" is in the collection.
+ *
+ * @serial
+ */
+ private boolean all_allowed;
+
+ /**
+ * Creates an empty ServicePermissions object.
+ *
+ */
+
+ public ServicePermissionCollection()
+ {
+ permissions = new Hashtable();
+ all_allowed = false;
+ }
+
+ /**
+ * Adds a permission to the <tt>ServicePermission</tt> objects using the key for the hash as
+ * the name.
+ *
+ * @param permission The Permission object to add.
+ *
+ * @exception IllegalArgumentException If the permission is not a ServicePermission object.
+ *
+ * @exception SecurityException If this <tt>ServicePermissionCollection</tt> object has been marked read-only.
+ */
+
+ public void add(Permission permission)
+ {
+ if (! (permission instanceof ServicePermission))
+ throw new IllegalArgumentException("invalid permission: "+
+ permission);
+ if (isReadOnly())
+ throw new SecurityException("attempt to add a Permission to a " +
+ "readonly PermissionCollection");
+
+ ServicePermission sp = (ServicePermission) permission;
+ String name = sp.getName();
+
+ ServicePermission existing =
+ (ServicePermission) permissions.get(name);
+
+ if (existing != null)
+ {
+ int oldMask = existing.getMask();
+ int newMask = sp.getMask();
+ if (oldMask != newMask)
+ {
+ permissions.put(name,
+ new ServicePermission(name,
+ oldMask | newMask));
+ }
+ }
+ else
+ {
+ permissions.put(name, permission);
+ }
+
+ if (!all_allowed)
+ {
+ if (name.equals("*"))
+ all_allowed = true;
+ }
+ }
+
+ /**
+ * Determines if a set of permissions implies the permissions
+ * expressed in <tt>permission</tt>.
+ *
+ * @param p The Permission object to compare.
+ *
+ * @return <tt>true</tt> if <tt>permission</tt> is a proper subset of a permission in
+ * the set; <tt>false</tt> otherwise.
+ */
+
+ public boolean implies(Permission permission)
+ {
+ if (!(permission instanceof ServicePermission))
+ return (false);
+
+ ServicePermission sp = (ServicePermission) permission;
+ ServicePermission x;
+
+ int desired = sp.getMask();
+ int effective = 0;
+
+ // short circuit if the "*" Permission was added
+ if (all_allowed)
+ {
+ x = (ServicePermission) permissions.get("*");
+ if (x != null)
+ {
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return (true);
+ }
+ }
+
+ // strategy:
+ // Check for full match first. Then work our way up the
+ // name looking for matches on a.b.*
+
+ String name = sp.getName();
+
+ x = (ServicePermission) permissions.get(name);
+
+ if (x != null)
+ {
+ // we have a direct hit!
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return (true);
+ }
+
+ // work our way up the tree...
+ int last, offset;
+
+ offset = name.length()-1;
+
+ while ((last = name.lastIndexOf(".", offset)) != -1)
+ {
+
+ name = name.substring(0, last+1) + "*";
+ x = (ServicePermission) permissions.get(name);
+
+ if (x != null)
+ {
+ effective |= x.getMask();
+ if ((effective & desired) == desired)
+ return (true);
+ }
+ offset = last -1;
+ }
+
+ // we don't have to check for "*" as it was already checked
+ // at the top (all_allowed), so we just return false
+ return (false);
+ }
+
+ /**
+ * Returns an enumeration of all the <tt>ServicePermission</tt> objects in the
+ * container.
+ *
+ * @return Enumeration of all the ServicePermission objects.
+ */
+
+ public Enumeration elements()
+ {
+ return (permissions.elements());
+ }
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceReference.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceReference.java
new file mode 100644
index 000000000..162cf02b4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceReference.java
@@ -0,0 +1,115 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceReference.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000-2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A reference to a service.
+ *
+ * <p> The Framework returns <tt>ServiceReference</tt> objects from the
+ * <tt>BundleContext.getServiceReference</tt> and <tt>BundleContext.getServiceReferences</tt>
+ * methods.
+ * <p>A <tt>ServiceReference</tt> may be shared between bundles and can be used to examine
+ * the properties of the service and to get the service object.
+ * <p>Every service registered in the Framework has a unique <tt>ServiceRegistration</tt> object
+ * and may have multiple, distinct <tt>ServiceReference</tt> objects referring to it.
+ * <tt>ServiceReference</tt> objects associated with a <tt>ServiceRegistration</tt> object have the same
+ * <tt>hashCode</tt> and are considered equal (more specifically, their <tt>equals()</tt>
+ * method will return <tt>true</tt> when compared).
+ * <p>If the same service object is registered
+ * multiple times, <tt>ServiceReference</tt> objects associated with different <tt>ServiceRegistration</tt> objects
+ * are not equal.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @see BundleContext#getServiceReference
+ * @see BundleContext#getServiceReferences
+ * @see BundleContext#getService
+ */
+
+public abstract interface ServiceReference
+{
+ /**
+ * Returns the property value to which the specified property key is mapped in the
+ * properties <tt>Dictionary</tt> object of the service referenced by this <tt>ServiceReference</tt> object.
+ *
+ * <p>Property keys are case-insensitive.
+ *
+ * <p>This method must continue to return property values after the service has been unregistered.
+ * This is so references to unregistered services (for example, <tt>ServiceReference</tt> objects
+ * stored in the log) can still be interrogated.
+ *
+ * @param key The property key.
+ * @return The property value to which the key is mapped; <tt>null</tt> if there is no property
+ * named after the key.
+ */
+ public abstract Object getProperty(String key);
+
+ /**
+ * Returns an array of the keys in the properties <tt>Dictionary</tt> object of the
+ * service referenced by this <tt>ServiceReference</tt> object.
+ *
+ * <p>This method will continue to return the keys after the service has been unregistered. This is so
+ * references to unregistered services (for example, <tt>ServiceReference</tt> objects stored in the
+ * log) can still be interrogated.
+ *
+ * <p> This method is <i>case-preserving</i>; this means that every key in the returned array must have
+ * the same case as the corresponding key in the properties <tt>Dictionary</tt> that was
+ * passed to the {@link BundleContext#registerService} or {@link ServiceRegistration#setProperties} methods.
+ *
+ * @return An array of property keys.
+ */
+ public abstract String[] getPropertyKeys();
+
+ /**
+ * Returns the bundle that registered the service referenced by this <tt>ServiceReference</tt> object.
+ *
+ * <p>This method will always return <tt>null</tt> when the service has been unregistered.
+ * This can be used to determine if the service has been unregistered.
+ *
+ * @return The bundle that registered the service referenced by this <tt>ServiceReference</tt> object;
+ * <tt>null</tt> if that service has already been unregistered.
+ * @see BundleContext#registerService
+ */
+ public abstract Bundle getBundle();
+
+ /**
+ * Returns the bundles that are using the service referenced by this <tt>ServiceReference</tt> object.
+ * Specifically, this method returns the bundles whose usage count for that service is greater
+ * than zero.
+ *
+ * @return An array of bundles whose usage count for the service referenced by
+ * this <tt>ServiceReference</tt> object is greater than zero; <tt>null</tt> if no
+ * bundles are currently using that service.
+ *
+ * @since 1.1
+ */
+ public abstract Bundle[] getUsingBundles();
+}
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceRegistration.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceRegistration.java
new file mode 100644
index 000000000..07445ecf6
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceRegistration.java
@@ -0,0 +1,112 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/ServiceRegistration.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2000, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+import java.util.Dictionary;
+
+/**
+ * A registered service.
+ *
+ * <p> The Framework returns a <tt>ServiceRegistration</tt> object when a
+ * <tt>BundleContext.registerService</tt> method is successful. The <tt>ServiceRegistration</tt>
+ * object is for the private use of the registering bundle and should not be shared with other bundles.
+ * <p>The <tt>ServiceRegistration</tt> object may be used to update the properties of the service or to
+ * unregister the service.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @see BundleContext#registerService
+ */
+
+public abstract interface ServiceRegistration
+{
+ /**
+ * Returns a <tt>ServiceReference</tt> object for a service being registered.
+ * <p>The <tt>ServiceReference</tt> object may be shared with other bundles.
+ *
+ * @exception java.lang.IllegalStateException If this <tt>ServiceRegistration</tt> object
+ * has already been unregistered.
+ * @return <tt>ServiceReference</tt> object.
+ */
+ public abstract ServiceReference getReference();
+
+ /**
+ * Updates the properties associated with a service.
+ *
+ * <p>The {@link Constants#OBJECTCLASS}and {@link Constants#SERVICE_ID}keys cannot be
+ * modified by this method. These values are set by the Framework when the service is
+ * registered in the OSGi environment.
+ *
+ * <p>The following steps are required to modify service properties:
+ * <ol>
+ * <li>The service's properties are replaced with the provided properties.
+ * <li>A service event of type {@link ServiceEvent#MODIFIED}is synchronously sent.
+ * </ol>
+ *
+ * @param properties The properties for this service.
+ * See {@link Constants}for a list of standard service property keys.
+ * Changes should not be made to this object after calling this method.
+ * To update the service's properties this method should be called again.
+ *
+ * @exception IllegalStateException If this <tt>ServiceRegistration</tt> object has already
+ * been unregistered.
+ *
+ * @exception IllegalArgumentException If <tt>properties</tt> contains case
+ * variants of the same key name.
+ */
+ public abstract void setProperties(Dictionary properties);
+
+ /**
+ * Unregisters a service.
+ * Remove a <tt>ServiceRegistration</tt> object from the Framework service registry.
+ * All <tt>ServiceReference</tt> objects associated with this <tt>ServiceRegistration</tt>
+ * object can no longer be used to interact with the service.
+ *
+ * <p>The following steps are required to unregister a service:
+ * <ol>
+ * <li>The service is removed from the Framework service registry so that it can no
+ * longer be used. <tt>ServiceReference</tt> objects for the service may no longer be used
+ * to get a service object for the service.
+ * <li>A service event of type {@link ServiceEvent#UNREGISTERING} is synchronously
+ * sent so that bundles using this service can release their use of it.
+ * <li>For each bundle whose use count for this service is greater than zero:
+ * <br>The bundle's use count for this service is set to zero.
+ * <br>If the service was registered with a {@link ServiceFactory} object, the
+ * <tt>ServiceFactory.ungetService</tt> method is called to release the service object for the bundle.
+ * </ol>
+ *
+ * @exception java.lang.IllegalStateException If this <tt>ServiceRegistration</tt> object has already been unregistered.
+ * @see BundleContext#ungetService
+ * @see ServiceFactory#ungetService
+ */
+ public abstract void unregister();
+}
+
+
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/SynchronousBundleListener.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/SynchronousBundleListener.java
new file mode 100644
index 000000000..5e9264af5
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/SynchronousBundleListener.java
@@ -0,0 +1,55 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/framework/SynchronousBundleListener.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.framework;
+
+/**
+ * A synchronous <tt>BundleEvent</tt> listener.
+ *
+ * <p><tt>SynchronousBundleListener</tt> is a listener interface that may be implemented by a bundle developer.
+ * <p>A <tt>SynchronousBundleListener</tt> object is registered with the Framework using the
+ * {@link BundleContext#addBundleListener}method.
+ * <tt>SynchronousBundleListener</tt> objects are called with a <tt>BundleEvent</tt> object when a bundle has been
+ * installed, started, stopped, updated, or uninstalled.
+ * <p>Unlike normal <tt>BundleListener</tt> objects, <tt>SynchronousBundleListener</tt>s are
+ * synchronously called during bundle life cycle processing. The bundle life cycle
+ * processing will not proceed until all <tt>SynchronousBundleListener</tt>s have
+ * completed. <tt>SynchronousBundleListener</tt> objects will be called prior to
+ * <tt>BundleListener</tt> objects.
+ * <p><tt>AdminPermission</tt> is required to add or remove a <tt>SynchronousBundleListener</tt> object.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ * @since 1.1
+ * @see BundleEvent
+ */
+
+public abstract interface SynchronousBundleListener extends BundleListener
+{
+}
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/ExportedPackage.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/ExportedPackage.java
new file mode 100644
index 000000000..5958031bc
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/ExportedPackage.java
@@ -0,0 +1,101 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/ExportedPackage.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2001).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+package org.osgi.service.packageadmin;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * An exported package.
+ *
+ * Instances implementing this interface are created by the
+ * Package Admin service.
+ *
+ * <p>The information about an exported package provided by
+ * this object is valid only until the next time
+ * <tt>PackageAdmin.refreshPackages()</tt> is
+ * called.
+ * If an <tt>ExportedPackage</tt> object becomes stale (that is, the package it references
+ * has been updated or removed as a result of calling
+ * <tt>PackageAdmin.refreshPackages()</tt>),
+ * its <tt>getName()</tt> and <tt>getSpecificationVersion()</tt> continue to return their
+ * old values, <tt>isRemovalPending()</tt> returns <tt>true</tt>, and <tt>getExportingBundle()</tt>
+ * and <tt>getImportingBundles()</tt> return <tt>null</tt>.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+public interface ExportedPackage {
+
+ /**
+ * Returns the name of the package associated with this <tt>ExportedPackage</tt> object.
+ *
+ * @return The name of this <tt>ExportedPackage</tt> object.
+ */
+ public String getName();
+
+ /**
+ * Returns the bundle exporting the package associated with this <tt>ExportedPackage</tt> object.
+ *
+ * @return The exporting bundle, or <tt>null</tt> if this <tt>ExportedPackage</tt> object
+ * has become stale.
+ */
+ public Bundle getExportingBundle();
+
+ /**
+ * Returns the resolved bundles that are currently importing the package
+ * associated with this <tt>ExportedPackage</tt> object.
+ *
+ * <p> The returned array always includes the bundle returned by
+ * {@link #getExportingBundle}since an exporter always implicitly
+ * imports its exported packages.
+ *
+ * @return The array of resolved bundles currently importing the package
+ * associated with this <tt>ExportedPackage</tt> object, or <tt>null</tt> if this <tt>ExportedPackage</tt>
+ * object has become stale.
+ */
+ public Bundle[] getImportingBundles();
+
+ /**
+ * Returns the specification version of this <tt>ExportedPackage</tt>, as
+ * specified in the exporting bundle's manifest file.
+ *
+ * @return The specification version of this <tt>ExportedPackage</tt> object, or
+ * <tt>null</tt> if no version information is available.
+ */
+ public String getSpecificationVersion();
+
+ /**
+ * Returns <tt>true</tt> if the package associated with this <tt>ExportedPackage</tt> object has been
+ * exported by a bundle that has been updated or uninstalled.
+ *
+ * @return <tt>true</tt> if the associated package is being
+ * exported by a bundle that has been updated or uninstalled, or if this
+ * <tt>ExportedPackage</tt> object has become stale; <tt>false</tt> otherwise.
+ */
+ public boolean isRemovalPending();
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/PackageAdmin.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/PackageAdmin.java
new file mode 100644
index 000000000..1061bcf65
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/PackageAdmin.java
@@ -0,0 +1,164 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/packageadmin/PackageAdmin.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2001, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.service.packageadmin;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Framework service which allows bundle programmers to inspect the packages
+ * exported in the Framework and eagerly update or uninstall bundles.
+ *
+ * If present, there will only be a single instance of this service
+ * registered with the Framework.
+ *
+ * <p>The term <i>exported package</i> (and the corresponding interface
+ * {@link ExportedPackage})refers to a package that has actually been
+ * exported (as opposed to one that is available for export).
+ *
+ * <p>The information about exported packages returned by this
+ * service is valid only until the next time {@link #refreshPackages}is
+ * called.
+ * If an <tt>ExportedPackage</tt> object becomes stale, (that is, the package it references
+ * has been updated or removed as a result of calling
+ * <tt>PackageAdmin.refreshPackages()</tt>),
+ * its <tt>getName()</tt> and <tt>getSpecificationVersion()</tt> continue to return their
+ * old values, <tt>isRemovalPending()</tt> returns <tt>true</tt>, and <tt>getExportingBundle()</tt>
+ * and <tt>getImportingBundles()</tt> return <tt>null</tt>.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+public interface PackageAdmin {
+ /**
+ * Gets the packages exported by the specified bundle.
+ *
+ * @param bundle The bundle whose exported packages are to be returned,
+ * or <tt>null</tt> if all the packages currently
+ * exported in the Framework are to be returned. If the
+ * specified bundle is the system bundle (that is, the
+ * bundle with id zero), this method returns all the packages
+ * on the system classpath whose name does not start with
+ * "java.". In an environment where the exhaustive list
+ * of packages on the system classpath is not known in
+ * advance, this method will return all currently known
+ * packages on the system classpath, that is, all packages
+ * on the system classpath that contains one or more classes
+ * that have been loaded.
+ *
+ * @return The array of packages exported by the specified bundle,
+ * or <tt>null</tt> if the specified bundle has not exported any packages.
+ */
+ public ExportedPackage[] getExportedPackages(Bundle bundle);
+
+ /**
+ * Gets the <tt>ExportedPackage</tt> object with the specified package name. All exported
+ * packages
+ * will be checked for the specified name. In an environment where the
+ * exhaustive list of packages on the system classpath is not known in
+ * advance, this method attempts to see if the named package is on the
+ * system classpath.
+ * This
+ * means that this method may discover an <tt>ExportedPackage</tt> object that was
+ * not present in the list returned by
+ * a prior call to <tt>getExportedPackages()</tt>.
+ *
+ * @param name The name of the exported package to be returned.
+ *
+ * @return The exported package with the specified name, or <tt>null</tt>
+ * if no expored package with that name exists.
+ */
+ public ExportedPackage getExportedPackage(String name);
+
+ /**
+ * Forces the update (replacement) or removal of packages exported by
+ * the specified bundles.
+ *
+ * <p> If no bundles are specified, this method will update or remove any
+ * packages exported by any bundles that were previously updated or
+ * uninstalled since the last call to this method.
+ * The technique by which this is accomplished
+ * may vary among different Framework implementations. One permissible
+ * implementation is to stop and restart the Framework.
+ *
+ * <p> This method returns to the caller immediately and then performs the
+ * following steps in its own thread:
+ *
+ * <ol>
+ * <li> Compute a graph of bundles starting with the specified bundles. If no
+ * bundles are specified, compute a graph of bundles starting with
+ * previously updated or uninstalled ones.
+ * Add to the graph any bundle that imports a package that is currently exported
+ * by a bundle in the graph. The graph is fully
+ * constructed when there is no bundle outside the graph that imports a
+ * package from a bundle in the graph. The graph may contain
+ * <tt>UNINSTALLED</tt> bundles that are currently still
+ * exporting packages.
+ *
+ * <li> Each bundle in the graph that is in the <tt>ACTIVE</tt> state
+ * will be stopped as described in the <tt>Bundle.stop</tt> method.
+ *
+ * <li> Each bundle in the graph that is in the
+ * <tt>RESOLVED</tt> state is moved
+ * to the <tt>INSTALLED</tt> state.
+ * The effect of this step is that bundles in the graph are no longer
+ * <tt>RESOLVED</tt>.
+ *
+ * <li> Each bundle in the graph that is in the <tt>UNINSTALLED</tt> state is
+ * removed from the graph and is now completely removed from the Framework.
+ *
+ * <li> Each bundle in the graph that was in the
+ * <tt>ACTIVE</tt> state prior to Step 2 is started as
+ * described in the <tt>Bundle.start</tt> method, causing all
+ * bundles required for the restart to be resolved.
+ * It is possible that, as a
+ * result of the previous steps, packages that were
+ * previously exported no longer are. Therefore, some bundles
+ * may be unresolvable until another bundle
+ * offering a compatible package for export has been installed in the
+ * Framework.
+ * <li>A framework event of type <tt>FrameworkEvent.PACKAGES_REFRESHED</tt> is broadcast.
+ * </ol>
+ *
+ * <p>For any exceptions that are thrown during any of these steps, a
+ * <tt>FrameworkEvent</tt> of type <tt>ERROR</tt> is
+ * broadcast, containing the exception.
+ * The source bundle for these events should be the specific bundle
+ * to which the exception is related. If no specific bundle can be
+ * associated with the exception then the System Bundle must be used
+ * as the source bundle for the event.
+ *
+ * @param bundles the bundles whose exported packages are to be updated or
+ * removed, or <tt>null</tt> for all previously updated or uninstalled bundles.
+ *
+ * @exception SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void refreshPackages(Bundle[] bundles);
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionAdmin.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionAdmin.java
new file mode 100644
index 000000000..8129f7270
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionAdmin.java
@@ -0,0 +1,133 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionAdmin.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2001, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.service.permissionadmin;
+
+/**
+ * The Permission Admin service allows management agents to
+ * manage the permissions of bundles. There is at most one Permission Admin
+ * service present in the OSGi environment.
+ * <p>
+ * Access to the Permission Admin service is protected by
+ * corresponding
+ * <tt>ServicePermission</tt>. In addition <tt>AdminPermission</tt>
+ * is required to actually set permissions.
+ *
+ * <p>Bundle permissions are managed using a permission table. A bundle's location
+ * serves as the key into this permission table. The value of a table entry is
+ * the set of permissions (of type <tt>PermissionInfo</tt>) granted to the
+ * bundle named by the given location.
+ * A bundle may have an entry in the permission table prior to being installed
+ * in the Framework.
+ *
+ * <p>The permissions specified in <tt>setDefaultPermissions</tt> are used as the
+ * default
+ * permissions which are granted to all bundles that do not have an entry in
+ * the permission table.
+ *
+ * <p>Any changes to a bundle's permissions in the permission table will take
+ * effect no later than when bundle's <tt>java.security.ProtectionDomain</tt>
+ * is next involved in a permission check, and will be made persistent.
+ *
+ * <p>Only permission classes on the system classpath or from an exported
+ * package are considered during a permission check.
+ * Additionally, only permission classes that are subclasses of
+ * <tt>java.security.Permission</tt> and define a 2-argument constructor
+ * that takes a <i>name</i> string and an <i>actions</i> string can be used.
+ * <p>
+ * Permissions implicitly granted by the Framework (for example, a bundle's
+ * permission to access its persistent storage area) cannot be changed, and
+ * are not reflected in the permissions returned by <tt>getPermissions</tt>
+ * and <tt>getDefaultPermissions</tt>.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+public interface PermissionAdmin {
+
+ /**
+ * Gets the permissions assigned to the bundle with the specified
+ * location.
+ *
+ * @param location The location of the bundle whose permissions are to
+ * be returned.
+ *
+ * @return The permissions assigned to the bundle with the specified
+ * location, or <tt>null</tt> if that bundle has not been assigned any
+ * permissions.
+ */
+ PermissionInfo[] getPermissions(String location);
+
+ /**
+ * Assigns the specified permissions to the bundle with the specified
+ * location.
+ *
+ * @param location The location of the bundle that will be assigned the
+ * permissions.
+ * @param permissions The permissions to be assigned, or <tt>null</tt>
+ * if the specified location is to be removed from the permission table.
+ * @exception SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt>.
+ */
+ void setPermissions(String location, PermissionInfo[] permissions);
+
+ /**
+ * Returns the bundle locations that have permissions assigned to them,
+ * that is, bundle locations for which an entry
+ * exists in the permission table.
+ *
+ * @return The locations of bundles that have been assigned any
+ * permissions, or <tt>null</tt> if the permission table is empty.
+ */
+ String[] getLocations();
+
+ /**
+ * Gets the default permissions.
+ *
+ * <p>These are the permissions granted to any bundle that does not
+ * have permissions assigned to its location.
+ *
+ * @return The default permissions, or <tt>null</tt> if no default
+ * permissions are set.
+ */
+ PermissionInfo[] getDefaultPermissions();
+
+ /**
+ * Sets the default permissions.
+ *
+ * <p>These are the permissions granted to any bundle that does not
+ * have permissions assigned to its location.
+ *
+ * @param permissions The default permissions, or <tt>null</tt>
+ * if the default permissions are to be removed from the permission table.
+ * @exception SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt>.
+ */
+ void setDefaultPermissions(PermissionInfo[] permissions);
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionInfo.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionInfo.java
new file mode 100644
index 000000000..43b9b87a1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionInfo.java
@@ -0,0 +1,453 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/permissionadmin/PermissionInfo.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) The Open Services Gateway Initiative (2001, 2002).
+ * All Rights Reserved.
+ *
+ * Implementation of certain elements of the Open Services Gateway Initiative
+ * (OSGI) Specification may be subject to third party intellectual property
+ * rights, including without limitation, patent rights (such a third party may
+ * or may not be a member of OSGi). OSGi is not responsible and shall not be
+ * held responsible in any manner for identifying or failing to identify any or
+ * all such third party intellectual property rights.
+ *
+ * This document and the information contained herein are provided on an "AS
+ * IS" basis and OSGI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ * NOT INFRINGE ANY RIGHTS AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL OSGI BE LIABLE FOR ANY
+ * LOSS OF PROFITS, LOSS OF BUSINESS, LOSS OF USE OF DATA, INTERRUPTION OF
+ * BUSINESS, OR FOR DIRECT, INDIRECT, SPECIAL OR EXEMPLARY, INCIDENTIAL,
+ * PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION WITH THIS
+ * DOCUMENT OR THE INFORMATION CONTAINED HEREIN, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGE.
+ *
+ * All Company, brand and product names may be trademarks that are the sole
+ * property of their respective owners. All rights reserved.
+ */
+
+package org.osgi.service.permissionadmin;
+
+/**
+ * Permission representation used by the Permission Admin service.
+ *
+ * <p>This class encapsulates three pieces of information: a Permission
+ * <i>type</i> (class name), which must be a subclass of
+ * <tt>java.security.Permission</tt>, and the <i>name</i> and <i>actions</i>
+ * arguments passed to its constructor.
+ *
+ * <p>In order for a permission represented by a <tt>PermissionInfo</tt> to be
+ * instantiated and considered during a permission check, its Permission class
+ * must be available from the system classpath or an exported package.
+ * This means that the instantiation of a permission represented by a
+ * <tt>PermissionInfo</tt> may be delayed until the package containing
+ * its Permission class has been exported by a bundle.
+ *
+ * @version $Revision: 1.1 $
+ * @author Open Services Gateway Initiative
+ */
+
+public class PermissionInfo
+{
+ private String type;
+ private String name;
+ private String actions;
+
+ /**
+ * Constructs a <tt>PermissionInfo</tt> from the given type, name, and actions.
+ *
+ * @param type The fully qualified class name of the permission
+ * represented by this <tt>PermissionInfo</tt>. The class must be a subclass of
+ * <tt>java.security.Permission</tt> and
+ * must define a 2-argument constructor that takes a <i>name</i> string
+ * and an <i>actions</i> string.
+ *
+ * @param name The permission name that will be passed as the first
+ * argument to the constructor of the <tt>Permission</tt> class identified by
+ * <tt>type</tt>.
+ *
+ * @param actions The permission actions
+ * that will be passed as the second
+ * argument to the constructor of the <tt>Permission</tt> class identified by
+ * <tt>type</tt>.
+ *
+ * @exception java.lang.NullPointerException if <tt>type</tt> is
+ * <tt>null</tt>.
+ * @exception java.lang.IllegalArgumentException if <tt>action</tt> is not
+ * <tt>null</tt> and <tt>name</tt> is <tt>null</tt>.
+ */
+ public PermissionInfo(String type, String name, String actions)
+ {
+ this.type = type;
+ this.name = name;
+ this.actions = actions;
+
+ if (type == null)
+ {
+ throw new NullPointerException("type is null");
+ }
+ if ((name == null) && (actions != null))
+ {
+ throw new IllegalArgumentException("name missing");
+ }
+ }
+
+ /**
+ * Constructs a <tt>PermissionInfo</tt> object from the given encoded <tt>PermissionInfo</tt> string.
+ *
+ * @param encodedPermission The encoded <tt>PermissionInfo</tt>.
+ * @see #getEncoded
+ * @exception java.lang.IllegalArgumentException if <tt>encodedPermission</tt> is
+ * not properly formatted.
+ */
+ public PermissionInfo(String encodedPermission)
+ {
+ if (encodedPermission == null)
+ {
+ throw new NullPointerException("missing encoded permission");
+ }
+ if (encodedPermission.length() == 0)
+ {
+ throw new IllegalArgumentException("empty encoded permission");
+ }
+
+ try
+ {
+ char[] encoded = encodedPermission.toCharArray();
+
+ /* the first character must be '(' */
+ if (encoded[0] != '(')
+ {
+ throw new IllegalArgumentException("first character not open parenthesis");
+ }
+
+ /* type is not quoted or encoded */
+ int end = 1;
+ int begin = end;
+
+ while ((encoded[end] != ' ') && (encoded[end] != ')'))
+ {
+ end++;
+ }
+
+ if (end == begin)
+ {
+ throw new IllegalArgumentException("expecting type");
+ }
+
+ this.type = new String(encoded, begin, end-begin);
+
+ /* type may be followed by name which is quoted and encoded */
+ if (encoded[end] == ' ')
+ {
+ end++;
+
+ if (encoded[end] != '"')
+ {
+ throw new IllegalArgumentException("expecting quoted name");
+ }
+
+ end++;
+ begin = end;
+
+ while (encoded[end] != '"')
+ {
+ if (encoded[end] == '\\')
+ {
+ end++;
+ }
+
+ end++;
+ }
+
+ this.name = decodeString(encoded, begin, end);
+ end++;
+
+ /* name may be followed by actions which is quoted and encoded */
+ if (encoded[end] == ' ')
+ {
+ end++;
+
+ if (encoded[end] != '"')
+ {
+ throw new IllegalArgumentException("expecting quoted actions");
+ }
+
+ end++;
+ begin = end;
+
+ while (encoded[end] != '"')
+ {
+ if (encoded[end] == '\\')
+ {
+ end++;
+ }
+
+ end++;
+ }
+
+ this.actions = decodeString(encoded, begin, end);
+ end++;
+ }
+ }
+
+ /* the final character must be ')' */
+ if ((encoded[end] != ')') || (end+1 != encoded.length))
+ {
+ throw new IllegalArgumentException("last character not " +
+ "close parenthesis");
+ }
+ }
+ catch (ArrayIndexOutOfBoundsException e)
+ {
+ throw new IllegalArgumentException("parsing terminated abruptly");
+ }
+ }
+
+ /**
+ * Returns the string encoding of this <tt>PermissionInfo</tt> in a
+ * form suitable for restoring this <tt>PermissionInfo</tt>.
+ *
+ * <p>The encoding format is:
+ * <pre>
+ * (type)
+ * </pre>
+ * or
+ * <pre>
+ * (type "<i>name</i>")
+ * </pre>
+ * or
+ * <pre>
+ * (type "<i>name</i>" "<i>actions</i>")
+ * </pre>
+ * where <i>name</i> and <i>actions</i> are strings that are encoded
+ * for proper parsing. Specifically,
+ * the <tt>"</tt>, <tt>\</tt>, carriage return, and linefeed characters are
+ * escaped using <tt>\"</tt>, <tt>\\</tt>, <tt>\r</tt>, and <tt>\n</tt>,
+ * respectively.
+ *
+ * <p>The encoded string must contain no leading or trailing whitespace
+ * characters. A single space character must be used between type and
+ * "<i>name</i>" and between "<i>name</i>" and "<i>actions</i>".
+ *
+ * @return The string encoding of this <tt>PermissionInfo</tt>.
+ */
+ public final String getEncoded()
+ {
+ StringBuffer output =
+ new StringBuffer(8 + type.length() +
+ ((((name == null) ? 0 : name.length()) +
+ ((actions == null) ? 0 : actions.length()))<<1));
+ output.append('(');
+ output.append(type);
+
+ if (name != null)
+ {
+ output.append(" \"");
+ encodeString(name, output);
+ if (actions != null)
+ {
+ output.append("\" \"");
+ encodeString(actions, output);
+ }
+ output.append('\"');
+ }
+
+ output.append(')');
+
+ return(output.toString());
+ }
+
+ /**
+ * Returns the string representation of this <tt>PermissionInfo</tt>.
+ * The string is created by calling the <tt>getEncoded</tt> method on
+ * this <tt>PermissionInfo</tt>.
+ *
+ * @return The string representation of this <tt>PermissionInfo</tt>.
+ */
+ public String toString()
+ {
+ return(getEncoded());
+ }
+
+ /**
+ * Returns the fully qualified class name of the permission
+ * represented by this
+ * <tt>PermissionInfo</tt>.
+ *
+ * @return The fully qualified class name of the permission
+ * represented by this <tt>PermissionInfo</tt>.
+ */
+ public final String getType()
+ {
+ return(type);
+ }
+
+ /**
+ * Returns the name of the permission represented by this
+ * <tt>PermissionInfo</tt>.
+ *
+ * @return The name of the permission represented by this
+ * <tt>PermissionInfo</tt>, or <tt>null</tt> if the permission does not
+ * have a name.
+ */
+ public final String getName()
+ {
+ return(name);
+ }
+
+ /**
+ * Returns the actions of the permission represented by this
+ * <tt>PermissionInfo</tt>.
+ *
+ * @return The actions of the permission represented by this
+ * <tt>PermissionInfo</tt>, or <tt>null</tt> if the permission does
+ * not have any actions associated with it.
+ */
+ public final String getActions()
+ {
+ return(actions);
+ }
+
+ /**
+ * Determines the equality of two <tt>PermissionInfo</tt> objects.
+ *
+ * This method checks that specified object has the same type, name and
+ * actions as this <tt>PermissionInfo</tt> object.
+ *
+ * @param obj The object to test for equality with this
+ * <tt>PermissionInfo</tt> object.
+ * @return <tt>true</tt> if <tt>obj</tt> is a
+ * <tt>PermissionInfo</tt>, and has the same type, name and actions as
+ * this <tt>PermissionInfo</tt> object; <tt>false</tt> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ {
+ return(true);
+ }
+
+ if (!(obj instanceof PermissionInfo))
+ {
+ return(false);
+ }
+
+ PermissionInfo other = (PermissionInfo) obj;
+
+ if (!type.equals(other.type) ||
+ ((name == null) ^ (other.name == null)) ||
+ ((actions == null) ^ (other.actions == null)))
+ {
+ return(false);
+ }
+
+ if (name != null)
+ {
+ if (actions != null)
+ {
+ return(name.equals(other.name) &&
+ actions.equals(other.actions));
+ }
+ else
+ {
+ return(name.equals(other.name));
+ }
+ }
+ else
+ {
+ return(true);
+ }
+ }
+
+ /**
+ * Returns the hash code value for this object.
+ *
+ * @return A hash code value for this object.
+ */
+
+ public int hashCode()
+ {
+ int hash = type.hashCode();
+
+ if (name != null)
+ {
+ hash ^= name.hashCode();
+ if (actions != null)
+ {
+ hash ^= actions.hashCode();
+ }
+ }
+
+ return(hash);
+ }
+
+ /**
+ * This escapes the quotes, backslashes, \n, and \r in the string using a
+ * backslash and appends the newly escaped string to a
+ * StringBuffer.
+ */
+ private static void encodeString(String str, StringBuffer output)
+ {
+ int len = str.length();
+
+ for (int i = 0; i < len; i++)
+ {
+ char c = str.charAt(i);
+
+ switch (c)
+ {
+ case '"':
+ case '\\':
+ output.append('\\');
+ output.append(c);
+ break;
+ case '\r':
+ output.append("\\r");
+ break;
+ case '\n':
+ output.append("\\n");
+ break;
+ default:
+ output.append(c);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Takes an encoded character array and decodes it into a new String.
+ */
+ private static String decodeString(char[] str, int begin, int end)
+ {
+ StringBuffer output = new StringBuffer(end - begin);
+
+ for (int i = begin; i < end; i++)
+ {
+ char c = str[i];
+
+ if (c == '\\')
+ {
+ i++;
+
+ if (i < end)
+ {
+ c = str[i];
+
+ if (c == 'n')
+ {
+ c = '\n';
+ }
+ else if (c == 'r')
+ {
+ c = '\r';
+ }
+ }
+ }
+
+ output.append(c);
+ }
+
+ return(output.toString());
+ }
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/startlevel/StartLevel.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/startlevel/StartLevel.java
new file mode 100644
index 000000000..edc2173e3
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/startlevel/StartLevel.java
@@ -0,0 +1,237 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/startlevel/StartLevel.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) 2002 - IBM Corporation
+ * All Rights Reserved.
+ *
+ * These materials have been contributed to the Open Services Gateway
+ * Initiative (OSGi) as "MEMBER LICENSED MATERIALS" as defined in, and
+ * subject to the terms of, the OSGi Member Agreement by and between OSGi and
+ * IBM, specifically including but not limited to, the license
+ * rights and warranty disclaimers as set forth in Sections 3.2 and 12.1
+ * thereof.
+ *
+ * All company, brand and product names contained within this document may be
+ * trademarks that are the sole property of the respective owners.
+ *
+ * The above notice must be included on all copies of this document that are
+ * made.
+ */
+
+package org.osgi.service.startlevel;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * The StartLevel service allows management agents to manage a start level
+ * assigned to each bundle and the active start level of the Framework.
+ * There is at most one StartLevel service present in the OSGi environment.
+ *
+ * <p>
+ * A start level is defined to be a state of execution in which the Framework
+ * exists. StartLevel values are defined as unsigned integers with 0 (zero)
+ * being the state where the Framework is not launched.
+ * Progressively higher integral values represent
+ * progressively higher start levels. e.g. 2 is a higher start level than 1.
+ * <p>
+ * Access to the StartLevel service is protected by
+ * corresponding
+ * <tt>ServicePermission</tt>. In addition the <tt>AdminPermission</tt>
+ * that is required to actually modify start level information.
+ * <p>
+ * Start Level support in the Framework includes the ability to
+ * control the beginning start level of the Framework, to modify the active
+ * start level of the Framework and to assign a specific start level to a bundle.
+ * How the beginning start level of a
+ * Framework is specified is implementation dependent. It may be a command line
+ * argument when invoking the Framework implementation.
+ * <p>
+ * When the Framework is first started it must be at start level zero.
+ * In this state, no bundles are running. This is the initial state of the
+ * Framework before it is launched.
+ *
+ * When the Framework is launched, the Framework will enter start level one
+ * and all bundles which are assigned to start level one and are
+ * persistently marked to be started are started as described in the
+ * <tt>Bundle.start</tt> method.
+ * Within a start level, bundles are started in ascending order by <tt>Bundle.getBundleId</tt>.
+ * The Framework will continue to increase
+ * the start level, starting bundles at each start level, until the Framework has
+ * reached a beginning start level. At this point the Framework has completed
+ * starting bundles and will then
+ * broadcast a Framework event of type <tt>FrameworkEvent.STARTED</tt>
+ * to announce it has completed its launch.
+ *
+ * <p>
+ * The StartLevel service can be used by management bundles to alter the active start level
+ * of the framework.
+ *
+ * @version $Revision: 1.1 $
+ * @author BJ Hargrave, IBM Corporation (hargrave@us.ibm.com)
+ */
+
+public interface StartLevel
+{
+ /**
+ * Return the active start level value of the Framework.
+ *
+ * If the Framework is in the process of changing the start level
+ * this method must return the active start level if this
+ * differs from the requested start level.
+ *
+ * @return The active start level value of the Framework.
+ */
+ public abstract int getStartLevel();
+
+ /**
+ * Modify the active start level of the Framework.
+ *
+ * <p>The Framework will move to the requested start level. This method
+ * will return immediately to the caller and the start level
+ * change will occur asynchronously on another thread.
+ *
+ * <p>If the specified start level is
+ * higher than the active start level, the
+ * Framework will continue to increase the start level
+ * until the Framework has reached the specified start level,
+ * starting bundles at each
+ * start level which are persistently marked to be started as described in the
+ * <tt>Bundle.start</tt> method.
+ *
+ * At each intermediate start level value on the
+ * way to and including the target start level, the framework must:
+ * <ol>
+ * <li>Change the active start level to the intermediate start level value.
+ * <li>Start bundles at the intermediate start level in
+ * ascending order by <tt>Bundle.getBundleId</tt>.
+ * </ol>
+ * When this process completes after the specified start level is reached,
+ * the Framework will broadcast a Framework event of
+ * type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to announce it has moved to the specified
+ * start level.
+ *
+ * <p>If the specified start level is lower than the active start level, the
+ * Framework will continue to decrease the start level
+ * until the Framework has reached the specified start level
+ * stopping bundles at each
+ * start level as described in the <tt>Bundle.stop</tt> method except that their
+ * persistently recorded state indicates that they must be restarted in the
+ * future.
+ *
+ * At each intermediate start level value on the
+ * way to and including the specified start level, the framework must:
+ * <ol>
+ * <li>Stop bundles at the intermediate start level in
+ * descending order by <tt>Bundle.getBundleId</tt>.
+ * <li>Change the active start level to the intermediate start level value.
+ * </ol>
+ * When this process completes after the specified start level is reached,
+ * the Framework will broadcast a Framework event of
+ * type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to announce it has moved to the specified
+ * start level.
+ *
+ * <p>If the specified start level is equal to the active start level, then
+ * no bundles are started or stopped, however, the Framework must broadcast
+ * a Framework event of type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to
+ * announce it has finished moving to the specified start level. This
+ * event may arrive before the this method return.
+ *
+ * @param startlevel The requested start level for the Framework.
+ * @throws IllegalArgumentException If the specified start level is less than or
+ * equal to zero.
+ * @throws SecurityException If the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public abstract void setStartLevel(int startlevel);
+
+ /**
+ * Return the assigned start level value for the specified Bundle.
+ *
+ * @param bundle The target bundle.
+ * @return The start level value of the specified Bundle.
+ * @exception java.lang.IllegalArgumentException If the specified bundle has been uninstalled.
+ */
+ public abstract int getBundleStartLevel(Bundle bundle);
+
+ /**
+ * Assign a start level value to the specified Bundle.
+ *
+ * <p>The specified bundle will be assigned the specified start level. The
+ * start level value assigned to the bundle will be persistently recorded
+ * by the Framework.
+ *
+ * If the new start level for the bundle is lower than or equal to the active start level of
+ * the Framework, the Framework will start the specified bundle as described
+ * in the <tt>Bundle.start</tt> method if the bundle is persistently marked
+ * to be started. The actual starting of this bundle must occur asynchronously.
+ *
+ * If the new start level for the bundle is higher than the active start level of
+ * the Framework, the Framework will stop the specified bundle as described
+ * in the <tt>Bundle.stop</tt> method except that the persistently recorded
+ * state for the bundle indicates that the bundle must be restarted in the
+ * future. The actual stopping of this bundle must occur asynchronously.
+ *
+ * @param bundle The target bundle.
+ * @param startlevel The new start level for the specified Bundle.
+ * @throws IllegalArgumentException
+ * If the specified bundle has been uninstalled or
+ * if the specified start level is less than or equal to zero, or the specified bundle is
+ * the system bundle.
+ * @throws SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public abstract void setBundleStartLevel(Bundle bundle, int startlevel);
+
+ /**
+ * Return the initial start level value that is assigned
+ * to a Bundle when it is first installed.
+ *
+ * @return The initial start level value for Bundles.
+ * @see #setInitialBundleStartLevel
+ */
+ public abstract int getInitialBundleStartLevel();
+
+ /**
+ * Set the initial start level value that is assigned
+ * to a Bundle when it is first installed.
+ *
+ * <p>The initial bundle start level will be set to the specified start level. The
+ * initial bundle start level value will be persistently recorded
+ * by the Framework.
+ *
+ * <p>When a Bundle is installed via <tt>BundleContext.installBundle</tt>,
+ * it is assigned the initial bundle start level value.
+ *
+ * <p>The default initial bundle start level value is 1
+ * unless this method has been
+ * called to assign a different initial bundle
+ * start level value.
+ *
+ * <p>Thie method does not change the start level values of installed
+ * bundles.
+ *
+ * @param startlevel The initial start level for newly installed bundles.
+ * @throws IllegalArgumentException If the specified start level is less than or
+ * equal to zero.
+ * @throws SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public abstract void setInitialBundleStartLevel(int startlevel);
+
+ /**
+ * Return the persistent state of the specified bundle.
+ *
+ * <p>This method returns the persistent state of a bundle.
+ * The persistent state of a bundle indicates whether a bundle
+ * is persistently marked to be started when it's start level is
+ * reached.
+ *
+ * @return <tt>true</tt> if the bundle is persistently marked to be started,
+ * <tt>false</tt> if the bundle is not persistently marked to be started.
+ * @exception java.lang.IllegalArgumentException If the specified bundle has been uninstalled.
+ */
+ public abstract boolean isBundlePersistentlyStarted(Bundle bundle);
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/AbstractURLStreamHandlerService.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/AbstractURLStreamHandlerService.java
new file mode 100644
index 000000000..f11da7f17
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/AbstractURLStreamHandlerService.java
@@ -0,0 +1,163 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/url/AbstractURLStreamHandlerService.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) 2002 - IBM Corporation
+ * All Rights Reserved.
+ *
+ * These materials have been contributed to the Open Services Gateway
+ * Initiative (OSGi) as "MEMBER LICENSED MATERIALS" as defined in, and
+ * subject to the terms of, the OSGi Member Agreement by and between OSGi and
+ * IBM, specifically including but not limited to, the license
+ * rights and warranty disclaimers as set forth in Sections 3.2 and 12.1
+ * thereof.
+ *
+ * All company, brand and product names contained within this document may be
+ * trademarks that are the sole property of the respective owners.
+ *
+ * The above notice must be included on all copies of this document that are
+ * made.
+ */
+
+package org.osgi.service.url;
+
+import java.net.URL;
+import java.net.URLStreamHandler;
+import java.net.URLConnection;
+import java.net.InetAddress;
+
+/**
+ * Abstract implementation of the <tt>URLStreamHandlerService</tt> interface.
+ * All the methods simply invoke the corresponding methods on <tt>java.net.URLStreamHandler</tt>
+ * except for <tt>parseURL</tt> and <tt>setURL</tt>, which use the <tt>URLStreamHandlerSetter</tt>
+ * parameter. Subclasses of this abstract class should not need to override the <tt>setURL</tt> and
+ * <tt>parseURL(URLStreamHandlerSetter,...)</tt> methods.
+ *
+ * @version $Revision: 1.1 $
+ * @author Ben Reed, IBM Corporation (breed@almaden.ibm.com)
+ */
+public abstract class AbstractURLStreamHandlerService extends URLStreamHandler
+implements URLStreamHandlerService
+{
+ /**
+ * @see "java.net.URLStreamHandler.openConnection"
+ */
+ public abstract URLConnection openConnection(URL u) throws java.io.IOException;
+
+ /**
+ * The <tt>URLStreamHandlerSetter</tt> object passed to the parseURL method.
+ */
+ protected URLStreamHandlerSetter realHandler;
+
+ /**
+ * Parse a URL using the <tt>URLStreamHandlerSetter</tt> object.
+ * This method sets the <tt>realHandler</tt> field with the specified
+ * <tt>URLStreamHandlerSetter</tt> object and then calls
+ * <tt>parseURL(URL,String,int,int)</tt>.
+ * @param realHandler The object on which the <tt>setURL</tt> method must be invoked for
+ * the specified URL.
+ * @see "java.net.URLStreamHandler.parseURL"
+ */
+ public void parseURL(URLStreamHandlerSetter realHandler,
+ URL u, String spec, int start, int limit)
+ {
+ this.realHandler = realHandler;
+ parseURL(u, spec, start, limit);
+ }
+
+ /**
+ * This method calls <tt>super.toExternalForm</tt>.
+ *
+ * @see "java.net.URLStreamHandler.toExternalForm"
+ */
+ public String toExternalForm(URL u)
+ {
+ return super.toExternalForm(u);
+ }
+
+ /**
+ * This method calls <tt>super.equals(URL,URL)</tt>.
+ *
+ * @see "java.net.URLStreamHandler.equals(URL,URL)"
+ */
+ public boolean equals(URL u1, URL u2)
+ {
+ return super.equals(u1, u2);
+ }
+
+ /**
+ * This method calls <tt>super.getDefaultPort</tt>.
+ *
+ * @see "java.net.URLStreamHandler.getDefaultPort"
+ */
+ public int getDefaultPort()
+ {
+ return super.getDefaultPort();
+ }
+
+ /**
+ * This method calls <tt>super.getHostAddress</tt>.
+ *
+ * @see "java.net.URLStreamHandler.getHostAddress"
+ */
+ public InetAddress getHostAddress(URL u)
+ {
+ return super.getHostAddress(u);
+ }
+
+ /**
+ * This method calls <tt>super.hashCode(URL)</tt>.
+ *
+ * @see "java.net.URLStreamHandler.hashCode(URL)"
+ */
+ public int hashCode(URL u)
+ {
+ return super.hashCode(u);
+ }
+
+ /**
+ * This method calls <tt>super.hostsEqual</tt>.
+ *
+ * @see "java.net.URLStreamHandler.hostsEqual"
+ */
+ public boolean hostsEqual(URL u1, URL u2)
+ {
+ return super.hostsEqual(u1, u2);
+ }
+
+ /**
+ * This method calls <tt>super.sameFile</tt>.
+ *
+ * @see "java.net.URLStreamHandler.sameFile"
+ */
+ public boolean sameFile(URL u1, URL u2)
+ {
+ return super.sameFile(u1, u2);
+ }
+
+ /**
+ * This method calls <tt>realHandler.setURL(URL,String,String,int,String,String)</tt>.
+ *
+ * @see "java.net.URLStreamHandler.setURL(URL,String,String,int,String,String)"
+ * @deprecated This method is only for compatibility with handlers written
+ * for JDK 1.1.
+ */
+ protected void setURL(URL u, String proto, String host, int port,
+ String file, String ref)
+ {
+ realHandler.setURL(u, proto, host, port, file, ref);
+ }
+
+ /**
+ * This method calls <tt>realHandler.setURL(URL,String,String,int,String,String,String,String)</tt>.
+ *
+ * @see "java.net.URLStreamHandler.setURL(URL,String,String,int,String,String,String,String)"
+ */
+ protected void setURL(URL u, String proto, String host, int port,
+ String auth, String user, String path,
+ String query, String ref)
+ {
+ realHandler.setURL(u, proto, host, port, auth, user, path,
+ query, ref);
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLConstants.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLConstants.java
new file mode 100644
index 000000000..630c49627
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLConstants.java
@@ -0,0 +1,47 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLConstants.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) 2002 - IBM Corporation
+ * All Rights Reserved.
+ *
+ * These materials have been contributed to the Open Services Gateway
+ * Initiative (OSGi) as "MEMBER LICENSED MATERIALS" as defined in, and
+ * subject to the terms of, the OSGi Member Agreement by and between OSGi and
+ * IBM, specifically including but not limited to, the license
+ * rights and warranty disclaimers as set forth in Sections 3.2 and 12.1
+ * thereof.
+ *
+ * All company, brand and product names contained within this document may be
+ * trademarks that are the sole property of the respective owners.
+ *
+ * The above notice must be included on all copies of this document that are
+ * made.
+ */
+
+package org.osgi.service.url;
+
+/**
+ * Defines standard names for property keys associated
+ * with {@link URLStreamHandlerService} and
+ * <tt>java.net.ContentHandler</tt> services.
+ *
+ * <p>The values associated with these keys are of type <tt>java.lang.String[]</tt>,
+ * unless otherwise indicated.
+ *
+ * @version $Revision: 1.1 $
+ * @author Ben Reed, IBM Corporation (breed@almaden.ibm.com)
+ */
+public interface URLConstants
+{
+ /**
+ * Service property naming the protocols serviced by a URLStreamHandlerService.
+ * The property's value is an array of protocol names.
+ */
+ public static final String URL_HANDLER_PROTOCOL = "url.handler.protocol";
+
+ /**
+ * Service property naming the MIME types serviced by a java.net.ContentHandler.
+ * The property's value is an array of MIME types.
+ */
+ public static final String URL_CONTENT_MIMETYPE = "url.content.mimetype";
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerService.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerService.java
new file mode 100644
index 000000000..0ef863b63
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerService.java
@@ -0,0 +1,96 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerService.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) 2002 - IBM Corporation
+ * All Rights Reserved.
+ *
+ * These materials have been contributed to the Open Services Gateway
+ * Initiative (OSGi) as "MEMBER LICENSED MATERIALS" as defined in, and
+ * subject to the terms of, the OSGi Member Agreement by and between OSGi and
+ * IBM, specifically including but not limited to, the license
+ * rights and warranty disclaimers as set forth in Sections 3.2 and 12.1
+ * thereof.
+ *
+ * All company, brand and product names contained within this document may be
+ * trademarks that are the sole property of the respective owners.
+ *
+ * The above notice must be included on all copies of this document that are
+ * made.
+ */
+
+package org.osgi.service.url;
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.InetAddress;
+
+/**
+ * Service interface with public versions of the protected <tt>java.net.URLStreamHandler</tt>
+ * methods.
+ * <p>
+ * The important differences between this interface and the <tt>URLStreamHandler</tt> class
+ * are that the <tt>setURL</tt> method is absent and the <tt>parseURL</tt> method takes a
+ * {@link URLStreamHandlerSetter} object as the first argument.
+ * Classes implementing this interface must call the <tt>setURL</tt> method on the <tt>URLStreamHandlerSetter</tt>
+ * object received in the <tt>parseURL</tt> method instead of <tt>URLStreamHandler.setURL</tt> to avoid a
+ * <tt>SecurityException</tt>.
+ * @see AbstractURLStreamHandlerService
+ *
+ * @version $Revision: 1.1 $
+ * @author Ben Reed, IBM Corporation (breed@almaden.ibm.com)
+ */
+public interface URLStreamHandlerService
+{
+ /**
+ * @see "java.net.URLStreamHandler.openConnection"
+ */
+ public URLConnection openConnection(URL u) throws java.io.IOException;
+
+ /**
+ * Parse a URL.
+ * This method is called by the <tt>URLStreamHandler</tt> proxy, instead of
+ * <tt>java.net.URLStreamHandler.parseURL</tt>, passing a
+ * <tt>URLStreamHandlerSetter</tt> object.
+ *
+ * @param realHandler The object on which <tt>setURL</tt> must be invoked for
+ * this URL.
+ * @see "java.net.URLStreamHandler.parseURL"
+ */
+ public void parseURL(URLStreamHandlerSetter realHandler,
+ URL u, String spec, int start, int limit);
+
+ /**
+ * @see "java.net.URLStreamHandler.toExternalForm"
+ */
+ public String toExternalForm(URL u);
+
+ /**
+ * @see "java.net.URLStreamHandler.equals(URL, URL)"
+ */
+ public boolean equals(URL u1, URL u2);
+
+ /**
+ * @see "java.net.URLStreamHandler.getDefaultPort"
+ */
+ public int getDefaultPort();
+
+ /**
+ * @see "java.net.URLStreamHandler.getHostAddress"
+ */
+ public InetAddress getHostAddress(URL u);
+
+ /**
+ * @see "java.net.URLStreamHandler.hashCode(URL)"
+ */
+ public int hashCode(URL u);
+
+ /**
+ * @see "java.net.URLStreamHandler.hostsEqual"
+ */
+ public boolean hostsEqual(URL u1, URL u2);
+
+ /**
+ * @see "java.net.URLStreamHandler.sameFile"
+ */
+ public boolean sameFile(URL u1, URL u2);
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerSetter.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerSetter.java
new file mode 100644
index 000000000..3003c4421
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerSetter.java
@@ -0,0 +1,65 @@
+/*
+ * $Header: /home/technology/org.eclipse.equinox/plugins/org.eclipse.osgi/osgi/src/org/osgi/service/url/URLStreamHandlerSetter.java,v 1.1 2003/11/10 17:49:34 jeff Exp $
+ *
+ * Copyright (c) 2002 - IBM Corporation
+ * All Rights Reserved.
+ *
+ * These materials have been contributed to the Open Services Gateway
+ * Initiative (OSGi) as "MEMBER LICENSED MATERIALS" as defined in, and
+ * subject to the terms of, the OSGi Member Agreement by and between OSGi and
+ * IBM, specifically including but not limited to, the license
+ * rights and warranty disclaimers as set forth in Sections 3.2 and 12.1
+ * thereof.
+ *
+ * All company, brand and product names contained within this document may be
+ * trademarks that are the sole property of the respective owners.
+ *
+ * The above notice must be included on all copies of this document that are
+ * made.
+ */
+
+package org.osgi.service.url;
+
+import java.net.URL;
+
+/**
+ * Interface used by <tt>URLStreamHandlerService</tt> objects to call the <tt>setURL</tt> method
+ * on the proxy <tt>URLStreamHandler</tt> object.
+ *
+ * <p>Objects of this type are passed to the {@link URLStreamHandlerService#parseURL}
+ * method.
+ * Invoking the <tt>setURL</tt> method on the <tt>URLStreamHandlerSetter</tt> object will invoke the
+ * <tt>setURL</tt> method on the proxy <tt>URLStreamHandler</tt> object that is actually
+ * registered with <tt>java.net.URL</tt> for the protocol.
+ *
+ * @version $Revision: 1.1 $
+ * @author Ben Reed, IBM Corporation (breed@almaden.ibm.com)
+ */
+public interface URLStreamHandlerSetter
+{
+ /**
+ * @see "java.net.URLStreamHandler.setURL(URL,String,String,int,String,String)"
+ *
+ * @deprecated This method is only for compatibility with handlers written
+ * for JDK 1.1.
+ */
+ public void setURL(URL u,
+ String protocol,
+ String host,
+ int port,
+ String file,
+ String ref);
+
+ /**
+ * @see "java.net.URLStreamHandler.setURL(URL,String,String,int,String,String,String,String)"
+ */
+ public void setURL(URL u,
+ String protocol,
+ String host,
+ int port,
+ String authority,
+ String userInfo,
+ String path,
+ String query,
+ String ref);
+}
diff --git a/bundles/org.eclipse.osgi/plugin.xml b/bundles/org.eclipse.osgi/plugin.xml
new file mode 100644
index 000000000..b04ed2847
--- /dev/null
+++ b/bundles/org.eclipse.osgi/plugin.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin
+ id="org.eclipse.osgi"
+ name="%pluginName"
+ version="3.0.0"
+ provider-name="%providerName">
+
+ <runtime>
+ <library name="osgi.jar">
+ <export name="*"/>
+ </library>
+ <library name="core.jar">
+ </library>
+ <library name="resolver.jar">
+ </library>
+ <library name="defaultAdaptor.jar">
+ </library>
+ <library name="eclipseAdaptor.jar">
+ </library>
+ <library name="console.jar">
+ </library>
+ </runtime>
+
+</plugin>
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IDependency.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IDependency.java
new file mode 100644
index 000000000..a52de24ef
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IDependency.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.dependencies;
+
+/**
+ * Not to be implemented by clients.
+ */
+public interface IDependency {
+ public Object getRequiredObjectId();
+ public Object getRequiredVersionId();
+ public IMatchRule getMatchRule();
+ public boolean isOptional();
+ public Object getResolvedVersionId();
+ public int getChangedMark();
+ public void resolve(Object resolvedVersionId, int changedMark);
+ public Object getUserObject();
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IDependencySystem.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IDependencySystem.java
new file mode 100644
index 000000000..a7ca5cc6d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IDependencySystem.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.dependencies;
+
+import java.util.List;
+
+/**
+ * Not to be implemented by clients.
+ */
+
+public interface IDependencySystem {
+ public class CyclicSystemException extends Exception {
+ public CyclicSystemException(String message) {
+ super(message);
+ }
+ }
+ public IResolutionDelta resolve() throws CyclicSystemException;
+ public IResolutionDelta resolve(boolean produceDelta) throws CyclicSystemException;
+ /**
+ * Returns the delta for the last resolution operation (<code>null</code> if never
+ * resolved or if last resolved with delta production disabled).
+ */
+ public IResolutionDelta getLastDelta();
+ public void addElements(IElement[] elementsToAdd);
+ public void addElement(IElement element);
+ public void removeElements(IElement[] elementsToRemove);
+ public void removeElement(IElement element);
+ public long getElementCount();
+ // returns all resolved elements ordered by pre-requisites
+ public List getResolved();
+ public IElement getElement(Object id, Object identifier);
+ public IElementSet getElementSet(Object id);
+ // factory methods - for the case the client does not have its own implementations for dependencies and elements
+ public IElement createElement(Object id, Object versionId, IDependency[] dependencies, boolean singleton, Object userObject);
+ public IDependency createDependency(Object requiredObjectId, IMatchRule satisfactionRule, Object requiredVersionId, boolean optional, Object userObject);
+ // global access to system version comparator
+ public int compare(Object obj1, Object obj2);
+ public void removeElement(Object id, Object versionId);
+ /**
+ * Forces a set of elements to be unresolved. All dependencies the elements may
+ * have as resolved are also unresolved. Also, any elements currently depending on
+ * the given elements will NOT be automatically unresolved. No delta is generated.
+ * These changes are only temporary, and do not affect the outcome of the next
+ * resolution (besides the generated delta).
+ */
+ public void unresolve(IElement[] elements);
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElement.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElement.java
new file mode 100644
index 000000000..497bf8786
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElement.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.dependencies;
+
+/**
+ * Not to be implemented by clients.
+ */
+public interface IElement {
+ public Object getId();
+ public Object getVersionId();
+ /** @return a non-null reference */
+ public IDependency[] getDependencies();
+ /** may return null */
+ public IDependency getDependency(Object id);
+ public boolean isSingleton();
+ public Object getUserObject();
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElementChange.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElementChange.java
new file mode 100644
index 000000000..ebd2d34d8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElementChange.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.dependencies;
+
+/**
+ * Represents a change that happened to an element's resolution status.
+ * Not to be implemented by clients.
+ */
+public interface IElementChange {
+ /** State transitions. */
+ public final static int ADDED = 0x01;
+ public final static int REMOVED = 0x02;
+ public final static int UPDATED = ADDED | REMOVED;
+ public final static int RESOLVED = 0x04;
+ public final static int UNRESOLVED = 0x08;
+ public final static int LINKAGE_CHANGED = 0x10;
+
+ /**
+ * Returns the affected element.
+ */
+ public IElement getElement();
+ /**
+ * Returns the kind of the transition.
+ */
+ public int getKind();
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElementSet.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElementSet.java
new file mode 100644
index 000000000..784adf414
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IElementSet.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.dependencies;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Not to be implemented by clients.
+ */
+public interface IElementSet {
+ public IDependencySystem getSystem();
+ /** @return false if there is at least one version that is a singleton. */
+ public boolean allowsConcurrency();
+ /**
+ * Returns the unique id for this element set.
+ */
+ public Object getId();
+ /**
+ * Is this a root element set?
+ */
+ public boolean isRoot();
+ /**
+ * Returns all elements available in this element set.
+ */
+ public Set getAvailable();
+ /**
+ * Returns all elements sets required this element set.
+ */
+ public Collection getRequired();
+ /**
+ * Returns all elements sets requiring this element set.
+ */
+ public Collection getRequiring();
+ /**
+ * Returns all elements currently resolved in this element set.
+ */
+ public Set getResolved();
+ /**
+ * Returns all elements currently satisfied in this element set.
+ */
+ public Set getSatisfied();
+ /**
+ * Returns all elements currently selected in this element set.
+ */
+ public Set getSelected();
+ /**
+ * Assumes resolved system - returns all elements whose dependencies
+ * were resolved to point to the specified element.
+ */
+ public Collection getRequiringElements(Object versionId);
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IMatchRule.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IMatchRule.java
new file mode 100644
index 000000000..719960b60
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IMatchRule.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.dependencies;
+
+
+/**
+ * Must be implemented by clients.
+ */
+public interface IMatchRule {
+ public boolean isSatisfied(Object required, Object available);
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IResolutionDelta.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IResolutionDelta.java
new file mode 100644
index 000000000..3d26e8e30
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/IResolutionDelta.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.dependencies;
+
+/**
+ * Collects changes that happened during a resolution operation.
+ * Not to be implemented by clients.
+ * @see IElementChange
+ */
+public interface IResolutionDelta {
+ IElementChange[] getAllChanges();
+ IElementChange getChange(Object id, Object versionId, Object userObject);
+}
+
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/ISelectionPolicy.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/ISelectionPolicy.java
new file mode 100644
index 000000000..14b68cb8d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/dependencies/ISelectionPolicy.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.dependencies;
+
+import java.util.Set;
+
+/**
+ * A policy that determines which elements from an element set will be picked during
+ * the selection stage.
+ * <p>
+ * Clients may implement.
+ * </p>
+ */
+public interface ISelectionPolicy {
+ /**
+ * Returns a set containing the selected elements for the given element set.
+ */
+ public Set selectMultiple(IElementSet elementSet);
+ /**
+ * Returns the selected element for the given element set.
+ */
+ public IElement selectSingle(IElementSet elementSet);
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Assert.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Assert.java
new file mode 100644
index 000000000..23bfeca7f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Assert.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+/**
+ * <code>Assert</code> is useful for for embedding runtime sanity checks
+ * in code.
+ * The predicate methods all test a condition and throw some
+ * type of unchecked exception if the condition does not hold.
+ * <p>
+ * Assertion failure exceptions, like most runtime exceptions, are
+ * thrown when something is misbehaving. Assertion failures are invariably
+ * unspecified behavior; consequently, clients should never rely on
+ * these being thrown (and certainly should not being catching them
+ * specifically).
+ * </p>
+ */
+public final class Assert {
+/* This class is not intended to be instantiated. */
+private Assert() {
+}
+/** Asserts that the given object is not <code>null</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ *
+ * @param object the value to test
+ * @exception IllegalArgumentException if the object is <code>null</code>
+ */
+public static void isNotNull(Object object) {
+ isNotNull(object, ""); //$NON-NLS-1$
+}
+/** Asserts that the given object is not <code>null</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param object the value to test
+ * @param message the message to include in the exception
+ * @exception IllegalArgumentException if the object is <code>null</code>
+ */
+public static void isNotNull(Object object, String message) {
+ if (object == null)
+ throw new NullPointerException("null argument:" + message); //$NON-NLS-1$
+}
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/AssertionFailedException.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/AssertionFailedException.java
new file mode 100644
index 000000000..3171d0a2f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/AssertionFailedException.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+/**
+ * <code>AssertionFailedException</code> is a runtime exception thrown
+ * by some of the methods in <code>Assert</code>.
+ * <p>
+ * This class is not declared public to prevent some misuses; programs that catch
+ * or otherwise depend on assertion failures are susceptible to unexpected
+ * breakage when assertions in the code are added or removed.
+ * </p>
+ */
+/* package */
+class AssertionFailedException extends RuntimeException {
+/** Constructs a new exception.
+ */
+public AssertionFailedException() {
+}
+/** Constructs a new exception with the given message.
+ */
+public AssertionFailedException(String detail) {
+ super(detail);
+}
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ComputeNodeOrder.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ComputeNodeOrder.java
new file mode 100644
index 000000000..8874f104d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ComputeNodeOrder.java
@@ -0,0 +1,521 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+import java.util.*;
+
+/**
+ * Borrowed from org.eclipse.core.internal.resources.ComputeProjectOrder
+ * Implementation of a sort algorithm for computing the node order. This
+ * algorithm handles cycles in the node reference graph in a reasonable way.
+ *
+ * @since 3.0
+ */
+public class ComputeNodeOrder {
+
+ /*
+ * Prevent class from being instantiated.
+ */
+ private ComputeNodeOrder() {
+ }
+
+ /**
+ * A directed graph. Once the vertexes and edges of the graph have been
+ * defined, the graph can be queried for the depth-first finish time of each
+ * vertex.
+ * <p>
+ * Ref: Cormen, Leiserson, and Rivest <it>Introduction to Algorithms</it>,
+ * McGraw-Hill, 1990. The depth-first search algorithm is in section 23.3.
+ * </p>
+ */
+ private static class Digraph {
+ /**
+ * struct-like object for representing a vertex along with various
+ * values computed during depth-first search (DFS).
+ */
+ public static class Vertex {
+ /**
+ * White is for marking vertexes as unvisited.
+ */
+ public static final String WHITE = "white"; //$NON-NLS-1$
+
+ /**
+ * Grey is for marking vertexes as discovered but visit not yet
+ * finished.
+ */
+ public static final String GREY = "grey"; //$NON-NLS-1$
+
+ /**
+ * Black is for marking vertexes as visited.
+ */
+ public static final String BLACK = "black"; //$NON-NLS-1$
+
+ /**
+ * Color of the vertex. One of <code>WHITE</code> (unvisited),
+ * <code>GREY</code> (visit in progress), or <code>BLACK</code>
+ * (visit finished). <code>WHITE</code> initially.
+ */
+ public String color = WHITE;
+
+ /**
+ * The DFS predecessor vertex, or <code>null</code> if there is no
+ * predecessor. <code>null</code> initially.
+ */
+ public Vertex predecessor = null;
+
+ /**
+ * Timestamp indicating when the vertex was finished (became BLACK)
+ * in the DFS. Finish times are between 1 and the number of
+ * vertexes.
+ */
+ public int finishTime;
+
+ /**
+ * The id of this vertex.
+ */
+ public Object id;
+
+ /**
+ * Ordered list of adjacent vertexes. In other words, "this" is the
+ * "from" vertex and the elements of this list are all "to"
+ * vertexes.
+ *
+ * Element type: <code>Vertex</code>
+ */
+ public List adjacent = new ArrayList(3);
+
+ /**
+ * Creates a new vertex with the given id.
+ *
+ * @param id the vertex id
+ */
+ public Vertex(Object id) {
+ this.id = id;
+ }
+ }
+
+ /**
+ * Ordered list of all vertexes in this graph.
+ *
+ * Element type: <code>Vertex</code>
+ */
+ private List vertexList = new ArrayList(100);
+
+ /**
+ * Map from id to vertex.
+ *
+ * Key type: <code>Object</code>; value type: <code>Vertex</code>
+ */
+ private Map vertexMap = new HashMap(100);
+
+ /**
+ * DFS visit time. Non-negative.
+ */
+ private int time;
+
+ /**
+ * Indicates whether the graph has been initialized. Initially
+ * <code>false</code>.
+ */
+ private boolean initialized = false;
+
+ /**
+ * Indicates whether the graph contains cycles. Initially
+ * <code>false</code>.
+ */
+ private boolean cycles = false;
+
+ /**
+ * Creates a new empty directed graph object.
+ * <p>
+ * After this graph's vertexes and edges are defined with
+ * <code>addVertex</code> and <code>addEdge</code>, call
+ * <code>freeze</code> to indicate that the graph is all there, and then
+ * call <code>idsByDFSFinishTime</code> to read off the vertexes ordered
+ * by DFS finish time.
+ * </p>
+ */
+ public Digraph() {
+ }
+
+ /**
+ * Freezes this graph. No more vertexes or edges can be added to this
+ * graph after this method is called. Has no effect if the graph is
+ * already frozen.
+ */
+ public void freeze() {
+ if (!initialized) {
+ initialized = true;
+ // only perform depth-first-search once
+ DFS();
+ }
+ }
+
+ /**
+ * Defines a new vertex with the given id. The depth-first search is
+ * performed in the relative order in which vertexes were added to the
+ * graph.
+ *
+ * @param id the id of the vertex
+ * @exception IllegalArgumentException if the vertex id is
+ * already defined or if the graph is frozen
+ */
+ public void addVertex(Object id) throws IllegalArgumentException {
+ if (initialized) {
+ throw new IllegalArgumentException();
+ }
+ Vertex vertex = new Vertex(id);
+ Object existing = vertexMap.put(id, vertex);
+ // nip problems with duplicate vertexes in the bud
+ if (existing != null) {
+ throw new IllegalArgumentException();
+ }
+ vertexList.add(vertex);
+ }
+
+ /**
+ * Adds a new directed edge between the vertexes with the given ids.
+ * Vertexes for the given ids must be defined beforehand with
+ * <code>addVertex</code>. The depth-first search is performed in the
+ * relative order in which adjacent "to" vertexes were added to a given
+ * "from" index.
+ *
+ * @param fromId the id of the "from" vertex
+ * @param toId the id of the "to" vertex
+ * @exception IllegalArgumentException if either vertex is undefined or
+ * if the graph is frozen
+ */
+ public void addEdge(Object fromId, Object toId)
+ throws IllegalArgumentException {
+ if (initialized) {
+ throw new IllegalArgumentException();
+ }
+ Vertex fromVertex = (Vertex) vertexMap.get(fromId);
+ Vertex toVertex = (Vertex) vertexMap.get(toId);
+ // nip problems with bogus vertexes in the bud
+ if (fromVertex == null) {
+ throw new IllegalArgumentException();
+ }
+ if (toVertex == null) {
+ throw new IllegalArgumentException();
+ }
+ fromVertex.adjacent.add(toVertex);
+ }
+
+ /**
+ * Returns the ids of the vertexes in this graph ordered by depth-first
+ * search finish time. The graph must be frozen.
+ *
+ * @param increasing <code>true</code> if objects are to be arranged
+ * into increasing order of depth-first search finish time, and
+ * <code>false</code> if objects are to be arranged into decreasing
+ * order of depth-first search finish time
+ * @return the list of ids ordered by depth-first search finish time
+ * (element type: <code>Object</code>)
+ * @exception IllegalArgumentException if the graph is not frozen
+ */
+ public List idsByDFSFinishTime(boolean increasing) {
+ if (!initialized) {
+ throw new IllegalArgumentException();
+ }
+ int len = vertexList.size();
+ Object[] r = new Object[len];
+ for (Iterator allV = vertexList.iterator(); allV.hasNext();) {
+ Vertex vertex = (Vertex) allV.next();
+ int f = vertex.finishTime;
+ // note that finish times start at 1, not 0
+ if (increasing) {
+ r[f - 1] = vertex.id;
+ } else {
+ r[len - f] = vertex.id;
+ }
+ }
+ return Arrays.asList(r);
+ }
+
+ /**
+ * Returns whether the graph contains cycles. The graph must be frozen.
+ *
+ * @return <code>true</code> if this graph contains at least one cycle,
+ * and <code>false</code> if this graph is cycle free
+ * @exception IllegalArgumentException if the graph is not frozen
+ */
+ public boolean containsCycles() {
+ if (!initialized) {
+ throw new IllegalArgumentException();
+ }
+ return cycles;
+ }
+
+ /**
+ * Returns the non-trivial components of this graph. A non-trivial
+ * component is a set of 2 or more vertexes that were traversed
+ * together. The graph must be frozen.
+ *
+ * @return the possibly empty list of non-trivial components, where
+ * each component is an array of ids (element type:
+ * <code>Object[]</code>)
+ * @exception IllegalArgumentException if the graph is not frozen
+ */
+ public List nonTrivialComponents() {
+ if (!initialized) {
+ throw new IllegalArgumentException();
+ }
+ // find the roots of each component
+ // Map<Vertex,List<Object>> components
+ Map components = new HashMap();
+ for (Iterator it = vertexList.iterator(); it.hasNext();) {
+ Vertex vertex = (Vertex) it.next();
+ if (vertex.predecessor == null) {
+ // this vertex is the root of a component
+ // if component is non-trivial we will hit a child
+ } else {
+ // find the root ancestor of this vertex
+ Vertex root = vertex;
+ while (root.predecessor != null) {
+ root = root.predecessor;
+ }
+ List component = (List)components.get(root);
+ if (component == null) {
+ component = new ArrayList(2);
+ component.add(root.id);
+ components.put(root, component);
+ }
+ component.add(vertex.id);
+ }
+ }
+ List result = new ArrayList(components.size());
+ for (Iterator it = components.values().iterator(); it.hasNext();) {
+ List component = (List) it.next();
+ if (component.size() > 1) {
+ result.add(component.toArray());
+ }
+ }
+ return result;
+ }
+
+// /**
+// * Performs a depth-first search of this graph and records interesting
+// * info with each vertex, including DFS finish time. Employs a recursive
+// * helper method <code>DFSVisit</code>.
+// * <p>
+// * Although this method is not used, it is the basis of the
+// * non-recursive <code>DFS</code> method.
+// * </p>
+// */
+// private void recursiveDFS() {
+// // initialize
+// // all vertex.color initially Vertex.WHITE;
+// // all vertex.predecessor initially null;
+// time = 0;
+// for (Iterator allV = vertexList.iterator(); allV.hasNext();) {
+// Vertex nextVertex = (Vertex) allV.next();
+// if (nextVertex.color == Vertex.WHITE) {
+// DFSVisit(nextVertex);
+// }
+// }
+// }
+//
+// /**
+// * Helper method. Performs a depth first search of this graph.
+// *
+// * @param vertex the vertex to visit
+// */
+// private void DFSVisit(Vertex vertex) {
+// // mark vertex as discovered
+// vertex.color = Vertex.GREY;
+// List adj = vertex.adjacent;
+// for (Iterator allAdjacent=adj.iterator(); allAdjacent.hasNext();) {
+// Vertex adjVertex = (Vertex) allAdjacent.next();
+// if (adjVertex.color == Vertex.WHITE) {
+// // explore edge from vertex to adjVertex
+// adjVertex.predecessor = vertex;
+// DFSVisit(adjVertex);
+// } else if (adjVertex.color == Vertex.GREY) {
+// // back edge (grey vertex means visit in progress)
+// cycles = true;
+// }
+// }
+// // done exploring vertex
+// vertex.color = Vertex.BLACK;
+// time++;
+// vertex.finishTime = time;
+// }
+
+ /**
+ * Performs a depth-first search of this graph and records interesting
+ * info with each vertex, including DFS finish time. Does not employ
+ * recursion.
+ */
+ private void DFS() {
+ // state machine rendition of the standard recursive DFS algorithm
+ int state;
+ final int NEXT_VERTEX = 1;
+ final int START_DFS_VISIT = 2;
+ final int NEXT_ADJACENT = 3;
+ final int AFTER_NEXTED_DFS_VISIT = 4;
+ // use precomputed objects to avoid garbage
+ final Integer NEXT_VERTEX_OBJECT = new Integer(NEXT_VERTEX);
+ final Integer AFTER_NEXTED_DFS_VISIT_OBJECT =
+ new Integer(AFTER_NEXTED_DFS_VISIT);
+ // initialize
+ // all vertex.color initially Vertex.WHITE;
+ // all vertex.predecessor initially null;
+ time = 0;
+ // for a stack, append to the end of an array-based list
+ List stack = new ArrayList(Math.max(1, vertexList.size()));
+ Iterator allAdjacent = null;
+ Vertex vertex = null;
+ Iterator allV = vertexList.iterator();
+ state = NEXT_VERTEX;
+ nextStateLoop : while (true) {
+ switch (state) {
+ case NEXT_VERTEX:
+ // on entry, "allV" contains vertexes yet to be visited
+ if (!allV.hasNext()) {
+ // all done
+ break nextStateLoop;
+ }
+ Vertex nextVertex = (Vertex) allV.next();
+ if (nextVertex.color == Vertex.WHITE) {
+ stack.add(NEXT_VERTEX_OBJECT);
+ vertex = nextVertex;
+ state = START_DFS_VISIT;
+ continue nextStateLoop;
+ } else {
+ state = NEXT_VERTEX;
+ continue nextStateLoop;
+ }
+ case START_DFS_VISIT:
+ // on entry, "vertex" contains the vertex to be visited
+ // top of stack is return code
+ // mark the vertex as discovered
+ vertex.color = Vertex.GREY;
+ allAdjacent = vertex.adjacent.iterator();
+ state = NEXT_ADJACENT;
+ continue nextStateLoop;
+ case NEXT_ADJACENT:
+ // on entry, "allAdjacent" contains adjacent vertexes to
+ // be visited; "vertex" contains vertex being visited
+ if (allAdjacent.hasNext()) {
+ Vertex adjVertex = (Vertex) allAdjacent.next();
+ if (adjVertex.color == Vertex.WHITE) {
+ // explore edge from vertex to adjVertex
+ adjVertex.predecessor = vertex;
+ stack.add(allAdjacent);
+ stack.add(vertex);
+ stack.add(AFTER_NEXTED_DFS_VISIT_OBJECT);
+ vertex = adjVertex;
+ state = START_DFS_VISIT;
+ continue nextStateLoop;
+ }
+ if (adjVertex.color == Vertex.GREY) {
+ // back edge (grey means visit in progress)
+ cycles = true;
+ }
+ state = NEXT_ADJACENT;
+ continue nextStateLoop;
+ } else {
+ // done exploring vertex
+ vertex.color = Vertex.BLACK;
+ time++;
+ vertex.finishTime = time;
+ state =
+ ((Integer) stack.remove(stack.size() - 1))
+ .intValue();
+ continue nextStateLoop;
+ }
+ case AFTER_NEXTED_DFS_VISIT:
+ // on entry, stack contains "vertex" and "allAjacent"
+ vertex = (Vertex) stack.remove(stack.size() - 1);
+ allAdjacent = (Iterator) stack.remove(stack.size() - 1);
+ state = NEXT_ADJACENT;
+ continue nextStateLoop;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Sorts the given list of probject in a manner that honors the given
+ * project reference relationships. That is, if project A references project
+ * B, then the resulting order will list B before A if possible. For graphs
+ * that do not contain cycles, the result is the same as a conventional
+ * topological sort. For graphs containing cycles, the order is based on
+ * ordering the strongly connected components of the graph. This has the
+ * effect of keeping each knot of projects together without otherwise
+ * affecting the order of projects not involved in a cycle. For a graph G,
+ * the algorithm performs in O(|G|) space and time.
+ * <p>
+ * When there is an arbitrary choice, vertexes are ordered as supplied.
+ * Arranged projects in descending alphabetical order generally results in
+ * an order that builds "A" before "Z" when there are no other constraints.
+ * </p>
+ * <p> Ref: Cormen, Leiserson, and Rivest <it>Introduction to
+ * Algorithms</it>, McGraw-Hill, 1990. The strongly-connected-components
+ * algorithm is in section 23.5.
+ * </p>
+ *
+ * @param projects a list of projects (element type:
+ * <code>IProject</code>)
+ * @param references a list of project references [A,B] meaning that A
+ * references B (element type: <code>IProject[]</code>)
+ * @return an object describing the resulting project order
+ */
+ public static Object[][] computeNodeOrder(
+ Object[] objects,
+ Object[][] references) {
+
+ // Step 1: Create the graph object.
+ final Digraph g1 = new Digraph();
+ // add vertexes
+ for (int i = 0; i < objects.length; i++)
+ g1.addVertex(objects[i]);
+ // add edges
+ for (int i = 0; i < references.length; i++)
+ // create an edge from q to p
+ // to cause q to come before p in eventual result
+ g1.addEdge(references[i][1], references[i][0]);
+ g1.freeze();
+
+ // Step 2: Create the transposed graph. This time, define the vertexes
+ // in decreasing order of depth-first finish time in g1
+ // interchange "to" and "from" to reverse edges from g1
+ final Digraph g2 = new Digraph();
+ // add vertexes
+ List resortedVertexes = g1.idsByDFSFinishTime(false);
+ for (Iterator it = resortedVertexes.iterator(); it.hasNext(); )
+ g2.addVertex(it.next());
+ // add edges
+ for (int i = 0; i < references.length; i++)
+ g2.addEdge(references[i][0], references[i][1]);
+ g2.freeze();
+
+ // Step 3: Return the vertexes in increasing order of depth-first finish
+ // time in g2
+ List sortedProjectList = g2.idsByDFSFinishTime(true);
+ Object[] orderedNodes = new Object[sortedProjectList.size()];
+ sortedProjectList.toArray(orderedNodes);
+ Object[][] knots;
+ boolean hasCycles = g2.containsCycles();
+ if (hasCycles) {
+ List knotList = g2.nonTrivialComponents();
+ knots = (Object[][]) knotList.toArray(new Object[knotList.size()][]);
+ } else {
+ knots = new Object[0][];
+ }
+ for (int i = 0; i < orderedNodes.length; i++)
+ objects[i] = orderedNodes[i];
+ return knots;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Dependency.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Dependency.java
new file mode 100644
index 000000000..f7e2edef8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Dependency.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dependencies;
+
+import org.eclipse.core.dependencies.IDependency;
+import org.eclipse.core.dependencies.IMatchRule;
+
+class Dependency implements IDependency {
+ private int changedMark;
+ private Object requiredObjectId;
+ private Object requiredVersionId;
+ private IMatchRule matchRule;
+ private boolean optional;
+ private Object resolvedVersionId;
+ private Object userObject;
+ public Dependency(Object requiredObjectId, IMatchRule matchRule, Object requiredVersionId, boolean optional, Object userObject) {
+ Assert.isNotNull(requiredObjectId);
+ Assert.isNotNull(matchRule);
+ this.requiredObjectId = requiredObjectId;
+ this.requiredVersionId = requiredVersionId;
+ this.matchRule = requiredVersionId == null ? new UnspecifiedVersionMatchRule() : matchRule;
+ this.optional = optional;
+ this.userObject = userObject;
+ }
+ /**
+ * @see IDependency#getMatchRule()
+ */
+ public IMatchRule getMatchRule() {
+ return this.matchRule;
+ }
+ /**
+ * @see IDependency#getRequiredObjectId()
+ */
+ public Object getRequiredObjectId() {
+ return this.requiredObjectId;
+ }
+ /**
+ * @see IDependency#getRequiredVersionId()
+ */
+ public Object getRequiredVersionId() {
+ return this.requiredVersionId;
+ }
+ /**
+ * @see IDependency#isOptional()
+ */
+ public boolean isOptional() {
+ return this.optional;
+ }
+ /**
+ * @see IDependency#getResolvedVersionId()
+ */
+ public Object getResolvedVersionId() {
+ return resolvedVersionId;
+ }
+ public void resolve(Object resolvedVersionId, int changedMark) {
+ if ((resolvedVersionId == null && this.resolvedVersionId == null) || (resolvedVersionId != null && resolvedVersionId.equals(this.resolvedVersionId)))
+ return;
+ this.resolvedVersionId = resolvedVersionId;
+ this.changedMark = changedMark;
+ }
+ public int getChangedMark() {
+ return changedMark;
+ }
+ public String toString() {
+ return " -> " + getRequiredObjectId() + "_" + getRequiredVersionId() + " (" + getMatchRule() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+ public Object getUserObject() {
+ return userObject;
+ }
+ class UnspecifiedVersionMatchRule implements IMatchRule {
+ public boolean isSatisfied(Object required, Object available) {
+ return true;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/DependencySystem.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/DependencySystem.java
new file mode 100644
index 000000000..41d8c1a39
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/DependencySystem.java
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dependencies;
+
+import java.util.*;
+
+import org.eclipse.core.dependencies.*;
+
+public class DependencySystem implements IDependencySystem {
+ public final static int SATISFACTION = 0;
+ public final static int SELECTION = 1;
+ public final static int RESOLUTION = 2;
+ public final static int UP_TO_DATE = Integer.MAX_VALUE;
+
+ private long elementCount;
+ private int mark;
+
+ private ResolutionDelta lastDelta = new ResolutionDelta();
+ private ResolutionDelta delta = new ResolutionDelta();
+ private Comparator comparator;
+
+ // id-accessible element sets collection
+ private Map elementSets = new HashMap();
+ // the policy to be used in the selection stage
+ private ISelectionPolicy selectionPolicy;
+ // should we print debug messages?
+ private boolean debug;
+
+ /**
+ * Uses a user-provided comparator for comparing versions.
+ */
+ public DependencySystem(Comparator comparator, ISelectionPolicy selectionPolicy, boolean debug) {
+ this.comparator = comparator;
+ this.selectionPolicy = selectionPolicy;
+ this.debug = debug;
+ }
+ public DependencySystem(Comparator comparator, ISelectionPolicy selectionPolicy) {
+ this(comparator, selectionPolicy, false);
+ }
+ public IElementSet getElementSet(Object id) {
+ ElementSet elementSet = (ElementSet) this.elementSets.get(id);
+ // create an element set for the given id if one does not exist yet
+ if (elementSet == null)
+ this.elementSets.put(id, elementSet = new ElementSet(id, this));
+ return elementSet;
+ }
+ public Collection discoverRoots() {
+ Collection roots = new LinkedList();
+ for (Iterator elementSetsIter = elementSets.values().iterator(); elementSetsIter.hasNext();) {
+ ElementSet elementSet = (ElementSet) elementSetsIter.next();
+ if (elementSet.isRoot())
+ roots.add(elementSet);
+ }
+ return roots;
+ }
+ /**
+ * Determines which versions of each element set are resolved.
+ */
+ public IResolutionDelta resolve() throws CyclicSystemException {
+ return this.resolve(true);
+ }
+ public IResolutionDelta resolve(boolean produceDelta) throws CyclicSystemException {
+ Collection roots = discoverRoots();
+ // traverse from roots to leaves - returns leaves
+ Collection satisfied = visit(roots, new SatisfactionVisitor(SATISFACTION));
+ // traverse from leaves to roots - returns roots
+ Collection selected = visit(satisfied, new SelectionVisitor(SELECTION, this.selectionPolicy));
+ // traverse from roots to leaves - returns leaves (result is ignored)
+ visit(selected, new ResolutionVisitor(RESOLUTION));
+ this.lastDelta = this.delta;
+ this.delta = new ResolutionDelta();
+ pruneEmptySets();
+ return this.lastDelta;
+ }
+ // clean up any dangling element sets that were removed and are not required by anybody
+ private void pruneEmptySets() {
+ for (Iterator elementSetsIter = elementSets.values().iterator(); elementSetsIter.hasNext();) {
+ ElementSet elementSet = (ElementSet) elementSetsIter.next();
+ if (elementSet.getElementCount() == 0 && elementSet.getRequiringCount() == 0)
+ elementSetsIter.remove();
+ }
+ }
+ /**
+ * Traverses a graph starting from the given element sets.
+ * Returns a set containing all leaf element sets that satisfied the visitor.
+ */
+ public Collection visit(Collection elementSets, IElementSetVisitor visitor) throws CyclicSystemException {
+ int visitCounter = 0;
+ int mark = getNewMark(visitor.getOrder());
+ if (elementSets.isEmpty())
+ return Collections.EMPTY_SET;
+ Collection leaves = new LinkedList();
+ while (!elementSets.isEmpty()) {
+ Collection nextLevel = new LinkedList();
+ // first visit all element sets in the given set
+ for (Iterator elementSetsIter = elementSets.iterator(); elementSetsIter.hasNext();) {
+ ElementSet elementSet = (ElementSet) elementSetsIter.next();
+ // skip if already visited
+ if (mark == elementSet.getVisitedMark())
+ continue;
+
+ // last time was visited it has been changed, need to recompute
+ // only a change in a previous phase causes the next phase to need to recompute
+ if (elementSet.getVisitedMark() == elementSet.getChangedMark() && visitor.getOrder() > getVisitorOrder(elementSet.getChangedMark()))
+ elementSet.markNeedingUpdate(visitor.getOrder());
+ boolean shouldVisit = true;
+ for (Iterator ancestorIter = visitor.getAncestors(elementSet).iterator(); ancestorIter.hasNext();) {
+ ElementSet ancestorNode = (ElementSet) ancestorIter.next();
+ if (ancestorNode.getVisitedMark() != mark) {
+ // one ancestor element set has not been visited yet - bail out
+ shouldVisit = false;
+ break;
+ }
+ if (ancestorNode.getChangedMark() == mark)
+ // ancestor has changed - we need to recompute
+ elementSet.markNeedingUpdate(visitor.getOrder());
+ }
+ if (!shouldVisit)
+ continue;
+
+ elementSet.setVisitedMark(mark);
+
+ // only update if necessary
+ if (elementSet.isNeedingUpdate(visitor.getOrder()))
+ visitor.update(elementSet);
+
+ visitCounter++;
+
+ if (visitor.getDescendants(elementSet).isEmpty())
+ leaves.add(elementSet);
+ else
+ nextLevel.addAll(visitor.getDescendants(elementSet));
+ }
+ elementSets = nextLevel;
+ }
+ // if visited more nodes than exist in the graph, a cycle has been found
+ // XXX: is this condition enough for detecting a cycle?
+ if (visitCounter != this.elementSets.size())
+ throw new CyclicSystemException(getCycleString());
+ return leaves;
+ }
+ // temporary hack (using ComputeNodeOrder) to find out what the cycles are
+ public String getCycleString() {
+ // find cycles
+ IElementSet[] nodes = (IElementSet[]) elementSets.values().toArray(new IElementSet[elementSets.size()]);
+ ArrayList dependencies = new ArrayList();
+ for (int i = 0; i < nodes.length; i++) {
+ for (Iterator required = nodes[i].getRequiring().iterator();required.hasNext();)
+ dependencies.add(new Object[] {nodes[i],required.next()});
+ }
+ Object[][] cycles = ComputeNodeOrder.computeNodeOrder(nodes,(Object[][])dependencies.toArray(new Object[dependencies.size()][]));
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < cycles.length; i++) {
+ result.append("{");
+ for (int j = 0; j < cycles[i].length; j++) {
+ result.append(((IElementSet)cycles[i][j]).getId());
+ result.append(",");
+ }
+ result.deleteCharAt(result.length() - 1);
+ result.append("},");
+ }
+ result.deleteCharAt(result.length() - 1);
+ return result.toString();
+ }
+
+ private int getVisitorOrder(int mark) {
+ return mark & 0xFF;
+ }
+ private int getNewMark(int order) {
+ mark = mark % 0xFF + 1;
+ return (mark << 8) + (order & 0xFF);
+ }
+
+ public void addElements(IElement[] elementsToAdd) {
+ for (int i = 0; i < elementsToAdd.length; i++)
+ addElement(elementsToAdd[i]);
+ }
+ public void addElement(IElement element) {
+ ((ElementSet) this.getElementSet(element.getId())).addElement(element);
+ this.elementCount++;
+ }
+ public void removeElements(IElement[] elementsToRemove) {
+ for (int i = 0; i < elementsToRemove.length; i++)
+ removeElement(elementsToRemove[i]);
+ }
+ public void removeElement(Object id, Object versionId) {
+ ElementSet elementSet = (ElementSet) elementSets.get(id);
+ if (elementSet == null)
+ return;
+ elementSet.removeElement(versionId);
+ }
+ public void removeElement(IElement element) {
+ ElementSet elementSet = (ElementSet) elementSets.get(element.getId());
+ if (elementSet == null)
+ return;
+ elementSet.removeElement(element);
+ }
+ public long getElementCount() {
+ return elementCount;
+ }
+ public Map getNodes() {
+ return this.elementSets;
+ }
+ public List getResolved() {
+ int mark = getNewMark(RESOLUTION);
+ Collection elementSets = discoverRoots();
+ if (elementSets.isEmpty())
+ return Collections.EMPTY_LIST;
+ final List resolved = new LinkedList();
+ while (!elementSets.isEmpty()) {
+ Collection nextLevel = new LinkedList();
+ for (Iterator elementSetsIter = elementSets.iterator(); elementSetsIter.hasNext();) {
+ ElementSet elementSet = (ElementSet) elementSetsIter.next();
+ // skip if already visited
+ if (mark == elementSet.getVisitedMark())
+ continue;
+ Collection resolvedInSet = elementSet.getResolved();
+ // ignore node (and requiring nodes) if none of its elements are resolved
+ if (resolvedInSet.isEmpty())
+ continue;
+ boolean shouldVisit = true;
+ for (Iterator ancestorIter = elementSet.getRequired().iterator(); ancestorIter.hasNext();) {
+ ElementSet ancestorNode = (ElementSet) ancestorIter.next();
+ if (ancestorNode.getVisitedMark() != mark) {
+ // one ancestor element set has not been visited yet - bail out
+ shouldVisit = false;
+ break;
+ }
+ }
+ if (!shouldVisit)
+ continue;
+ elementSet.setVisitedMark(mark);
+ resolved.addAll(resolvedInSet);
+ nextLevel.addAll(elementSet.getRequiring());
+ }
+ elementSets = nextLevel;
+ }
+ return resolved;
+ }
+ public String toString() {
+ StringBuffer result = new StringBuffer();
+ for (Iterator elementSetsIter = elementSets.values().iterator(); elementSetsIter.hasNext();) {
+ IElementSet elementSet = (IElementSet) elementSetsIter.next();
+ for (Iterator elementsIter = elementSet.getAvailable().iterator(); elementsIter.hasNext();) {
+ IElement element = (IElement) elementsIter.next();
+ result.append(element + ": " + Arrays.asList(element.getDependencies())); //$NON-NLS-1$
+ result.append(',');
+ }
+ result.deleteCharAt(result.length() - 1);
+ result.append('\n');
+ }
+ return result.toString();
+ }
+ void recordElementStatusChanged(IElement element, int kind) {
+ this.delta.recordChange(element, kind);
+ }
+ void recordDependencyChanged(Collection oldResolved, Collection newResolved, Map present) {
+ for (Iterator oldResolvedIter = oldResolved.iterator(); oldResolvedIter.hasNext();) {
+ IElement element = (IElement) oldResolvedIter.next();
+ if (!newResolved.contains(element))
+ this.delta.recordChange(element, IElementChange.UNRESOLVED);
+ }
+ for (Iterator newResolvedIter = newResolved.iterator(); newResolvedIter.hasNext();) {
+ IElement element = (IElement) newResolvedIter.next();
+ if (!oldResolved.contains(element))
+ this.delta.recordChange(element, IElementChange.RESOLVED);
+ }
+ }
+ public IElement getElement(Object id, Object versionId) {
+ ElementSet elementSet = (ElementSet) elementSets.get(id);
+ if (elementSet == null)
+ return null;
+ return elementSet.getElement(versionId);
+ }
+
+ public IElement createElement(Object id, Object versionId, IDependency[] dependencies, boolean singleton, Object userObject) {
+ return new Element(id, versionId, dependencies, singleton, userObject);
+ }
+ public IDependency createDependency(Object requiredObjectId, IMatchRule satisfactionRule, Object requiredVersionId, boolean optional, Object userObject) {
+ return new Dependency(requiredObjectId, satisfactionRule, requiredVersionId, optional, userObject);
+ }
+ public int compare(Object obj1, Object obj2) {
+ return comparator.compare(obj1, obj2);
+ }
+ public IResolutionDelta getLastDelta() {
+ return lastDelta;
+ }
+ boolean inDebugMode() {
+ return debug;
+ }
+ public Collection getRequiringElements(IElement required) {
+ ElementSet containing = (ElementSet) getElementSet(required.getId());
+ return containing.getRequiringElements(required.getVersionId());
+ }
+ public void unresolve(IElement[] elements) {
+ int mark = getNewMark(RESOLUTION);
+ for (int i = 0; i < elements.length; i++) {
+ ElementSet set = (ElementSet) getElementSet(elements[i].getId());
+ if (set == null)
+ return;
+ set.unresolve(elements[i],mark);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Element.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Element.java
new file mode 100644
index 000000000..67c619396
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/Element.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dependencies;
+
+import org.eclipse.core.dependencies.IDependency;
+import org.eclipse.core.dependencies.IElement;
+
+class Element implements IElement {
+ private Object id;
+ private Object versionId;
+ private IDependency[] dependencies;
+ private boolean singleton;
+ private Object userObject;
+ public Element(Object id, Object versionId, IDependency[] dependencies, boolean singleton, Object userObject) {
+ Assert.isNotNull(id);
+ Assert.isNotNull(versionId);
+ Assert.isNotNull(dependencies);
+ this.id = id;
+ this.versionId = versionId;
+ this.dependencies = dependencies;
+ this.singleton = singleton;
+ this.userObject = userObject;
+ }
+ public Object getId() {
+ return id;
+ }
+ public Object getVersionId() {
+ return versionId;
+ }
+ public IDependency[] getDependencies() {
+ return dependencies;
+ }
+ public IDependency getDependency(Object id) {
+ for (int i = 0; i < dependencies.length; i++)
+ if (dependencies[i].getRequiredObjectId().equals(id))
+ return dependencies[i];
+ return null;
+ }
+ public boolean isSingleton() {
+ return singleton;
+ }
+ public Object getUserObject() {
+ return userObject;
+ }
+ public String toString() {
+ return this.id + "_" + this.versionId; //$NON-NLS-1$
+ }
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Element))
+ return false;
+ Element other = (Element) obj;
+ return (other.userObject != null && other.userObject.equals(this.userObject)) || (this.id.equals(other.id) && this.versionId.equals(other.versionId) && other.userObject == null && this.userObject == null);
+ }
+ public int hashCode() {
+ return (id.hashCode() << 16) | (versionId.hashCode() & 0xFFFF);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ElementChange.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ElementChange.java
new file mode 100644
index 000000000..f2866e981
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ElementChange.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+import org.eclipse.core.dependencies.IElement;
+import org.eclipse.core.dependencies.IElementChange;
+
+class ElementChange implements IElementChange {
+ private IElement element;
+ private int kind;
+ ElementChange(IElement element, int kind) {
+ this.element = element;
+ this.kind = kind;
+ }
+ public Object getVersionId() {
+ return element.getVersionId();
+ }
+ public int getKind() {
+ return kind;
+ }
+ public IElement getElement() {
+ return element;
+ }
+ public String toString() {
+ StringBuffer result = new StringBuffer();
+ result.append(element.getId());
+ result.append('_');
+ result.append(getVersionId());
+ result.append(" ("); //$NON-NLS-1$
+ result.append(getStatusName(getKind()));
+ result.append(')');
+ return result.toString();
+ }
+ private String getStatusName(int status) {
+ StringBuffer statusStr = new StringBuffer();
+ if ((status & ADDED) != 0)
+ statusStr.append("ADDED|");
+ if ((status & REMOVED) != 0)
+ statusStr.append("REMOVED|");
+ if ((status & RESOLVED) != 0)
+ statusStr.append("RESOLVED|");
+ if ((status & UNRESOLVED) != 0)
+ statusStr.append("UNRESOLVED|");
+ if ((status & LINKAGE_CHANGED) != 0)
+ statusStr.append("LINKAGE_CHANGED|");
+ if (statusStr.length() == 0)
+ statusStr.append("UNKNOWN");
+ else
+ statusStr.deleteCharAt(statusStr.length() - 1);
+ return statusStr.toString();
+ }
+ void setKind(int kind) {
+ this.kind = kind;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ElementSet.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ElementSet.java
new file mode 100644
index 000000000..4e73112e4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ElementSet.java
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dependencies;
+
+import java.util.*;
+
+import org.eclipse.core.dependencies.*;
+
+public class ElementSet implements IElementSet {
+ private DependencySystem system;
+ private Object id;
+ // # of iteration this element set was last visited
+ transient private int visitedMark;
+ // # of iteration this element set was last changed
+ transient private int changedMark;
+ // does this element set need to be recomputed? (the value is one of {UP_TO_DATE,SATISFACTION,SELECTION,RESOLUTION})
+ transient private int needingUpdate;
+ private int singletonsCount;
+ private Collection requiring;
+ private Collection required;
+ private Map available;
+ private Set satisfied;
+ private Set selected;
+ private Set resolved;
+ private Map dependencyCounters;
+ public ElementSet(Object id, DependencySystem system) {
+ this.id = id;
+ this.system = system;
+ this.setNeedingUpdate(DependencySystem.SATISFACTION);
+ this.available = new HashMap();
+ this.satisfied = Collections.EMPTY_SET;
+ this.selected = Collections.EMPTY_SET;
+ this.resolved = Collections.EMPTY_SET;
+ this.required = new LinkedList();
+ this.requiring = new LinkedList();
+ this.dependencyCounters = new HashMap();
+ }
+ public IDependencySystem getSystem() {
+ return system;
+ }
+ /** @return false if there is at least one version that does not allow concurrency. */
+ public boolean allowsConcurrency() {
+ return singletonsCount == 0;
+ }
+ void addElement(IElement element) {
+ if (this.available.containsKey(element.getVersionId()))
+ return;
+ this.setNeedingUpdate(DependencySystem.SATISFACTION);
+ this.available.put(element.getVersionId(), element);
+ IDependency[] dependencies = element.getDependencies();
+ for (int i = 0; i < dependencies.length; i++)
+ this.addRequired(dependencies[i].getRequiredObjectId());
+ if (element.isSingleton())
+ this.singletonsCount++;
+ system.recordElementStatusChanged(element, IElementChange.ADDED);
+ }
+ void removeElement(IElement element) {
+ removeElement(element.getVersionId());
+ }
+ void removeElement(Object versionId) {
+ IElement toRemove = (IElement) this.available.remove(versionId);
+ if (toRemove == null)
+ return;
+ this.markNeedingUpdate(DependencySystem.SATISFACTION);
+ IDependency[] dependencies = toRemove.getDependencies();
+ for (int i = 0; i < dependencies.length; i++)
+ removeRequired(dependencies[i].getRequiredObjectId());
+ // if does not allow concurrency, decrement preventingConcurrencyCount
+ if (toRemove.isSingleton())
+ this.singletonsCount--;
+ int change = IElementChange.REMOVED;
+// if (resolved.contains(toRemove))
+// change |= IElementChange.UNRESOLVED;
+ system.recordElementStatusChanged(toRemove, change);
+ }
+ public Object getId() {
+ return id;
+ }
+ public boolean isRoot() {
+ return required.isEmpty();
+ }
+ public Set getAvailable() {
+ return new HashSet(available.values());
+ }
+ public Collection getRequired() {
+ return required;
+ }
+
+ public Collection getRequiring() {
+ return requiring;
+ }
+
+ public Set getResolved() {
+ return resolved;
+ }
+ public void resolveDependency(IElement dependent, IDependency dependency, Object resolvedVersionId) {
+ dependency.resolve(resolvedVersionId, this.visitedMark);
+ }
+ public void setResolved(Set newResolved) {
+ this.setNeedingUpdate(DependencySystem.UP_TO_DATE);
+
+ //TODO: this may well be optimized...
+ // maybe just a pre-requisite changed
+ for (Iterator resolvedIter = this.resolved.iterator(); resolvedIter.hasNext();) {
+ IElement resolvedElement = (IElement) resolvedIter.next();
+ IDependency[] dependencies = resolvedElement.getDependencies();
+ for (int i = 0; i < dependencies.length; i++)
+ if (dependencies[i].getChangedMark() == this.getVisitedMark()) {
+ system.recordElementStatusChanged(resolvedElement, IElementChange.LINKAGE_CHANGED);
+ break;
+ }
+ }
+ if (newResolved.equals(this.resolved))
+ return;
+ this.setChangedMark(visitedMark);
+ Collection oldResolved = this.resolved;
+ this.resolved = Collections.unmodifiableSet(newResolved);
+ system.recordDependencyChanged(oldResolved, newResolved, available);
+ }
+ public Set getSelected() {
+ return selected;
+ }
+ public void setSelected(Set selected) {
+ this.setNeedingUpdate(DependencySystem.RESOLUTION);
+ if (selected.equals(this.selected))
+ return;
+ this.setChangedMark(visitedMark);
+ this.selected = Collections.unmodifiableSet(selected);
+ }
+ public Set getSatisfied() {
+ return satisfied;
+ }
+ public void setSatisfied(Set satisfied) {
+ this.setNeedingUpdate(DependencySystem.SELECTION);
+ if (satisfied.equals(this.satisfied))
+ return;
+ this.setChangedMark(visitedMark);
+ this.satisfied = Collections.unmodifiableSet(satisfied);
+ }
+ public String toString() {
+ return this.id + ": " + available; //$NON-NLS-1$
+ }
+ public boolean equals(Object elementSet) {
+ return ((IElementSet) elementSet).getId().equals(this.id);
+ }
+ public int hashCode() {
+ return this.id.hashCode();
+ }
+ class DependencyCounter {
+ int value;
+ }
+ private void addRequired(Object requiredId) {
+ this.setNeedingUpdate(DependencySystem.SATISFACTION);
+ ElementSet requiredNode = (ElementSet) system.getElementSet(requiredId);
+ DependencyCounter counter = (DependencyCounter) this.dependencyCounters.get(requiredId);
+ if (counter == null) {
+ this.dependencyCounters.put(requiredId, counter = new DependencyCounter());
+ // links requiring and required element sets in both directions
+ this.required.add(requiredNode);
+ requiredNode.requiring.add(this);
+ requiredNode.setNeedingUpdate(Math.min(requiredNode.getNeedingUpdate(),DependencySystem.SELECTION));
+ }
+ counter.value++;
+ }
+ private void removeRequired(Object requiredId) {
+ ElementSet requiredNode = (ElementSet) system.getElementSet(requiredId);
+ DependencyCounter counter = (DependencyCounter) this.dependencyCounters.get(requiredId);
+ if (counter == null) {
+ if (system.inDebugMode())
+ System.err.println("Trying to remove non-existent dependency: " + this.id + " -> " + requiredId); //$NON-NLS-1$ //$NON-NLS-2$
+ return;
+ }
+ counter.value--;
+ if (counter.value == 0) {
+ this.dependencyCounters.remove(requiredId);
+ // removes links between requiring and required element sets
+ this.required.remove(requiredNode);
+ requiredNode.requiring.remove(this);
+ requiredNode.setNeedingUpdate(DependencySystem.SELECTION);
+ }
+ }
+ int getRequiringCount() {
+ return requiring.size();
+ }
+ int getElementCount() {
+ return available.size();
+ }
+ int getVisitedMark() {
+ return visitedMark;
+ }
+ void setVisitedMark(int mark) {
+ this.visitedMark = mark;
+ }
+ int getChangedMark() {
+ return changedMark;
+ }
+ private void setChangedMark(int mark) {
+ this.changedMark = mark;
+ }
+ void markNeedingUpdate(int order) {
+ setNeedingUpdate(order);
+ }
+ boolean isNeedingUpdate(int order) {
+ return getNeedingUpdate() <= order;
+ }
+ IElement getElement(Object versionId) {
+ return (IElement) this.available.get(versionId);
+ }
+ public Collection getRequiringElements(Object versionId) {
+ Collection result = new LinkedList();
+ for (Iterator requiringSetsIter = requiring.iterator(); requiringSetsIter.hasNext();) {
+ ElementSet requiringSet = (ElementSet) requiringSetsIter.next();
+ for (Iterator iter = requiringSet.getResolved().iterator(); iter.hasNext();) {
+ Element element = (Element) iter.next();
+ IDependency requisite = element.getDependency(this.id);
+ if (requisite != null && versionId.equals(requisite.getResolvedVersionId()))
+ result.add(element);
+ }
+ }
+ return result;
+ }
+ public void unresolve(IElement element, int mark) {
+ setVisitedMark(mark);
+ if (!resolved.contains(element))
+ return;
+ Set newResolved = new HashSet(resolved);
+ newResolved.remove(element);
+ resolved = Collections.unmodifiableSet(newResolved);
+
+ IDependency[] dependencies = element.getDependencies();
+ // unresolved dependencies
+ for (int i = 0; i < dependencies.length; i++)
+ resolveDependency(element,dependencies[i],null);
+ setChangedMark(mark);
+ setNeedingUpdate(DependencySystem.SATISFACTION);
+ }
+ private void setNeedingUpdate(int needingUpdate) {
+ this.needingUpdate = needingUpdate;
+ }
+ private int getNeedingUpdate() {
+ return needingUpdate;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/IElementSetVisitor.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/IElementSetVisitor.java
new file mode 100644
index 000000000..5175f7a2c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/IElementSetVisitor.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+import java.util.Collection;
+
+/**
+ * Not to be implemented by clients.
+ */
+public interface IElementSetVisitor {
+ public int getOrder();
+ public abstract void update(ElementSet node);
+ public abstract Collection getAncestors(ElementSet node);
+ public abstract Collection getDescendants(ElementSet node);
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ResolutionDelta.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ResolutionDelta.java
new file mode 100644
index 000000000..6764b7fa5
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ResolutionDelta.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.dependencies.*;
+
+public class ResolutionDelta implements IResolutionDelta {
+ private Map changes;
+ ResolutionDelta() {
+ this.changes = new HashMap();
+ }
+ public IElementChange[] getAllChanges() {
+ return (IElementChange[]) changes.values().toArray(new IElementChange[changes.size()]);
+ }
+ public IElementChange getChange(Object id, Object versionId, Object userObject) {
+ return (IElementChange) changes.get(new ElementIdentifier(id, versionId, userObject));
+ }
+ class ElementIdentifier {
+ private Object id;
+ private Object versionId;
+ private Object userObject;
+ ElementIdentifier(Object id, Object versionId, Object userObject) {
+ this.id = id;
+ this.versionId = versionId;
+ this.userObject = userObject;
+ }
+ public int hashCode() {
+ return (id.hashCode() << 16) | (versionId.hashCode() & 0xFFFF);
+ }
+ public boolean equals(Object anObject) {
+ if (!(anObject instanceof ElementIdentifier))
+ return false;
+ ElementIdentifier change = (ElementIdentifier) anObject;
+ return (change.userObject != null && change.userObject.equals(this.userObject)) || (this.id.equals(change.id) && this.versionId.equals(change.versionId) && change.userObject == null && this.userObject == null);
+ }
+ }
+ /**
+ * Record a new status change.
+ */
+ void recordChange(IElement element, int kind) {
+ // check if a change has already been recorded for the element
+ ElementChange existingChange = (ElementChange) this.getChange(element.getId(), element.getVersionId(), element.getUserObject());
+ // if not, just record it and we are done
+ if (existingChange == null) {
+ this.changes.put(new ElementIdentifier(element.getId(), element.getVersionId(), element.getUserObject()), new ElementChange(element, kind));
+ return;
+ }
+ // a removal cancels any existing addition
+ if (kind == IElementChange.REMOVED)
+ if (existingChange.getKind() == IElementChange.ADDED) {
+ // if it was just an addition, just forget the change
+ this.changes.remove(new ElementIdentifier(element.getId(), element.getVersionId(), element.getUserObject()));
+ return;
+ } else if ((existingChange.getKind() & IElementChange.ADDED) != 0) {
+ // if it was an addition among other things, forget the addition bit, and ensure the removal bit is set
+ existingChange.setKind((existingChange.getKind() & ~IElementChange.ADDED) | IElementChange.REMOVED);
+ return;
+ }
+ // otherwise, just update the new status for the existing change object
+ existingChange.setKind(existingChange.getKind() | kind);
+ }
+ public String toString() {
+ return changes.values().toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ResolutionVisitor.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ResolutionVisitor.java
new file mode 100644
index 000000000..7ce30c281
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/ResolutionVisitor.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+import java.util.*;
+
+import org.eclipse.core.dependencies.*;
+
+public class ResolutionVisitor implements IElementSetVisitor {
+ private int order;
+ public ResolutionVisitor(int order) {
+ this.order = order;
+ }
+ public int getOrder() {
+ return order;
+ }
+ public Collection getAncestors(ElementSet elementSet) {
+ return elementSet.getRequired();
+ }
+ public Collection getDescendants(ElementSet elementSet) {
+ return elementSet.getRequiring();
+ }
+ public void update(ElementSet elementSet) {
+ // if there are no pre-requisites, all selected versions are resolved
+ if (elementSet.getRequired().isEmpty()) {
+ elementSet.setResolved(elementSet.getSelected());
+ return;
+ }
+ Set resolved = new HashSet();
+
+ for (Iterator elementsIter = elementSet.getSelected().iterator(); elementsIter.hasNext();) {
+ IElement element = (IElement) elementsIter.next();
+ IDependency[] dependencies = element.getDependencies();
+ boolean versionResolved = true;
+ for (int i = 0; i < dependencies.length; i++) {
+ IElementSet requiredNode = elementSet.getSystem().getElementSet(dependencies[i].getRequiredObjectId());
+ List requiredNodeResolvedVersions = new ArrayList(requiredNode.getResolved());
+ Object highestRequiredVersionId = null;
+ for (Iterator requiredNodeResolvedVersionsIter = requiredNodeResolvedVersions.iterator(); requiredNodeResolvedVersionsIter.hasNext();) {
+ IElement requiredResolvedVersion = (IElement) requiredNodeResolvedVersionsIter.next();
+ if (dependencies[i].getMatchRule().isSatisfied(dependencies[i].getRequiredVersionId(), requiredResolvedVersion.getVersionId()))
+ if (highestRequiredVersionId == null || elementSet.getSystem().compare(requiredResolvedVersion.getVersionId(), highestRequiredVersionId) > 0)
+ highestRequiredVersionId = requiredResolvedVersion.getVersionId();
+ }
+ if (highestRequiredVersionId == null && !dependencies[i].isOptional()) {
+ versionResolved = false;
+ break;
+ }
+ // new version id will be null if dependency cannot be satisfied but is optional
+ elementSet.resolveDependency(element, dependencies[i], highestRequiredVersionId);
+ }
+ if (versionResolved)
+ resolved.add(element);
+ }
+ elementSet.setResolved(resolved);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SatisfactionVisitor.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SatisfactionVisitor.java
new file mode 100644
index 000000000..7348a5999
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SatisfactionVisitor.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+import java.util.*;
+
+import org.eclipse.core.dependencies.*;
+
+public class SatisfactionVisitor implements IElementSetVisitor {
+ private int order;
+ public SatisfactionVisitor(int order) {
+ this.order = order;
+ }
+ public int getOrder() {
+ return order;
+ }
+ public Collection getAncestors(ElementSet elementSet) {
+ return elementSet.getRequired();
+ }
+ public Collection getDescendants(ElementSet elementSet) {
+ return elementSet.getRequiring();
+ }
+ public void update(ElementSet elementSet) {
+ // if there are no pre-requisites, all available versions are satisfied
+ if (elementSet.getRequired().isEmpty()) {
+ elementSet.setSatisfied(elementSet.getAvailable());
+ return;
+ }
+
+ Set satisfied = new HashSet();
+ for (Iterator elementsIter = elementSet.getAvailable().iterator(); elementsIter.hasNext();) {
+ IElement element = (IElement) elementsIter.next();
+ IDependency[] dependencies = element.getDependencies();
+ boolean versionSatisfied = true;
+ for (int i = 0; i < dependencies.length; i++) {
+ // optional pre-requisites are not relevant for satisfaction
+ if (dependencies[i].isOptional())
+ continue;
+
+ IElementSet requiredNode = elementSet.getSystem().getElementSet(dependencies[i].getRequiredObjectId());
+
+ Collection requiredNodeSatisfiedVersions = requiredNode.getSatisfied();
+ boolean depSatisfied = false;
+ for (Iterator requiredNodeSatisfiedVersionsIter = requiredNodeSatisfiedVersions.iterator(); requiredNodeSatisfiedVersionsIter.hasNext();) {
+ IElement requiredSatisfiedVersion = (IElement) requiredNodeSatisfiedVersionsIter.next();
+ if (dependencies[i].getMatchRule().isSatisfied(dependencies[i].getRequiredVersionId(), requiredSatisfiedVersion.getVersionId())) {
+ depSatisfied = true;
+ break;
+ }
+ }
+ if (!depSatisfied) {
+ versionSatisfied = false;
+ break;
+ }
+ }
+ if (versionSatisfied)
+ satisfied.add(element);
+ }
+ elementSet.setSatisfied(satisfied);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SelectionVisitor.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SelectionVisitor.java
new file mode 100644
index 000000000..0371168c7
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SelectionVisitor.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.dependencies;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.core.dependencies.IElement;
+import org.eclipse.core.dependencies.ISelectionPolicy;
+
+/**
+ * Clients may override to provide alternative policies.
+ */
+public class SelectionVisitor implements IElementSetVisitor {
+ private int order;
+ private ISelectionPolicy selectionPolicy;
+ public SelectionVisitor(int order, ISelectionPolicy selectionPolicy) {
+ this.order = order;
+ this.selectionPolicy = selectionPolicy;
+ }
+ public int getOrder() {
+ return order;
+ }
+ public final Collection getAncestors(ElementSet elementSet) {
+ return elementSet.getRequiring();
+ }
+ public final Collection getDescendants(ElementSet elementSet) {
+ return elementSet.getRequired();
+ }
+ public void update(ElementSet elementSet) {
+
+ // no versions satisfied, so no versions selected
+ if (elementSet.getSatisfied().isEmpty()) {
+ elementSet.setSelected(Collections.EMPTY_SET);
+ return;
+ }
+ // all versions allow concurrency - select only those which are required, or the highest
+ if (elementSet.allowsConcurrency()) {
+ elementSet.setSelected(this.selectionPolicy.selectMultiple(elementSet));
+ return;
+ }
+ // otherwise, we must pick a single one (if any)
+ IElement selected = this.selectionPolicy.selectSingle(elementSet);
+ elementSet.setSelected(selected == null ? Collections.EMPTY_SET : Collections.singleton(selected));
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SimpleSelectionPolicy.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SimpleSelectionPolicy.java
new file mode 100644
index 000000000..f319ae6a6
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/core/internal/dependencies/SimpleSelectionPolicy.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.dependencies;
+
+import java.util.*;
+
+import org.eclipse.core.dependencies.*;
+
+/**
+ * Simple selection policy.
+ */
+
+public class SimpleSelectionPolicy implements ISelectionPolicy {
+ public Set selectMultiple(IElementSet elementSet) {
+ // all satisfied are selected
+ return new HashSet(elementSet.getSatisfied());
+ }
+ public IElement selectSingle(IElementSet elementSet) {
+ // just pick the satisfied element with the highest version
+ IElement highest = null;
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ if (highest == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(), highest.getVersionId()) > 0)
+ highest = satisfiedVersion;
+ }
+ return highest;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDeltaImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDeltaImpl.java
new file mode 100644
index 000000000..0ba042564
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDeltaImpl.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.service.resolver.BundleDelta;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+
+public class BundleDeltaImpl implements BundleDelta {
+
+ private BundleDescription bundleDescription;
+ private int type;
+
+ public BundleDeltaImpl(BundleDescription bundleDescription) {
+ this(bundleDescription,0);
+ }
+ public BundleDeltaImpl(BundleDescription bundleDescription, int type) {
+ this.bundleDescription = bundleDescription;
+ this.type = type;
+ }
+ public BundleDescription getBundle() {
+ return bundleDescription;
+ }
+ public int getType() {
+ return type;
+ }
+ public void setType(int type) {
+ this.type = type;
+ }
+ public String toString() {
+ return bundleDescription.getUniqueId() + '_' + bundleDescription.getVersion() + " (" + toTypeString(type) + ")";
+ }
+ private static String toTypeString(int type) {
+ StringBuffer typeStr = new StringBuffer();
+ if ((type & BundleDelta.ADDED) != 0)
+ typeStr.append("ADDED,");
+ if ((type & BundleDelta.REMOVED) != 0)
+ typeStr.append("REMOVED,");
+ if ((type & BundleDelta.RESOLVED) != 0)
+ typeStr.append("RESOLVED,");
+ if ((type & BundleDelta.UNRESOLVED) != 0)
+ typeStr.append("UNRESOLVED,");
+ if ((type & BundleDelta.LINKAGE_CHANGED) != 0)
+ typeStr.append("LINKAGE_CHANGED,");
+ if ((type & BundleDelta.UPDATED) != 0)
+ typeStr.append("UPDATED,");
+ if (typeStr.length() > 0)
+ typeStr.deleteCharAt(typeStr.length() - 1);
+ return typeStr.toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java
new file mode 100644
index 000000000..5e6f6a2a1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleDescriptionImpl.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+
+import org.eclipse.osgi.framework.internal.core.KeyedElement;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Bundle;
+
+public class BundleDescriptionImpl implements BundleDescription, KeyedElement {
+ private long bundleId = -1;
+ private String uniqueId;
+ private String location;
+ private int state;
+ private Version version;
+ private HostSpecification host;
+ private PackageSpecification[] packages;
+ private String[] providedPackages;
+ private BundleSpecification[] requiredBundles;
+ private State containingState;
+
+ public BundleDescriptionImpl() {
+ }
+ public String getLocation() {
+ return location;
+ }
+ public boolean isResolved() {
+ return (state & Bundle.RESOLVED) != 0;
+ }
+ public State getContainingState() {
+ return containingState;
+ }
+ public int getState() {
+ return state;
+ }
+ public Version getVersion() {
+ return version;
+ }
+ public BundleDescription[] getFragments() {
+ if (host != null)
+ return new BundleDescription[0];
+ return ((StateImpl) containingState).getFragments(this);
+ }
+ public Dictionary getManifest() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+ public HostSpecification getHost() {
+ return host;
+ }
+ public void setContainingState(State value) {
+ containingState = value;
+ }
+ public void setHost(HostSpecification host) {
+ this.host = host;
+ if (host != null)
+ ((VersionConstraintImpl) host).setBundle(this);
+ }
+ public void setLocation(String location) {
+ this.location = location;
+ }
+ public void setPackages(PackageSpecification[] packages) {
+ this.packages = packages;
+ if (packages != null)
+ for (int i = 0; i < packages.length; i++)
+ ((VersionConstraintImpl) packages[i]).setBundle(this);
+ }
+ public void setProvidedPackages(String[] providedPackages) {
+ this.providedPackages = providedPackages;
+ }
+ public void setRequiredBundles(BundleSpecification[] requiredBundles) {
+ this.requiredBundles = requiredBundles;
+ if (requiredBundles != null)
+ for (int i = 0; i < requiredBundles.length; i++)
+ ((VersionConstraintImpl) requiredBundles[i]).setBundle(this);
+ }
+ public void setState(int state) {
+ this.state = state;
+ }
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+ public PackageSpecification[] getPackages() {
+ if (packages == null)
+ return new PackageSpecification[0];
+ return packages;
+ }
+ public String[] getProvidedPackages() {
+ if (providedPackages == null)
+ return new String[0];
+ return providedPackages;
+ }
+ public BundleSpecification[] getRequiredBundles() {
+ if (requiredBundles == null)
+ return new BundleSpecification[0];
+ return requiredBundles;
+ }
+ public BundleSpecification getRequiredBundle(String name) {
+ if (requiredBundles == null)
+ return null;
+ for (int i = 0; i < requiredBundles.length; i++)
+ if (requiredBundles[i].getName().equals(name))
+ return requiredBundles[i];
+ return null;
+ }
+ public String getUniqueId() {
+ return uniqueId;
+ }
+ public PackageSpecification getPackage(String name) {
+ if (packages == null)
+ return null;
+ for (int i = 0; i < packages.length; i++)
+ if (packages[i].getName().equals(name))
+ return packages[i];
+ return null;
+ }
+ public String toString() {
+ return getUniqueId() + "_" + getVersion();
+ }
+ public long getBundleId() {
+ return bundleId;
+ }
+ public void setBundleId(long bundleId) {
+ this.bundleId = bundleId;
+ }
+ public Object getKey() {
+ return new Long(bundleId);
+ }
+ public boolean compare(KeyedElement other) {
+ if (!(other instanceof BundleDescriptionImpl))
+ return false;
+ BundleDescriptionImpl otherBundleDescription = (BundleDescriptionImpl) other;
+ return bundleId == otherBundleDescription.bundleId;
+ }
+ public int getKeyHashCode() {
+ return (int) (bundleId % Integer.MAX_VALUE);
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.osgi.service.resolver.BundleDescription#getUnsatisfiedConstraints
+ */
+ public VersionConstraint[] getUnsatisfiedConstraints() {
+ if (containingState == null)
+ // it is a bug in the client to call this method when not attached to a state
+ throw new IllegalStateException("Does not belong to a state"); //$NON-NLS-1$
+ ArrayList unsatisfied = new ArrayList();
+ if (host != null && host.isResolved() && !isResolvable(host))
+ unsatisfied.add(host);
+ if (requiredBundles != null)
+ for (int i = 0; i < requiredBundles.length; i++)
+ if (!requiredBundles[i].isResolved() && !isResolvable(requiredBundles[i]))
+ unsatisfied.add(requiredBundles[i]);
+ if (packages != null)
+ for (int i = 0; i < packages.length; i++)
+ if (!packages[i].isResolved() && !isResolvable(packages[i]))
+ unsatisfied.add(packages[i]);
+ return (VersionConstraint[]) unsatisfied.toArray(new VersionConstraint[unsatisfied.size()]);
+ }
+ /**
+ * @param specification
+ * @return
+ */
+ private boolean isResolvable(PackageSpecification specification) {
+ if (specification.isExported())
+ return true;
+ PackageSpecification exported = ((StateImpl) containingState).getExportedPackage(specification.getName(), null);
+ if (exported == null)
+ return false;
+ return specification.isSatisfiedBy(exported.getVersionSpecification());
+ }
+ private boolean isResolvable(VersionConstraint specification) {
+ BundleDescription[] availableBundles = containingState.getBundles(specification.getName());
+ for (int i = 0; i < availableBundles.length; i++)
+ if (availableBundles[i].isResolved() && specification.isSatisfiedBy(availableBundles[i].getVersion()))
+ return true;
+ return false;
+ }
+
+ public int hashCode() {
+ if (uniqueId == null)
+ return (int) (bundleId % Integer.MAX_VALUE);
+ return (int) ((bundleId * (uniqueId.hashCode())) % Integer.MAX_VALUE);
+ }
+ public boolean equals(Object object) {
+ if (!(object instanceof BundleDescription))
+ return false;
+ BundleDescription other = (BundleDescription) object;
+ return this.bundleId == other.getBundleId() && (this.uniqueId == null & other.getUniqueId() == null || this.uniqueId != null && this.uniqueId.equals(other.getUniqueId()));
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleSpecificationImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleSpecificationImpl.java
new file mode 100644
index 000000000..ed5fe8372
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/BundleSpecificationImpl.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+
+public class BundleSpecificationImpl extends VersionConstraintImpl implements BundleSpecification {
+ private boolean exported;
+ private boolean optional;
+ public void setExported(boolean exported) {
+ this.exported = exported;
+ }
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+ public boolean isExported() {
+ return exported;
+ }
+ public boolean isOptional() {
+ return optional;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/Eclipse21SelectionPolicy.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/Eclipse21SelectionPolicy.java
new file mode 100644
index 000000000..7b845d18f
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/Eclipse21SelectionPolicy.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+
+import org.eclipse.core.dependencies.*;
+
+public class Eclipse21SelectionPolicy implements ISelectionPolicy {
+ public Set selectMultiple(IElementSet elementSet) {
+ Set selected = new HashSet();
+ for (Iterator requiringIter = elementSet.getRequiring().iterator(); requiringIter.hasNext();) {
+ IElementSet requiringNode = (IElementSet) requiringIter.next();
+ Collection requiringNodeSelectedVersions = requiringNode.getSelected();
+ // loop through the selected versions (one if it is a non-library plug-in) of this requiring plug-in
+ for (Iterator requiringVersionsIter = requiringNodeSelectedVersions.iterator(); requiringVersionsIter.hasNext();) {
+ IElement requiringSelectedVersion = (IElement) requiringVersionsIter.next();
+ // the selected version may not require this element set (but it still can be selected)
+ IDependency requiringSelectedVersionDependency = requiringSelectedVersion.getDependency(elementSet.getId());
+ if (requiringSelectedVersionDependency == null)
+ continue;
+ // find the best version for this pre-requisite
+ IElement bestVersion = null;
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ boolean satisfiesDependency = requiringSelectedVersionDependency.getMatchRule().isSatisfied(requiringSelectedVersionDependency.getRequiredVersionId(), satisfiedVersion.getVersionId());
+ if (satisfiesDependency) {
+ boolean betterThanBest = bestVersion == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(),bestVersion.getVersionId()) > 0;
+ if (betterThanBest)
+ bestVersion = satisfiedVersion;
+ }
+ }
+ if (bestVersion != null)
+ selected.add(bestVersion);
+ }
+ }
+ // if none of the versions are required (or satisfy any selected re-requisites), pick the highest
+ if (selected.isEmpty()) {
+ IElement bestVersion = null;
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ boolean betterThanBest = bestVersion == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(),bestVersion.getVersionId()) > 0;
+ if (betterThanBest)
+ bestVersion = satisfiedVersion;
+ }
+ selected = Collections.singleton(bestVersion);
+ }
+ return selected;
+ }
+ public IElement selectSingle(IElementSet elementSet) {
+ // none of its versions are required by other element sets - so just pick the highest
+ if (elementSet.getRequiring().isEmpty()) {
+ // otherwise, pick the highest version
+ IElement highest = null;
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ if (highest == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(), highest.getVersionId()) > 0)
+ highest = satisfiedVersion;
+ }
+ return highest;
+ }
+
+ // let's pick the highest that satisfies all or the highest required
+ IElement highest = null;
+ int highestStatus = 0; // 0 - not required, 1 - satisfy some, 2 - satisfy all mandatory, 3 - satisfy all
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ boolean satisfiesAllMandatory = true;
+ boolean satisfiesAll = true;
+ boolean isRequired = false;
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ for (Iterator requiringIter = elementSet.getRequiring().iterator(); requiringIter.hasNext();) {
+ IElementSet requiringNode = (IElementSet) requiringIter.next();
+ Collection requiringNodeSelectedVersions = requiringNode.getSelected();
+ // loop through the selected versions (one if it is a non-library plug-in) of this requiring plug-in
+ for (Iterator requiringVersionsIter = requiringNodeSelectedVersions.iterator(); requiringVersionsIter.hasNext();) {
+ IElement requiringSelectedVersion = (IElement) requiringVersionsIter.next();
+ // the selected version may not require this element set (but it still can be selected)
+ IDependency requiringSelectedVersionDep = requiringSelectedVersion.getDependency(elementSet.getId());
+ if (requiringSelectedVersionDep != null) {
+ boolean satisfiesDependency = requiringSelectedVersionDep.getMatchRule().isSatisfied(requiringSelectedVersionDep.getRequiredVersionId(), satisfiedVersion.getVersionId());
+ isRequired |= satisfiesDependency;
+ satisfiesAll &= satisfiesDependency;
+ satisfiesAllMandatory &= (satisfiesDependency | requiringSelectedVersionDep.isOptional());
+ }
+ }
+ }
+ int status = satisfiesAll ? 3 : (satisfiesAllMandatory ? 2 : (isRequired ? 1 : 0));
+ boolean higherThanHighest = highest == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(), highest.getVersionId()) > 0;
+ if (status > highestStatus || (status == highestStatus && higherThanHighest)) {
+ highest = satisfiedVersion;
+ highestStatus = status;
+ }
+ }
+ return highest;
+ }
+
+
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/Eclipse30SelectionPolicy.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/Eclipse30SelectionPolicy.java
new file mode 100644
index 000000000..e3f363ffc
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/Eclipse30SelectionPolicy.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+
+import org.eclipse.core.dependencies.*;
+
+/**
+ * This is a variant of Eclipse 2.1 resolution policy that takes into account the current state
+ * of the element in a sense that resolved elements tend to stay resolved (the "least perturbation mode").
+ */
+public class Eclipse30SelectionPolicy implements ISelectionPolicy {
+ public Set selectMultiple(IElementSet elementSet) {
+ Set selected = new HashSet();
+ // pre-select all elements that were previously resolved and are satisfied
+ Collection previouslyResolvedSet = elementSet.getResolved();
+ for (Iterator iter = previouslyResolvedSet.iterator(); iter.hasNext(); ) {
+ IElement previouslyResolved = (IElement) iter.next();
+ // if the previously resolved element is satisfied, select it
+ if (elementSet.getSatisfied().contains(previouslyResolved))
+ selected.add(previouslyResolved);
+ }
+ // may want to select others
+ for (Iterator requiringIter = elementSet.getRequiring().iterator(); requiringIter.hasNext();) {
+ IElementSet requiringNode = (IElementSet) requiringIter.next();
+ Collection requiringNodeSelectedVersions = requiringNode.getSelected();
+ // loop through the selected versions (one if it is a non-library plug-in) of this requiring plug-in
+ for (Iterator requiringVersionsIter = requiringNodeSelectedVersions.iterator(); requiringVersionsIter.hasNext();) {
+ IElement requiringSelectedVersion = (IElement) requiringVersionsIter.next();
+ // the selected version may not require this element set (but it still can be selected)
+ IDependency requiringSelectedVersionDependency = requiringSelectedVersion.getDependency(elementSet.getId());
+ if (requiringSelectedVersionDependency == null)
+ continue;
+ // find the best version for this pre-requisite
+ IElement bestVersion = null;
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ boolean satisfiesDependency = requiringSelectedVersionDependency.getMatchRule().isSatisfied(requiringSelectedVersionDependency.getRequiredVersionId(), satisfiedVersion.getVersionId());
+ if (satisfiesDependency) {
+ boolean betterThanBest = bestVersion == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(),bestVersion.getVersionId()) > 0;
+ if (betterThanBest)
+ bestVersion = satisfiedVersion;
+ }
+ }
+ if (bestVersion != null)
+ selected.add(bestVersion);
+ }
+ }
+ // if none of the versions are required (or satisfy any selected re-requisites), pick the highest
+ if (selected.isEmpty()) {
+ IElement bestVersion = null;
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ boolean betterThanBest = bestVersion == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(),bestVersion.getVersionId()) > 0;
+ if (betterThanBest)
+ bestVersion = satisfiedVersion;
+ }
+ selected = Collections.singleton(bestVersion);
+ }
+ return selected;
+ }
+ public IElement selectSingle(IElementSet elementSet) {
+ Collection previouslyResolvedSet = elementSet.getResolved();
+ // we already have one element resolved
+ if (!previouslyResolvedSet.isEmpty()) {
+ // TODO: need a cheaper way to obtain the single element
+ IElement previouslyResolved = (IElement) previouslyResolvedSet.iterator().next();
+ // if it is satisfied, just return it
+ if (elementSet.getSatisfied().contains(previouslyResolved))
+ return previouslyResolved;
+ }
+
+ // none of its versions are required by other element sets - so just pick the highest
+ if (elementSet.getRequiring().isEmpty()) {
+ // otherwise, pick the highest version
+ IElement highest = null;
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ if (highest == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(), highest.getVersionId()) > 0)
+ highest = satisfiedVersion;
+ }
+ return highest;
+ }
+
+ // let's pick the highest that satisfies all or the highest required
+ IElement highest = null;
+ int highestStatus = 0; // 0 - not required, 1 - satisfy some, 2 - satisfy all mandatory, 3 - satisfy all
+ for (Iterator satisfiedIter = elementSet.getSatisfied().iterator(); satisfiedIter.hasNext();) {
+ boolean satisfiesAllMandatory = true;
+ boolean satisfiesAll = true;
+ boolean isRequired = false;
+ IElement satisfiedVersion = (IElement) satisfiedIter.next();
+ for (Iterator requiringIter = elementSet.getRequiring().iterator(); requiringIter.hasNext();) {
+ IElementSet requiringNode = (IElementSet) requiringIter.next();
+ Collection requiringNodeSelectedVersions = requiringNode.getSelected();
+ // loop through the selected versions (one if it is a non-library plug-in) of this requiring plug-in
+ for (Iterator requiringVersionsIter = requiringNodeSelectedVersions.iterator(); requiringVersionsIter.hasNext();) {
+ IElement requiringSelectedVersion = (IElement) requiringVersionsIter.next();
+ // the selected version may not require this element set (but it still can be selected)
+ IDependency requiringSelectedVersionDep = requiringSelectedVersion.getDependency(elementSet.getId());
+ if (requiringSelectedVersionDep != null) {
+ boolean satisfiesDependency = requiringSelectedVersionDep.getMatchRule().isSatisfied(requiringSelectedVersionDep.getRequiredVersionId(), satisfiedVersion.getVersionId());
+ isRequired |= satisfiesDependency;
+ satisfiesAll &= satisfiesDependency;
+ satisfiesAllMandatory &= (satisfiesDependency | requiringSelectedVersionDep.isOptional());
+ }
+ }
+ }
+ int status = satisfiesAll ? 3 : (satisfiesAllMandatory ? 2 : (isRequired ? 1 : 0));
+ boolean higherThanHighest = highest == null || elementSet.getSystem().compare(satisfiedVersion.getVersionId(), highest.getVersionId()) > 0;
+ if (status > highestStatus || (status == highestStatus && higherThanHighest)) {
+ highest = satisfiedVersion;
+ highestStatus = status;
+ }
+ }
+ return highest;
+ }
+
+
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/HostSpecificationImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/HostSpecificationImpl.java
new file mode 100644
index 000000000..af66ca1da
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/HostSpecificationImpl.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.service.resolver.HostSpecification;
+
+public class HostSpecificationImpl extends VersionConstraintImpl implements HostSpecification {
+ private boolean reloadHost;
+ public boolean reloadHost() {
+ return reloadHost;
+ }
+ public void setReloadHost(boolean reloadHost) {
+ this.reloadHost = reloadHost;
+ }
+ public boolean isOptional() {
+ // a fragment cannot exist without its master
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/PackageSpecificationImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/PackageSpecificationImpl.java
new file mode 100644
index 000000000..c0a681dec
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/PackageSpecificationImpl.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.service.resolver.PackageSpecification;
+
+public class PackageSpecificationImpl extends VersionConstraintImpl implements PackageSpecification {
+ private boolean export;
+ public boolean isExported() {
+ return export;
+ }
+ public void setExport(boolean export) {
+ this.export = export;
+ }
+ public String toString() {
+ return super.toString() + " (" + (export ? "exp" : " imp") + ")"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverHelper.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverHelper.java
new file mode 100644
index 000000000..9d3ac9d9a
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverHelper.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+import java.util.Comparator;
+
+import org.eclipse.core.dependencies.*;
+import org.eclipse.core.internal.dependencies.DependencySystem;
+import org.eclipse.osgi.service.resolver.*;
+
+public class ResolverHelper {
+
+ private final static Version NULL_VERSION = new Version(0, 0, 0);
+
+ static class BundleVersionComparator implements Comparator {
+ public int compare(Object arg0, Object arg1) {
+ Version v1 = (Version) arg0;
+ Version v2 = (Version) arg1;
+ return v1.isGreaterThan(v2) ? 1 : v1.isPerfect(v2) ? 0 : -1;
+ }
+ }
+ private static final IMatchRule COMPATIBLE = new EclipseCompatibleMatchRule();
+ private static final IMatchRule EQUIVALENT = new EclipseEquivalentMatchRule();
+ private static final IMatchRule GREATER_OR_EQUAL = new EclipseGreaterOrEqualMatchRule();
+ private static final IMatchRule PERFECT = new EclipsePerfectMatchRule();
+
+ public static IMatchRule getMatchRule(int b) {
+ switch (b) {
+ case VersionConstraint.EQUIVALENT_MATCH :
+ return EQUIVALENT;
+ case VersionConstraint.GREATER_EQUAL_MATCH :
+ return GREATER_OR_EQUAL;
+ case VersionConstraint.PERFECT_MATCH :
+ return PERFECT;
+ case VersionConstraint.COMPATIBLE_MATCH :
+ return COMPATIBLE;
+ case VersionConstraint.NO_MATCH :
+ return COMPATIBLE;
+ }
+ throw new IllegalArgumentException("match byte: " + b); //$NON-NLS-1$
+ }
+ private final static class UnsatisfiableRule implements IMatchRule {
+ public boolean isSatisfied(Object required, Object available) {
+ return false;
+ }
+ public String toString() {
+ return "unsatisfiable"; //$NON-NLS-1$
+ }
+ }
+ private final static class EclipsePerfectMatchRule implements IMatchRule {
+ public boolean isSatisfied(Object required, Object available) {
+ return ((Version) available).isPerfect((Version) required);
+ }
+ public String toString() {
+ return "perfect"; //$NON-NLS-1$
+ }
+ }
+ private final static class EclipseCompatibleMatchRule implements IMatchRule {
+ public boolean isSatisfied(Object required, Object available) {
+ return ((Version) available).isCompatibleWith((Version) required);
+ }
+ public String toString() {
+ return "compatible"; //$NON-NLS-1$
+ }
+ }
+ private final static class EclipseGreaterOrEqualMatchRule implements IMatchRule {
+ public boolean isSatisfied(Object required, Object available) {
+ return ((Version) available).isGreaterOrEqualTo((Version) required);
+ }
+ public String toString() {
+ return "greaterOrEqual"; //$NON-NLS-1$
+ }
+ }
+ private final static class EclipseEquivalentMatchRule implements IMatchRule {
+ public boolean isSatisfied(Object required, Object available) {
+ return ((Version) available).isEquivalentTo((Version) required);
+ }
+ public String toString() {
+ return "equivalent"; //$NON-NLS-1$
+ }
+ }
+ public static IElement createElement(BundleDescription bundleDescription, IDependencySystem system) {
+ String uniqueId = getUniqueId(bundleDescription);
+ Version version = getVersion(bundleDescription);
+ return system.createElement(uniqueId, version, createPrerequisites(bundleDescription, system), true, bundleDescription);
+ }
+ private static Version getVersion(BundleDescription bundleDescription) {
+ Version version = bundleDescription.getVersion();
+ if (version == null)
+ version = Version.EMPTY_VERSION;
+ return version;
+ }
+ private static String getUniqueId(BundleDescription bundleDescription) {
+ String uniqueId = bundleDescription.getUniqueId();
+ if (uniqueId == null)
+ // could not be null
+ uniqueId = Long.toString(bundleDescription.getBundleId());
+ return uniqueId;
+ }
+ /**
+ * Creates a prerequisite that cannot ever be satisfied.
+ */
+ private static IDependency[] createUnsatisfiablePrerequisites(IDependencySystem system) {
+ return new IDependency[]{system.createDependency("", new UnsatisfiableRule(), null, false, null)}; //$NON-NLS-1$
+ }
+ private static IDependency[] createPrerequisites(BundleDescription bundleDesc, IDependencySystem system) {
+ BundleSpecification[] required = bundleDesc.getRequiredBundles();
+ HostSpecification host = bundleDesc.getHost();
+ int dependencyCount = required == null ? 0 : required.length;
+ if (host != null)
+ dependencyCount++;
+ if (dependencyCount == 0)
+ return new IDependency[0];
+
+ IDependency[] prereqs = new IDependency[dependencyCount];
+ for (int i = 0; i < required.length; i++)
+ prereqs[i] = createPrerequisite(system, required[i]);
+ if (host != null)
+ prereqs[prereqs.length - 1] = createPrerequisite(system, host);
+ return prereqs;
+ }
+ private static IDependency createPrerequisite(IDependencySystem system, VersionConstraint constraint) {
+ boolean optional = (constraint instanceof BundleSpecification) && ((BundleSpecification) constraint).isOptional();
+ Version requiredVersion = constraint.getVersionSpecification();
+ if (NULL_VERSION.equals(requiredVersion))
+ requiredVersion = null;
+ return system.createDependency(constraint.getName(), getMatchRule(constraint.getMatchingRule()), requiredVersion, optional, constraint);
+ }
+ public static IDependencySystem createDependencySystem(ISelectionPolicy policy) {
+ return new DependencySystem(new ResolverHelper.BundleVersionComparator(), policy);
+ }
+ public static IDependencySystem buildDependencySystem(State state, ISelectionPolicy selectionPolicy) {
+ IDependencySystem dependencySystem = createDependencySystem(selectionPolicy);
+ BundleDescription[] bundles = state.getBundles();
+ for (int i = 0; i < bundles.length; i++)
+ dependencySystem.addElement(ResolverHelper.createElement(bundles[i], dependencySystem));
+ return dependencySystem;
+ }
+ public static void remove(BundleDescription description, IDependencySystem system) {
+ system.removeElement(getUniqueId(description), getVersion(description));
+ }
+ public static void add(BundleDescription description, IDependencySystem system) {
+ system.addElement(createElement(description, system));
+ }
+ public static void unresolve(BundleDescription bundle, IDependencySystem system) {
+ IElement element = system.getElement(getUniqueId(bundle), getVersion(bundle));
+ if (element != null)
+ system.unresolve(new IElement[]{element});
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverImpl.java
new file mode 100644
index 000000000..c8b3dbcfb
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/ResolverImpl.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+
+import org.eclipse.core.dependencies.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Bundle;
+
+public class ResolverImpl implements Resolver {
+ private State state;
+ private IDependencySystem dependencies;
+ public void resolve(BundleDescription[] reRefresh) {
+ // unresolving the given bundles will force them to be re-resolved
+ for (int i = 0; i < reRefresh.length; i++)
+ unresolveBundle(reRefresh[i]);
+ resolve();
+ }
+ /**
+ * TODO: need to devise a way to report problems (a la IStatus)
+ */
+ public synchronized void resolve() {
+ if (state == null)
+ throw new IllegalStateException("no state set"); //$NON-NLS-1$
+ if (dependencies == null)
+ dependencies = ResolverHelper.buildDependencySystem(state, new Eclipse30SelectionPolicy());
+ IResolutionDelta delta;
+ try {
+ delta = dependencies.resolve();
+ } catch (IDependencySystem.CyclicSystemException e) {
+ e.printStackTrace();
+ return;
+ }
+ processInnerDelta(delta);
+ resolvePackages();
+ }
+ public void setState(State newState) {
+ // to avoid infinite (mutual) recursion
+ if (state == newState)
+ return;
+ // if it was linked to a previous state, unlink first
+ if (state != null)
+ state.setResolver(null);
+ state = newState;
+ if (newState != null)
+ state.setResolver(this);
+ // forget any dependency state created before
+ flush();
+ }
+ /*
+ * Applies changes in the constraint system to the state object.
+ */
+ private void processInnerDelta(IResolutionDelta delta) {
+ // now apply changes reported in the inner delta to the state
+ IElementChange[] changes = delta.getAllChanges();
+ for (int i = 0; i < changes.length; i++) {
+ IElement element = changes[i].getElement();
+ BundleDescription bundle = (BundleDescription) element.getUserObject();
+ int kind = changes[i].getKind();
+ if ((kind & IElementChange.RESOLVED) != 0) {
+ state.resolveBundle(bundle, Bundle.RESOLVED);
+ resolveConstraints(element, bundle);
+ } else if (kind == (IElementChange.UNRESOLVED | IElementChange.REMOVED)) {
+ state.resolveBundle(bundle, Bundle.INSTALLED);
+ } else if (kind == IElementChange.UNRESOLVED)
+ state.resolveBundle(bundle, Bundle.INSTALLED);
+ else if (kind == IElementChange.LINKAGE_CHANGED)
+ resolveConstraints(element, bundle);
+ }
+ }
+ private void resolveConstraints(IElement element, BundleDescription bundle) {
+ // tells the state that some of the constraints have
+ // changed
+ IDependency[] dependencies = element.getDependencies();
+ for (int j = 0; j < dependencies.length; j++) {
+ if (dependencies[j].getResolvedVersionId() == null)
+ // an optional requisite that was not resolved
+ continue;
+ VersionConstraint constraint = (VersionConstraint) dependencies[j].getUserObject();
+ Version actualVersion = (Version) dependencies[j].getResolvedVersionId();
+ BundleDescription supplier = state.getBundle(constraint.getName(), actualVersion);
+ state.resolveConstraint(constraint, actualVersion, supplier);
+ }
+ }
+ public void bundleAdded(BundleDescription bundle) {
+ if (dependencies == null)
+ return;
+ ResolverHelper.add(bundle, dependencies);
+ }
+ public void bundleRemoved(BundleDescription bundle) {
+ if (dependencies == null)
+ return;
+ ResolverHelper.remove(bundle, dependencies);
+ }
+ public State getState() {
+ return state;
+ }
+ public void flush() {
+ dependencies = null;
+ }
+ /*
+ * Ensures that all currently resolved bundles have their import-package
+ * clauses satisfied.
+ */
+ private boolean resolvePackages() {
+ /* attempt to resolve the proposed bundles */
+ Map availablePackages;
+ boolean success;
+ int tries = 0;
+ do {
+ tries++;
+ success = true;
+ BundleDescription[] initialBundles = state.getResolvedBundles();
+ availablePackages = new HashMap(11);
+ /* do all exports first */
+ for (int i = 0; i < initialBundles.length; i++) {
+ PackageSpecification[] required = initialBundles[i].getPackages();
+ for (int j = 0; j < required.length; j++)
+ // override previously exported package if any (could
+ // preserve instead)
+ if (required[j].isExported()) {
+ Version toExport = required[j].getVersionSpecification();
+ PackageSpecification existing = (PackageSpecification) availablePackages.get(required[j].getName());
+ Version existingVersion = existing == null ? null : existing.getVersionSpecification();
+ if (existingVersion == null || (toExport != null && toExport.isGreaterThan(existingVersion)))
+ availablePackages.put(required[j].getName(), required[j]);
+ }
+ }
+ /* now try to resolve imported packages */
+ for (int i = 0; i < initialBundles.length; i++) {
+ PackageSpecification[] required = initialBundles[i].getPackages();
+ for (int j = 0; j < required.length; j++) {
+ PackageSpecification exported = (PackageSpecification) availablePackages.get(required[j].getName());
+ Version exportedVersion = exported == null ? null : exported.getVersionSpecification();
+ Version importedVersion = required[j].getVersionSpecification();
+ if (exported == null || (importedVersion != null && (exportedVersion == null || !exportedVersion.isGreaterOrEqualTo(importedVersion)))) {
+ unresolveRequirementChain(initialBundles[i]);
+ success = false;
+ // one missing import is enough to discard this bundle
+ break;
+ }
+ }
+ }
+ } while (!success);
+ /* now bind the exports/imports */
+ BundleDescription[] resolvedBundles = state.getResolvedBundles();
+ for (int i = 0; i < resolvedBundles.length; i++) {
+ PackageSpecification[] required = resolvedBundles[i].getPackages();
+ for (int j = 0; j < required.length; j++) {
+ PackageSpecification exported = (PackageSpecification) availablePackages.get(required[j].getName());
+ state.resolveConstraint(required[j], exported.getVersionSpecification(), exported.getBundle());
+ }
+ }
+ /* return false if a at least one bundle was unresolved during this */
+ return tries > 1;
+ }
+ /*
+ * Unresolves a bundle and all bundles that require it.
+ */
+ private void unresolveRequirementChain(BundleDescription bundle) {
+ if (!bundle.isResolved())
+ return;
+ state.resolveBundle(bundle, Bundle.INSTALLED);
+ if (bundle.getUniqueId() == null)
+ return;
+ IElementSet bundleElementSet = dependencies.getElementSet(bundle.getUniqueId());
+ Collection requiring = bundleElementSet.getRequiringElements(bundle.getVersion());
+ for (Iterator requiringIter = requiring.iterator(); requiringIter.hasNext(); ) {
+ IElement requiringElement = (IElement) requiringIter.next();
+ BundleDescription requiringBundle = state.getBundle((String) requiringElement.getId(), (Version) requiringElement.getVersionId());
+ if (requiringBundle != null)
+ unresolveRequirementChain(requiringBundle);
+ }
+ }
+ private void unresolveBundle(BundleDescription bundle) {
+ if (!bundle.isResolved())
+ return;
+ if (dependencies != null)
+ ResolverHelper.unresolve(bundle, dependencies);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
new file mode 100644
index 000000000..7ab44b135
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+
+import org.eclipse.osgi.framework.util.ManifestElement;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+
+/**
+ * This class builds bundle description objects from manifests
+ */
+class StateBuilder {
+ static BundleDescription createBundleDescription(Dictionary manifest, String location) throws BundleException {
+ BundleDescriptionImpl result = new BundleDescriptionImpl();
+ result.setUniqueId((String) manifest.get(Constants.BUNDLE_GLOBALNAME));
+ if (result.getUniqueId() == null)
+ result.setUniqueId((String) manifest.get(Constants.BUNDLE_NAME));
+ String version = (String) manifest.get(Constants.BUNDLE_VERSION);
+ result.setVersion((version != null) ? new Version(version) : Version.EMPTY_VERSION);
+
+ result.setLocation(location);
+
+ ManifestElement[] host = ManifestElement.parseBundleDescriptions((String) manifest.get(Constants.HOST_BUNDLE));
+ if (host != null)
+ result.setHost(createHostSpecification(result, host[0]));
+
+ ManifestElement[] imports = ManifestElement.parsePackageDescription((String) manifest.get(Constants.IMPORT_PACKAGE));
+ ManifestElement[] exports = ManifestElement.parsePackageDescription((String) manifest.get(Constants.EXPORT_PACKAGE));
+ result.setPackages(createPackages(result, exports, imports));
+
+ ManifestElement[] provides = ManifestElement.parsePackageDescription((String) manifest.get(Constants.PROVIDE_PACKAGE));
+ result.setProvidedPackages(createProvidedPackages(result, provides));
+
+ ManifestElement[] requires = ManifestElement.parseBundleDescriptions((String) manifest.get(Constants.REQUIRE_BUNDLE));
+ result.setRequiredBundles(createRequiredBundles(result, requires));
+
+ return result;
+ }
+
+ private static BundleSpecification[] createRequiredBundles(BundleDescriptionImpl parent, ManifestElement[] specs) {
+ if (specs == null)
+ return null;
+ BundleSpecification[] result = new BundleSpecification[specs.length];
+ for (int i = 0; i < specs.length; i++)
+ result[i] = createRequiredBundle(parent, specs[i]);
+ return result;
+ }
+
+ private static BundleSpecification createRequiredBundle(BundleDescriptionImpl parent, ManifestElement spec) {
+ BundleSpecificationImpl result = new BundleSpecificationImpl();
+ result.setName(spec.getValue());
+ String version = spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (version != null)
+ result.setVersionSpecification(new Version(version));
+ result.setMatchingRule(parseMatchingRule(spec.getAttribute(Constants.VERSION_MATCH_ATTRIBUTE)));
+ result.setExported(spec.getAttribute(Constants.PROVIDE_PACKAGES_ATTRIBUTE) != null);
+ result.setOptional(spec.getAttribute(Constants.OPTIONAL_ATTRIBUTE) != null);
+ return result;
+ }
+ private static byte parseMatchingRule(String match) {
+ if (match == null)
+ return VersionConstraint.GREATER_EQUAL_MATCH;
+ if (match.equals(Constants.VERSION_MATCH_EQUIVALENT))
+ return VersionConstraint.EQUIVALENT_MATCH;
+ if (match.equals(Constants.VERSION_MATCH_COMPATIBLE))
+ return VersionConstraint.COMPATIBLE_MATCH;
+ if (match.equals(Constants.VERSION_MATCH_PERFECT))
+ return VersionConstraint.PERFECT_MATCH;
+ return VersionConstraint.GREATER_EQUAL_MATCH;
+ }
+ private static String[] createProvidedPackages(BundleDescription parent, ManifestElement[] specs) {
+ if (specs == null || specs.length == 0)
+ return null;
+ String[] result = new String[specs.length];
+ for (int i = 0; i < specs.length; i++)
+ result[i] = specs[i].getValue();
+ return result;
+ }
+
+ private static PackageSpecification[] createPackages(BundleDescription parent, ManifestElement[] exported, ManifestElement[] imported) {
+ int capacity = (exported == null ? 0 : exported.length) + (imported == null ? 0 : imported.length);
+ if (capacity == 0)
+ return null;
+ Map packages = new HashMap(capacity);
+ if (imported != null)
+ for (int i = 0; i < imported.length; i++)
+ packages.put(imported[i].getValue(), createPackage(parent, imported[i], false));
+ if (exported != null)
+ for (int i = 0; i < exported.length; i++)
+ packages.put(exported[i].getValue(), createPackage(parent, exported[i], true));
+ return (PackageSpecification[]) packages.values().toArray(new PackageSpecification[packages.size()]);
+ }
+
+ private static PackageSpecification createPackage(BundleDescription parent, ManifestElement spec, boolean export) {
+ PackageSpecificationImpl result = new PackageSpecificationImpl();
+ result.setName(spec.getValue());
+ String version = spec.getAttribute(Constants.PACKAGE_SPECIFICATION_VERSION);
+ if (version != null)
+ result.setVersionSpecification(new Version(version));
+ result.setExport(export);
+ return result;
+ }
+
+ private static HostSpecification createHostSpecification(BundleDescription parent, ManifestElement spec) {
+ if (spec == null)
+ return null;
+ HostSpecificationImpl result = new HostSpecificationImpl();
+ result.setName(spec.getValue());
+ String version = spec.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (version != null)
+ result.setVersionSpecification(new Version(version));
+ result.setMatchingRule(parseMatchingRule(spec.getAttribute(Constants.VERSION_MATCH_ATTRIBUTE)));
+ result.setReloadHost(false); //$NON-NLS-1$
+ return result;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateDeltaImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateDeltaImpl.java
new file mode 100644
index 000000000..60cac2c90
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateDeltaImpl.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.Bundle;
+
+public class StateDeltaImpl implements StateDelta {
+ private State state;
+ private Map changes = new HashMap();
+ public StateDeltaImpl(State state) {
+ this.state = state;
+ }
+ public BundleDelta[] getChanges() {
+ return (BundleDelta[]) changes.values().toArray(new BundleDelta[changes.size()]);
+ }
+ public BundleDelta[] getChanges(int mask, boolean exact) {
+ List result = new ArrayList();
+ for (Iterator changesIter = changes.values().iterator(); changesIter.hasNext();) {
+ BundleDelta change = (BundleDelta) changesIter.next();
+ if (change.getType() == mask || (!exact && (change.getType() & mask) == mask))
+ result.add(change);
+ }
+ return (BundleDelta[]) result.toArray(new BundleDelta[result.size()]);
+ }
+ public State getState() {
+ return state;
+ }
+ void recordBundleAdded(BundleDescriptionImpl added) {
+ Object key = added.getKey();
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(key);
+ if (change != null) {
+ if ((change.getType() & BundleDelta.REMOVED) != 0)
+ change.setType(BundleDelta.UPDATED | (change.getType() & ~BundleDelta.REMOVED));
+ else
+ throw new IllegalStateException();
+ } else
+ changes.put(key, new BundleDeltaImpl(added, BundleDelta.ADDED));
+
+ }
+ void recordBundleRemoved(BundleDescriptionImpl removed) {
+ Object key = removed.getKey();
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(key);
+ if (change != null) {
+ if ((change.getType() & BundleDelta.ADDED) != 0) {
+ changes.remove(key);
+ return;
+ }
+ change.setType(change.getType() | BundleDelta.REMOVED);
+ } else
+ changes.put(key, new BundleDeltaImpl(removed, BundleDelta.REMOVED));
+ }
+ void recordConstraintResolved(BundleDescriptionImpl changedLinkage, boolean optional) {
+ Object key = changedLinkage.getKey();
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(key);
+ int newType = optional ? BundleDelta.OPTIONAL_LINKAGE_CHANGED : BundleDelta.LINKAGE_CHANGED;
+ // LINKAGE_CHANGED overrides OPTIONAL_LINKAGE_CHANGED, but nothing else
+ if (change == null || (newType == BundleDelta.LINKAGE_CHANGED && change.getType() == BundleDelta.OPTIONAL_LINKAGE_CHANGED))
+ changes.put(key, new BundleDeltaImpl(changedLinkage, newType));
+ }
+ void recordBundleResolved(BundleDescriptionImpl resolved, int status) {
+ Object key = resolved.getKey();
+ BundleDeltaImpl change = (BundleDeltaImpl) changes.get(key);
+ int newType = status == Bundle.RESOLVED ? BundleDelta.RESOLVED : BundleDelta.UNRESOLVED;
+ if (change == null) {
+ change = new BundleDeltaImpl(resolved, newType);
+ changes.put(key, change);
+ return;
+ }
+ int currentType = change.getType();
+ if ((newType == BundleDelta.RESOLVED && currentType == BundleDelta.UNRESOLVED) || (newType == BundleDelta.UNRESOLVED && currentType == BundleDelta.RESOLVED)) {
+ changes.remove(key);
+ return;
+ }
+ // new type will have only one of RESOLVED|UNRESOLVED bits set
+ newType = newType | (currentType & ~(BundleDelta.RESOLVED | BundleDelta.UNRESOLVED));
+ change.setType(newType);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java
new file mode 100644
index 000000000..a41e92736
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java
@@ -0,0 +1,336 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.*;
+
+import org.eclipse.osgi.framework.debug.DebugOptions;
+import org.eclipse.osgi.framework.internal.core.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.BundleException;
+
+public class StateImpl implements State {
+ transient private Resolver resolver;
+ transient private StateDeltaImpl changes;
+ transient private Map listeners = new HashMap(11);
+ transient private KeyedHashSet resolvedBundles = new KeyedHashSet();
+ private boolean resolved = true;
+ private long timeStamp = System.currentTimeMillis();
+ private KeyedHashSet bundleDescriptions = new KeyedHashSet(false);
+
+ public static boolean DEBUG_RESOLVER = false;
+ private static long cumulativeTime;
+
+ public void addBundle(BundleDescription description) {
+ if (description.getBundleId() < 0)
+ throw new IllegalArgumentException("no id set");
+ if (!basicAddBundle(description))
+ return;
+ timeStamp++;
+ resolved = false;
+ getDelta().recordBundleAdded((BundleDescriptionImpl) description);
+ if (resolver != null)
+ resolver.bundleAdded(description);
+ }
+ public void updateBundle(BundleDescription oldBundle, BundleDescription newBundle) {
+ throw new UnsupportedOperationException("not implemented"); //$NON-NLS-1$
+ }
+ public StateChangeEvent compare(State state) {
+ throw new UnsupportedOperationException("not implemented"); //$NON-NLS-1$
+ }
+ public BundleDescription removeBundle(long bundleId) {
+ BundleDescription toRemove = getBundle(bundleId);
+ if (toRemove == null || !removeBundle(toRemove))
+ return null;
+ return toRemove;
+ }
+ public boolean removeBundle(BundleDescription toRemove) {
+ if (!bundleDescriptions.remove((KeyedElement) toRemove))
+ return false;
+ timeStamp++;
+ resolved = false;
+ getDelta().recordBundleRemoved((BundleDescriptionImpl) toRemove);
+ if (resolver != null)
+ resolver.bundleRemoved(toRemove);
+ return true;
+ }
+ public StateDelta getChanges() {
+ return changes;
+ }
+ public StateDeltaImpl getDelta() {
+ if (changes == null)
+ changes = getNewDelta();
+ return changes;
+ }
+ private StateDeltaImpl getNewDelta() {
+ return new StateDeltaImpl(this);
+ }
+ public BundleDescription[] getBundles(final String requiredUniqueId) {
+ final List bundles = new ArrayList();
+ for (Iterator iter = bundleDescriptions.iterator(); iter.hasNext(); ) {
+ BundleDescription bundle = (BundleDescription) iter.next();
+ if (requiredUniqueId.equals(bundle.getUniqueId()))
+ bundles.add(bundle);
+ }
+ return (BundleDescription[]) bundles.toArray(new BundleDescription[bundles.size()]);
+ }
+ public BundleDescription[] getBundles() {
+ return (BundleDescription[]) bundleDescriptions.elements(new BundleDescription[bundleDescriptions.size()]);
+ }
+ public BundleDescription getBundle(long id) {
+ return (BundleDescription) bundleDescriptions.getByKey(new Long(id));
+ }
+ // TODO: this does not comply with the spec
+ public BundleDescription getBundle(String requiredUniqueId, Version requiredVersion) {
+ BundleDescription[] bundles = getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ if (requiredUniqueId.equals(bundles[i].getUniqueId()) && bundles[i].getVersion().equals(requiredVersion))
+ return bundles[i];
+ }
+ return null;
+ }
+ public long getTimeStamp() {
+ return timeStamp;
+ }
+ public boolean isResolved() {
+ return resolved || isEmpty();
+ }
+ public void resolveConstraint(VersionConstraint constraint, Version actualVersion, BundleDescription supplier) {
+ VersionConstraintImpl modifiable = ((VersionConstraintImpl) constraint);
+ if (modifiable.getActualVersion() != actualVersion || modifiable.getSupplier() != supplier) {
+ timeStamp++;
+ modifiable.setActualVersion(actualVersion);
+ modifiable.setSupplier(supplier);
+ if (constraint instanceof BundleSpecification || constraint instanceof HostSpecification) {
+ boolean optional = (constraint instanceof BundleSpecification) && ((BundleSpecification) constraint).isOptional();
+ getDelta().recordConstraintResolved((BundleDescriptionImpl) constraint.getBundle(), optional);
+ }
+ }
+ }
+ public void resolveBundle(BundleDescription bundle, int status) {
+ timeStamp++;
+ ((BundleDescriptionImpl) bundle).setState(status);
+ getDelta().recordBundleResolved((BundleDescriptionImpl) bundle, status);
+ if (status == Bundle.RESOLVED)
+ resolvedBundles.add((KeyedElement) bundle);
+ else {
+ // ensures no links are left
+ unresolveConstraints(bundle);
+ // remove the bundle from the resolved pool
+ resolvedBundles.remove((KeyedElement) bundle);
+ }
+ }
+ private void unresolveConstraints(BundleDescription bundle) {
+ HostSpecification host = bundle.getHost();
+ if (host != null)
+ ((VersionConstraintImpl) host).unresolve();
+ PackageSpecification[] packages = bundle.getPackages();
+ for (int i = 0; i < packages.length; i++)
+ ((VersionConstraintImpl) packages[i]).unresolve();
+ BundleSpecification[] requiredBundles = bundle.getRequiredBundles();
+ for (int i = 0; i < requiredBundles.length; i++)
+ ((VersionConstraintImpl) requiredBundles[i]).unresolve();
+ timeStamp++;
+ }
+
+ public Resolver getResolver() {
+ return resolver;
+ }
+ public void setResolver(Resolver newResolver) {
+ if (resolver == newResolver)
+ return;
+ if (resolver != null)
+ resolver.setState(null);
+ resolver = newResolver;
+ if (resolver == null)
+ return;
+ resolver.setState(this);
+ }
+ private StateDelta resolve(boolean incremental, BundleDescription[] reResolve) {
+ if (resolver == null)
+ throw new IllegalStateException("no resolver set"); //$NON-NLS-1$
+ long start = 0;
+ if (DEBUG_RESOLVER)
+ start = System.currentTimeMillis();
+ if (!incremental)
+ flush();
+ if (resolved && reResolve == null)
+ return new StateDeltaImpl(this);
+ if (reResolve != null)
+ resolver.resolve(reResolve);
+ else
+ resolver.resolve();
+ resolved = true;
+ // TODO: need to fire events for listeners
+ StateDelta savedChanges = changes == null ? new StateDeltaImpl(this) : changes;
+ changes = new StateDeltaImpl(this);
+
+ if (DEBUG_RESOLVER) {
+ cumulativeTime = cumulativeTime + (System.currentTimeMillis() - start);
+ DebugOptions.getDefault().setOption("org.eclipse.core.runtime.adaptor/resolver/timing/value", Long.toString(cumulativeTime));
+ }
+
+ return savedChanges;
+ }
+ private void flush() {
+ resolver.flush();
+ resolved = false;
+ if (resolvedBundles.isEmpty())
+ return;
+ for (Iterator iter = resolvedBundles.iterator(); iter.hasNext(); ) {
+ BundleDescriptionImpl resolvedBundle = (BundleDescriptionImpl) iter.next();
+ resolvedBundle.setState(0);
+ }
+ resolvedBundles.clear();
+ }
+ public StateDelta resolve() {
+ return resolve(true, null);
+ }
+ public StateDelta resolve(boolean incremental) {
+ return resolve(incremental, null);
+ }
+ public StateDelta resolve(BundleDescription[] reResolve) {
+ return resolve(true, reResolve);
+ }
+ public void setOverrides(Object value) {
+ throw new UnsupportedOperationException();
+ }
+ public BundleDescription[] getResolvedBundles() {
+ return (BundleDescription[]) resolvedBundles.elements(new BundleDescription[resolvedBundles.size()]);
+ }
+ public void addStateChangeListener(StateChangeListener listener, int flags) {
+ if (listeners.containsKey(listener))
+ return;
+ listeners.put(listener, new Integer(flags));
+ }
+ public void removeStateChangeListener(StateChangeListener listener) {
+ listeners.remove(listener);
+ }
+ public boolean isEmpty() {
+ return bundleDescriptions.isEmpty();
+ }
+ public void setResolved(boolean resolved) {
+ this.resolved = resolved;
+ }
+ public boolean basicAddBundle(BundleDescription description) {
+ ((BundleDescriptionImpl) description).setContainingState(this);
+ return bundleDescriptions.add((BundleDescriptionImpl) description);
+ }
+ void addResolvedBundle(BundleDescriptionImpl resolved) {
+ resolvedBundles.add(resolved);
+ }
+ public BundleDescription[] getDependentBundles(BundleDescription[] roots) {
+ Set remaining = new HashSet(Arrays.asList(resolvedBundles.elements()));
+ KeyedHashSet reachable = new KeyedHashSet(roots.length);
+ // put the roots in the graph
+ for (int i = 0; i < roots.length; i++)
+ if (roots[i].isResolved()) {
+ reachable.add((KeyedElement) roots[i]);
+ remaining.remove(roots[i]);
+ }
+ boolean changed;
+ do {
+ changed = false;
+ // start over each iteration
+ for (Iterator remainingIter = remaining.iterator(); remainingIter.hasNext(); ) {
+ BundleDescription candidate = (BundleDescription) remainingIter.next();
+ if (isDependent(candidate, reachable)) {
+ reachable.add((KeyedElement) candidate);
+ remainingIter.remove();
+ changed = true;
+ }
+ }
+ } while (changed);
+ return (BundleDescription[]) reachable.elements(new BundleDescription[reachable.size()]);
+ }
+ private boolean isDependent(BundleDescription candidate, KeyedHashSet elements) {
+ // is a fragment of any of them?
+ HostSpecification candidateHost = candidate.getHost();
+ if (candidateHost != null && candidateHost.isResolved() && elements.contains((KeyedElement) candidateHost.getSupplier()))
+ return true;
+ // does require any of them?
+ BundleSpecification[] candidateRequired = candidate.getRequiredBundles();
+ for (int i = 0; i < candidateRequired.length; i++)
+ if (candidateRequired[i].isResolved() && elements.contains((KeyedElement) candidateRequired[i].getSupplier()))
+ return true;
+ // does import any of their packages?
+ PackageSpecification[] candidatePackages = candidate.getPackages();
+ for (int i = 0; i < candidatePackages.length; i++)
+ if (candidatePackages[i].isResolved() && candidatePackages[i].getSupplier() != candidate && elements.contains((KeyedElement) candidatePackages[i].getSupplier()))
+ return true;
+ return false;
+ }
+ public PackageSpecification getExportedPackage(String packageName, Version version) {
+ boolean ignoreVersion = version == null;
+ for (Iterator iter = resolvedBundles.iterator(); iter.hasNext(); ) {
+ PackageSpecification[] packages = ((BundleDescriptionImpl) iter.next()).getPackages();
+ for (int i = 0; i < packages.length; i++)
+ if (packages[i].getName().equals(packageName) && (ignoreVersion || packages[i].getVersionSpecification().equals(version)) && (packages[i].getSupplier() != null))
+ return packages[i].getSupplier().getPackage(packageName);
+ }
+ return null;
+ }
+ public PackageSpecification[] getExportedPackages(BundleDescription bundle) {
+ if (!bundle.isResolved())
+ return new PackageSpecification[0];
+ PackageSpecification[] allPackages = bundle.getPackages();
+ PackageSpecification[] exported = new PackageSpecification[allPackages.length];
+ int exportedCount = 0;
+ for (int i = 0; i < allPackages.length; i++)
+ if (allPackages[i].isExported() && allPackages[i].getSupplier() == bundle)
+ exported[exportedCount++] = allPackages[i];
+ if (exportedCount < exported.length) {
+ PackageSpecification[] tmpExported = new PackageSpecification[exportedCount];
+ System.arraycopy(exported, 0, tmpExported, 0, exportedCount);
+ exported = tmpExported;
+ }
+ return exported;
+ }
+ public PackageSpecification[] getExportedPackages() {
+ final List allExportedPackages = new ArrayList();
+ for (Iterator iter = resolvedBundles.iterator(); iter.hasNext(); ) {
+ BundleDescription bundle = (BundleDescription) iter.next();
+ PackageSpecification[] bundlePackages = bundle.getPackages();
+ for (int i = 0; i < bundlePackages.length; i++)
+ if (bundlePackages[i].isExported() && bundlePackages[i].getSupplier() == bundle)
+ allExportedPackages.add(bundlePackages[i]);
+ }
+ return (PackageSpecification[]) allExportedPackages.toArray(new PackageSpecification[allExportedPackages.size()]);
+ }
+ public BundleDescription[] getImportingBundles(final PackageSpecification exportedPackage) {
+ if (!exportedPackage.isResolved())
+ return null;
+ final List allImportingBundles = new ArrayList();
+ for (Iterator iter = resolvedBundles.iterator(); iter.hasNext(); ) {
+ BundleDescription bundle = (BundleDescription) iter.next();
+ PackageSpecification[] bundlePackages = bundle.getPackages();
+ for (int i = 0; i < bundlePackages.length; i++)
+ if (bundlePackages[i].getName().equals(exportedPackage.getName()) && bundlePackages[i].getSupplier() == exportedPackage.getBundle()) {
+ allImportingBundles.add(bundle);
+ break;
+ }
+ }
+ return (BundleDescription[]) allImportingBundles.toArray(new BundleDescription[allImportingBundles.size()]);
+ }
+ public BundleDescription[] getFragments(final BundleDescription host) {
+ final List fragments = new ArrayList();
+ for (Iterator iter = bundleDescriptions.iterator(); iter.hasNext(); ) {
+ BundleDescription bundle = (BundleDescription) iter.next();
+ HostSpecification hostSpec = bundle.getHost();
+ if (hostSpec != null && hostSpec.getSupplier() == host)
+ fragments.add(bundle);
+ }
+ return (BundleDescription[]) fragments.toArray(new BundleDescription[fragments.size()]);
+ }
+ public void setTimeStamp(long newTimeStamp) {
+ timeStamp = newTimeStamp;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateManager.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateManager.java
new file mode 100644
index 000000000..13f31b6f5
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateManager.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.io.*;
+import java.util.Dictionary;
+
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.State;
+import org.osgi.framework.BundleException;
+
+public class StateManager implements PlatformAdmin {
+ public static boolean DEBUG_READER = false;
+ private long readStartupTime;
+
+ private StateImpl state;
+ private File stateLocation;
+ private StateObjectFactory factory;
+
+ public StateManager(File bundleRootDir) {
+ stateLocation = new File(bundleRootDir, ".state"); //$NON-NLS-1$
+ readState();
+ }
+
+ public void shutdown() throws IOException {
+ writeState();
+ state = null;
+ }
+ private void readState() {
+ if (!stateLocation.isFile())
+ return;
+
+ if (DEBUG_READER)
+ readStartupTime = System.currentTimeMillis();
+
+ FileInputStream fileInput;
+ try {
+ fileInput = new FileInputStream(stateLocation);
+ } catch (FileNotFoundException e) {
+ // TODO: log before bailing
+ e.printStackTrace();
+ return;
+ }
+ DataInputStream input = null;
+ try {
+ input = new DataInputStream(new BufferedInputStream(fileInput,65536));
+ StateReader reader = new StateReader();
+ state = reader.loadCache(input);
+ state.setResolver(new ResolverImpl());
+ } catch (IOException ioe) {
+ // TODO: how do we log this?
+ ioe.printStackTrace();
+ } finally {
+ if (DEBUG_READER)
+ System.out.println("Time to read state: " + (System.currentTimeMillis() - readStartupTime));
+ }
+ }
+ private void writeState() throws IOException {
+ if (state == null)
+ return;
+ DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(stateLocation)));
+ StateWriter writer = new StateWriter();
+ writer.saveState(state, output);
+ }
+ public State getSystemState() {
+ if (state == null) {
+ state = (StateImpl) factory.createState();
+ state.setResolver(new ResolverImpl());
+ }
+ return state;
+ }
+ public State getState() {
+ return factory.createState(getSystemState());
+ }
+ public BundleDescription uninstall(BundleData bundledata) {
+ long bundleId = bundledata.getBundleID();
+ return getSystemState().removeBundle(bundleId);
+ }
+ public BundleDescription update(Dictionary manifest, String location, long bundleId) throws BundleException {
+ State systemState = getSystemState();
+ systemState.removeBundle(bundleId);
+ BundleDescription newDescription = getFactory().createBundleDescription(manifest, location,bundleId);
+ systemState.addBundle(newDescription);
+ return newDescription;
+ }
+ public BundleDescription install(Dictionary manifest, String location, long bundleId) throws BundleException {
+ BundleDescription bundleDescription = getFactory().createBundleDescription(manifest, location,bundleId);
+ getSystemState().addBundle(bundleDescription);
+ return bundleDescription;
+ }
+ public StateObjectFactory getFactory() {
+ if (factory == null)
+ factory = new StateObjectFactoryImpl();
+ return factory;
+ }
+ public void commit(State state) throws BundleException {
+ //TODO: implement this
+ throw new UnsupportedOperationException("not implemented yet");
+ }
+ public Resolver getResolver() {
+ return new ResolverImpl();
+ }
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java
new file mode 100644
index 000000000..b6d4c09f8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.util.Dictionary;
+
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.StateObjectFactory;
+import org.osgi.framework.BundleException;
+
+public class StateObjectFactoryImpl implements StateObjectFactory {
+ public BundleDescription createBundleDescription(Dictionary manifest, String location, long id) {
+ BundleDescriptionImpl result;
+ try {
+ result = (BundleDescriptionImpl) StateBuilder.createBundleDescription(manifest, location);
+ } catch (BundleException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ result.setBundleId(id);
+ return result;
+ }
+ public BundleDescription createBundleDescription(long id, String globalName, Version version, String location, BundleSpecification[] required, HostSpecification host,PackageSpecification[] packages, String[] providedPackages) {
+ BundleDescriptionImpl bundle = new BundleDescriptionImpl();
+ bundle.setBundleId(id);
+ bundle.setUniqueId(globalName);
+ bundle.setVersion(version);
+ bundle.setLocation(location);
+ bundle.setRequiredBundles(required);
+ bundle.setPackages(packages);
+ bundle.setHost(host);
+ //TODO: need to fix the state API to use String[] to represent provided packages
+ //bundle.setProvidedPackages(providedPackages);
+ return bundle;
+ }
+ public BundleDescription createBundleDescription(BundleDescription original) {
+ BundleDescriptionImpl bundle = new BundleDescriptionImpl();
+ bundle.setBundleId(original.getBundleId());
+ bundle.setUniqueId(original.getUniqueId());
+ bundle.setVersion(original.getVersion());
+ bundle.setLocation(original.getLocation());
+ BundleSpecification[] originalRequired = original.getRequiredBundles();
+ BundleSpecification[] newRequired = new BundleSpecification[originalRequired.length];
+ for (int i = 0; i < newRequired.length; i++)
+ newRequired[i] = createBundleSpecification(originalRequired[i]);
+ bundle.setRequiredBundles(newRequired);
+ PackageSpecification[] originalPackages = original.getPackages();
+ PackageSpecification[] newPackages = new PackageSpecification[originalPackages.length];
+ for (int i = 0; i < newPackages.length; i++)
+ newPackages[i] = createPackageSpecification(originalPackages[i]);
+ bundle.setPackages(newPackages);
+ if (original.getHost() != null)
+ bundle.setHost(createHostSpecification(original.getHost()));
+ String[] originalProvidedPackages = original.getProvidedPackages();
+ String[] newProvidedPackages = new String[originalProvidedPackages.length];
+ System.arraycopy(originalProvidedPackages,0,newProvidedPackages,0,originalProvidedPackages.length);
+ bundle.setProvidedPackages(newProvidedPackages);
+ return bundle;
+ }
+ public BundleSpecification createBundleSpecification(BundleDescription bundle, String requiredGlobalName, Version requiredVersion, byte matchingRule, boolean export, boolean optional) {
+ BundleSpecificationImpl bundleSpec = new BundleSpecificationImpl();
+ bundleSpec.setBundle(bundle);
+ bundleSpec.setName(requiredGlobalName);
+ bundleSpec.setVersionSpecification(requiredVersion);
+ bundleSpec.setMatchingRule(matchingRule);
+ bundleSpec.setExported(export);
+ bundleSpec.setOptional(optional);
+ return bundleSpec;
+ }
+ public BundleSpecification createBundleSpecification(BundleSpecification original) {
+ BundleSpecificationImpl bundleSpec = new BundleSpecificationImpl();
+ bundleSpec.setName(original.getName());
+ bundleSpec.setVersionSpecification(original.getVersionSpecification());
+ bundleSpec.setMatchingRule(original.getMatchingRule());
+ bundleSpec.setExported(original.isExported());
+ bundleSpec.setOptional(original.isOptional());
+ return bundleSpec;
+ }
+ public HostSpecification createHostSpecification(BundleDescription bundle, String hostGlobalName, Version hostVersion, byte matchingRule, boolean reloadHost) {
+ HostSpecificationImpl hostSpec = new HostSpecificationImpl();
+ hostSpec.setBundle(bundle);
+ hostSpec.setName(hostGlobalName);
+ hostSpec.setVersionSpecification(hostVersion);
+ hostSpec.setMatchingRule(matchingRule);
+ hostSpec.setReloadHost(reloadHost);
+ return hostSpec;
+ }
+ public HostSpecification createHostSpecification(HostSpecification original) {
+ HostSpecificationImpl hostSpec = new HostSpecificationImpl();
+ hostSpec.setName(original.getName());
+ hostSpec.setVersionSpecification(original.getVersionSpecification());
+ hostSpec.setMatchingRule(original.getMatchingRule());
+ hostSpec.setReloadHost(original.reloadHost());
+ return hostSpec;
+ }
+ public PackageSpecification createPackageSpecification(BundleDescription bundle, String packageName, Version packageVersion, boolean exported) {
+ PackageSpecificationImpl packageSpec = new PackageSpecificationImpl();
+ packageSpec.setBundle(bundle);
+ packageSpec.setName(packageName);
+ packageSpec.setVersionSpecification(packageVersion);
+ packageSpec.setExport(exported);
+ return packageSpec;
+ }
+ public PackageSpecification createPackageSpecification(PackageSpecification original) {
+ PackageSpecificationImpl packageSpec = new PackageSpecificationImpl();
+ packageSpec.setName(original.getName());
+ packageSpec.setVersionSpecification(original.getVersionSpecification());
+ packageSpec.setExport(original.isExported());
+ return packageSpec;
+ }
+ public State createState() {
+ return new StateImpl();
+ }
+ public State createState(State original) {
+ StateImpl newState = new StateImpl();
+ BundleDescription[] bundles = original.getBundles();
+ for (int i = 0; i < bundles.length; i++)
+ newState.addBundle(createBundleDescription(bundles[i]));
+ return newState;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java
new file mode 100644
index 000000000..835f1ffa7
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.osgi.service.resolver.*;
+
+public class StateReader {
+
+ // objectTable will be a hashmap of objects. The objects will be things
+ // like a plugin descriptor, extension, extension point, etc. The integer
+ // index value will be used in the cache to allow cross-references in the
+ // cached registry.
+ protected List objectTable = new ArrayList();
+
+ public static final byte STATE_CACHE_VERSION = 3;
+ public static final byte NULL = 0;
+ public static final byte OBJECT = 1;
+ public static final byte INDEX = 2;
+
+ private int addToObjectTable(Object object) {
+ objectTable.add(object);
+ // return the index of the object just added (i.e. size - 1)
+ return (objectTable.size() - 1);
+ }
+
+ private StateImpl readState(DataInputStream in) throws IOException {
+ if (in.readByte() != STATE_CACHE_VERSION)
+ return null;
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (StateImpl) objectTable.get(in.readInt());
+
+ StateImpl result = new StateImpl();
+ addToObjectTable(result);
+ int length = in.readInt();
+ if (length == 0)
+ return result;
+ for (int i = 0; i < length; i++)
+ result.basicAddBundle(readBundleDescription(in));
+ result.setTimeStamp(in.readLong());
+ result.setResolved(in.readBoolean());
+ if (!result.isResolved())
+ return result;
+ int resolvedLength = in.readInt();
+ for(int i = 0;i < resolvedLength;i++)
+ result.addResolvedBundle(readBundleDescription(in));
+ return result;
+ }
+ private BundleDescriptionImpl readBundleDescription(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (BundleDescriptionImpl) objectTable.get(in.readInt());
+ BundleDescriptionImpl result = new BundleDescriptionImpl();
+ addToObjectTable(result);
+ result.setBundleId(in.readLong());
+ result.setUniqueId(readString(in, false));
+ result.setLocation(readString(in, false));
+ result.setState(in.readInt());
+ result.setVersion(readVersion(in));
+ result.setHost(readHostSpec(in));
+ int packageCount = in.readInt();
+ if (packageCount > 0) {
+ PackageSpecification[] packages = new PackageSpecification[packageCount];
+ for (int i = 0; i < packages.length; i++)
+ packages[i] = readPackageSpec(in);
+
+ result.setPackages(packages);
+ }
+ int providedPackageCount = in.readInt();
+ if (providedPackageCount > 0) {
+ String[] providedPackages = new String[providedPackageCount];
+ for (int i = 0; i < providedPackages.length; i++)
+ providedPackages[i] = in.readUTF();
+ result.setProvidedPackages(providedPackages);
+ }
+ int requiredBundleCount = in.readInt();
+ if (requiredBundleCount > 0) {
+ BundleSpecification[] requiredBundles = new BundleSpecification[requiredBundleCount];
+ for (int i = 0; i < requiredBundles.length; i++)
+ requiredBundles[i] = readBundleSpec(in);
+ result.setRequiredBundles(requiredBundles);
+ }
+ return result;
+ }
+ private BundleSpecificationImpl readBundleSpec(DataInputStream in) throws IOException {
+ BundleSpecificationImpl result = new BundleSpecificationImpl();
+ readVersionConstraint(result, in);
+ result.setExported(in.readBoolean());
+ result.setOptional(in.readBoolean());
+ return result;
+ }
+ private PackageSpecificationImpl readPackageSpec(DataInputStream in) throws IOException {
+ PackageSpecificationImpl result = new PackageSpecificationImpl();
+ readVersionConstraint(result, in);
+ result.setExport(in.readBoolean());
+ return result;
+ }
+ private PackageSpecificationImpl readProvidedPackage(DataInputStream in) throws IOException {
+ PackageSpecificationImpl result = new PackageSpecificationImpl();
+ // no chance this will be null
+ result.setName(in.readUTF());
+ return result;
+ }
+ private HostSpecificationImpl readHostSpec(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ HostSpecificationImpl result = new HostSpecificationImpl();
+ readVersionConstraint(result, in);
+ result.setReloadHost(in.readBoolean());
+ return result;
+ }
+ // called by readers for VersionConstraintImpl subclasses
+ private void readVersionConstraint(VersionConstraintImpl version, DataInputStream in) throws IOException {
+ version.setName(readString(in, false));
+ version.setVersionSpecification(readVersion(in));
+ version.setMatchingRule(in.readByte());
+ version.setActualVersion(readVersion(in));
+ version.setSupplier(readBundleDescription(in));
+ }
+ private Version readVersion(DataInputStream in) throws IOException {
+ byte tag = readTag(in);
+ if (tag == NULL)
+ return null;
+ if (tag == INDEX)
+ return (Version) objectTable.get(in.readInt());
+ int majorComponent = in.readInt();
+ int minorComponent = in.readInt();
+ int serviceComponent = in.readInt();
+ String qualifierComponent = readString(in, false);
+ Version result = new Version(majorComponent, minorComponent, serviceComponent, qualifierComponent);
+ addToObjectTable(result);
+ return result;
+ }
+ public final StateImpl loadCache(DataInputStream input) throws IOException {
+ try {
+ return readState(input);
+ } finally {
+ input.close();
+ }
+ }
+ private String readString(DataInputStream in, boolean intern) throws IOException {
+ byte type = in.readByte();
+ if (type == NULL)
+ return null;
+ if (intern)
+ return in.readUTF().intern();
+ else
+ return in.readUTF();
+ }
+ private byte readTag(DataInputStream in) throws IOException {
+ return in.readByte();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java
new file mode 100644
index 000000000..b068453fe
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.osgi.service.resolver.*;
+
+public class StateWriter {
+
+ // objectTable will be a hashmap of objects. The objects will be things
+ // like a plugin descriptor, extension, extension point, etc. The integer
+ // index value will be used in the cache to allow cross-references in the
+ // cached registry.
+ protected Map objectTable = new HashMap();
+
+ public static final byte STATE_CACHE_VERSION = 3;
+ public static final byte NULL = 0;
+ public static final byte OBJECT = 1;
+ public static final byte INDEX = 2;
+
+ private int addToObjectTable(Object object) {
+ objectTable.put(object, new Integer(objectTable.size()));
+ // return the index of the object just added (i.e. size - 1)
+ return (objectTable.size() - 1);
+ }
+ private int getFromObjectTable(Object object) {
+ if (objectTable != null) {
+ Object objectResult = objectTable.get(object);
+ if (objectResult != null) {
+ return ((Integer) objectResult).intValue();
+ }
+ }
+ return -1;
+ }
+ private boolean writePrefix(Object object, DataOutputStream out) throws IOException {
+ if (writeIndex(object, out))
+ return true;
+ // add this object to the object table first
+ addToObjectTable(object);
+ out.writeByte(OBJECT);
+ return false;
+ }
+ private void writeState(StateImpl state, DataOutputStream out) throws IOException {
+ out.write(STATE_CACHE_VERSION);
+ if (writePrefix(state, out))
+ return;
+ BundleDescription[] bundles = state.getBundles();
+ out.writeInt(bundles.length);
+ if (bundles.length == 0)
+ return;
+ for (int i = 0; i < bundles.length; i++)
+ writeBundleDescription((BundleDescriptionImpl) bundles[i], out);
+ out.writeLong(state.getTimeStamp());
+ out.writeBoolean(state.isResolved());
+ if (!state.isResolved())
+ return;
+ BundleDescription[] resolvedBundles = state.getResolvedBundles();
+ out.writeInt(resolvedBundles.length);
+ for (int i = 0; i < resolvedBundles.length; i++)
+ writeBundleDescription((BundleDescriptionImpl) resolvedBundles[i], out);
+ }
+ private void writeBundleDescription(BundleDescriptionImpl bundle, DataOutputStream out) throws IOException {
+ if (writePrefix(bundle, out))
+ return;
+ out.writeLong(bundle.getBundleId());
+ writeStringOrNull(bundle.getUniqueId(), out);
+ writeStringOrNull(bundle.getLocation(), out);
+ out.writeInt(bundle.getState());
+ writeVersion(bundle.getVersion(), out);
+ writeHostSpec((HostSpecificationImpl) bundle.getHost(), out);
+
+ PackageSpecification[] packages = bundle.getPackages();
+ out.writeInt(packages.length);
+ for (int i = 0; i < packages.length; i++)
+ writePackageSpec((PackageSpecificationImpl) packages[i], out);
+
+ String[] providedPackages = bundle.getProvidedPackages();
+ out.writeInt(providedPackages.length);
+ for (int i = 0; i < providedPackages.length; i++)
+ out.writeUTF(providedPackages[i]);
+
+ BundleSpecification[] requiredBundles = bundle.getRequiredBundles();
+ out.writeInt(requiredBundles.length);
+ for (int i = 0; i < requiredBundles.length; i++)
+ writeBundleSpec((BundleSpecificationImpl) requiredBundles[i], out);
+ }
+ private void writeBundleSpec(BundleSpecificationImpl bundle, DataOutputStream out) throws IOException {
+ writeVersionConstraint(bundle, out);
+ out.writeBoolean(bundle.isExported());
+ out.writeBoolean(bundle.isOptional());
+ }
+ private void writePackageSpec(PackageSpecificationImpl packageSpec, DataOutputStream out) throws IOException {
+ writeVersionConstraint(packageSpec, out);
+ out.writeBoolean(packageSpec.isExported());
+ }
+ private void writeProvidedPackage(PackageSpecificationImpl packageSpec, DataOutputStream out) throws IOException {
+ out.writeUTF(packageSpec.getName());
+ }
+ private void writeHostSpec(HostSpecificationImpl host, DataOutputStream out) throws IOException {
+ if (host == null) {
+ out.writeByte(NULL);
+ return;
+ }
+ out.writeByte(OBJECT);
+ writeVersionConstraint(host, out);
+ out.writeBoolean(host.reloadHost());
+ }
+ // called by writers for VersionConstraintImpl subclasses
+ private void writeVersionConstraint(VersionConstraintImpl version, DataOutputStream out) throws IOException {
+ writeStringOrNull(version.getName(), out);
+ writeVersion(version.getVersionSpecification(), out);
+ out.writeByte(version.getMatchingRule());
+ writeVersion(version.getActualVersion(), out);
+ writeBundleDescription((BundleDescriptionImpl) version.getSupplier(), out);
+ }
+ private void writeVersion(Version version, DataOutputStream out) throws IOException {
+ // TODO: should assess whether avoiding sharing versions would be good
+ if (writePrefix(version, out))
+ return;
+ out.writeInt(version.getMajorComponent());
+ out.writeInt(version.getMinorComponent());
+ out.writeInt(version.getServiceComponent());
+ writeStringOrNull(version.getQualifierComponent(), out);
+ }
+ private boolean writeIndex(Object object, DataOutputStream out) throws IOException {
+ if (object == null) {
+ out.writeByte(NULL);
+ return true;
+ }
+ int index = getFromObjectTable(object);
+ if (index == -1)
+ return false;
+ out.writeByte(INDEX);
+ out.writeInt(index);
+ return true;
+ }
+ public void saveState(StateImpl state, DataOutputStream output) throws IOException {
+ try {
+ writeState(state, output);
+ } finally {
+ output.close();
+ }
+ }
+ private void writeStringOrNull(String string, DataOutputStream out) throws IOException {
+ if (string == null)
+ out.writeByte(NULL);
+ else {
+ out.writeByte(OBJECT);
+ out.writeUTF(string);
+ }
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java
new file mode 100644
index 000000000..d013256f2
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.resolver;
+
+import org.eclipse.osgi.service.resolver.*;
+
+public abstract class VersionConstraintImpl implements VersionConstraint {
+ private String name;
+ private Version versionSpecification;
+ private byte matchingRule = GREATER_EQUAL_MATCH;
+ private BundleDescription bundle;
+ private BundleDescription supplier;
+ private Version actualVersion;
+ public String getName() {
+ return name;
+ }
+ public Version getVersionSpecification() {
+ return versionSpecification;
+ }
+ public Version getActualVersion() {
+ return actualVersion;
+ }
+ public byte getMatchingRule() {
+ return matchingRule;
+ }
+ public BundleDescription getBundle() {
+ return bundle;
+ }
+ public BundleDescription getSupplier() {
+ return supplier;
+ }
+ public boolean isResolved() {
+ return supplier != null;
+ }
+
+ public void setActualVersion(Version actualVersion) {
+ this.actualVersion = actualVersion;
+ }
+ public void setSupplier(BundleDescription supplier) {
+ this.supplier = supplier;
+ }
+ public void setMatchingRule(byte matchingRule) {
+ this.matchingRule = matchingRule;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public void setVersionSpecification(Version versionSpecification) {
+ this.versionSpecification = versionSpecification;
+ }
+ public void setBundle(BundleDescription bundle) {
+ this.bundle = bundle;
+ }
+ public void unresolve() {
+ actualVersion = null;
+ supplier = null;
+ }
+ public boolean isSatisfiedBy(Version provided) {
+ Version required = getVersionSpecification();
+ if (required == null)
+ return true;
+ switch (getMatchingRule()) {
+ case PERFECT_MATCH :
+ return provided.isPerfect(required);
+ case EQUIVALENT_MATCH :
+ return provided.isEquivalentTo(required);
+ case COMPATIBLE_MATCH :
+ return provided.isCompatibleWith(required);
+ default :
+ return provided.isGreaterOrEqualTo(required);
+ }
+ }
+ public String toString() {
+ return "name: " + name + " - version: " + versionSpecification;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/debug/Debug.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/debug/Debug.java
new file mode 100644
index 000000000..ece10fbc0
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/debug/Debug.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.debug;
+
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public class Debug
+{
+ public static final boolean DEBUG = true;
+ public static boolean DEBUG_GENERAL = false; // "debug"
+ public static boolean DEBUG_BUNDLE_TIME = false; //"debug.bundleTime"
+ public static boolean DEBUG_LOADER = false; // "debug.loader"
+ public static boolean DEBUG_EVENTS = false; // "debug.events"
+ public static boolean DEBUG_SERVICES = false; // "debug.services"
+ public static boolean DEBUG_PACKAGES = false; // "debug.packages"
+ public static boolean DEBUG_MANIFEST = false; // "debug.manifest"
+ public static boolean DEBUG_FILTER = false; // "debug.filter"
+ public static boolean DEBUG_SECURITY = false; // "debug.security"
+ public static boolean DEBUG_STARTLEVEL = false; // "debug.startlevel"
+ public static boolean DEBUG_PACKAGEADMIN = false; // "debug.packageadmin"
+ public static boolean DEBUG_PACKAGEADMIN_TIMING = false; //"debug.packageadmin/timing"
+ static {
+ DebugOptions dbgOptions = DebugOptions.getDefault();
+ if (dbgOptions != null) {
+ DEBUG_GENERAL = dbgOptions.getBooleanOption("debug", false);
+ DEBUG_BUNDLE_TIME = dbgOptions.getBooleanOption("debug.bundleTime", false) || dbgOptions.getBooleanOption("org.eclipse.core.runtime/timing/startup", false);
+ DEBUG_LOADER = dbgOptions.getBooleanOption("debug.loader",false);
+ DEBUG_EVENTS = dbgOptions.getBooleanOption("debug.events",false);
+ DEBUG_SERVICES = dbgOptions.getBooleanOption("debug.services",false);
+ DEBUG_PACKAGES = dbgOptions.getBooleanOption("debug.packages",false);
+ DEBUG_MANIFEST = dbgOptions.getBooleanOption("debug.manifest",false);
+ DEBUG_FILTER = dbgOptions.getBooleanOption("debug.filter",false);
+ DEBUG_SECURITY = dbgOptions.getBooleanOption("debug.security",false);
+ DEBUG_STARTLEVEL = dbgOptions.getBooleanOption("debug.startlevel",false);
+ DEBUG_PACKAGEADMIN = dbgOptions.getBooleanOption("debug.packageadmin",false) ;
+ DEBUG_PACKAGEADMIN_TIMING = dbgOptions.getBooleanOption("debug.packageadmin/timing", false) || dbgOptions.getBooleanOption("org.eclipse.core.runtime/debug", false);
+ }
+ }
+ public static PrintStream out = System.out;
+
+ public static void print(boolean x)
+ {
+ out.print(x);
+ }
+ public static void print(char x)
+ {
+ out.print(x);
+ }
+ public static void print(int x)
+ {
+ out.print(x);
+ }
+ public static void print(long x)
+ {
+ out.print(x);
+ }
+ public static void print(float x)
+ {
+ out.print(x);
+ }
+ public static void print(double x)
+ {
+ out.print(x);
+ }
+ public static void print(char x[])
+ {
+ out.print(x);
+ }
+ public static void print(String x)
+ {
+ out.print(x);
+ }
+ public static void print(Object x)
+ {
+ out.print(x);
+ }
+ public static void println(boolean x)
+ {
+ out.println(x);
+ }
+ public static void println(char x)
+ {
+ out.println(x);
+ }
+ public static void println(int x)
+ {
+ out.println(x);
+ }
+ public static void println(long x)
+ {
+ out.println(x);
+ }
+ public static void println(float x)
+ {
+ out.println(x);
+ }
+ public static void println(double x)
+ {
+ out.println(x);
+ }
+ public static void println(char x[])
+ {
+ out.println(x);
+ }
+ public static void println(String x)
+ {
+ out.println(x);
+ }
+ public static void println(Object x)
+ {
+ out.println(x);
+ }
+ public static void printStackTrace(Throwable x)
+ {
+ printStackTrace(x,out);
+ }
+ private static void printStackTrace(Throwable t, PrintStream out)
+ {
+ t.printStackTrace(out);
+
+ Method[] methods = t.getClass().getMethods();
+
+ int size = methods.length;
+ Class throwable = Throwable.class;
+
+ for (int i = 0; i < size; i++)
+ {
+ Method method = methods[i];
+
+ if (Modifier.isPublic(method.getModifiers()) &&
+ method.getName().startsWith("get") &&
+ throwable.isAssignableFrom(method.getReturnType()) &&
+ (method.getParameterTypes().length == 0))
+ {
+ try
+ {
+ Throwable nested = (Throwable) method.invoke(t, null);
+
+ if ((nested != null) && (nested != t))
+ {
+ out.println("Nested Exception:");
+ printStackTrace(nested, out);
+ }
+ }
+ catch (IllegalAccessException e)
+ {
+ }
+ catch (InvocationTargetException e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventListeners.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventListeners.java
new file mode 100644
index 000000000..2dc9c5a58
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventListeners.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+/**
+ * This class manages the list of listeners.
+ * Listeners may be added or removed as necessary.
+ */
+
+public class EventListeners
+{
+ /**
+ * This field contains the listener list.
+ */
+ ListenerList list;
+
+ /**
+ * Constructor for EventListeners. An empty list of listeners is created.
+ *
+ */
+ public EventListeners()
+ {
+ list = null;
+ }
+
+ /**
+ * This method is called to add a listener to the list.
+ *
+ * @param listener This is the listener object to be added to the list.
+ * @param listenerObject This is an optional listener-specific object.
+ * This object will be passed to the EventSource along with the listener
+ * when the listener is to be called.
+ */
+ public synchronized void addListener(Object listener, Object listenerObject)
+ {
+ if (listener != null)
+ {
+ list = ListenerList.addListener(list, listener, listenerObject);
+ }
+ }
+
+ /**
+ * This method is called to remove a listener from the list.
+ *
+ * @param listener This is the listener object to be removed from the list.
+ */
+ public synchronized void removeListener(Object listener)
+ {
+ if (listener != null)
+ {
+ list = ListenerList.removeListener(list, listener);
+ }
+ }
+
+ /**
+ * This method is called to remove all listeners from the list.
+ */
+ public synchronized void removeAllListeners()
+ {
+ list = null;
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventManager.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventManager.java
new file mode 100644
index 000000000..b1b85f6ea
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventManager.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+
+
+/**
+ * This class is the central class for the event manager. Each
+ * program that wishes to use the event manager should construct
+ * an EventManager object and use that object to construct
+ * EventQueue for dispatching events.
+ */
+
+public class EventManager
+{
+ /**
+ * EventThread for asynchronous dispatch of events.
+ */
+ protected EventThread thread;
+
+ /**
+ * EventThread Name
+ */
+ protected String threadName;
+
+ /**
+ * EventManager constructor. An EventManager object is responsible for
+ * the delivery of events to listeners via an EventSource.
+ *
+ */
+ public EventManager()
+ {
+ thread = null;
+ }
+
+ public EventManager(String threadName){
+ this();
+ this.threadName = threadName;
+ }
+
+ /**
+ * This method can be called to release any resources associated with this
+ * EventManager.
+ *
+ */
+ public synchronized void close()
+ {
+ if (thread != null)
+ {
+ thread.close();
+ thread = null;
+ }
+ }
+
+ /**
+ * Asynchronously dispatch an event to the set of listeners. An event dispatch thread
+ * maintained by the associated EventManager is used to deliver the events.
+ * This method may return immediately to the caller.
+ *
+ * @param ll The set of listeners to which the event will be dispatched.
+ * @param eventAction This value is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ * @param eventObject This object is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ */
+ void dispatchEventAsynchronous(ListenerList ll, int eventAction, Object eventObject)
+ {
+ EventThread thread = getEventThread();
+
+ for (; ll != null; ll = ll.list)
+ {
+ thread.postEvent((ListenerList) ll.listener, (EventSource) ll.object, eventAction, eventObject);
+ }
+ }
+
+ /**
+ * Synchronously dispatch an event to the set of listeners. The event may
+ * be dispatch on the current thread or an event dispatch thread
+ * maintained by the associated EventManager.
+ * This method will not return to the caller until an EventSource
+ * has been called (and returned) for each listener.
+ *
+ * @param ll The set of listeners to which the event will be dispatched.
+ * @param eventAction This value is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ * @param eventObject This object is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ */
+ void dispatchEventSynchronous(ListenerList ll, int eventAction, Object eventObject)
+ {
+ for (; ll != null; ll = ll.list)
+ {
+ ((ListenerList) ll.listener).dispatchEvent((EventSource) ll.object, eventAction, eventObject);
+ }
+ }
+
+ /**
+ * Returns an EventThread to use for dispatching events asynchronously.
+ *
+ * @return EventThread.
+ */
+ private synchronized EventThread getEventThread()
+ {
+ if (thread == null)
+ {
+ if (threadName == null)
+ {
+ thread = new EventThread();
+ }
+ else
+ {
+ thread = new EventThread(threadName);
+ }
+ }
+
+ return(thread);
+ }
+}
+
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventQueue.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventQueue.java
new file mode 100644
index 000000000..e3f767348
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventQueue.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+
+
+/**
+ * The EventQueue is used to build the set of listeners and then dispatch
+ * events to those listeners. An EventQueue object is associated with a
+ * specific EventManager object.
+ *
+ * <p>EventQueue objects are created on demand to build a set of listeners
+ * that should receive a specific event. Once the set is created, the event
+ * can then be synchronously or asynchronously delivered to the set of
+ * listeners. After the event has been dispatched for delivery, the
+ * EventQueue object should be discarded. A new EventQueue object should be
+ * created for the delivery of another specific event.
+ */
+
+public class EventQueue
+{
+ /**
+ * EventManager with which this queue is associated.
+ */
+ protected EventManager manager;
+ /**
+ * Set of listeners (list of listener lists).
+ */
+ protected ListenerList queue;
+
+ /**
+ * EventQueue constructor. This method creates an empty event queue.
+ *
+ * @param manager The EventManager this queue is associated with.
+ * @exception java.lang.NullPointerException if manager is null.
+ */
+ public EventQueue(EventManager manager)
+ {
+ this.manager = manager;
+ if (manager == null)
+ {
+ throw new NullPointerException();
+ }
+
+ queue = null;
+ }
+
+ /**
+ * Build the set of listeners. This method can be called multiple times, prior to
+ * calling one of the dispatchEvent methods, to build the set of listeners for the
+ * delivery of a specific event. The current list of listeners in the given ListenerList,
+ * at the time this method is called, is added to the set.
+ *
+ * @param listeners An EventListeners object to add to the queue. All listeners
+ * previously added to
+ * the EventListeners object will be called when an event is dispatched.
+ * @param source An EventSource object to use when dispatching an event
+ * to the listeners on this ListenerList.
+ */
+ public synchronized void queueListeners(EventListeners listeners, EventSource source)
+ {
+ if (listeners != null)
+ {
+ ListenerList list = listeners.list;
+
+ if (list != null)
+ {
+ queue = ListenerList.addListener(queue, list, source);
+ }
+ }
+ }
+
+ /**
+ * Asynchronously dispatch an event to the set of listeners. An event dispatch thread
+ * maintained by the associated EventManager is used to deliver the events.
+ * This method may return immediately to the caller.
+ *
+ * @param eventAction This value is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ * @param eventObject This object is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ */
+ public void dispatchEventAsynchronous(int eventAction, Object eventObject)
+ {
+ manager.dispatchEventAsynchronous(queue, eventAction, eventObject);
+ }
+
+ /**
+ * Synchronously dispatch an event to the set of listeners. The event may
+ * be dispatched on the current thread or an event dispatch thread
+ * maintained by the associated EventManager.
+ * This method will not return to the caller until an EventSource
+ * has been called (and returned) for each listener.
+ *
+ * @param eventAction This value is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ * @param eventObject This object is passed back to the event source when the call back
+ * is made to the EventSource object along with each listener.
+ */
+ public void dispatchEventSynchronous(int eventAction, Object eventObject)
+ {
+ manager.dispatchEventSynchronous(queue, eventAction, eventObject);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventSource.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventSource.java
new file mode 100644
index 000000000..fc4e51d8b
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventSource.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+
+
+/**
+ * The EventSource interface contains the method that is called by the
+ * Event Manager to complete the event delivery to the event listener.
+ */
+
+public abstract interface EventSource
+{
+ /**
+ * This method is the call back that is called once for each listener.
+ * This method must cast the EventListener object to the appropriate listener
+ * class for the event type and call the appropriate listener method.
+ *
+ * @param listener This listener must be cast to the appropriate listener
+ * class for the events created by this source and the appropriate listener method
+ * must then be called.
+ * @param listenerObject This is the optional object that was passed to
+ * ListenerList.addListener when the listener was added to the ListenerList.
+ * @param eventAction This value was passed to the EventQueue object via one of its
+ * dispatchEvent* method calls. It can provide information (such
+ * as which listener method to call) so that this method
+ * can complete the delivery of the event to the listener.
+ * @param eventObject This object was passed to the EventQueue object via one of its
+ * dispatchEvent* method calls. This object was created by the event source and
+ * is passed to this method. It should contain all the necessary information (such
+ * as what event object to pass) so that this method
+ * can complete the delivery of the event to the listener.
+ */
+ public abstract void dispatchEvent(Object listener, Object listenerObject, int eventAction, Object eventObject);
+}
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventThread.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventThread.java
new file mode 100644
index 000000000..1eb7e31da
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/EventThread.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+
+
+/**
+ * This class is used for asynchronously dispatching events.
+ */
+
+class EventThread extends Thread
+{
+ /**
+ * EventThreadItem is a nested top-level (non-member) class. This class
+ * represents the items which are placed on the queue.
+ */
+ static class EventThreadItem
+ {
+ /** listener list for this event */
+ private final ListenerList listeners;
+ /** source of this event */
+ private final EventSource source;
+ /** action for this event */
+ private final int action;
+ /** object for this event */
+ private final Object object;
+ /** next item in event queue */
+ EventThreadItem next;
+
+ /**
+ * Constructor for event queue item
+ *
+ * @param l Listener list for this event
+ * @param s Source for this event
+ * @param a Action for this event
+ * @param o Object for this event
+ */
+ EventThreadItem(ListenerList l, EventSource s, int a, Object o)
+ {
+ listeners = l;
+ source = s;
+ action = a;
+ object = o;
+ next = null;
+ }
+
+ /**
+ * This method will dispatch this event queue item to its listeners
+ */
+ void dispatchEvent()
+ {
+ listeners.dispatchEvent(source, action, object);
+ }
+ }
+
+ /** item at the head of the event queue */
+ private EventThreadItem head;
+ /** item at the tail of the event queue */
+ private EventThreadItem tail;
+ /** if false the thread must terminate */
+ private volatile boolean running;
+
+ /**
+ * Constructor for the event queue. The queue is created empty and the
+ * queue dispatcher thread is started.
+ */
+ EventThread(String threadName)
+ {
+ super(threadName);
+ init();
+ }
+
+ /**
+ * Constructor for the event queue. The queue is created empty and the
+ * queue dispatcher thread is started.
+ */
+ EventThread()
+ {
+ super();
+ init();
+ }
+
+ void init(){
+ running = true;
+ head = null;
+ tail = null;
+
+ setDaemon(true); /* Mark thread as daemon thread */
+ start(); /* Start thread */
+ }
+
+ /**
+ * Stop thread.
+ */
+ void close()
+ {
+ running = false;
+ interrupt();
+ }
+
+ /**
+ * This method is the event queue dispatcher thread. It pulls events from
+ * the queue and dispatches them.
+ */
+ public void run()
+ {
+ while (running)
+ {
+ try
+ {
+ getNextEvent().dispatchEvent();
+ }
+ catch (Throwable t)
+ {
+ }
+ }
+ }
+
+ /**
+ * This methods takes the input parameters and creates an EventThreadItem
+ * and queues it.
+ * The thread is notified.
+ *
+ * @param l Listener list for this event
+ * @param s Source for this event
+ * @param a Action for this event
+ * @param o Object for this event
+ */
+ synchronized void postEvent(ListenerList l, EventSource s, int a, Object o)
+ {
+ EventThreadItem item = new EventThreadItem(l, s, a, o);
+
+ if (head == null) /* if the queue was empty */
+ {
+ head = item;
+ tail = item;
+ }
+ else /* else add to end of queue */
+ {
+ tail.next = item;
+ tail = item;
+ }
+
+ notify();
+ }
+
+ /**
+ * This method is called by the thread to remove
+ * items from the queue so that they can be dispatched to their listeners.
+ * If the queue is empty, the thread waits.
+ *
+ * @return The EventThreadItem removed from the top of the queue.
+ */
+ private synchronized EventThreadItem getNextEvent() throws InterruptedException
+ {
+ while (running && (head == null))
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ if (!running) /* if we are stopping */
+ {
+ throw new InterruptedException(); /* throw an exception */
+ }
+
+ EventThreadItem item = head;
+ head = item.next;
+ if (head == null)
+ {
+ tail = null;
+ }
+
+ return(item);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/ListenerList.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/ListenerList.java
new file mode 100644
index 000000000..cdaa49347
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/eventmgr/ListenerList.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.eventmgr;
+
+/**
+ * This internal class manages the list of listeners. Each object is
+ * immutable, once created it cannot be modified. The most expensive
+ * operation on the list is removeListener. This operation must descend
+ * the list until the listener is found, then as it returns up the call
+ * chain, new ListenerList objects are created and returned to rebuild the list.
+ */
+
+class ListenerList
+{
+ /**
+ * Next item in list.
+ */
+ final ListenerList list;
+
+ /**
+ * Listener referenced by this item.
+ */
+ final Object listener;
+
+ /**
+ * Listener object referenced by this item.
+ */
+ final Object object;
+
+ static final boolean DEBUG = true;
+
+ /**
+ * Private constructor used by addListener and removeListener.
+ *
+ * @param oldlist Existing list or null if no list.
+ * @param l Listener to be added to list.
+ * @return List object.
+ */
+ private ListenerList(ListenerList oldlist, Object l, Object o)
+ {
+ list = oldlist;
+ listener = l;
+ object = o;
+ }
+
+ /**
+ * Static method to add a listener to a list.
+ *
+ * @param oldlist Existing list to which the listener is to be added.
+ * @param l Listener to be added to list.
+ * @param o Listener specific object to be added to list.
+ * @return New list with listener added.
+ */
+ static ListenerList addListener(ListenerList oldlist, Object l, Object o)
+ {
+ /* Create the linked list element in the listener's memory space */
+ // resman begin
+ return (new ListenerList(oldlist, l, o));
+ // resman end
+ }
+
+ /**
+ * Static method to remove a listener from the list.
+ *
+ * @param oldlist Existing list from which the listener is to be removed
+ * @param l Listener to be removed from list
+ * @return New list with listener removed.
+ */
+ static ListenerList removeListener(ListenerList oldlist, Object l)
+ {
+ if (oldlist != null) /* list is not empty */
+ {
+ try
+ {
+ return (oldlist.removeListener(l));
+ }
+ catch (IllegalArgumentException e)
+ {
+ /* Listener to be removed was not found */
+ }
+ }
+ return (oldlist);
+ }
+
+ /**
+ * Private method to recurse down the list looking for the listener to remove.
+ * When the listener is found, the call chain returns creating new List
+ * objects rebuilding the list.
+ *
+ * @param l Listener to be removed from list
+ * @return New sublist with listener removed.
+ * @exception java.lang.IllegalArgumentException If listener to be removed
+ * is not found
+ */
+ private ListenerList removeListener(Object l)
+ throws IllegalArgumentException
+ {
+ if (listener == l) /* Check if this the guy to remove */
+ {
+ return (list); /* return the next on the list */
+ }
+
+ if (list != null) /* we've not reached the end of the list */
+ {
+ ListenerList ll = list.removeListener(l);
+
+ /* Create the linked list element in the listener's memory space */
+ // resman begin
+ return (new ListenerList(ll, listener, object));
+ // resman end
+ }
+
+ /* We have reached the end of the list and have not found the listener */
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * This method calls the EventSource object to complete the dispatch of
+ * the event. If there are more listeners in the list, call dispatchEvent
+ * on the next item on the list.
+ *
+ * @param source Call back object which is called to complete the delivery of
+ * the event.
+ * @param action This value was passed by the event source and
+ * is passed to this method. This is passed on to the call back object.
+ * @param object This object was created by the event source and
+ * is passed to this method. This is passed on to the call back object.
+ */
+ void dispatchEvent(EventSource source, int action, Object object)
+ {
+ /* Remember the current thread's active memory space */
+ // resman begin
+ for (ListenerList ll = this; ll != null; ll = ll.list)
+ {
+ /* Dispatch the event in the listener's memory space */
+ // resman select memoryspace
+
+ try
+ {
+ /* Call the call back method with the listener */
+ source.dispatchEvent(ll.listener, ll.object, action, object);
+ }
+ catch (Throwable t)
+ {
+ /* Consume and ignore any exceptions thrown by the listener */
+
+ if (DEBUG) {
+ System.out.println("Exception in " + ll.listener);
+ t.printStackTrace();
+ }
+ }
+ }
+ // resman end
+ }
+}
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/ExternalMessages.properties b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/ExternalMessages.properties
new file mode 100644
index 000000000..bf8be01a8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/ExternalMessages.properties
@@ -0,0 +1,78 @@
+######################################################################
+# Copyright (c) 2003 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+######################################################################
+
+#External Messages for EN locale
+ECLIPSE_OSGI_NAME=Eclipse OSGi Framework
+ECLIPSE_OSGI_VERSION=3.6.0
+OSGI_VERSION=OSGi Service Platform Release 3
+ECLIPSE_COPYRIGHT=Copyright (c) 2003 IBM Corporation and others. \nAll rights reserved. This program and the accompanying materials \nare made available under the terms of the Common Public License v1.0 \nwhich accompanies this distribution, and is available at \nhttp://www.eclipse.org/legal/cpl-v10.html \nContributors: \n IBM Corporation - initial API and implementation
+OSGI_SYSTEMBUNDLE_CREATE_EXCEPTION=Exception while creating System Bundle: {0}
+OSGI_INTERNAL_ERROR=Internal Error in the OSGi framework. Please report this problem.
+BUNDLE_INSTALL_RECURSION_EXCEPTION=Bundle is already being installed
+BUNDLE_INSTALL_REQUIRED_EE_EXCEPTION=No match is available for the required execution environment: {0}
+BUNDLE_INSTALL_SAME_UNIQUEID=A bundle with the unique ID and version is already installed: {0} {1}
+BUNDLE_UNINSTALLED_EXCEPTION=Bundle has been uninstalled
+BUNDLE_CONTEXT_INVALID_EXCEPTION=BundleContext is no longer valid
+BUNDLE_NATIVECODE_EXCEPTION=The Bundle-NativeCode file {0} could not be not found
+BUNDLE_NATIVECODE_MATCH_EXCEPTION=No Bundle-NativeCode match
+BUNDLE_JXE_UNSUPPORTED_EXCEPTION=JXE Bundles are not supported in this environment
+BUNDLE_STATE_CHANGE_EXCEPTION=Bundle state change in progress
+BUNDLE_REFRESH_FAILURE=PackageAdmin.refreshPackages failed to complete
+BUNDLE_ACTIVATOR_EXCEPTION=Exception in {0}.{1}()
+BUNDLE_INVALID_ACTIVATOR_EXCEPTION=The BundleActivator {0} is invalid
+BUNDLE_INVALID_CLASSPATH_EXCEPTION=The BundleActivator {0} was not loaded by the bundle's ClassLoader; This may indicate the bundle is on the system classpath
+BUNDLE_UNRESOLVED_EXCEPTION=The bundle could not be resolved
+BUNDLE_UNRESOLVED_PACKAGE=imported package {0}
+BUNDLE_UNRESOLVED_BUNDLE=required bundle {0}
+BUNDLE_UNRESOLVED_HOST=host {0}
+BUNDLE_UNRESOLVED_NOT_CHOSEN_EXCEPTION=The bundle could not be resolved. Reason: probably another version has been chosen
+BUNDLE_UNRESOLVED_UNSATISFIED_CONSTRAINT_EXCEPTION=The bundle could not be resolved. Reason: missing {0}
+BUNDLE_JXE_LOAD_EXCEPTION=The file rom.classes in the bundle could not be loaded
+BUNDLE_JXE_UNLOAD_EXCEPTION=Unable to unload {0}/{1}
+BUNDLE_LIBRARY_NOT_FOUND_EXCEPTION=The native library {0} was not found in the bundle
+BUNDLE_SYSTEMBUNDLE_UNINSTALL_EXCEPTION=The System Bundle cannot be uninstalled
+BUNDLE_READ_EXCEPTION=An error occured trying to read the bundle
+BUNDLE_CLASSPATH_ENTRY_NOT_FOUND_EXCEPTION=The Bundle-ClassPath entry {0} was not found in the bundle
+BUNDLE_NO_CLASSPATH_MATCH=The Bundle-ClassPath does not have any matching entries
+BUNDLE_NOT_IN_STATE=The bundle does not exist in the state: {0}
+STATE_UNRESOLVED_WRONG_BUNDLE=The state unresolved a bundle that was not in the list of bundles to resolve: {0}
+FRAGMENT_CANNOT_START=A fragment bundle cannot be started.
+FRAGMENT_CANNOT_STOP=A fragment bundle cannot be stopped.
+ADAPTOR_STORAGE_EXCEPTION=The FrameworkAdaptor object could not perform the operation
+ADAPTOR_DIRECTORY_EXCEPTION=\"{0}\" is not a directory
+ADAPTOR_URL_CREATE_EXCEPTION=\"{0}\" is an invalid URL
+ADAPTOR_DIRECTORY_CREATE_EXCEPTION=The directory \"{0}\" could not be created
+RESOURCE_NOT_FOUND_EXCEPTION=The resource {0} could not be found
+MANIFEST_NOT_FOUND_EXCEPTION=The manifest file {0} could not be found in the bundle {1}
+MANIFEST_INVALID_LINE_EXCEPTION=The manifest line \"{0}\" is invalid
+MANIFEST_INVALID_HEADER_EXCEPTION=The {0} \"{1}\" is invalid
+MANIFEST_INVALID_PACKAGE_EXCEPTION=The package description {0} in invalid
+REGISTRY_UPDATE_EXCEPTION=A registry update failed
+SERVICE_FACTORY_EXCEPTION=Exception in {0}.{1}()
+SERVICE_CLASS_NOT_FOUND_EXCEPTION=The service class {0} could not be loaded from an exported package
+SERVICE_NOT_INSTANCEOF_CLASS_EXCEPTION={0}.getService() returned a service object that is not an instanceof the service class {1}
+SERVICE_OBJECT_NULL_EXCEPTION={0}.getService() returned a null service object
+SERVICE_ARGUMENT_NULL_EXCEPTION=The service parameter is null
+SERVICE_ALREADY_UNREGISTERED_EXCEPTION=The service has been unregistered
+SERVICE_EMPTY_CLASS_LIST_EXCEPTION=The array of service names is empty
+HEADER_DUPLICATE_KEY_EXCEPTION "The key \"{0}\" already exists in another case variation
+FILTER_MISSING_LEFTPAREN=Missing \'(\' at character {0}
+FILTER_MISSING_RIGHTPAREN=Missing \')\' at character {0}
+FILTER_TRAILING_CHARACTERS=Extraneous trailing characters at character {0}
+FILTER_TERMINATED_ABRUBTLY=Filter ended abruptly
+FILTER_INVALID_OPERATOR=Invalid operator at character {0}
+FILTER_MISSING_ATTR=Missing attr at character {0}
+FILTER_MISSING_VALUE=Missing value at character {0}
+FILTER_INVALID_VALUE=Invalid value at character {0}
+PROPERTIES_INVALID_ADDRESSLENGTH=Invalid osgi.properties value \"org.eclipse.osgi.framework.internal.core.addresslength={0}\"
+PROPERTIES_INVALID_ENDIAN=Invalid osgi.properties value \"org.eclipse.osgi.framework.internal.core.endian={0}\"
+PROPERTIES_INVALID_FW_STARTLEVEL=Invalid initial framework start level. Value must be a positive integer. Using Default={0}
+STARTLEVEL_EXCEPTION_INVALID_REQUESTED_STARTLEVEL=The requested start level of {0} is invalid. The value must be a positive integer.
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/Msg.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/Msg.java
new file mode 100644
index 000000000..72bfd2cc3
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/Msg.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+/**
+ * This class retrieves strings from a resource bundle
+ * and returns them, formatting them with MessageFormat
+ * when required.
+ * <p>
+ * It is used by the system classes to provide national
+ * language support, by looking up messages in the
+ * <code>
+ * org.eclipse.osgi.framework.internal.core.ExternalMessages
+ * </code>
+ * resource bundle. Note that if this file is not available,
+ * or an invalid key is looked up, or resource bundle support
+ * is not available, the key itself will be returned as the
+ * associated message. This means that the <em>KEY</em> should
+ * a reasonable human-readable (english) string.
+ */
+
+public class Msg
+{
+
+ static public org.eclipse.osgi.framework.msg.MessageFormat formatter;
+
+ // Attempt to load the message bundle.
+ static
+ {
+ formatter = new org.eclipse.osgi.framework.msg.MessageFormat("org.eclipse.osgi.framework.internal.core.ExternalMessages");
+ }
+}
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFile.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFile.java
new file mode 100644
index 000000000..f6c4635b8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFile.java
@@ -0,0 +1,536 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.reliablefile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import org.eclipse.osgi.framework.internal.defaultadaptor.AdaptorMsg;
+
+/**
+ * ReliableFile class used by ReliableFileInputStream and ReliableOutputStream.
+ * This class encapsulates all the logic for reliable file support.
+ */
+
+public class ReliableFile
+{
+ /**
+ * Extension of tmp file used during writing.
+ * A reliable file with this extension should
+ * never be directly used.
+ */
+ public static final String tmpExt = ".tmp";
+
+ /**
+ * Extension of previous generation of the reliable file.
+ * A reliable file with this extension should
+ * never be directly used.
+ */
+ public static final String oldExt = ".bak";
+
+ /**
+ * Extension of next generation of the reliable file.
+ * A reliable file with this extension should
+ * never be directly used.
+ */
+ public static final String newExt = ".new";
+
+ /** List of active ReliableFile objects: File => ReliableFile */
+ private static Hashtable files;
+
+ static
+ {
+ files = new Hashtable(30); /* initialize files */
+ }
+
+ /** File object for original file */
+ private File orgFile;
+
+ /** File object for the temporary output file */
+ private File tmpFile;
+
+ /** File object for old data file */
+ private File oldFile;
+
+ /** File object for file containing new data */
+ private File newFile;
+
+
+ /** True if this object is open for read or write */
+ private boolean locked;
+
+ /** Use code of this object. When zero this object must be removed from files */
+ private int use;
+
+ /**
+ * ReliableFile object factory. This method is called by ReliableFileInputStream
+ * and ReliableFileOutputStream to get a ReliableFile object for a target file.
+ * If the object is in the cache, the cached copy is returned.
+ * Otherwise a new ReliableFile object is created and returned.
+ * The use count of the returned ReliableFile object is incremented.
+ *
+ * @param name Name of the target file.
+ * @return A ReliableFile object for the target file.
+ * @throws IOException If the target file is a directory.
+ */
+ static ReliableFile getReliableFile(String name) throws IOException
+ {
+ return getReliableFile(new File(name));
+ }
+
+ /**
+ * ReliableFile object factory. This method is called by ReliableFileInputStream
+ * and ReliableFileOutputStream to get a ReliableFile object for a target file.
+ * If the object is in the cache, the cached copy is returned.
+ * Otherwise a new ReliableFile object is created and returned.
+ * The use count of the returned ReliableFile object is incremented.
+ *
+ * @param file File object for the target file.
+ * @return A ReliableFile object for the target file.
+ * @throws IOException If the target file is a directory.
+ */
+ static ReliableFile getReliableFile(File file) throws IOException
+ {
+ if (file.isDirectory())
+ {
+ throw new FileNotFoundException(AdaptorMsg.formatter.getString("RELIABLEFILE_FILE_IS_DIRECTORY"));
+ }
+
+ synchronized (files)
+ {
+ ReliableFile reliable = (ReliableFile)files.get(file);
+
+ if (reliable == null)
+ {
+ reliable = new ReliableFile(file);
+
+ files.put(file, reliable);
+ }
+
+ reliable.use++;
+
+ return reliable;
+ }
+ }
+
+ /**
+ * Decrement this object's use count. If the use count
+ * drops to zero, remove this object from the cache.
+ *
+ */
+ private void release()
+ {
+ synchronized (files)
+ {
+ use--;
+
+ if (use <= 0)
+ {
+ files.remove(orgFile);
+ }
+ }
+ }
+
+ /**
+ * Private constructor used by the static getReliableFile factory methods.
+ *
+ * @param file File object for the target file.
+ */
+ private ReliableFile(File file)
+ {
+ String name = file.getPath();
+
+ orgFile = file;
+ tmpFile = new File(name + tmpExt);
+ oldFile = new File(name + oldExt);
+ newFile = new File(name + newExt);
+ use = 0;
+ locked = false;
+ }
+
+ /**
+ * Recovers the target file, if necessary, and returns an InputStream
+ * object for reading the target file.
+ *
+ * @return An InputStream object which can be used to read the target file.
+ * @throws IOException If an error occurs preparing the file.
+ */
+ synchronized InputStream getInputStream() throws IOException
+ {
+ try
+ {
+ lock();
+ }
+ catch (IOException e)
+ {
+ /* the lock request failed; decrement the use count */
+ release();
+
+ throw e;
+ }
+
+ try
+ {
+ recoverFile();
+
+ return new FileInputStream(orgFile.getPath());
+ }
+ catch (IOException e)
+ {
+ unlock();
+
+ release();
+
+ throw e;
+ }
+ }
+
+ /**
+ * Close the target file for reading.
+ *
+ * @throws IOException If an error occurs closing the file.
+ */
+ /* This method does not need to be synchronized if it only calls release. */
+ void closeInputFile() throws IOException
+ {
+ unlock();
+
+ release();
+ }
+
+ /**
+ * Recovers the target file, if necessary, and returns an OutputStream
+ * object for writing the target file.
+ *
+ * @return An OutputStream object which can be used to write the target file.
+ * @throws IOException If an error occurs preparing the file.
+ */
+ synchronized OutputStream getOutputStream(boolean append) throws IOException
+ {
+ try
+ {
+ lock();
+ }
+ catch (IOException e)
+ {
+ /* the lock request failed; decrement the use count */
+ release();
+
+ throw e;
+ }
+
+ try
+ {
+ if (append)
+ {
+ recoverFile();
+
+ if (orgFile.exists())
+ {
+ cp(orgFile, tmpFile);
+ }
+ }
+
+ return new FileOutputStream(tmpFile.getPath(), append);
+ }
+ catch (IOException e)
+ {
+ unlock();
+
+ release();
+
+ throw e;
+ }
+ }
+
+ /**
+ * Close the target file for reading.
+ *
+ * @throws IOException If an error occurs closing the file.
+ */
+ synchronized void closeOutputFile() throws IOException
+ {
+ try
+ {
+ boolean orgExists = orgFile.exists();
+ boolean newExists = newFile.exists();
+
+ if (newExists)
+ {
+ rm(oldFile);
+ mv(newFile, oldFile);
+ }
+
+ mv(tmpFile, newFile);
+
+ if (orgExists)
+ {
+ if (newExists)
+ {
+ rm(orgFile);
+ }
+ else
+ {
+ rm(oldFile);
+ mv(orgFile, oldFile);
+ }
+ }
+
+ mv(newFile, orgFile);
+ }
+ finally
+ {
+ unlock();
+
+ release();
+ }
+ }
+
+ /**
+ * This method recovers the reliable file if necessary.
+ *
+ * @throws IOException If an error occurs recovering the file.
+ */
+ private void recoverFile() throws IOException
+ {
+ boolean orgExists = orgFile.exists();
+ boolean newExists = newFile.exists();
+ boolean oldExists = oldFile.exists();
+
+ if (newExists)
+ {
+ if (orgExists && !oldExists)
+ {
+ mv(orgFile, oldFile);
+ }
+
+ cp(newFile, orgFile);
+
+ if (orgExists || oldExists)
+ {
+ rm(newFile);
+ }
+ else
+ {
+ mv(newFile, oldFile);
+ }
+ }
+ else
+ {
+ if (oldExists && !orgExists)
+ {
+ cp(oldFile, orgFile);
+ }
+ }
+ }
+
+ /**
+ * Lock the target file.
+ *
+ * @throws IOException If the file is already locked.
+ */
+ private void lock() throws IOException
+ {
+ if (locked)
+ {
+ throw new FileNotFoundException(AdaptorMsg.formatter.getString("RELIABLEFILE_FILE_LOCKED"));
+ }
+
+ locked = true;
+ }
+
+ /**
+ * Unlock the target file.
+ */
+ private void unlock()
+ {
+ locked = false;
+ }
+
+ /**
+ * Rename a file.
+ *
+ * @param from The original file.
+ * @param to The new file name.
+ * @throws IOException If the rename failed.
+ */
+ private static void mv(File from, File to) throws IOException
+ {
+ if (!from.renameTo(to))
+ {
+ throw new IOException(AdaptorMsg.formatter.getString("RELIABLEFILE_RENAME_FAILED"));
+ }
+ }
+
+ /**
+ * Copy a file.
+ *
+ * @param from The original file.
+ * @param to The target file.
+ * @throws IOException If the copy failed.
+ */
+ private static final int CP_BUF_SIZE = 4096;
+ private static void cp(File from, File to) throws IOException
+ {
+ FileInputStream in = null;
+ FileOutputStream out = null;
+
+ try
+ {
+ out = new FileOutputStream(to);
+
+ int length = (int)from.length();
+ if (length > 0)
+ {
+ if (length > CP_BUF_SIZE)
+ {
+ length = CP_BUF_SIZE;
+ }
+
+ in = new FileInputStream(from);
+
+ byte buffer[] = new byte[length];
+ int count;
+ while ((count = in.read(buffer, 0, length)) > 0)
+ {
+ out.write(buffer, 0, count);
+ }
+
+ in.close();
+ in = null;
+ }
+
+ out.close();
+ out = null;
+ }
+ catch (IOException e)
+ {
+ // close open streams
+ if (out != null)
+ {
+ try
+ {
+ out.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+
+ if (in != null)
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException ee)
+ {
+ }
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ * Delete a file.
+ *
+ * @param file The file to delete.
+ * @throws IOException If the delete failed.
+ */
+ private static void rm(File file) throws IOException
+ {
+ if (file.exists() && !file.delete())
+ {
+ throw new IOException(AdaptorMsg.formatter.getString("RELIABLEFILE_DELETE_FAILED"));
+ }
+ }
+
+ /**
+ * Answers a boolean indicating whether or not the specified reliable file
+ * exists on the underlying file system.
+ *
+ * @return <code>true</code> if the specified reliable file exists,
+ * <code>false</code> otherwise.
+ */
+ public static boolean exists(File file)
+ {
+ if (file.exists()) /* quick test */
+ {
+ return true;
+ }
+
+ String name = file.getPath();
+
+ return new File(name + oldExt).exists() || new File(name + newExt).exists();
+ }
+
+ /**
+ * Delete this reliable file on the underlying file system.
+ *
+ * @throws IOException If the delete failed.
+ */
+ private synchronized void delete() throws IOException
+ {
+ try
+ {
+ lock();
+ }
+ catch (IOException e)
+ {
+ /* the lock request failed; decrement the use count */
+ release();
+
+ throw e;
+ }
+
+ try
+ {
+ rm(oldFile);
+ rm(orgFile);
+ rm(newFile);
+ rm(tmpFile);
+ }
+ finally
+ {
+ unlock();
+
+ release();
+ }
+ }
+
+ /**
+ * Delete the specified reliable file
+ * on the underlying file system.
+ *
+ * @return <code>true</code> if the specified reliable file was deleted,
+ * <code>false</code> otherwise.
+ */
+ public static boolean delete(File file)
+ {
+ try
+ {
+ getReliableFile(file).delete();
+
+ return true;
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileInputStream.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileInputStream.java
new file mode 100644
index 000000000..c29095d32
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileInputStream.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.reliablefile;
+
+import java.io.File;
+import java.io.FilterInputStream;
+import java.io.IOException;
+
+/**
+ * A ReliableFile FileInputStream replacement class.
+ * This class can be used just like FileInputStream. The class
+ * is in partnership with ReliableFileOutputStream to avoid losing
+ * file data by using multiple files.
+ *
+ * @see ReliableFileOutputStream
+ */
+public class ReliableFileInputStream extends FilterInputStream
+{
+ /**
+ * ReliableFile object for this file.
+ */
+ private ReliableFile reliable;
+
+ /**
+ * Constructs a new ReliableFileInputStream on the file named <code>name</code>. If the
+ * file does not exist, the <code>FileNotFoundException</code> is thrown.
+ * The <code>name</code> may be absolute or relative
+ * to the System property <code>"user.dir"</code>.
+ *
+ * @param name the file on which to stream reads.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileInputStream(String name) throws IOException
+ {
+ this(ReliableFile.getReliableFile(name));
+ }
+
+ /**
+ * Constructs a new ReliableFileInputStream on the File <code>file</code>. If the
+ * file does not exist, the <code>FileNotFoundException</code> is thrown.
+ *
+ * @param file the File on which to stream reads.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileInputStream(File file) throws IOException
+ {
+ this(ReliableFile.getReliableFile(file));
+ }
+
+ /**
+ * Private constructor used by other constructors.
+ *
+ * @param reliable the ReliableFile on which to read.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ private ReliableFileInputStream(ReliableFile reliable) throws IOException
+ {
+ super(reliable.getInputStream());
+
+ this.reliable = reliable;
+ }
+
+ /**
+ * Closes this input stream and releases any system resources associated
+ * with the stream.
+ *
+ * @exception java.io.IOException If an error occurs closing the file.
+ */
+ public synchronized void close() throws IOException
+ {
+ if (reliable != null)
+ {
+ try
+ {
+ super.close();
+ }
+ finally
+ {
+ reliable.closeInputFile();
+ reliable = null;
+ }
+ }
+ }
+
+ /**
+ * Call close to finalize the underlying ReliableFile.
+ */
+ protected void finalize() throws IOException
+ {
+ close();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileOutputStream.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileOutputStream.java
new file mode 100644
index 000000000..ee6fdeb34
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/reliablefile/ReliableFileOutputStream.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.reliablefile;
+
+import java.io.File;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+
+/**
+ * A ReliableFile FileOutputStream replacement class.
+ * This class can be used just like FileOutputStream. The class
+ * is in partnership with ReliableFileInputStream to avoid losing
+ * file data by using multiple files.
+ *
+ * @see ReliableFileInputStream
+ */
+public class ReliableFileOutputStream extends FilterOutputStream
+{
+ /**
+ * ReliableFile object for the file.
+ */
+ private ReliableFile reliable;
+
+ /**
+ * Constructs a new ReliableFileOutputStream on the File <code>file</code>. If the
+ * file exists, it is written over. See the constructor which can append to
+ * the file if so desired.
+ *
+ * @param file the File on which to stream reads.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileOutputStream(File file) throws IOException
+ {
+ this(ReliableFile.getReliableFile(file), false);
+ }
+
+ /**
+ * Constructs a new ReliableFileOutputStream on the File <code>file</code>.
+ *
+ * @param file the File on which to stream reads.
+ * @param append a boolean indicating whether or not to append to an existing file.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileOutputStream(File file, boolean append) throws IOException
+ {
+ this(ReliableFile.getReliableFile(file), append);
+ }
+
+ /**
+ * Constructs a new ReliableFileOutputStream on the file named <code>name</code>. If
+ * the file exists, it is written over. See the constructor which can append to
+ * the file if so desired.
+ * The <code>name</code> may be absolute or relative
+ * to the System property <code>"user.dir"</code>.
+ *
+ * @param name the file on which to stream writes.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileOutputStream(String name) throws IOException
+ {
+ this(ReliableFile.getReliableFile(name), false);
+ }
+
+ /**
+ * Constructs a new ReliableFileOutputStream on the file named <code>name</code>.
+ * The <code>name</code> may be absolute or relative
+ * to the System property <code>"user.dir"</code>.
+ *
+ * @param name the file on which to stream writes.
+ * @param append a boolean indicating whether or not to append to an existing file.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ public ReliableFileOutputStream(String name, boolean append) throws IOException
+ {
+ this(ReliableFile.getReliableFile(name), append);
+ }
+
+ /**
+ * Private constructor used by other constructors.
+ *
+ * @param reliable the ReliableFile on which to read.
+ * @param append a boolean indicating whether or not to append to an existing file.
+ * @exception java.io.IOException If an error occurs opening the file.
+ */
+ private ReliableFileOutputStream(ReliableFile reliable, boolean append) throws IOException
+ {
+ super(reliable.getOutputStream(append));
+
+ this.reliable = reliable;
+ }
+
+ /**
+ * Closes this output stream and releases any system resources
+ * associated with this stream. The general contract of <code>close</code>
+ * is that it closes the output stream. A closed stream cannot perform
+ * output operations and cannot be reopened.
+ *
+ * @exception java.io.IOException If an error occurs closing the file.
+ */
+ public synchronized void close() throws IOException
+ {
+ if (reliable != null)
+ {
+ try
+ {
+ super.close();
+ }
+ finally
+ {
+ reliable.closeOutputFile();
+ reliable = null;
+ }
+ }
+ }
+
+ /**
+ * Call close to finalize the underlying ReliableFile.
+ */
+ protected void finalize() throws IOException
+ {
+ close();
+ }
+
+
+ /**
+ * Override default FilterOutputStream method.
+ */
+ public void write(byte b[], int off, int len) throws IOException
+ {
+ out.write(b, off, len);
+ }
+}

Back to the top