Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.virgo.kernel.userregion')
-rw-r--r--org.eclipse.virgo.kernel.userregion/.classpath48
-rw-r--r--org.eclipse.virgo.kernel.userregion/.project36
-rw-r--r--org.eclipse.virgo.kernel.userregion/.settings/com.springsource.server.ide.bundlor.core.prefs3
-rw-r--r--org.eclipse.virgo.kernel.userregion/.settings/org.eclipse.wst.common.project.facet.core.xml4
-rw-r--r--org.eclipse.virgo.kernel.userregion/.settings/org.springframework.ide.eclipse.core.prefs67
-rw-r--r--org.eclipse.virgo.kernel.userregion/.springBeans13
-rw-r--r--org.eclipse.virgo.kernel.userregion/build.xml8
-rw-r--r--org.eclipse.virgo.kernel.userregion/ivy.xml39
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/Activator.java293
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/EmptyModuleContextAccessor.java23
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/InitialArtifactDeployer.java175
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/KernelStartedAwaiter.java48
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingRegistryHook.java55
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingStrategy.java190
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/UserRegionLogEvents.java51
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxBootDelegationHelper.java101
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxConsoleManager.java105
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxHookRegistrar.java53
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxOsgiFramework.java138
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxUtils.java139
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelBundleClassLoader.java449
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelClassLoaderCreator.java47
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionDumpContributor.java64
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionStateDumper.java102
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardPackageAdminUtil.java45
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardStateWriter.java44
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardSystemStateAccessor.java41
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StateWriter.java40
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/SystemStateAccessor.java34
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/TransformedManifestProvidingBundleFileWrapper.java297
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/UsesAnalyser.java394
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/AbstractTrackedPackageImports.java376
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/AdditionalTrackedPackageImports.java54
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/BundleManifestProcessor.java282
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/BundleTrackedPackageImports.java78
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/CollectingTrackedPackageImports.java36
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ContainingTrackedPackageImports.java62
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/EmptyTrackedPackageImports.java35
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ImportExpansionHandler.java562
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/StandardImportPromotionVector.java85
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/StandardTrackedPackageImportsFactory.java68
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImports.java92
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImportsFactory.java83
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/BundleDescriptionComparator.java32
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/DependencyCalculator.java376
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/GenericQuasiResolutionFailure.java42
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/PackageQuasiResolutionFailure.java63
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/PackageUsesQuasiResolutionFailure.java35
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/ResolutionFailureDetective.java40
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiBundle.java293
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiExportPackage.java162
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiFramework.java415
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiFrameworkFactory.java160
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiImportPackage.java169
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiParameterised.java125
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiRequiredBundle.java111
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardResolutionFailureDetective.java392
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/main/resources/EventLogMessages.properties5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/InitialArtifactDeployerTests.java156
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingStrategyTests.java259
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/AbstractOsgiFrameworkLaunchingTests.java196
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/BundleInstallationTests.java199
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/BundleUpdateTests.java75
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxBootDelegationHelperTests.java48
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxOsgiFrameworkTests.java103
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelBundleClassLoaderTests.java111
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/LoadTimeWeavingTests.java131
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ManifestUtils.java94
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionStateDumperTests.java116
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StubHashGenerator.java25
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/TestUtils.java32
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/UsesAnalyserTests.java162
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ImportExpansionHandlerTests.java751
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImportsTests.java405
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/BundleDescriptionComparatorTests.java208
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiBundleTests.java152
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiExportPackageTests.java88
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiImportPackageTests.java120
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiRequiredBundleTests.java107
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBaseDescription.java57
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBundleDescription.java348
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBundleSpecification.java103
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubExportPackageDescription.java75
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubImportPackageSpecification.java109
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubParameterised.java41
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubStateHelper.java118
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/EquinoxOsgiFrameworkTests/faulty/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/KernelBundleClassLoaderTests/dependant.jarbin0 -> 871 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/KernelBundleClassLoaderTests/depender.jarbin0 -> 737 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/eight/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/error/missingimport/bundle/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/five/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/four/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/fragments/unresolvable/bundle/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/fragments/unresolvable/fragment/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/a/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/b/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/bundle/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/nine/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/one/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatindep/bundle/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatindep/dep/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatisfied/bundle/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/outsiderepo/bundle/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/seven/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/six/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/three/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/two/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/bundle/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/hibernate325/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/hibernate326/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/spring/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/x/high/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/x/low/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/bit/standalone/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/BundleInstallationTests/repository.properties43
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/BundleUpdateTests/repository.properties43
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/EquinoxOsgiFrameworkTests/repository.properties4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/ImportExpansionHandlerTests/repository.properties13
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/InstallFromLocationTests/repository.properties4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/KernelBundleClassLoaderTests/repository.properties13
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/LoadTimeWeavingTests/repository.properties4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/ResolutionStateDumperTests/repository.properties4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/config/UsesAnalyserTests/repository.properties4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/config/repository.properties7
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/java5-server.profile152
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/java6-server.profile216
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/org.eclipse.osgi-3.4.0.v20080529-1200.jarbin0 -> 994119 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/server.profile146
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/fail/child/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/fail/parent/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/frag/child/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/frag/host/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/four/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/one/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/three/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/two/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/jars/dummy.jarbin0 -> 774 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/jars/mockbundle.jarbin0 -> 450 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-domain.jarbin0 -> 801 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-em.jarbin0 -> 801 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-include.jarbin0 -> 1704 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiapp-1.0.0.jarbin0 -> 59309 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiapp-2.0.0.jarbin0 -> 59542 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiservice-1.0.0.jarbin0 -> 4710 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiservice-2.0.0.jarbin0 -> 5142 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd-other/other/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/a/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/app/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/b/META-INF/MANIFEST.MF7
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/c/META-INF/MANIFEST.MF7
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/hibernate/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/hibernate2/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/spring/META-INF/MANIFEST.MF7
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/web/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/bundles/fragmentOne/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/bundles/fragmentTwo/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/bundles/fragmentwithnoexports/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/bundles/host/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/bundles/multi-version-export/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/bundles/noexports/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/bundles/overlapper/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/libraries/com.foo.libd3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/libraries/missing.optional.bundle.libd3
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/silht/libraries/spring.libd5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/dependent/bundles/p/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/dependent/bundles/q/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/dependent/bundles/r1/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/dependent/bundles/r2/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/dependent/bundles/s1/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/dependent/bundles/s2/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/install/bundles/p/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/install/bundles/q/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/install/bundles/r1/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/install/bundles/r2/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/install/bundles/s1/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/install/bundles/s2/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/transitiveconstraint/tmA.jarbin0 -> 527 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/transitiveconstraint/tmB.jarbin0 -> 360 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/transitiveconstraint/tmC.jarbin0 -> 356 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/src/test/resources/uat/transitiveconstraint/tmD.jarbin0 -> 352 bytes
-rw-r--r--org.eclipse.virgo.kernel.userregion/template.mf23
182 files changed, 12752 insertions, 0 deletions
diff --git a/org.eclipse.virgo.kernel.userregion/.classpath b/org.eclipse.virgo.kernel.userregion/.classpath
new file mode 100644
index 00000000..fa5bb951
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/.classpath
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="false"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="src/main/resources">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="false"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/resources">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-4.7.0.jar" sourcepath="/IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-sources-4.7.0.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.medic/org.eclipse.virgo.medic/1.0.1.D-20100420092100/org.eclipse.virgo.medic-1.0.1.D-20100420092100.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.medic/org.eclipse.virgo.medic/1.0.0.CI-B20/org.eclipse.virgo.medic-sources-1.0.0.CI-B20.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-3.5.1.R35x_v20091005.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-sources-3.5.1.R35x_v20091005.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-1.5.10.jar" sourcepath="/KERNEL_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-sources-1.5.10.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.slf4j/com.springsource.slf4j.nop/1.5.10/com.springsource.slf4j.nop-1.5.10.jar" sourcepath="/KERNEL_IVY_CACHE/org.slf4j/com.springsource.slf4j.nop/1.5.10/com.springsource.slf4j.nop-sources-1.5.10.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.aspectj/com.springsource.org.aspectj.runtime/1.6.6.RELEASE/com.springsource.org.aspectj.runtime-1.6.6.RELEASE.jar" sourcepath="/KERNEL_IVY_CACHE/org.aspectj/com.springsource.org.aspectj.runtime/1.6.6.RELEASE/com.springsource.org.aspectj.runtime-1.6.6.RELEASE.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.repository/org.eclipse.virgo.repository/2.1.0.D-20100426135454/org.eclipse.virgo.repository-2.1.0.D-20100426135454.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.repository/org.eclipse.virgo.repository/2.1.0.D-20100426135454/org.eclipse.virgo.repository-sources-2.1.0.D-20100426135454.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.osgi/2.1.0.D-20100420091708/org.eclipse.virgo.util.osgi-2.1.0.D-20100420091708.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.osgi/2.1.0.D-20100420091708/org.eclipse.virgo.util.osgi-sources-2.1.0.D-20100420091708.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.common/2.1.0.D-20100420091708/org.eclipse.virgo.util.common-2.1.0.D-20100420091708.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.common/2.1.0.D-20100420091708/org.eclipse.virgo.util.common-sources-2.1.0.D-20100420091708.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.math/2.1.0.D-20100420091708/org.eclipse.virgo.util.math-2.1.0.D-20100420091708.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.math/2.1.0.D-20100420091708/org.eclipse.virgo.util.math-sources-2.1.0.D-20100420091708.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.osgi/org.eclipse.virgo.osgi.extensions.equinox/2.1.0.D-20100420091535/org.eclipse.virgo.osgi.extensions.equinox-2.1.0.D-20100420091535.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.osgi/org.eclipse.virgo.osgi.extensions.equinox/2.0.0.D-20090913205430/org.eclipse.virgo.osgi.extensions.equinox-sources-2.0.0.D-20090913205430.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100420091708/org.eclipse.virgo.util.io-2.1.0.D-20100420091708.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100420091708/org.eclipse.virgo.util.io-sources-2.1.0.D-20100420091708.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.parser.manifest/2.1.0.D-20100420091708/org.eclipse.virgo.util.parser.manifest-2.1.0.D-20100420091708.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.parser.manifest/2.1.0.D-20100420091708/org.eclipse.virgo.util.parser.manifest-sources-2.1.0.D-20100420091708.jar"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.virgo.kernel.services"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar" sourcepath="/KERNEL_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.teststubs/org.eclipse.virgo.teststubs.osgi/1.0.0.D-20100420091314/org.eclipse.virgo.teststubs.osgi-1.0.0.D-20100420091314.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.test/org.eclipse.virgo.teststubs.osgi/1.0.0.CI-B39/org.eclipse.virgo.teststubs.osgi-sources-1.0.0.CI-B39.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.eclipse.virgo.medic/org.eclipse.virgo.medic.test/1.0.1.D-20100420092100/org.eclipse.virgo.medic.test-1.0.1.D-20100420092100.jar" sourcepath="/KERNEL_IVY_CACHE/org.eclipse.virgo.medic/org.eclipse.virgo.medic.test/1.0.1.D-20100420092100/org.eclipse.virgo.medic.test-sources-1.0.1.D-20100420092100.jar"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.virgo.kernel.core"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.virgo.kernel.artifact"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.virgo.kernel.osgi"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.virgo.kernel.deployer"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.apache.felix/org.apache.felix.eventadmin/1.0.0/org.apache.felix.eventadmin-1.0.0.jar" sourcepath="/KERNEL_IVY_CACHE/org.apache.felix/org.apache.felix.eventadmin/1.0.0/org.apache.felix.eventadmin-sources-1.0.0.jar"/>
+ <classpathentry kind="var" path="KERNEL_IVY_CACHE/org.apache.felix/org.apache.felix.configadmin/1.2.4/org.apache.felix.configadmin-1.2.4.jar" sourcepath="/KERNEL_IVY_CACHE/org.apache.felix/org.apache.felix.configadmin/1.2.4/org.apache.felix.configadmin-sources-1.2.4.jar"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.eclipse.virgo.kernel.userregion/.project b/org.eclipse.virgo.kernel.userregion/.project
new file mode 100644
index 00000000..b1b7033d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/.project
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.virgo.kernel.userregion</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.wst.common.project.facet.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.ajdt.core.ajbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.springframework.ide.eclipse.core.springbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.springsource.server.ide.bundlor.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.ajdt.ui.ajnature</nature>
+ <nature>com.springsource.server.ide.facet.core.bundlenature</nature>
+ <nature>org.springframework.ide.eclipse.core.springnature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.virgo.kernel.userregion/.settings/com.springsource.server.ide.bundlor.core.prefs b/org.eclipse.virgo.kernel.userregion/.settings/com.springsource.server.ide.bundlor.core.prefs
new file mode 100644
index 00000000..1bf62998
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/.settings/com.springsource.server.ide.bundlor.core.prefs
@@ -0,0 +1,3 @@
+#Fri Aug 07 16:08:57 BST 2009
+com.springsource.server.ide.bundlor.core.template.properties.files=../build.versions
+eclipse.preferences.version=1
diff --git a/org.eclipse.virgo.kernel.userregion/.settings/org.eclipse.wst.common.project.facet.core.xml b/org.eclipse.virgo.kernel.userregion/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 00000000..801f856c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+ <installed facet="com.springsource.server.bundle" version="1.0"/>
+</faceted-project>
diff --git a/org.eclipse.virgo.kernel.userregion/.settings/org.springframework.ide.eclipse.core.prefs b/org.eclipse.virgo.kernel.userregion/.settings/org.springframework.ide.eclipse.core.prefs
new file mode 100644
index 00000000..aadbe76f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/.settings/org.springframework.ide.eclipse.core.prefs
@@ -0,0 +1,67 @@
+#Mon Aug 10 10:45:21 BST 2009
+eclipse.preferences.version=1
+org.springframework.ide.eclipse.core.builders.enable.aopreferencemodelbuilder=true
+org.springframework.ide.eclipse.core.builders.enable.beanmetadatabuilder=true
+org.springframework.ide.eclipse.core.builders.enable.osgibundleupdater=true
+org.springframework.ide.eclipse.core.enable.project.preferences=false
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.sts.server.quickfix.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.core.springvalidator=false
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.applicationSymbolicNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.applicationVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleActivationPolicyRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleActivatorRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleManifestVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleSymbolicNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.exportPackageRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.importRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.parsingProblemsRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.requireBundleRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.AvoidDriverManagerDataSource-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.ImportElementsAtTopRulee-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.ParentBeanSpecifiesAbstractClassRule-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.RefElementRule-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.TooManyBeansInFileRule-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.UnnecessaryValueElementRule-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.UseBeanInheritance-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.legacyxmlusage.jndiobjectfactory-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importBundleVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importLibraryVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importPackageVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.requireBundleVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanAlias-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanClass-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanConstructorArgument-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanDefinition-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanDefinitionHolder-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanFactory-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanInitDestroyMethod-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanProperty-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanReference-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.methodOverride-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.parsingProblems-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.requiredProperty-org.springframework.ide.eclipse.beans.core.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.core.springClasspath-org.springframework.ide.eclipse.core.springvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.action-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.actionstate-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.attribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.attributemapper-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.beanaction-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.evaluationaction-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.evaluationresult-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.exceptionhandler-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.import-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.inputattribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.mapping-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.outputattribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.set-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.state-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.subflowstate-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.transition-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.variable-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.webflowstate-org.springframework.ide.eclipse.webflow.core.validator=true
diff --git a/org.eclipse.virgo.kernel.userregion/.springBeans b/org.eclipse.virgo.kernel.userregion/.springBeans
new file mode 100644
index 00000000..4dbe37b1
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/.springBeans
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beansProjectDescription>
+ <version>1</version>
+ <pluginVersion><![CDATA[2.2.7.200910202224-RELEASE]]></pluginVersion>
+ <configSuffixes>
+ <configSuffix><![CDATA[xml]]></configSuffix>
+ </configSuffixes>
+ <enableImports><![CDATA[false]]></enableImports>
+ <configs>
+ </configs>
+ <configSets>
+ </configSets>
+</beansProjectDescription>
diff --git a/org.eclipse.virgo.kernel.userregion/build.xml b/org.eclipse.virgo.kernel.userregion/build.xml
new file mode 100644
index 00000000..b5948ec6
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/build.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="org.eclipse.virgo.kernel.userregion">
+
+ <property file="${basedir}/../build.properties"/>
+ <property file="${basedir}/../build.versions"/>
+ <import file="${basedir}/../virgo-build/weaving/default.xml"/>
+
+</project>
diff --git a/org.eclipse.virgo.kernel.userregion/ivy.xml b/org.eclipse.virgo.kernel.userregion/ivy.xml
new file mode 100644
index 00000000..85b109b6
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/ivy.xml
@@ -0,0 +1,39 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
+<ivy-module xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation='http://incubator.apache.org/ivy/schemas/ivy.xsd' version='1.3'>
+
+ <info organisation='org.eclipse.virgo.kernel' module='${ant.project.name}' />
+
+ <configurations>
+ <include file='${virgo.build.dir}/common/default-ivy-configurations.xml'/>
+ </configurations>
+
+ <publications>
+ <artifact name='${ant.project.name}'/>
+ <artifact name='${ant.project.name}-sources' ext='jar' type='src'/>
+ </publications>
+
+ <dependencies>
+ <dependency org="org.junit" name='com.springsource.org.junit' rev='${org.junit}' conf='test->runtime'/>
+ <dependency org="org.eclipse.virgo.medic" name='org.eclipse.virgo.medic' rev='${org.eclipse.virgo.medic}' conf='aspects, compile->runtime' />
+ <dependency org="org.eclipse.virgo.medic" name="org.eclipse.virgo.medic.test" rev="${org.eclipse.virgo.medic}" conf="test->runtime"/>
+ <dependency org="org.eclipse.osgi" name='org.eclipse.osgi' rev='${org.eclipse.osgi}' conf='compile->compile' />
+ <dependency org="org.slf4j" name='com.springsource.slf4j.api' rev='${org.slf4j}' conf='compile->runtime' />
+ <dependency org="org.eclipse.virgo.kernel" name="org.eclipse.virgo.kernel.services" rev="latest.integration" conf="compile->compile"/>
+ <dependency org="org.eclipse.virgo.kernel" name="org.eclipse.virgo.kernel.artifact" rev="latest.integration" conf="compile->compile"/>
+ <dependency org="org.eclipse.virgo.kernel" name="org.eclipse.virgo.kernel.deployer" rev="latest.integration" conf="compile->compile"/>
+ <dependency org="org.slf4j" name='com.springsource.slf4j.nop' rev='${org.slf4j}' conf='test->runtime' />
+ <dependency org="org.eclipse.virgo.repository" name='org.eclipse.virgo.repository' rev='${org.eclipse.virgo.repository}' conf='compile->compile' />
+ <dependency org="org.eclipse.virgo.util" name='org.eclipse.virgo.util.osgi' rev='${org.eclipse.virgo.util}' conf='compile->compile' />
+ <dependency org="org.eclipse.virgo.util" name='org.eclipse.virgo.util.common' rev='${org.eclipse.virgo.util}' conf='compile->compile' />
+ <dependency org="org.eclipse.virgo.util" name='org.eclipse.virgo.util.math' rev='${org.eclipse.virgo.util}' conf='compile->compile' />
+ <dependency org="org.eclipse.virgo.osgi" name='org.eclipse.virgo.osgi.extensions.equinox' rev='${org.eclipse.virgo.osgi}' conf='compile->compile' />
+ <dependency org="org.eclipse.virgo.util" name='org.eclipse.virgo.util.io' rev='${org.eclipse.virgo.util}' conf='compile->compile' />
+ <dependency org="org.eclipse.virgo.util" name='org.eclipse.virgo.util.parser.manifest' rev='${org.eclipse.virgo.util}' conf='compile->compile' />
+ <dependency org="org.easymock" name="com.springsource.org.easymock" rev="${org.easymock}" conf="test->runtime"/>
+ <dependency org="org.eclipse.virgo.teststubs" name='org.eclipse.virgo.teststubs.osgi' rev='${org.eclipse.virgo.teststubs}' conf='test->runtime'/>
+ <dependency org="org.eclipse.virgo.kernel" name="org.eclipse.virgo.kernel.osgi" rev="latest.integration" conf="compile->compile"/>
+
+ <override org="org.eclipse.virgo.repository" rev="${org.eclipse.virgo.repository}"/>
+ </dependencies>
+</ivy-module>
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/Activator.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/Activator.java
new file mode 100644
index 00000000..ccf60264
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/Activator.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.kernel.module.ModuleContextAccessor;
+import org.eclipse.virgo.kernel.osgi.framework.ImportExpander;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFramework;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkUtils;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiServiceHolder;
+import org.eclipse.virgo.kernel.osgi.framework.PackageAdminUtil;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiFrameworkFactory;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.MetaInfResourceClassLoaderDelegateHook;
+import org.eclipse.virgo.kernel.core.Shutdown;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer;
+import org.eclipse.virgo.kernel.deployer.core.DeployUriNormaliser;
+import org.eclipse.virgo.kernel.shim.scope.ScopeFactory;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.EquinoxHookRegistrar;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.EquinoxOsgiFramework;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.ResolutionDumpContributor;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.StandardPackageAdminUtil;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.TransformedManifestProvidingBundleFileWrapper;
+import org.eclipse.virgo.kernel.userregion.internal.importexpansion.ImportExpansionHandler;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.ResolutionFailureDetective;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiFrameworkFactory;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardResolutionFailureDetective;
+import org.eclipse.virgo.medic.dump.DumpContributor;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.repository.Repository;
+import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
+
+/**
+ * {@link BundleActivator} for the Equinox-specific OSGi integration
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public class Activator implements BundleActivator {
+
+ private static final long SYSTEM_BUNDLE_ID = 0;
+
+ private static final String PROPERTY_USER_REGION_ARTIFACTS = "initialArtifacts";
+
+ private static final String PROPERTY_USER_REGION_COMMANDLINE_ARTIFACTS = "commandLineArtifacts";
+
+ private final ServiceRegistrationTracker registrationTracker = new ServiceRegistrationTracker();
+
+ private volatile EquinoxHookRegistrar hookRegistrar;
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start(BundleContext context) throws Exception {
+ ResolutionFailureDetective rfd = createResolutionFailureDetective(context);
+ Repository repository = OsgiFrameworkUtils.getService(context, Repository.class).getService();
+ PackageAdmin packageAdmin = OsgiFrameworkUtils.getService(context, PackageAdmin.class).getService();
+
+ EventLogger eventLogger = OsgiFrameworkUtils.getService(context, EventLogger.class).getService();
+
+ ImportExpansionHandler importExpansionHandler = createImportExpansionHandler(context, packageAdmin, repository, eventLogger);
+ this.registrationTracker.track(context.registerService(ImportExpander.class.getName(), importExpansionHandler, null));
+
+ TransformedManifestProvidingBundleFileWrapper bundleTransformerHandler = createBundleTransformationHandler(importExpansionHandler);
+
+ OsgiFramework osgiFramework = createOsgiFramework(context, packageAdmin, bundleTransformerHandler);
+ this.registrationTracker.track(context.registerService(OsgiFramework.class.getName(), osgiFramework, null));
+
+ DumpContributor dumpContributor = createResolutionDumpContributor(context);
+ this.registrationTracker.track(context.registerService(DumpContributor.class.getName(), dumpContributor, null));
+
+ QuasiFrameworkFactory quasiFrameworkFactory = createQuasiFrameworkFactory(context, rfd, repository, bundleTransformerHandler);
+ this.registrationTracker.track(context.registerService(QuasiFrameworkFactory.class.getName(), quasiFrameworkFactory, null));
+
+ EquinoxHookRegistrar hookRegistrar = createHookRegistrar(context, packageAdmin, bundleTransformerHandler);
+ hookRegistrar.init();
+ this.hookRegistrar = hookRegistrar;
+
+ PackageAdminUtil packageAdminUtil = createPackageAdminUtil(context);
+ this.registrationTracker.track(context.registerService(PackageAdminUtil.class.getName(), packageAdminUtil, null));
+
+ scheduleRegistrationOfServiceScopingRegistryHooks(context);
+
+ Properties properties = new Properties();
+ properties.put(Constants.SERVICE_RANKING, Integer.MIN_VALUE);
+ this.registrationTracker.track(context.registerService(ModuleContextAccessor.class.getName(), new EmptyModuleContextAccessor(), properties));
+
+ scheduleInitialArtifactDeployerCreation(context, eventLogger);
+ }
+
+ private ResolutionFailureDetective createResolutionFailureDetective(BundleContext context) {
+ PlatformAdmin platformAdmin = OsgiFrameworkUtils.getService(context, PlatformAdmin.class).getService();
+ return new StandardResolutionFailureDetective(platformAdmin);
+ }
+
+ private OsgiFramework createOsgiFramework(BundleContext context, PackageAdmin packageAdmin, TransformedManifestProvidingBundleFileWrapper bundleTransformerHandler) {
+ return new EquinoxOsgiFramework(context, packageAdmin, bundleTransformerHandler);
+ }
+
+ private DumpContributor createResolutionDumpContributor(BundleContext bundleContext) {
+ return new ResolutionDumpContributor(bundleContext);
+ }
+
+ private QuasiFrameworkFactory createQuasiFrameworkFactory(BundleContext bundleContext, ResolutionFailureDetective detective, Repository repository, TransformedManifestProvidingBundleFileWrapper bundleTransformerHandler) {
+ return new StandardQuasiFrameworkFactory(bundleContext, detective, repository, bundleTransformerHandler);
+ }
+
+ private TransformedManifestProvidingBundleFileWrapper createBundleTransformationHandler(ImportExpansionHandler importExpander) {
+ return new TransformedManifestProvidingBundleFileWrapper(importExpander);
+ }
+
+ private ImportExpansionHandler createImportExpansionHandler(BundleContext context, PackageAdmin packageAdmin, Repository repository, EventLogger eventLogger) {
+
+ Set<String> packagesExportedBySystemBundle = new HashSet<String>(30);
+ ExportedPackage[] exportedPackages = packageAdmin.getExportedPackages(context.getBundle(SYSTEM_BUNDLE_ID));
+
+ for (ExportedPackage exportedPackage : exportedPackages) {
+ packagesExportedBySystemBundle.add(exportedPackage.getName());
+ }
+
+ return new ImportExpansionHandler(repository, context, packagesExportedBySystemBundle, eventLogger);
+ }
+
+ private EquinoxHookRegistrar createHookRegistrar(BundleContext context, PackageAdmin packageAdmin, TransformedManifestProvidingBundleFileWrapper bundleFileWrapper) {
+ MetaInfResourceClassLoaderDelegateHook hook = new MetaInfResourceClassLoaderDelegateHook(context, packageAdmin);
+ return new EquinoxHookRegistrar(bundleFileWrapper, hook);
+ }
+
+ private PackageAdminUtil createPackageAdminUtil(BundleContext context) {
+ return new StandardPackageAdminUtil(context);
+ }
+
+ private void scheduleRegistrationOfServiceScopingRegistryHooks(final BundleContext context) {
+ Runnable runnable = new ServiceScopingHookRegisteringRunnable(context, this.registrationTracker);
+ Thread thread = new Thread(runnable);
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+ private void scheduleInitialArtifactDeployerCreation(BundleContext context, EventLogger eventLogger) {
+ KernelStartedAwaiter startedAwaiter = new KernelStartedAwaiter();
+
+ Properties properties = new Properties();
+ properties.put(EventConstants.EVENT_TOPIC, "org/eclipse/virgo/kernel/*");
+ this.registrationTracker.track(context.registerService(EventHandler.class.getName(), startedAwaiter, properties));
+
+ Runnable runnable = new InitialArtifactDeployerCreatingRunnable(context, eventLogger, this.registrationTracker, startedAwaiter);
+ Thread thread = new Thread(runnable);
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop(BundleContext context) throws Exception {
+ this.registrationTracker.unregisterAll();
+ EquinoxHookRegistrar hookRegistrar = this.hookRegistrar;
+
+ if (hookRegistrar != null) {
+ hookRegistrar.destroy();
+ this.hookRegistrar = null;
+ }
+ }
+
+ private static final class ServiceScopingHookRegisteringRunnable implements Runnable {
+
+ private final BundleContext context;
+
+ private final ServiceRegistrationTracker registrationTracker;
+
+ public ServiceScopingHookRegisteringRunnable(BundleContext context, ServiceRegistrationTracker registrationTracker) {
+ this.context = context;
+ this.registrationTracker = registrationTracker;
+ }
+
+ public void run() {
+ ScopeFactory scopeFactory = OsgiFrameworkUtils.getService(context, ScopeFactory.class).getService();
+ ScopeServiceRepository scopeServiceRepository = getPotentiallyDelayedService(context, ScopeServiceRepository.class);
+
+ ServiceScopingStrategy serviceScopingStrategy = new ServiceScopingStrategy(scopeFactory, scopeServiceRepository);
+
+ ServiceScopingRegistryHook serviceScopingRegistryHook = new ServiceScopingRegistryHook(serviceScopingStrategy);
+
+ this.registrationTracker.track(context.registerService(new String[] { "org.osgi.framework.hooks.service.FindHook",
+ "org.osgi.framework.hooks.service.EventHook" }, serviceScopingRegistryHook, null));
+ }
+ }
+
+ private static final class InitialArtifactDeployerCreatingRunnable implements Runnable {
+
+ private static final String USER_REGION_CONFIGURATION_PID = "org.eclipse.virgo.kernel.userregion";
+
+ private final BundleContext context;
+
+ private final EventLogger eventLogger;
+
+ private final KernelStartedAwaiter startAwaiter;
+
+ private final ServiceRegistrationTracker registrationTracker;
+
+ public InitialArtifactDeployerCreatingRunnable(BundleContext context, EventLogger eventLogger,
+ ServiceRegistrationTracker registrationTracker, KernelStartedAwaiter startAwaiter) {
+ this.context = context;
+ this.eventLogger = eventLogger;
+ this.startAwaiter = startAwaiter;
+ this.registrationTracker = registrationTracker;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void run() {
+ EventAdmin eventAdmin = OsgiFrameworkUtils.getService(context, EventAdmin.class).getService();
+ DeployUriNormaliser uriNormaliser = getPotentiallyDelayedService(context, DeployUriNormaliser.class);
+ ApplicationDeployer deployer = getPotentiallyDelayedService(context, ApplicationDeployer.class);
+ Shutdown shutdown = OsgiFrameworkUtils.getService(context, Shutdown.class).getService();
+
+ Dictionary<String, String> artifactConfiguration = getRegionArtifactConfiguration();
+
+ InitialArtifactDeployer initialArtifactDeployer = new InitialArtifactDeployer(this.startAwaiter, deployer, artifactConfiguration.get(PROPERTY_USER_REGION_ARTIFACTS), artifactConfiguration.get(PROPERTY_USER_REGION_COMMANDLINE_ARTIFACTS), uriNormaliser, eventAdmin, eventLogger, shutdown);
+ Properties properties = new Properties();
+ properties.put(EventConstants.EVENT_TOPIC, "org/eclipse/virgo/kernel/*");
+ this.registrationTracker.track(context.registerService(EventHandler.class.getName(), initialArtifactDeployer, properties));
+
+ try {
+ initialArtifactDeployer.deployArtifacts();
+ } catch (InterruptedException _) {
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, String> getRegionArtifactConfiguration() {
+ ConfigurationAdmin configAdmin = OsgiFrameworkUtils.getService(this.context, ConfigurationAdmin.class).getService();
+ try {
+ Configuration config = configAdmin.getConfiguration(USER_REGION_CONFIGURATION_PID);
+ Dictionary<String, String> properties = (Dictionary<String, String>) config.getProperties();
+ return properties;
+ } catch (IOException ioe) {
+ throw new RuntimeException("Failed to read region artifact configuration", ioe);
+ }
+ }
+ }
+
+ private static <T> T getPotentiallyDelayedService(BundleContext context, Class<T> serviceClass) {
+ T service = null;
+
+ while (service == null) {
+ try {
+ OsgiServiceHolder<T> serviceHolder = OsgiFrameworkUtils.getService(context, serviceClass);
+ if (serviceHolder != null) {
+ service = serviceHolder.getService();
+ }
+ } catch (IllegalStateException e) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
+ return service;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/EmptyModuleContextAccessor.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/EmptyModuleContextAccessor.java
new file mode 100644
index 00000000..3273871c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/EmptyModuleContextAccessor.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.module.ModuleContext;
+import org.eclipse.virgo.kernel.module.ModuleContextAccessor;
+
+public class EmptyModuleContextAccessor implements ModuleContextAccessor {
+ public ModuleContext getModuleContext(Bundle bundle) {
+ return null;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/InitialArtifactDeployer.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/InitialArtifactDeployer.java
new file mode 100644
index 00000000..7bf2dffd
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/InitialArtifactDeployer.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventHandler;
+
+
+import org.eclipse.virgo.kernel.core.Shutdown;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer;
+import org.eclipse.virgo.kernel.deployer.core.DeployUriNormaliser;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.StringUtils;
+
+
+/**
+ * <code>InitialArtifactDeployer</code> is responsible for deploying the configured
+ * set of initial artifacts.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class InitialArtifactDeployer implements EventHandler {
+
+ private static final String THREAD_NAME_SYSTEM_ARTIFACTS = "system-artifacts";
+
+ private static final String THREAD_NAME_USER_ARTIFACTS = "user-artifacts";
+
+ private static final DeploymentOptions ARTIFACT_DEPLOYMENT_OPTIONS = new DeploymentOptions(false, false, true);
+
+ private static final String TOPIC_SYSTEM_ARTIFACTS_DEPLOYED = "org/eclipse/virgo/kernel/userregion/systemartifacts/DEPLOYED";
+
+ private static final String TOPIC_USER_ARTIFACTS_DEPLOYED = "org/eclipse/virgo/kernel/userregion/userartifacts/DEPLOYED";
+
+ private final ApplicationDeployer deployer;
+
+ private final DeployUriNormaliser normaliser;
+
+ private final EventAdmin eventAdmin;
+
+ private final String systemArtifactsProperty;
+
+ private final String userArtifactsProperty;
+
+ private final EventLogger eventLogger;
+
+ private final Shutdown shutdown;
+
+ private final KernelStartedAwaiter startAwaiter;
+
+ InitialArtifactDeployer(KernelStartedAwaiter startAwaiter, ApplicationDeployer deployer, String systemArtifactsProperty, String userArtifactsProperty, DeployUriNormaliser normaliser, EventAdmin eventAdmin, EventLogger eventLogger, Shutdown shutdown) {
+ this.deployer = deployer;
+ this.normaliser = normaliser;
+ this.eventAdmin = eventAdmin;
+ this.systemArtifactsProperty = systemArtifactsProperty;
+ this.userArtifactsProperty = userArtifactsProperty;
+ this.eventLogger = eventLogger;
+ this.shutdown = shutdown;
+ this.startAwaiter = startAwaiter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleEvent(Event event) {
+ if (TOPIC_SYSTEM_ARTIFACTS_DEPLOYED.equals(event.getTopic())) {
+ this.eventLogger.log(UserRegionLogEvents.SYSTEM_ARTIFACTS_DEPLOYED);
+ deployUserArtifacts();
+ }
+ }
+
+ void deployArtifacts() throws InterruptedException {
+
+ this.startAwaiter.awaitKernelStarted();
+
+ List<URI> systemArtifacts = getSystemArtifacts();
+ deployArtifacts(systemArtifacts, THREAD_NAME_SYSTEM_ARTIFACTS, TOPIC_SYSTEM_ARTIFACTS_DEPLOYED);
+ }
+
+ private void deployUserArtifacts(){
+ List<URI> userArtifacts = getUserArtifacts();
+ deployArtifacts(userArtifacts, THREAD_NAME_USER_ARTIFACTS, TOPIC_USER_ARTIFACTS_DEPLOYED);
+ }
+
+ private List<URI> getUserArtifacts() {
+ return getRepositoryUrisForArtifacts(this.userArtifactsProperty);
+ }
+
+ private List<URI> getSystemArtifacts() {
+ return getRepositoryUrisForArtifacts(this.systemArtifactsProperty);
+ }
+
+ private List<URI> getRepositoryUrisForArtifacts(String artifactsProperty) {
+ String[] artifacts = StringUtils.commaDelimitedListToStringArray(artifactsProperty);
+
+ List<URI> repositoryUris = new ArrayList<URI>();
+
+ for (String artifact : artifacts) {
+ repositoryUris.add(URI.create(artifact.trim()));
+ }
+
+ return repositoryUris;
+ }
+
+ private void deployArtifacts(List<URI> artifacts, String threadName, String completionEventTopic) {
+ Runnable artifactDeployingRunnable = new ArtifactDeployingRunnable(artifacts, completionEventTopic);
+ Thread deployThread = new Thread(artifactDeployingRunnable, threadName);
+ deployThread.start();
+ }
+
+ private final class ArtifactDeployingRunnable implements Runnable {
+
+ private final List<URI> artifacts;
+
+ private final String completionEventTopic;
+
+ private ArtifactDeployingRunnable(List<URI> artifacts, String completionEventTopic) {
+ this.artifacts = artifacts;
+ this.completionEventTopic = completionEventTopic;
+ }
+
+ public void run() {
+ try {
+ validateArtifacts();
+ deployArtifacts();
+ eventAdmin.postEvent(new Event(this.completionEventTopic, null));
+ } catch (DeploymentException de) {
+ eventLogger.log(UserRegionLogEvents.INITIAL_ARTIFACT_DEPLOYMENT_FAILED);
+ shutdown.shutdown();
+ }
+ }
+
+ private void deployArtifacts() throws DeploymentException {
+ for (URI artifact : this.artifacts) {
+ deployer.deploy(artifact, ARTIFACT_DEPLOYMENT_OPTIONS);
+ }
+ }
+
+ private void validateArtifacts() throws DeploymentException {
+ boolean normaliseFailed = false;
+
+ for (URI uri : this.artifacts) {
+ try {
+ normaliser.normalise(uri);
+ } catch (DeploymentException de) {
+ normaliseFailed = true;
+ }
+ }
+
+ if (normaliseFailed) {
+ throw new DeploymentException("Validation of artifacts failed");
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/KernelStartedAwaiter.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/KernelStartedAwaiter.java
new file mode 100644
index 00000000..66392ba4
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/KernelStartedAwaiter.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+
+/**
+ * A helper class that awaits the <code>EventAdmin<code> event for the
+ * kernel having started.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class KernelStartedAwaiter implements EventHandler {
+
+ private static final String TOPIC_KERNEL_STARTED = "org/eclipse/virgo/kernel/STARTED";
+
+ private final CountDownLatch latch = new CountDownLatch(1);
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleEvent(Event event) {
+ if (TOPIC_KERNEL_STARTED.equals(event.getTopic())) {
+ this.latch.countDown();
+ }
+ }
+
+ void awaitKernelStarted() throws InterruptedException {
+ this.latch.await();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingRegistryHook.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingRegistryHook.java
new file mode 100644
index 00000000..755a5fce
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingRegistryHook.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.service.EventHook;
+import org.osgi.framework.hooks.service.FindHook;
+
+/**
+ * Service registry hook that enforces the service scoping behaviour.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+final class ServiceScopingRegistryHook implements FindHook, EventHook {
+
+ private final ServiceScopingStrategy serviceScopingStrategy;
+
+ public ServiceScopingRegistryHook(ServiceScopingStrategy serviceScopingStrategy) {
+ this.serviceScopingStrategy = serviceScopingStrategy;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void find(BundleContext context, String name, String filter, boolean allServices, Collection references) {
+ this.serviceScopingStrategy.scopeReferences(references, context, name, filter);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void event(ServiceEvent event, Collection contexts) {
+ ServiceReference ref = event.getServiceReference();
+ for (Iterator iterator = contexts.iterator(); iterator.hasNext();) {
+ BundleContext context = (BundleContext) iterator.next();
+ if (!this.serviceScopingStrategy.isPotentiallyVisible(ref, context)) {
+ iterator.remove();
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingStrategy.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingStrategy.java
new file mode 100644
index 00000000..a456d077
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingStrategy.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.eclipse.virgo.kernel.shim.scope.Scope;
+import org.eclipse.virgo.kernel.shim.scope.ScopeFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+
+/**
+ * {@link ServiceScopingStrategy} encapsulates the service scoping algorithms used by {@link ServiceScopingRegistryHook}
+ * .
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread safe.
+ *
+ */
+final class ServiceScopingStrategy {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final ScopeFactory scopeFactory;
+
+ private ScopeServiceRepository scopeServiceRepository;
+
+ public ServiceScopingStrategy(ScopeFactory scopeFactory, ScopeServiceRepository scopeServiceRepository) {
+ this.scopeFactory = scopeFactory;
+ this.scopeServiceRepository = scopeServiceRepository;
+ }
+
+ /**
+ * Returns true if and only the given service reference is potentially visible from the given bundle context. This
+ * is the case if and only if the service reference is in the same scope as the bundle context or the service
+ * reference is in the global scope.
+ * <p/>
+ * The given service reference may not be actually visible from the given bundle context if the given service
+ * reference is in the global scope, the given bundle context is scoped, and there is another service in the scope
+ * which shadows the given service reference.
+ */
+ boolean isPotentiallyVisible(ServiceReference serviceReference, BundleContext consumingBundleContext) {
+ boolean matchesScope = true;
+ Scope serviceScope = getServiceScope(serviceReference);
+ if (serviceScope != null && !serviceScope.isGlobal()) {
+ Scope bundleScope = this.scopeFactory.getBundleScope(consumingBundleContext.getBundle());
+ if (!bundleScope.equals(serviceScope)) {
+ matchesScope = false;
+ }
+ }
+ return matchesScope;
+ }
+
+ /**
+ * Takes the given collection of service references and restricts the collection to the scope of the given bundle
+ * context. It is not sufficient simply to discard service references in non-matching scopes because the global
+ * scope may (see below) be searched after an application scope.
+ * <p/>
+ * The exact behaviour depends on the {@link ScopeServiceRepository} which models, with varying degrees of accuracy,
+ * the services which are published in a particular scope. One variation in accuracy is due to the fact that web
+ * bundles typically have their application context files in a directory other than <code>META-INF/spring</code> and
+ * this is not currently taken into account when the repository is built. Another variation is due to the fact that
+ * Spring DM supports a manifest header which specifies the directory containing application context files. Again
+ * this is not currently taken into account when the repository is built.
+ */
+ @SuppressWarnings("unchecked")
+ void scopeReferences(Collection references, BundleContext consumingBundleContext, String className, String filter) {
+ Bundle consumingBundle = consumingBundleContext.getBundle();
+ Scope lookupScope = getLookupScope(consumingBundle, className, filter);
+ Scope consumerScope = getBundleScope(consumingBundle);
+
+ /*
+ * If the consumer is scoped, look in the consumer's scope before looking in the global scope. This avoids wrongly using
+ * the global scope when the service model did not include services which are nevertheless present in the application scope.
+ * If some of the service references are in the consumer's scope, restrict the set to just those.
+ */
+ if (lookupScope.isGlobal() && !consumerScope.isGlobal()) {
+ Collection scopedReferences = getScopedReferences(references, consumerScope);
+ if (!scopedReferences.isEmpty()) {
+ removeAllExcept(references, scopedReferences);
+ return;
+ }
+ }
+ restrictServicesToScope(references, lookupScope);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void removeAllExcept(Collection references, Collection scopedReferences) {
+ /*
+ * The simple implementation of clearing references and then using addAll to add
+ * in scopedReferences is no good as the find hook is passed a shrinkable collection
+ * that does not support add or addAll.
+ */
+ Iterator iterator = references.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ if (!scopedReferences.contains(ref)) {
+ iterator.remove();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Collection getScopedReferences(Collection references, Scope scope) {
+ Collection scopedReferences = new HashSet<ServiceReference>();
+ logger.debug("References input to getScopedReferences: {}", references.size());
+ Iterator iterator = references.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ Scope serviceScope = getServiceScope(ref);
+ if (scope.equals(serviceScope)) {
+ logger.debug("Adding {} ", ref);
+ scopedReferences.add(ref);
+ }
+ }
+ logger.debug("References output from getScopedReferences: {}", scopedReferences.size());
+ return scopedReferences;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void restrictServicesToScope(Collection references, Scope scope) {
+ logger.debug("Before filtering: {}", references.size());
+ Iterator iterator = references.iterator();
+ while (iterator.hasNext()) {
+ ServiceReference ref = (ServiceReference) iterator.next();
+ Scope serviceScope = getServiceScope(ref);
+ if (!scope.equals(serviceScope)) {
+ logger.debug("Removing {} ", ref);
+ iterator.remove();
+ }
+ }
+ logger.debug("After filtering: {}", references.size());
+ }
+
+ /**
+ * Gets the {@link Scope} in which this lookup is being performed.
+ */
+ private Scope getLookupScope(Bundle consumer, String name, String filter) {
+ Scope consumerScope = getBundleScope(consumer);
+ /*
+ * The lookup scope is that of the consuming bundle unless the consuming bundle is in an application scope with
+ * no service matching the given name and filter in which case the lookup scope is global.
+ */
+ Scope lookupScope = !consumerScope.isGlobal() && !scopeHasMatchingService(consumerScope, name, filter) ? this.scopeFactory.getGlobalScope()
+ : consumerScope;
+ logger.debug("{} > {} [{}] ({})", new Object[] { lookupScope, name, filter, consumer });
+ return lookupScope;
+ }
+
+ private Scope getBundleScope(Bundle consumer) {
+ return this.scopeFactory.getBundleScope(consumer);
+ }
+
+ private boolean scopeHasMatchingService(Scope scope, String name, String filter) {
+ try {
+ return this.scopeServiceRepository.scopeHasMatchingService(scope.getScopeName(), name, filter);
+ } catch (InvalidSyntaxException e) {
+ logger.warn("Filter '{}' is not valid", e, filter);
+ return false;
+ }
+ }
+
+ private Scope getServiceScope(ServiceReference ref) {
+ try {
+ return this.scopeFactory.getServiceScope(ref);
+ } catch (IllegalStateException ise) {
+ return null;
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/UserRegionLogEvents.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/UserRegionLogEvents.java
new file mode 100644
index 00000000..1139c696
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/UserRegionLogEvents.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import org.eclipse.virgo.kernel.serviceability.LogEventDelegate;
+import org.eclipse.virgo.medic.eventlog.Level;
+import org.eclipse.virgo.medic.eventlog.LogEvent;
+
+/**
+ * User region log events.
+ * <p />
+ *
+ */
+public enum UserRegionLogEvents implements LogEvent {
+
+ SYSTEM_ARTIFACTS_DEPLOYED(1, Level.INFO), //
+ INITIAL_ARTIFACT_DEPLOYMENT_FAILED(2, Level.ERROR), //
+ SYSTEM_BUNDLE_OVERLAP(3, Level.WARNING), //
+ ALTERNATE_INSTRUMENTED_LIBRARY_FOUND(4, Level.WARNING);;
+
+ private static final String PREFIX = "UR";
+
+ private final LogEventDelegate delegate;
+
+ private UserRegionLogEvents(int code, Level level) {
+ this.delegate = new LogEventDelegate(PREFIX, code, level);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getEventCode() {
+ return this.delegate.getEventCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Level getLevel() {
+ return this.delegate.getLevel();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxBootDelegationHelper.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxBootDelegationHelper.java
new file mode 100644
index 00000000..b9491061
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxBootDelegationHelper.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.util.ArrayList;
+
+/**
+ * A helper class for determinining whether or not Equinox will load a class via boot delegation.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+class EquinoxBootDelegationHelper {
+
+ private final String[] exactPackageNames;
+
+ private final String[] startsWithPackageNames;
+
+ private final boolean allPackagesAreBootDelegated;
+
+ /**
+ * Create a new BootDelegationHelper that will provide boot delegation information for the given Equinox OSGi
+ * Framework.
+ *
+ * @param bootDelegationProperty The Equinox boot delegation property from which boot delegation information is to
+ * be derived.
+ */
+ public EquinoxBootDelegationHelper(String bootDelegationProperty) {
+
+ boolean delegateAllPackages = false;
+ ArrayList<String> stemMatches = new ArrayList<String>();
+ ArrayList<String> exactMatches = new ArrayList<String>();
+
+ if (bootDelegationProperty != null && bootDelegationProperty.trim().length() > 0) {
+
+ String[] components = bootDelegationProperty.split(",");
+ for (String component : components) {
+ component = component.trim();
+ if (component.equals("*")) {
+ delegateAllPackages = true;
+ } else if (component.length() > 2 && component.endsWith(".*")) {
+ stemMatches.add(component.substring(0, component.length() - 2));
+ } else {
+ exactMatches.add(component);
+ }
+ }
+ }
+
+ this.exactPackageNames = exactMatches.toArray(new String[exactMatches.size()]);
+ this.startsWithPackageNames = stemMatches.toArray(new String[stemMatches.size()]);
+ this.allPackagesAreBootDelegated = delegateAllPackages;
+ }
+
+ /**
+ * Returns true if the class with the given name will be loaded via boot delegation
+ *
+ * @param className The name of the class
+ * @return Whether the class will be loaded via boot delegation.
+ */
+ public boolean isBootDelegated(String className) {
+ if (this.allPackagesAreBootDelegated) {
+ return true;
+ } else {
+ for (String packageStem : this.startsWithPackageNames) {
+ if (className.startsWith(packageStem)) {
+ return true;
+ }
+ }
+ String packageName = determinePackageName(className);
+ if (packageName != null) {
+ for (String exactPackage : this.exactPackageNames) {
+ if (packageName.equals(exactPackage)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static String determinePackageName(String className) {
+ int dotIndex = className.lastIndexOf('.');
+
+ if (dotIndex == -1 || dotIndex == 0) {
+ return null;
+ } else {
+ return className.substring(0, dotIndex);
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxConsoleManager.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxConsoleManager.java
new file mode 100644
index 00000000..ea8a108f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxConsoleManager.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import org.eclipse.osgi.framework.internal.core.BundleContextImpl;
+import org.eclipse.osgi.framework.internal.core.FrameworkConsole;
+import org.osgi.framework.BundleContext;
+
+import org.eclipse.virgo.kernel.osgi.framework.OsgiConfiguration;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkLogEvents;
+
+import org.eclipse.virgo.kernel.core.Shutdown;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.io.NetUtils;
+
+/**
+ * Manages the lifecycle of the Equinox OSGi console.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+final class EquinoxConsoleManager {
+
+ private static final String CONSOLE_THREAD_NAME = "console-thread";
+
+ private final Object monitor = new Object();
+
+ private final BundleContext bundleContext;
+
+ private final OsgiConfiguration configuration;
+
+ private final Shutdown shutdown;
+
+ private final EventLogger eventLogger;
+
+ private FrameworkConsole console;
+
+ private Thread consoleThread;
+
+ public EquinoxConsoleManager(BundleContext bundleContext, OsgiConfiguration configuration, Shutdown shutdown, EventLogger eventLogger) {
+ this.bundleContext = bundleContext;
+ this.configuration = configuration;
+ this.shutdown = shutdown;
+ this.eventLogger = eventLogger;
+ }
+
+ public void start() {
+ FrameworkConsole console = null;
+ Thread consoleThread = null;
+ if (this.configuration.isConsoleEnabled()) {
+ BundleContextImpl bundleContext = (BundleContextImpl) this.bundleContext;
+ int consolePort = this.configuration.getConsolePort();
+
+ checkConsolePortAvailable(consolePort);
+
+ console = new FrameworkConsole(bundleContext.getFramework(), consolePort, null);
+ consoleThread = new Thread(console, CONSOLE_THREAD_NAME);
+ consoleThread.setDaemon(true);
+ consoleThread.start();
+
+ this.eventLogger.log(OsgiFrameworkLogEvents.OSGI_CONSOLE_PORT, consolePort);
+ }
+ synchronized (this.monitor) {
+ this.console = console;
+ this.consoleThread = consoleThread;
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void stop() {
+ FrameworkConsole console;
+ Thread consoleThread;
+ synchronized (this.monitor) {
+ console = this.console;
+ consoleThread = this.consoleThread;
+ this.console = null;
+ this.consoleThread = null;
+ }
+ if (console != null) {
+ console.shutdown();
+ }
+ if (consoleThread != null) {
+ consoleThread.stop();
+ }
+ }
+
+ private void checkConsolePortAvailable(int consolePort) {
+ if (!NetUtils.isPortAvailable(consolePort)) {
+ this.eventLogger.log(OsgiFrameworkLogEvents.OSGI_CONSOLE_PORT_IN_USE, consolePort);
+ this.shutdown.immediateShutdown();
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxHookRegistrar.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxHookRegistrar.java
new file mode 100644
index 00000000..bcb5efce
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxHookRegistrar.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegateHook;
+
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.BundleFileClosingBundleFileWrapperFactoryHook;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.PluggableBundleFileWrapperFactoryHook;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.PluggableClassLoadingHook;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.PluggableDelegatingClassLoaderDelegateHook;
+
+/**
+ * <code>EquinoxHookRegistrar</code> is responsible for registering
+ * the kernel's hooks with Equinox.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+public final class EquinoxHookRegistrar {
+
+ private final TransformedManifestProvidingBundleFileWrapper bundleFileWrapper;
+
+ private final ClassLoaderDelegateHook metaInfResourceClassLoaderDelegateHook;
+
+ public EquinoxHookRegistrar(TransformedManifestProvidingBundleFileWrapper bundleFileWrapper, ClassLoaderDelegateHook metaInfResourceClassLoaderDelegateHook) {
+ this.bundleFileWrapper = bundleFileWrapper;
+ this.metaInfResourceClassLoaderDelegateHook = metaInfResourceClassLoaderDelegateHook;
+ }
+
+ public void init() {
+ PluggableClassLoadingHook.getInstance().setClassLoaderCreator(new KernelClassLoaderCreator());
+ PluggableBundleFileWrapperFactoryHook.getInstance().setBundleFileWrapper(this.bundleFileWrapper);
+ PluggableDelegatingClassLoaderDelegateHook.getInstance().addDelegate(this.metaInfResourceClassLoaderDelegateHook);
+ }
+
+ public void destroy() throws Exception {
+ PluggableClassLoadingHook.getInstance().setClassLoaderCreator(null);
+ PluggableBundleFileWrapperFactoryHook.getInstance().setBundleFileWrapper(null);
+ PluggableDelegatingClassLoaderDelegateHook.getInstance().removeDelegate(this.metaInfResourceClassLoaderDelegateHook);
+ BundleFileClosingBundleFileWrapperFactoryHook.getInstance().cleanup();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxOsgiFramework.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxOsgiFramework.java
new file mode 100644
index 00000000..f295d648
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxOsgiFramework.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.State;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import org.eclipse.virgo.kernel.osgi.framework.ManifestTransformer;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkUtils;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiServiceHolder;
+import org.eclipse.virgo.kernel.osgi.framework.support.AbstractOsgiFramework;
+
+/**
+ * Implementation of <code>OsgiFramework</code> using Equinox.
+ *
+ */
+public class EquinoxOsgiFramework extends AbstractOsgiFramework {
+
+ private final OsgiServiceHolder<PlatformAdmin> platformAdmin;
+
+ private final EquinoxBootDelegationHelper bootDelegationHelper;
+
+ private final TransformedManifestProvidingBundleFileWrapper bundleTransformationHandler;
+
+
+ /**
+ * Creates a new <code>EquinoxOsgiFramework</code>.
+ * @param context execution context of bundle
+ * @param packageAdmin framework service for access to {@link State}
+ * @param bundleTransformationHandler wrapper for bundle manifest transformations
+ */
+ public EquinoxOsgiFramework(BundleContext context, PackageAdmin packageAdmin, TransformedManifestProvidingBundleFileWrapper bundleTransformationHandler) {
+ super(context, packageAdmin);
+ this.bootDelegationHelper = new EquinoxBootDelegationHelper(FrameworkProperties.getProperty(Constants.FRAMEWORK_BOOTDELEGATION));
+ this.platformAdmin = OsgiFrameworkUtils.getService(context, PlatformAdmin.class);
+ this.bundleTransformationHandler = bundleTransformationHandler;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void refresh(Bundle bundle) throws BundleException {
+ ClassLoader cl = getBundleClassLoader(bundle);
+ List<Bundle> refreshBundles = new ArrayList<Bundle>();
+ if (cl instanceof KernelBundleClassLoader) {
+ KernelBundleClassLoader pbcl = (KernelBundleClassLoader) cl;
+ if (pbcl.isInstrumented()) {
+ Bundle[] dependencies = getDirectDependencies(bundle);
+ for (Bundle dependency : dependencies) {
+ if (OsgiFrameworkUtils.sameScope(bundle, dependency)) {
+ dependency.update();
+ refreshBundles.add(dependency);
+ }
+ }
+ }
+ }
+ bundle.update();
+ refreshBundles.add(bundle);
+ Bundle[] toRefresh = refreshBundles.toArray(new Bundle[refreshBundles.size()]);
+ getPackageAdmin().refreshPackages(toRefresh);
+ }
+
+ final void stop() {
+ if (this.platformAdmin != null) {
+ getBundleContext().ungetService(this.platformAdmin.getServiceReference());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ClassLoader getBundleClassLoader(Bundle bundle) {
+ return EquinoxUtils.getBundleClassLoader(bundle);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isBootDelegated(String className) {
+ if (this.bootDelegationHelper != null) {
+ return this.bootDelegationHelper.isBootDelegated(className);
+ }
+
+ throw new IllegalStateException("OsgiFramework must have been started prior to querying its boot delegation");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Bundle[] getDirectDependencies(Bundle bundle, boolean includeFragments) {
+ BundleContext bundleContext = getBundleContext();
+ ServiceReference serviceRef = bundleContext.getServiceReference(PlatformAdmin.class.getName());
+ try {
+ PlatformAdmin serverAdmin = (PlatformAdmin) bundleContext.getService(serviceRef);
+ return EquinoxUtils.getDirectDependencies(bundle, bundleContext, serverAdmin, includeFragments);
+ } finally {
+ bundleContext.ungetService(serviceRef);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Bundle[] getDirectDependencies(Bundle bundle) {
+ return getDirectDependencies(bundle, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void update(Bundle bundle, ManifestTransformer manifestTransformer) throws BundleException {
+ this.bundleTransformationHandler.pushManifestTransformer(manifestTransformer);
+ try {
+ bundle.update();
+ } finally {
+ this.bundleTransformationHandler.popManifestTransformer();
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxUtils.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxUtils.java
new file mode 100644
index 00000000..b9e3c19d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxUtils.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.osgi.framework.internal.core.BundleHost;
+import org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader;
+import org.eclipse.osgi.internal.loader.BundleLoader;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.State;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import org.eclipse.virgo.kernel.osgi.framework.BundleClassLoaderUnavailableException;
+
+/**
+ * Utility methods for working with Equinox internals.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+public final class EquinoxUtils {
+
+ /**
+ * Gets the {@link ClassLoader} for the supplied {@link Bundle}.
+ *
+ * @param bundle the bundle.
+ * @return the bundle <code>ClassLoader</code>.
+ */
+ public static ClassLoader getBundleClassLoader(Bundle bundle) {
+ ClassLoader classLoader = null;
+ if (BundleHost.class.isAssignableFrom(bundle.getClass())) {
+ BundleHost bundleHost = (BundleHost) bundle;
+
+ Class<?>[] parmTypes = {};
+ Method checkLoaderMethod;
+ try {
+ checkLoaderMethod = BundleHost.class.getDeclaredMethod("checkLoader", parmTypes);
+ Object[] args = {};
+ checkLoaderMethod.setAccessible(true);
+ BundleLoader bundleLoader = (BundleLoader) checkLoaderMethod.invoke(bundleHost, args);
+
+ if (bundleLoader == null) {
+ throw new IllegalStateException("Unable to access BundleLoader for bundle '" + bundle.getSymbolicName() + "'.");
+ }
+
+ Method createClassLoaderMethod = BundleLoader.class.getDeclaredMethod("createClassLoader", parmTypes);
+ createClassLoaderMethod.setAccessible(true);
+
+ classLoader = (DefaultClassLoader) createClassLoaderMethod.invoke(bundleLoader, args);
+ } catch (Exception e) {
+ throw new BundleClassLoaderUnavailableException("Failed to get class loader for bundle '" + bundle
+ + "' - possible resolution problem.", e);
+ }
+ }
+ return classLoader;
+ }
+
+ /**
+ * Gets all direct dependencies of the supplied {@link Bundle}.
+ *
+ * @param bundle the <code>Bundle</code>.
+ * @param bundleContext the {@link BundleContext} to use for service access - typically the system
+ * <code>BundleContext</code>.
+ * @param serverAdmin the {@link PlatformAdmin} service.
+ * @return the direct dependencies.
+ */
+ public static Bundle[] getDirectDependencies(Bundle bundle, BundleContext bundleContext, PlatformAdmin serverAdmin) {
+ return getDirectDependencies(bundle, bundleContext, serverAdmin, false);
+ }
+
+ /**
+ * Gets all direct dependencies of the supplied {@link Bundle}, optionally included fragments of the direct
+ * dependencies in the returned array.
+ *
+ * @param bundle the <code>Bundle</code>.
+ * @param bundleContext the {@link BundleContext} to use for service access - typically the system
+ * <code>BundleContext</code>.
+ * @param serverAdmin the {@link PlatformAdmin} service.
+ * @param includeFragments whether to include fragments or no
+ * @return an array of {@link Bundle}s which are direct dependencies
+ */
+ public static Bundle[] getDirectDependencies(Bundle bundle, BundleContext bundleContext, PlatformAdmin serverAdmin, boolean includeFragments) {
+ State state = serverAdmin.getState(false);
+
+ ExportPackageDescription[] exportPackageDescriptions = serverAdmin.getStateHelper().getVisiblePackages(state.getBundle(bundle.getBundleId()));
+
+ Set<Bundle> dependencies = new HashSet<Bundle>();
+
+ for (ExportPackageDescription exportPackageDescription : exportPackageDescriptions) {
+ BundleDescription bundleDescription = exportPackageDescription.getExporter();
+ if (bundleDescription.getBundleId() != bundle.getBundleId()) {
+ Bundle dependencyBundle = bundleContext.getBundle(bundleDescription.getBundleId());
+ // Handle an uninstalled dependent bundle gracefully.
+ if (dependencyBundle != null) {
+ dependencies.add(dependencyBundle);
+ if (includeFragments) {
+ BundleDescription[] fragmentDescriptions = bundleDescription.getFragments();
+ for (BundleDescription fragmentDescription : fragmentDescriptions) {
+ Bundle fragment = bundleContext.getBundle(fragmentDescription.getBundleId());
+ if (fragment != null) {
+ dependencies.add(fragment);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return dependencies.toArray(new Bundle[dependencies.size()]);
+ }
+
+ /**
+ * Queries whether the supplied {@link Bundle} is the system bundle.
+ *
+ * @param bundle the <code>Bundle</code>.
+ * @return <code>true</code> if <code>bundle</code> is the system bundle, otherwise <code>false</code>.
+ */
+ public static boolean isSystemBundle(Bundle bundle) {
+ return bundle != null && bundle.getBundleId() == 0;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelBundleClassLoader.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelBundleClassLoader.java
new file mode 100644
index 00000000..073175d7
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelBundleClassLoader.java
@@ -0,0 +1,449 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.IOException;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.osgi.framework.ExtendedClassNotFoundException;
+import org.eclipse.virgo.kernel.osgi.framework.ExtendedNoClassDefFoundError;
+import org.eclipse.virgo.kernel.osgi.framework.InstrumentableClassLoader;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkUtils;
+
+/**
+ * Extension to {@link DefaultClassLoader} that adds instrumentation support.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * As threadsafe as <code>DefaultClassLoader</code>.
+ *
+ */
+public final class KernelBundleClassLoader extends DefaultClassLoader implements InstrumentableClassLoader {
+
+ private static final String[] EXCLUDED_PACKAGES = new String[] { "java.", "javax.", "sun.", "oracle." };
+
+ private static final String HEADER_INSTRUMENT_PACKAGE = "Instrument-Package";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(KernelBundleClassLoader.class);
+
+ private final List<ClassFileTransformer> classFileTransformers = new CopyOnWriteArrayList<ClassFileTransformer>();
+
+ private final String[] instrumentedPackages;
+
+ private final String[] classpath;
+
+ private final String bundleScope;
+
+ private final Set<Class<Driver>> loadedDriverClasses = new HashSet<Class<Driver>>();
+
+ private final Object monitor = new Object();
+
+ private volatile boolean instrumented;
+
+ /**
+ * Constructs a new <code>ServerBundleClassLoader</code>.
+ *
+ * @param parent the parent <code>ClassLoader</code>.
+ * @param delegate the delegate for this ClassLoader</code>
+ * @param domain the domain for this ClassLoader</code>
+ * @param bundledata the bundledata for this ClassLoader</code>
+ * @param classpath the classpath for this ClassLoader</code>
+ */
+ KernelBundleClassLoader(ClassLoader parent, ClassLoaderDelegate delegate, ProtectionDomain domain, BaseData bundledata, String[] classpath) {
+ super(parent, delegate, domain, bundledata, classpath);
+ this.classpath = classpath;
+ this.bundleScope = OsgiFrameworkUtils.getScopeName(bundledata.getBundle());
+ this.instrumentedPackages = findInstrumentedPackages(bundledata.getBundle());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addClassFileTransformer(ClassFileTransformer transformer) {
+ this.instrumented = true;
+ synchronized (this.classFileTransformers) {
+ if (this.classFileTransformers.contains(transformer)) {
+ return;
+ }
+ this.classFileTransformers.add(transformer);
+ }
+ Bundle[] bundles = getDependencyBundles(false);
+ for (Bundle bundle : bundles) {
+ if (propagateInstrumentationTo(bundle)) {
+ ClassLoader bundleClassLoader = getBundleClassLoader(bundle);
+ if (bundleClassLoader instanceof KernelBundleClassLoader) {
+ ((KernelBundleClassLoader) bundleClassLoader).addClassFileTransformer(transformer);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param bundle
+ * @return
+ */
+ private ClassLoader getBundleClassLoader(Bundle bundle) {
+ return EquinoxUtils.getBundleClassLoader(bundle);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ try {
+ Class loadedClass = super.loadClass(name, resolve);
+ storeClassIfDriver(loadedClass);
+ return loadedClass;
+ } catch (ClassNotFoundException e) {
+ throw new ExtendedClassNotFoundException(this, e);
+ } catch (NoClassDefFoundError e) {
+ throw new ExtendedNoClassDefFoundError(this, e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isInstrumented() {
+ return this.instrumented;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getClassFileTransformerCount() {
+ return this.classFileTransformers.size();
+ }
+
+ /**
+ * Finds the explicit list of packages to include in instrumentation (if specified).
+ */
+ private String[] findInstrumentedPackages(Bundle bundle) {
+ String headerValue = (String) bundle.getHeaders().get(HEADER_INSTRUMENT_PACKAGE);
+ if (headerValue == null || headerValue.length() == 0) {
+ return new String[0];
+ } else {
+ String[] vals = headerValue.split(",");
+ String[] packageNames = new String[vals.length];
+ for (int x = 0; x < packageNames.length; x++) {
+ packageNames[x] = vals[x].trim();
+ }
+ return packageNames;
+ }
+ }
+
+ private boolean propagateInstrumentationTo(Bundle bundle) {
+ return !EquinoxUtils.isSystemBundle(bundle) && this.bundleScope != null && this.bundleScope.equals(OsgiFrameworkUtils.getScopeName(bundle));
+ }
+
+ private boolean shouldInstrument(String className) {
+ return includedForInstrumentation(className) && !excludedFromInstrumentation(className);
+ }
+
+ private boolean includedForInstrumentation(String className) {
+ if (this.instrumentedPackages.length == 0) {
+ return true;
+ }
+ for (String instrumentedPackage : this.instrumentedPackages) {
+ if (className.startsWith(instrumentedPackage)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean excludedFromInstrumentation(String className) {
+ for (String excludedPackage : EXCLUDED_PACKAGES) {
+ if (className.startsWith(excludedPackage)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ThrowAwayClassLoader createThrowAway() {
+ final ClasspathManager manager = new ClasspathManager(this.manager.getBaseData(), this.classpath, this);
+ manager.initialize();
+ return AccessController.doPrivileged(new PrivilegedAction<ThrowAwayClassLoader>() {
+ public ThrowAwayClassLoader run() {
+ return new ThrowAwayClassLoader(manager);
+ }
+ });
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Class<?> defineClass(String name, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry) {
+
+ byte[] transformedBytes = classbytes;
+ if (shouldInstrument(name)) {
+ for (ClassFileTransformer transformer : this.classFileTransformers) {
+ try {
+ String transformName = name.replaceAll("\\.", "/");
+ byte[] transform = transformer.transform(this, transformName, null, this.domain, transformedBytes);
+ if (transform != null) {
+ transformedBytes = transform;
+ }
+ } catch (IllegalClassFormatException e) {
+ throw new ClassFormatError("Error reading class from bundle entry '" + entry.getName() + "'. " + e.getMessage());
+ }
+ }
+ }
+ try {
+ Class<?> definedClass = super.defineClass(name, transformedBytes, classpathEntry, entry);
+ storeClassIfDriver(definedClass);
+ return definedClass;
+ } catch (NoClassDefFoundError e) {
+ throw new ExtendedNoClassDefFoundError(this, e);
+ }
+ }
+
+ /**
+ * @param definedClass
+ */
+ @SuppressWarnings("unchecked")
+ private void storeClassIfDriver(Class<?> candidateClass) {
+ if (Driver.class.isAssignableFrom(candidateClass)) {
+ synchronized(this.monitor) {
+ this.loadedDriverClasses.add((Class<Driver>)candidateClass);
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ clearJdbcDrivers();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void clearJdbcDrivers() {
+ Set<Class<Driver>> localLoadedDriverClasses;
+ synchronized (this.monitor) {
+ localLoadedDriverClasses = new HashSet<Class<Driver>>(this.loadedDriverClasses);
+ }
+
+ synchronized(DriverManager.class) {
+ try {
+ Field writeDriversField = DriverManager.class.getDeclaredField("writeDrivers");
+ writeDriversField.setAccessible(true);
+ Vector writeDrivers = (Vector)writeDriversField.get(null);
+
+ Iterator driverElements = writeDrivers.iterator();
+
+ while(driverElements.hasNext()) {
+ Object driverObj = driverElements.next();
+ Field driverField = driverObj.getClass().getDeclaredField("driver");
+ driverField.setAccessible(true);
+ if (localLoadedDriverClasses.contains(driverField.get(driverObj).getClass())) {
+ driverElements.remove();
+ }
+ }
+
+ Vector readDrivers = (Vector)writeDrivers.clone();
+ Field readDriversField = DriverManager.class.getDeclaredField("readDrivers");
+ readDriversField.setAccessible(true);
+ readDriversField.set(null, readDrivers);
+
+ } catch (Exception e) {
+ LOGGER.warn("Failure when clearing JDBC drivers for " + this, e);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s: [bundle=%s]", getClass().getSimpleName(), this.delegate);
+ }
+
+ private Bundle[] getDependencyBundles(boolean includeDependenciesFragments) {
+ Bundle bundle = this.manager.getBaseData().getBundle();
+ BundleContext systemBundleContext = getBundleContext();
+ PlatformAdmin serverAdmin = getPlatformAdmin();
+ Bundle[] deps = EquinoxUtils.getDirectDependencies(bundle, systemBundleContext, serverAdmin, includeDependenciesFragments);
+ return deps;
+ }
+
+ /**
+ * Gets the {@link BundleContext} for this ClassLoader's {@link Bundle}.
+ *
+ * @return the <code>BundleContext</code>.
+ */
+ private BundleContext getBundleContext() {
+ return this.manager.getBaseData().getAdaptor().getContext();
+ }
+
+ /**
+ * Gets the {@link PlatformAdmin} service.
+ *
+ * @return the <code>PlatformAdmin</code> service.
+ */
+ private PlatformAdmin getPlatformAdmin() {
+ return this.manager.getBaseData().getAdaptor().getPlatformAdmin();
+ }
+
+ /**
+ * Throwaway classloader for OSGi bundles.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * As threadsafe as {@link ClassLoader}.
+ *
+ */
+ final class ThrowAwayClassLoader extends ClassLoader {
+
+ private final ConcurrentMap<String, Class<?>> loadedClasses = new ConcurrentHashMap<String, Class<?>>();
+
+ private final ClasspathManager manager;
+
+ /**
+ * @param manager
+ */
+ private ThrowAwayClassLoader(ClasspathManager manager) {
+ this.manager = manager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (!shouldInstrument(name)) {
+ return KernelBundleClassLoader.this.loadClass(name, resolve);
+ }
+ Class<?> cls = KernelBundleClassLoader.this.findLoadedClass(name);
+ if (cls == null) {
+ cls = this.loadedClasses.get(name);
+ if (cls == null) {
+ cls = findClassInternal(name, true);
+ if (cls == null) {
+ cls = KernelBundleClassLoader.this.loadClass(name, resolve);
+ }
+ }
+ }
+ if (cls == null) {
+ throw new ClassNotFoundException(name);
+ }
+ if (resolve) {
+ resolveClass(cls);
+ }
+ this.loadedClasses.putIfAbsent(name, cls);
+ return cls;
+ }
+
+ /**
+ * Attempts to find a <code>Class</code> from the enclosing bundle.
+ *
+ * @param name the name of the <code>Class</code>
+ * @param traverseDependencies should dependency bundles be checked for the class.
+ */
+ Class<?> findClassInternal(String name, boolean traverseDependencies) {
+ String path = name.replaceAll("\\.", "/").concat(".class");
+
+ BundleEntry entry = this.manager.findLocalEntry(path);
+ if (entry == null) {
+ if (traverseDependencies) {
+ return findClassFromImport(name);
+ } else {
+ return null;
+ }
+ }
+ byte[] bytes;
+ try {
+ bytes = entry.getBytes();
+ } catch (IOException e) {
+ bytes = null;
+ }
+ return bytes == null ? null : defineClass(name, bytes, 0, bytes.length);
+ }
+
+ /**
+ * Attempts to locate a <code>Class</code> from one of the imported bundles.
+ *
+ * @param name the <code>Class</code> name.
+ * @return the located <code>Class</code>, or <code>null</code> if no <code>Class</code> can be found.
+ */
+ private Class<?> findClassFromImport(String name) {
+ Bundle[] deps = getDependencyBundles(false);
+ for (Bundle dep : deps) {
+ ClassLoader depClassLoader = getBundleClassLoader(dep);
+ if (depClassLoader instanceof KernelBundleClassLoader) {
+ KernelBundleClassLoader pbcl = (KernelBundleClassLoader) depClassLoader;
+ Class<?> loadedClass = pbcl.publicFindLoaded(name);
+ if (loadedClass != null) {
+ return loadedClass;
+ }
+ ThrowAwayClassLoader throwAway = pbcl.createThrowAway();
+ Class<?> cls = throwAway.findClassInternal(name, false);
+ if (cls != null) {
+ return cls;
+ }
+ }
+
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public URL getResource(String name) {
+ return KernelBundleClassLoader.this.getResource(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ return KernelBundleClassLoader.this.getResources(name);
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelClassLoaderCreator.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelClassLoaderCreator.java
new file mode 100644
index 00000000..a998d5df
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelClassLoaderCreator.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
+import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader;
+
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.ClassLoaderCreator;
+
+/**
+ * {@link ClassLoaderCreator} to replace the standard Equinox {@link DefaultClassLoader} with the
+ * {@link KernelBundleClassLoader}. <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+final class KernelClassLoaderCreator implements ClassLoaderCreator {
+
+ /**
+ * Creates a {@link KernelBundleClassLoader} in place of the standard Equinox {@link DefaultClassLoader}.
+ */
+ public BaseClassLoader createClassLoader(final ClassLoader parent, final ClassLoaderDelegate delegate, final BundleProtectionDomain domain,
+ final BaseData data, final String[] bundleclasspath) {
+ return AccessController.doPrivileged(new PrivilegedAction<BaseClassLoader>() {
+ public BaseClassLoader run() {
+ return new KernelBundleClassLoader(parent, delegate, domain, data, bundleclasspath);
+ }
+ });
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionDumpContributor.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionDumpContributor.java
new file mode 100644
index 00000000..84a9270b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionDumpContributor.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.File;
+
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.State;
+import org.osgi.framework.BundleContext;
+
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkUtils;
+import org.eclipse.virgo.medic.dump.Dump;
+import org.eclipse.virgo.medic.dump.DumpContributionFailedException;
+import org.eclipse.virgo.medic.dump.DumpContributor;
+
+/**
+ * An implementation of {@link DumpContributor} that generates a resolution state dump.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe
+ *
+ */
+public class ResolutionDumpContributor implements DumpContributor {
+
+ public final static String RESOLUTION_STATE_KEY = "resolution.state";
+
+ private final ResolutionStateDumper resolutionStateDumper;
+
+ public ResolutionDumpContributor(BundleContext bundleContext) {
+ PlatformAdmin platformAdmin = OsgiFrameworkUtils.getService(bundleContext, PlatformAdmin.class).getService();
+ this.resolutionStateDumper = new ResolutionStateDumper(new StandardSystemStateAccessor(platformAdmin), new StandardStateWriter(platformAdmin.getFactory()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void contribute(Dump dump) throws DumpContributionFailedException {
+ File outputFile = dump.createFile("osgi.zip");
+ if (dump.getContext().containsKey(RESOLUTION_STATE_KEY)) {
+ resolutionStateDumper.dump(outputFile, (State) dump.getContext().get(RESOLUTION_STATE_KEY));
+ } else {
+ resolutionStateDumper.dump(outputFile);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return "resolution";
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionStateDumper.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionStateDumper.java
new file mode 100644
index 00000000..afdb4308
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionStateDumper.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.osgi.service.resolver.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.util.io.PathReference;
+import org.eclipse.virgo.util.io.ZipUtils;
+
+/**
+ * Utility class that writes the current Equinox resolver {@link State} to a ZIP file.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+final class ResolutionStateDumper {
+
+ private static final String ENTRY_NAME_STATE = "state/";
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final SystemStateAccessor systemStateAccessor;
+
+ private final StateWriter stateWriter;
+
+ /**
+ * Creates a new <code>ResolutionStateDumper</code>.
+ * @param systemStateAccessor to access live system {@link State}
+ * @param stateWriter to write a {@link State} to permanent storage
+ */
+ public ResolutionStateDumper(SystemStateAccessor systemStateAccessor, StateWriter stateWriter) {
+ this.systemStateAccessor = systemStateAccessor;
+ this.stateWriter = stateWriter;
+ }
+
+ /**
+ * Dump the global resolver {@link State} into a ZIP file at the supplied location.
+ *
+ * @param outputFile the location to create the ZIP file at.
+ */
+ public void dump(File outputFile) {
+ dump(outputFile, this.systemStateAccessor.getSystemState());
+ }
+
+ /**
+ * Dump a resolver {@link State} into a ZIP file at the supplied location.
+ *
+ * @param outputFile the location to create the ZIP file at.
+ * @param state the state to dump
+ */
+ public void dump(File outputFile, State state) {
+ File outdir = new File(getTmpDir(), "resolve-" + System.currentTimeMillis());
+ if (outdir.mkdirs()) {
+ try {
+ this.stateWriter.writeState(state, outdir);
+ } catch (IOException e) {
+ this.logger.error("Unable to write resolver state.", e);
+ }
+ } else {
+ throw new RuntimeException("Unable to create temporary directory '" + outdir.getAbsolutePath() + "'.");
+ }
+
+ try {
+ zipStateDirectory(outputFile, outdir);
+ } catch (IOException e) {
+ this.logger.error("Unable to create ZIP of state dump", e);
+ } finally {
+ if (!new PathReference(outdir).delete(true)) {
+ this.logger.warn("Temporary state directory '%s' was not removed after use.", outdir.getAbsolutePath());
+ }
+ }
+ }
+
+ private void zipStateDirectory(File outputFile, File dumpDir) throws IOException {
+ PathReference output = new PathReference(outputFile);
+ PathReference toZip = new PathReference(dumpDir);
+
+ ZipUtils.zipTo(toZip, output, ENTRY_NAME_STATE);
+ }
+
+ private String getTmpDir() {
+ String path = System.getProperty("java.io.tmpdir");
+ return path;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardPackageAdminUtil.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardPackageAdminUtil.java
new file mode 100644
index 00000000..217ceb60
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardPackageAdminUtil.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import org.eclipse.osgi.framework.internal.core.PackageAdminImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkUtils;
+import org.eclipse.virgo.kernel.osgi.framework.PackageAdminUtil;
+
+/**
+ * {@link StandardPackageAdminUtil} is the implementation of {@link PackageAdminUtil}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public final class StandardPackageAdminUtil implements PackageAdminUtil {
+
+ private final PackageAdmin packageAdmin;
+
+ public StandardPackageAdminUtil(BundleContext bundleContext) {
+ this.packageAdmin = OsgiFrameworkUtils.getService(bundleContext, PackageAdmin.class).getService();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void synchronouslyRefreshPackages(Bundle[] bundles) {
+ ((PackageAdminImpl)this.packageAdmin).refreshPackages(bundles, true);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardStateWriter.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardStateWriter.java
new file mode 100644
index 00000000..fb6148a0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardStateWriter.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.osgi.service.resolver.StateObjectFactory;
+
+
+/**
+ * Standard implementation of {@link StateWriter}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+class StandardStateWriter implements StateWriter {
+
+ private final StateObjectFactory factory;
+
+ StandardStateWriter(StateObjectFactory factory) {
+ this.factory = factory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void writeState(State state, File output) throws IOException {
+ this.factory.writeState(state, output);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardSystemStateAccessor.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardSystemStateAccessor.java
new file mode 100644
index 00000000..0b2740fa
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StandardSystemStateAccessor.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import org.eclipse.osgi.internal.baseadaptor.StateManager;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.State;
+
+/**
+ * Standard implementation of {@link SystemStateAccessor}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class StandardSystemStateAccessor implements SystemStateAccessor {
+
+ private final PlatformAdmin platformAdmin;
+
+ StandardSystemStateAccessor(PlatformAdmin platformAdmin) {
+ this.platformAdmin = platformAdmin;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public State getSystemState() {
+ return ((StateManager)this.platformAdmin).getSystemState();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StateWriter.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StateWriter.java
new file mode 100644
index 00000000..c0aa3a42
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StateWriter.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.osgi.service.resolver.State;
+
+
+/**
+ * A <code>StateWriter</code> is used to write a {@link State} to disk.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Implementations must be thread-safe.
+ *
+ */
+public interface StateWriter {
+
+ /**
+ * Writes the given <code>State</code> to the given <code>output</code> location.
+ *
+ * @param state The <code>State</code> to write
+ * @param outputDir The directory to which it should be written.
+ *
+ * @throws IOException if a failure occurs when writing the state
+ */
+ void writeState(State state, File outputDir) throws IOException;
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/SystemStateAccessor.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/SystemStateAccessor.java
new file mode 100644
index 00000000..6f957f66
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/SystemStateAccessor.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import org.eclipse.osgi.service.resolver.State;
+
+
+/**
+ * A <code>SystemStateAccessor</code> provides access to the live system {@link State}.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+interface SystemStateAccessor {
+
+ /**
+ * Returns the {@link State} for the system
+ * @return the system <code>State</code>.
+ */
+ State getSystemState();
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/TransformedManifestProvidingBundleFileWrapper.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/TransformedManifestProvidingBundleFileWrapper.java
new file mode 100644
index 00000000..5e097fe5
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/TransformedManifestProvidingBundleFileWrapper.java
@@ -0,0 +1,297 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Stack;
+import java.util.jar.JarFile;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportExpander;
+import org.eclipse.virgo.kernel.osgi.framework.ManifestTransformer;
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyDependenciesException;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.BundleFileWrapper;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+
+/**
+ * A <code>BundleFileWrapper</code> implementation that wraps {@link BundleFile BundleFiles} and replaces the manifest in the
+ * <code>BundleFile</code> will one that has been transformed in memory.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+public class TransformedManifestProvidingBundleFileWrapper implements BundleFileWrapper {
+
+ private final ImportExpander importExpander;
+
+ private final ThreadLocal<Stack<ManifestTransformer>> manifestTransformer;
+
+ public TransformedManifestProvidingBundleFileWrapper(ImportExpander importExpander) {
+ this.manifestTransformer = new ThreadLocal<Stack<ManifestTransformer>>() {
+ @Override
+ public Stack<ManifestTransformer> initialValue() {
+ return new Stack<ManifestTransformer>();
+ }
+ };
+ this.importExpander = importExpander;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleFile wrapBundleFile(BundleFile bundleFile) {
+ return new TransformedManifestProvidingBundleFile(bundleFile, this.importExpander);
+ }
+
+ public void pushManifestTransformer(ManifestTransformer manifestTransformer) {
+ this.manifestTransformer.get().push(manifestTransformer);
+ }
+
+ public void popManifestTransformer() {
+ this.manifestTransformer.get().pop();
+ }
+
+ /**
+ * A concrete extension of {@link BundleFile} that intercepts attempts to access a bundle's manifest
+ * a returns a transform copy of the manifest that has, e.g. expanded any Import-Library and Import-Bundle
+ * headers into the corresponding Import-Package header entries.
+ * <p>
+ * <strong>Concurrent Semantics</strong><br />
+ * As thread-safe as the encapsulated <code>BundleFile</code> instance.
+ *
+ */
+ private class TransformedManifestProvidingBundleFile extends BundleFile {
+
+ private final BundleFile bundleFile;
+
+ private final ImportExpander importExpander;
+
+ private volatile BundleEntry manifestEntry;
+
+ private final Object monitor = new Object();
+
+ private TransformedManifestProvidingBundleFile(BundleFile bundleFile, ImportExpander importExpander) {
+ this.bundleFile = bundleFile;
+ this.importExpander = importExpander;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void close() throws IOException {
+ this.bundleFile.close();
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean containsDir(String dir) {
+ return this.bundleFile.containsDir(dir);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BundleEntry getEntry(String path) {
+ if (path.equals(JarFile.MANIFEST_NAME)) {
+ synchronized (monitor) {
+ if (this.manifestEntry == null) {
+ BundleEntry entry = this.bundleFile.getEntry(path);
+
+ Stack<ManifestTransformer> manifestTransformers = TransformedManifestProvidingBundleFileWrapper.this.manifestTransformer.get();
+
+ ManifestTransformer manifestTransformer;
+
+ if (!manifestTransformers.isEmpty()) {
+ manifestTransformer = manifestTransformers.peek();
+ } else {
+ manifestTransformer = null;
+ }
+
+ BundleManifest originalManifest;
+
+ if (entry != null) {
+ try {
+ InputStreamReader manifestReader = new InputStreamReader(entry.getInputStream());
+ originalManifest = BundleManifestFactory.createBundleManifest(manifestReader);
+ manifestReader.close();
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ } else {
+ originalManifest = BundleManifestFactory.createBundleManifest();
+ }
+
+ BundleManifest transformedManifest = originalManifest;
+
+ if (manifestTransformer != null) {
+ transformedManifest = manifestTransformer.transform(originalManifest);
+ }
+
+ try {
+ this.importExpander.expandImports(Collections.singletonList(transformedManifest));
+ } catch (UnableToSatisfyDependenciesException utsde) {
+ throw new RuntimeException(utsde);
+ }
+
+ try {
+ this.manifestEntry = new TransformedManifestBundleEntry(transformedManifest);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ return this.manifestEntry;
+ } else {
+ return this.bundleFile.getEntry(path);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public Enumeration getEntryPaths(String path) {
+ Enumeration paths = this.bundleFile.getEntryPaths(path);
+ return paths;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public File getFile(String path, boolean nativeCode) {
+ return this.bundleFile.getFile(path, nativeCode);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void open() throws IOException {
+ this.bundleFile.open();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public File getBaseFile() {
+ return this.bundleFile.getBaseFile();
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public URL getResourceURL(String path, long hostBundleID, int index) {
+ return this.bundleFile.getResourceURL(path, hostBundleID, index);
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public URL getResourceURL(String path, long hostBundleID) {
+ return this.bundleFile.getResourceURL(path, hostBundleID);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public URL getResourceURL(String path, BaseData hostData, int index) {
+ return this.bundleFile.getResourceURL(path, hostData, index);
+ }
+ }
+
+ private static class TransformedManifestBundleEntry extends BundleEntry {
+
+ private final byte[] manifestBytes;
+
+ private final long time;
+
+ private TransformedManifestBundleEntry(BundleManifest transformedManifest) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ transformedManifest.write(new OutputStreamWriter(baos));
+
+ this.manifestBytes = baos.toByteArray();
+
+ time = System.currentTimeMillis();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public URL getFileURL() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(this.manifestBytes);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public URL getLocalURL() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return "MANIFEST.MF";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getSize() {
+ return this.manifestBytes.length;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getTime() {
+ return this.time;
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/UsesAnalyser.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/UsesAnalyser.java
new file mode 100644
index 00000000..18331325
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/equinox/UsesAnalyser.java
@@ -0,0 +1,394 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.ResolverError;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.osgi.service.resolver.VersionConstraint;
+import org.osgi.framework.Constants;
+
+import org.eclipse.virgo.util.math.Sets;
+
+/**
+ * Utility class for analysing uses failures in a given bundle.
+ *
+ * <strong>Concurrent Semantics</strong><br/>
+ * thread-safe
+ *
+ */
+public final class UsesAnalyser {
+
+ public AnalysedUsesConflict[] getUsesConflicts(State state, ResolverError usesError) {
+ VersionConstraint constraint = usesError.getUnsatisfiedConstraint();
+
+ List<AnalysedUsesConflict> analysedUsesConflicts = new ArrayList<AnalysedUsesConflict>();
+
+ if (constraint instanceof ImportPackageSpecification) {
+ ImportPackageSpecification rootImport = (ImportPackageSpecification) constraint;
+ Map<String, Set<SourcedPackage>> directPackages = generateExportPackagesVisibleInFailedBundle(state, rootImport);
+
+ for (ExportPackageDescription exportPackage : getResolvedCandidateExports(state, rootImport)) {
+ Map<String, Set<SourcedPackage>> usedPackages = generateExportPackagesUsedViaExportPackage(state, exportPackage);
+ analysedUsesConflicts.addAll(findConflictingExports(usedPackages, directPackages));
+ }
+
+ if (analysedUsesConflicts.isEmpty()) {
+ // be more aggressive
+ for (ExportPackageDescription exportPackage : getUnresolvedCandidateExports(state, rootImport)) {
+ Map<String, Set<SourcedPackage>> usedPackages = generateExportPackagesUsedViaExportPackage(state, exportPackage);
+ analysedUsesConflicts.addAll(findConflictingExports(usedPackages, directPackages));
+ }
+ }
+ }
+ return analysedUsesConflicts.toArray(new AnalysedUsesConflict[analysedUsesConflicts.size()]);
+ }
+
+ public ResolverError[] getUsesResolverErrors(State state, BundleDescription bundle) {
+ ResolverError[] errors = state.getResolverErrors(bundle);
+ if (errors!=null) {
+ List<ResolverError> usesErrors = new ArrayList<ResolverError>(errors.length);
+ for (ResolverError re : errors) {
+ if (re.getType()==ResolverError.IMPORT_PACKAGE_USES_CONFLICT) {
+ usesErrors.add(re);
+ }
+ }
+ return usesErrors.toArray(new ResolverError[usesErrors.size()]);
+ }
+ return null;
+ }
+
+ private List<AnalysedUsesConflict> findConflictingExports(Map<String, Set<SourcedPackage>> usedPackages, Map<String, Set<SourcedPackage>> directPackages) {
+ List<AnalysedUsesConflict> usesConflicts = new ArrayList<AnalysedUsesConflict>();
+ Set<String> packagesInCommon = Sets.<String>intersection(usedPackages.keySet(), directPackages.keySet());
+ for (String packageName : packagesInCommon) {
+ Set<SourcedPackage> allUsed = usedPackages.get(packageName);
+ Set<SourcedPackage> allWired = directPackages.get(packageName);
+
+ for (SourcedPackage sourcedPackage : allUsed) {
+ UsedBySourcedPackage usedSourced = (UsedBySourcedPackage) sourcedPackage;
+ if (!exportDescriptionOccursIn(usedSourced.getSource(), allWired)) {
+ for (SourcedPackage sp : allWired) {
+ usesConflicts.add(new AnalysedUsesConflict(usedSourced, sp));
+ }
+ }
+ }
+ }
+ return usesConflicts;
+ }
+
+ private static final boolean exportDescriptionOccursIn(ExportPackageDescription source, Set<SourcedPackage> allWired) {
+ for (SourcedPackage w : allWired) {
+ if (w.getSource().equals(source)) return true;
+ }
+ return false;
+ }
+
+ private Map<String, Set<SourcedPackage>> generateExportPackagesUsedViaExportPackage(State state, ExportPackageDescription exportPackage) {
+ Map<String, Set<SourcedPackage>> usedPackages = new HashMap<String, Set<SourcedPackage>>();
+ Set<String> knownPackages = new HashSet<String>();
+
+ addUsedImportedPackages(state, usedPackages, exportPackage, exportPackage, knownPackages);
+ return usedPackages;
+ }
+
+ private Map<String, Set<SourcedPackage>> generateExportPackagesVisibleInFailedBundle(State state, ImportPackageSpecification rootImport) {
+ BundleDescription failedBundle = rootImport.getBundle();
+ Map<String, Set<SourcedPackage>> directPackages = getOtherImportedPackages(state, rootImport);
+
+ Map<String, Set<SourcedPackage>> additionalPackages = new HashMap<String, Set<SourcedPackage>>();
+
+ // here we add all the exports visible through transitive uses
+ Set<Entry<String, Set<SourcedPackage>>> keys = directPackages.entrySet();
+
+ Set<String> knownPackages = new HashSet<String>();
+
+ for (Entry<String, Set<SourcedPackage>> key : keys) {
+ for (SourcedPackage sp : key.getValue()) {
+ ExportPackageDescription source = sp.getSource();
+ addUsedImportedPackages(state, additionalPackages, source, source, knownPackages);
+ }
+ }
+
+ directPackages.putAll(additionalPackages);
+ directPackages.putAll(getExportedPackages(failedBundle));
+ return directPackages;
+ }
+
+ private ExportPackageDescription[] getResolvedCandidateExports(State state, ImportPackageSpecification rootImport) {
+ List<ExportPackageDescription> exports = new ArrayList<ExportPackageDescription>();
+
+ BundleDescription[] bundles = state.getResolvedBundles();
+ if (bundles!=null) {
+ for (BundleDescription bundle : bundles) {
+ for (ExportPackageDescription exportPackage : bundle.getExportPackages()) {
+ if (rootImport.isSatisfiedBy(exportPackage)) {
+ exports.add(exportPackage);
+ }
+ }
+ }
+ }
+
+ return exports.toArray(new ExportPackageDescription[exports.size()]);
+ }
+
+ private ExportPackageDescription[] getUnresolvedCandidateExports(State state, ImportPackageSpecification rootImport) {
+ List<ExportPackageDescription> exports = new ArrayList<ExportPackageDescription>();
+
+ BundleDescription[] resolvedBundles = state.getResolvedBundles();
+
+ BundleDescription[] bundles = state.getBundles();
+ if (bundles!=null) {
+ for (BundleDescription bundle : bundles) {
+ if (notInArray(bundle, resolvedBundles)) {
+ for (ExportPackageDescription exportPackage : bundle.getExportPackages()) {
+ if (rootImport.isSatisfiedBy(exportPackage)) {
+ exports.add(exportPackage);
+ }
+ }
+ }
+ }
+ }
+
+ return exports.toArray(new ExportPackageDescription[exports.size()]);
+ }
+
+ private static final boolean notInArray(BundleDescription bundle, BundleDescription[] resolvedBundles) {
+ if (resolvedBundles==null) return true;
+ for (BundleDescription b : resolvedBundles) {
+ if (b==bundle) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void addUsedImportedPackages(State state, Map<String, Set<SourcedPackage>> packages, ExportPackageDescription exportPackage, ExportPackageDescription topDependency, Set<String> knownPackages) {
+ String[] packageNames = (String[]) exportPackage.getDirective(Constants.USES_DIRECTIVE);
+ if (packageNames!=null) {
+ BundleDescription bundle = exportPackage.getExporter();
+
+ ExportPackageDescription[] allExports = bundle.getExportPackages();
+ ImportPackageSpecification[] allImports = bundle.getImportPackages();
+ ExportPackageDescription[] allResolvedImports = bundle.getResolvedImports();
+
+ for (String packageName : packageNames) {
+ ExportPackageDescription localExport = findExportPackageDescriptionInArray(allExports, packageName);
+ if (null!=localExport) {
+ addSourcedPackageToMapSet(packages, packageName, new UsedBySourcedPackage(topDependency, localExport));
+ }
+
+ ExportPackageDescription localResolvedImport = findExportPackageDescriptionInArray(allResolvedImports, packageName);
+ if (null!=localResolvedImport) {
+ if (!knownPackages.contains(packageName)) {
+ knownPackages.add(packageName);
+ addSourcedPackageToMapSet(packages, packageName, new UsedBySourcedPackage(topDependency, localResolvedImport));
+ addUsedImportedPackages(state, packages, localResolvedImport, topDependency, knownPackages);
+ }
+ } else {
+ ImportPackageSpecification anImport = findImportPackageSpecificationInArray(allImports, packageName);
+ if (anImport!=null) {
+ ExportPackageDescription[] matchingExports = getCandidateExports(state, anImport);
+ for (ExportPackageDescription matchingExport : matchingExports) {
+ knownPackages.add(packageName);
+ addSourcedPackageToMapSet(packages, packageName, new UsedBySourcedPackage(topDependency, matchingExport));
+ addUsedImportedPackages(state, packages, matchingExport, topDependency, knownPackages);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static final ImportPackageSpecification findImportPackageSpecificationInArray(ImportPackageSpecification[] allImports, String packageName) {
+ for (ImportPackageSpecification ips : allImports) {
+ if (packageName.equals(ips.getName())) {
+ return ips;
+ }
+ }
+ return null;
+ }
+
+ private static final void addSourcedPackageToMapSet(Map<String, Set<SourcedPackage>> packages, String packageName, SourcedPackage sourcedPackage) {
+ Set<SourcedPackage> sourcedSet = packages.get(packageName);
+ if (sourcedSet==null) {
+ sourcedSet = new HashSet<SourcedPackage>();
+ }
+ sourcedSet.add(sourcedPackage);
+ packages.put(packageName, sourcedSet);
+ }
+
+ private static final ExportPackageDescription findExportPackageDescriptionInArray(ExportPackageDescription[] allExports, String packageName) {
+ for (ExportPackageDescription epd : allExports) {
+ if (packageName.equals(epd.getName())) {
+ return epd;
+ }
+ }
+ return null;
+ }
+
+ private Map<String, Set<SourcedPackage>> getOtherImportedPackages(State state, ImportPackageSpecification rootImport) {
+ BundleDescription bundle = rootImport.getBundle();
+
+ Map<String, Set<SourcedPackage>> packages = new HashMap<String, Set<SourcedPackage>>();
+
+ ImportPackageSpecification[] importSpecifications = bundle.getImportPackages();
+ for (ImportPackageSpecification importSpecification : importSpecifications) {
+ if (rootImport!=importSpecification) {
+ if (!Constants.RESOLUTION_OPTIONAL.equals(importSpecification.getDirective(Constants.RESOLUTION_DIRECTIVE))) {
+ ExportPackageDescription[] exportPackages = getCandidateExports(state, importSpecification);
+ for (ExportPackageDescription exportPackage : exportPackages ) {
+ addSourcedPackageToMapSet(packages, exportPackage.getName(), new ImportedSourcedPackage(rootImport, exportPackage));
+ }
+ }
+ }
+ }
+ return packages;
+ }
+
+ private ExportPackageDescription[] getCandidateExports(State state, ImportPackageSpecification importSpecification) {
+ ExportPackageDescription[] pkgs = getResolvedCandidateExports(state, importSpecification);
+ if (pkgs.length==0) pkgs = getUnresolvedCandidateExports(state, importSpecification);
+ return pkgs;
+ }
+
+ private Map<String, Set<SourcedPackage>> getExportedPackages(BundleDescription bundle) {
+ ExportPackageDescription[] packageArray = bundle.getExportPackages();
+ Map<String, Set<SourcedPackage>> packages = new HashMap<String, Set<SourcedPackage>>();
+ if (packageArray!=null)
+ for (ExportPackageDescription exportPackage : packageArray) {
+ addSourcedPackageToMapSet(packages, exportPackage.getName(), new SourcedPackage(exportPackage));
+ }
+ return packages;
+ }
+
+ private final static String stringOf(ExportPackageDescription source) {
+ BundleDescription bundle = source.getSupplier();
+ StringBuilder sb = new StringBuilder("'");
+ sb.append(source.getName())
+ .append("_").append(source.getVersion())
+ .append("' in bundle ").append(stringOf(bundle));
+ return sb.toString();
+ }
+
+ private final static String stringOf(BundleDescription bundle) {
+ StringBuilder sb = new StringBuilder("'");
+ sb.append(bundle.getSymbolicName()).append("_").append(bundle.getVersion()).append("'");
+ return sb.toString();
+ }
+
+ public static interface UsesViolation {
+
+ VersionConstraint getConstraint();
+
+ PossibleMatch[] getPossibleMatches();
+ }
+
+ public static interface PossibleMatch {
+
+ ExportPackageDescription getSupplier();
+
+ boolean isDependentConstraintMismatch();
+
+ DependentConstraintCollision[] getCollisions();
+ }
+
+ public static interface DependentConstraintCollision {
+
+ ImportPackageSpecification getConsumerConstraint();
+
+ ImportPackageSpecification getSupplierConstraint();
+
+ CollisionReason getCollisionReason();
+
+ }
+
+ public static enum CollisionReason {
+ DISJOINT_VERSION_RANGES, ATTRIBUTE_MISMATCH
+ }
+
+ private static class SourcedPackage {
+ private final ExportPackageDescription source;
+ public SourcedPackage(ExportPackageDescription source) {
+ this.source = source;
+ }
+ public String toString() {
+ return stringOf(this.source);
+ }
+ public ExportPackageDescription getSource() {
+ return this.source;
+ }
+ }
+
+ private static final class ImportedSourcedPackage extends SourcedPackage {
+ private final ImportPackageSpecification rootImport;
+ public ImportedSourcedPackage(ImportPackageSpecification rootImport, ExportPackageDescription source) {
+ super(source);
+ this.rootImport = rootImport;
+ }
+ public String toString() {
+ StringBuilder sb = new StringBuilder(super.toString());
+ sb.append(" imported by bundle ").append(stringOf(this.rootImport.getBundle()));
+ return sb.toString();
+ }
+ }
+
+ private static final class UsedBySourcedPackage extends SourcedPackage {
+ private final ExportPackageDescription usedBy;
+ public UsedBySourcedPackage(ExportPackageDescription usedBy, ExportPackageDescription source) {
+ super(source);
+ this.usedBy = usedBy;
+ }
+ public String toString() {
+ StringBuilder sb = new StringBuilder(super.toString());
+ sb.append(" used by ").append(stringOf(this.usedBy));
+ return sb.toString();
+ }
+ public ExportPackageDescription getUsedBy() {
+ return this.usedBy;
+ }
+ }
+
+ public static final class AnalysedUsesConflict {
+ private final UsedBySourcedPackage usedPackage;
+ private final SourcedPackage resolvedPackage;
+ private AnalysedUsesConflict(UsedBySourcedPackage used, SourcedPackage resolved) {
+ this.usedPackage = used;
+ this.resolvedPackage = resolved;
+ }
+ public String[] getConflictStatement() {
+ return new String[]
+ { "package " + this.usedPackage.toString()
+ , "conflicts with " + this.resolvedPackage.toString()
+ };
+ }
+ public ExportPackageDescription getConflictingPackage() {
+ return this.usedPackage==null ? null : this.usedPackage.getSource();
+ }
+ public ExportPackageDescription getUsesRootPackage() {
+ return this.usedPackage==null ? null : this.usedPackage.getUsedBy();
+ }
+ public ExportPackageDescription getPackage() {
+ return this.resolvedPackage==null ? null : this.resolvedPackage.getSource();
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/AbstractTrackedPackageImports.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/AbstractTrackedPackageImports.java
new file mode 100644
index 00000000..b2b1919b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/AbstractTrackedPackageImports.java
@@ -0,0 +1,376 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+
+import org.eclipse.virgo.kernel.serviceability.Assert;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.Resolution;
+
+/**
+ * {@link AbstractTrackedPackageImports} provides the general implementations of {@link TrackedPackageImports}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+abstract class AbstractTrackedPackageImports implements TrackedPackageImports {
+
+ protected static final String SOURCE_SEPARATOR = ", ";
+
+ private static final String VERSION_ATTRIBUTE_NAME = "version";
+
+ private static final String VERSION_ALTERNATE_ATTRIBUTE_NAME = "specification-version";
+
+ private static final String BUNDLE_VERSION_ATTRIBUTE_NAME = "bundle-version";
+
+ private static final Object tieMonitor = new Object(); // locking for hash clashes in isEquivalent
+
+ private final Object monitor = new Object();
+
+ /**
+ * The current merged package imports are held as a map of package name to {@link ImportedPackage}. Each package
+ * import contains one and only one imported package name which corresponds to the package name used to index the
+ * package import in the map. The map is valid if and only if mergeException is <code>null</code>.
+ */
+ protected final Map<String, ImportedPackage> mergedPackageImports = new HashMap<String, ImportedPackage>();
+
+ private ImportMergeException mergeException = null;
+
+ protected final List<TrackedPackageImports> sources = new ArrayList<TrackedPackageImports>();
+
+ /**
+ * Construct an {@link AbstractTrackedPackageImports} from the given package imports.
+ *
+ * @param initialPackageImports a map of package name to {@link ImportedPackage}
+ */
+ AbstractTrackedPackageImports(Map<String, ImportedPackage> packageImports) {
+ this.mergedPackageImports.putAll(packageImports);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void merge(TrackedPackageImports importsToMerge) throws ImportMergeException {
+ synchronized (this.monitor) {
+ checkMergeException();
+ try {
+ // Add the new imports before merging so they are included in any diagnostics.
+ sources.add(importsToMerge);
+ doMerge(importsToMerge);
+ } catch (ImportMergeException e) {
+ this.mergeException = e;
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * Merge the given package imports into this collection of package imports. If there is a conflict, issue
+ * diagnostics and throw {@link ImportMergeException}.
+ * <p />
+ * Pre-condition: the monitor is held and the current merged imports have no conflicts.
+ *
+ * @param importsToMerge
+ * @throws ImportMergeException
+ */
+ private void doMerge(TrackedPackageImports importsToMerge) throws ImportMergeException {
+ List<ImportedPackage> mergedImportsToMerge = importsToMerge.getMergedImports();
+ for (ImportedPackage packageImportToMerge : mergedImportsToMerge) {
+ String pkg = packageImportToMerge.getPackageName();
+ ImportedPackage mergedPackageImport = this.mergedPackageImports.get(pkg);
+ if (mergedPackageImport == null) {
+ this.mergedPackageImports.put(pkg, packageImportToMerge);
+ } else {
+ mergePackageImport(mergedPackageImport, packageImportToMerge);
+ }
+ }
+
+ }
+
+ /**
+ * Merge the given source package import into the given target package import. Throw {@link ImportMergeException} if
+ * and only if there is a merge clash.
+ *
+ * @param targetPackageImport the package import to be merged and updated
+ * @param sourceImportToMerge the package import to be merged in
+ * @throws ImportMergeException thrown if there is a merge clash
+ */
+ private void mergePackageImport(ImportedPackage targetPackageImport, ImportedPackage sourceImportToMerge) throws ImportMergeException {
+ mergeAttributes(targetPackageImport, sourceImportToMerge);
+ mergeDirectives(targetPackageImport, sourceImportToMerge);
+
+ }
+
+ /**
+ * Merge the attributes of the source package import into those of the target package import. Throw
+ * {@link ImportMergeException} if and only if there is a merge clash.
+ *
+ * @param targetPackageImport the package import to be merged and updated
+ * @param sourceImportToMerge the package import to be merged in
+ * @throws ImportMergeException thrown if there is a merge clash
+ */
+ private void mergeAttributes(ImportedPackage targetPackageImport, ImportedPackage sourceImportToMerge) throws ImportMergeException {
+ Map<String, String> targetAttributes = targetPackageImport.getAttributes();
+ Map<String, String> sourceAttributes = sourceImportToMerge.getAttributes();
+
+ mergeVersionRanges(targetPackageImport, sourceImportToMerge);
+
+ mergeBundleVersionRanges(targetPackageImport, sourceImportToMerge);
+
+ for (Entry<String, String> sourceAttributeEntry : sourceAttributes.entrySet()) {
+ String sourceAttributeName = sourceAttributeEntry.getKey();
+ if (!isVersionAttribute(sourceAttributeName)) {
+ String sourceAttributeValue = sourceAttributeEntry.getValue();
+ String targetAttributeValue = targetAttributes.get(sourceAttributeName);
+ if (targetAttributeValue != null) {
+ if (!targetAttributeValue.equals(sourceAttributeValue)) {
+ throw new ImportMergeException(targetPackageImport.getPackageName(), getPackageSources(targetPackageImport),
+ "conflicting values '" + sourceAttributeValue + "', '" + targetAttributeValue + "' of attribute '" + sourceAttributeName
+ + "'");
+ }
+ } else {
+ targetAttributes.put(sourceAttributeName, sourceAttributeValue);
+ }
+ }
+ }
+ }
+
+ /**
+ * Merge the version ranges of the given source and target attributes and update the target attributes if necessary
+ * with the merged range. If the version ranges are disjoint, throw {@link ImportMergeException}.
+ *
+ * @param targetPackageImport the package import to be merged and updated
+ * @param sourceAttributes
+ * @throws ImportMergeException
+ */
+ private void mergeVersionRanges(ImportedPackage targetPackageImport, ImportedPackage sourceImportToMerge) throws ImportMergeException {
+ Map<String, String> sourceAttributes = sourceImportToMerge.getAttributes();
+ VersionRange sourceVersionRange = getVersionRange(sourceAttributes);
+ if (sourceVersionRange != null) {
+ Map<String, String> targetAttributes = targetPackageImport.getAttributes();
+ VersionRange targetVersionRange = getVersionRange(targetAttributes);
+ VersionRange mergedVersionRange;
+ if (targetVersionRange == null) {
+ mergedVersionRange = sourceVersionRange;
+ } else {
+ mergedVersionRange = VersionRange.intersection(sourceVersionRange, targetVersionRange);
+ if (mergedVersionRange.isEmpty()) {
+ throw new ImportMergeException(targetPackageImport.getPackageName(), getPackageSources(targetPackageImport),
+ "disjoint package version ranges");
+ }
+ }
+ targetAttributes.put(VERSION_ATTRIBUTE_NAME, mergedVersionRange.toParseString());
+ }
+ }
+
+ /**
+ * Get the version range for the given attributes.
+ *
+ * @param attributes the attributes which may specify a version range
+ * @return the version range or <code>null</code> if no version range is specified
+ */
+ private VersionRange getVersionRange(Map<String, String> attributes) {
+ String versionRangeString = attributes.get(VERSION_ATTRIBUTE_NAME);
+ if (versionRangeString == null) {
+ versionRangeString = attributes.get(VERSION_ALTERNATE_ATTRIBUTE_NAME);
+ }
+ return versionRangeString == null ? null : new VersionRange(versionRangeString);
+ }
+
+ /**
+ * Merge the bundle version ranges of the given source and target attributes and update the target attributes if
+ * necessary with the merged range. If the bundle version ranges are disjoint, throw {@link ImportMergeException}.
+ *
+ * @param targetPackageImport the package import to be merged and updated
+ * @param sourceAttributes
+ * @throws ImportMergeException
+ */
+ private void mergeBundleVersionRanges(ImportedPackage targetPackageImport, ImportedPackage sourceImportToMerge) throws ImportMergeException {
+ VersionRange sourceVersionRange = sourceImportToMerge.getBundleVersion();
+
+ // Map<String, String> targetAttributes = targetPackageImport.getAttributes();
+ VersionRange targetVersionRange = targetPackageImport.getBundleVersion();
+ VersionRange mergedVersionRange;
+ if (targetVersionRange == null) {
+ mergedVersionRange = sourceVersionRange;
+ } else {
+ mergedVersionRange = VersionRange.intersection(sourceVersionRange, targetVersionRange);
+ if (mergedVersionRange.isEmpty()) {
+ throw new ImportMergeException(targetPackageImport.getPackageName(), getPackageSources(targetPackageImport),
+ "disjoint bundle version ranges " + sourceVersionRange.toString() + " and " + targetVersionRange.toString());
+ }
+ }
+ targetPackageImport.setBundleVersion(mergedVersionRange);
+ }
+
+ /**
+ * Return <code>true</code> if and only if the given attribute name is that of a version or bundle version
+ * attribute.
+ *
+ * @param attributeName the attribute name
+ * @return whether or not the given attribute name is that of a version or bundle version attribute
+ */
+ private boolean isVersionAttribute(String attributeName) {
+ return VERSION_ATTRIBUTE_NAME.equals(attributeName) || VERSION_ALTERNATE_ATTRIBUTE_NAME.equals(attributeName)
+ || BUNDLE_VERSION_ATTRIBUTE_NAME.equals(attributeName);
+ }
+
+ /**
+ * Merge the directives of the source package import into those of the target package import.
+ *
+ * @param targetPackageImport
+ * @param sourceImportToMerge
+ */
+ private void mergeDirectives(ImportedPackage targetPackageImport, ImportedPackage sourceImportToMerge) {
+ if (targetPackageImport.getResolution() == Resolution.OPTIONAL && sourceImportToMerge.getResolution() == Resolution.MANDATORY) {
+ targetPackageImport.setResolution(Resolution.MANDATORY);
+ }
+ }
+
+ /**
+ * Get a string describing the sources of the package of the given package import. This should help in diagnosing
+ * the root cause of a conflicting merge.
+ *
+ * @param pkg the package whose sources are required
+ * @return a string describing the given package's sources
+ */
+ private String getPackageSources(ImportedPackage packageImport) {
+ return getSources(packageImport.getPackageName());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getSources(String pkg) {
+ synchronized (this.monitor) {
+ StringBuilder sourcesDescription = new StringBuilder();
+ boolean first = true;
+ String source = getSource(pkg);
+ if (source != null) {
+ sourcesDescription.append(source);
+ first = false;
+ }
+ for (TrackedPackageImports trackedPackageImports : this.sources) {
+ String sources = trackedPackageImports.getSources(pkg);
+ if (sources != null) {
+ if (!first) {
+ sourcesDescription.append(SOURCE_SEPARATOR);
+ }
+ sourcesDescription.append(sources);
+ first = false;
+ }
+ }
+ return first ? null : sourcesDescription.toString();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final List<ImportedPackage> getMergedImports() throws ImportMergeException {
+ synchronized (this.monitor) {
+ checkMergeException();
+ List<ImportedPackage> mergedImports = new ArrayList<ImportedPackage>();
+ for (ImportedPackage packageImport : this.mergedPackageImports.values()) {
+ mergedImports.add(packageImport);
+ }
+ return mergedImports;
+ }
+ }
+
+ /**
+ * If a merge failure has occurred, re-throw the {@link ImportMergeException}.
+ *
+ * @throws ImportMergeException
+ */
+ private void checkMergeException() throws ImportMergeException {
+ if (this.mergeException != null) {
+ throw this.mergeException;
+ }
+ }
+
+ /**
+ * Convert a given list of package imports with no duplicate package names to a map of package name to
+ * {@link PackageImport}.
+ *
+ * @param importedPackages a list of <code>PackageImport</code>
+ * @return a map of package name to <code>PackageImport</code>
+ */
+ protected static Map<String, ImportedPackage> convertImportedPackageListToMap(List<ImportedPackage> importedPackages) {
+ Map<String, ImportedPackage> initialPackageImports = new HashMap<String, ImportedPackage>();
+ for (ImportedPackage importedPackage : importedPackages) {
+ Assert.isNull(initialPackageImports.put(importedPackage.getPackageName(), importedPackage),
+ "input packageImports must not contain duplicate items");
+ }
+
+ return initialPackageImports;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmpty() {
+ synchronized (this.monitor) {
+ return this.mergedPackageImports.isEmpty();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEquivalent(TrackedPackageImports otherTrackedPackageImports) {
+ if (otherTrackedPackageImports == null) {
+ return isEmpty();
+ }
+
+ Assert.isInstanceOf(AbstractTrackedPackageImports.class, otherTrackedPackageImports,
+ "otherTrackedPackageImports must be of type AbstractTrackedPackageImports");
+ AbstractTrackedPackageImports otherAbstractTrackedPackageImports = (AbstractTrackedPackageImports) otherTrackedPackageImports;
+ // Lock the object monitors in a predictable order to avoid deadlocks.
+ // Use hash to determine ordering, and tieMonitor (static monitor) when hashes coincide.
+ int thisHash = System.identityHashCode(this);
+ int otherHash = System.identityHashCode(otherTrackedPackageImports);
+ if (thisHash > otherHash) {
+ synchronized (this.monitor) {
+ synchronized (otherAbstractTrackedPackageImports.monitor) {
+ return this.mergedPackageImports.equals(otherAbstractTrackedPackageImports.mergedPackageImports);
+ }
+ }
+ } else if (thisHash < otherHash) {
+ synchronized (otherAbstractTrackedPackageImports.monitor) {
+ synchronized (this.monitor) {
+ return this.mergedPackageImports.equals(otherAbstractTrackedPackageImports.mergedPackageImports);
+ }
+ }
+ } else {
+ synchronized (tieMonitor) {
+ synchronized (otherAbstractTrackedPackageImports.monitor) {
+ synchronized (this.monitor) {
+ return this.mergedPackageImports.equals(otherAbstractTrackedPackageImports.mergedPackageImports);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/AdditionalTrackedPackageImports.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/AdditionalTrackedPackageImports.java
new file mode 100644
index 00000000..fdbecd2f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/AdditionalTrackedPackageImports.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.List;
+
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+/**
+ * {@link AdditionalTrackedPackageImports} is a {@link TrackedPackageImports} representing a collection of package
+ * imports to be added to one or more bundles from a given source.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+class AdditionalTrackedPackageImports extends AbstractTrackedPackageImports {
+
+ private final List<ImportedPackage> importedPackages;
+
+ private final String source;
+
+ /**
+ * Construct an {@link AdditionalTrackedPackageImports} with the given collection of package imports and the given
+ * source.
+ *
+ * @param importedPackages the list of package imports
+ * @param source the source
+ */
+ AdditionalTrackedPackageImports(List<ImportedPackage> importedPackages, String source) {
+ super(convertImportedPackageListToMap(importedPackages));
+ this.importedPackages = importedPackages;
+ this.source = source;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getSource(String pkg) {
+ return !convertImportedPackageListToMap(this.importedPackages).containsKey(pkg) ? null : this.source;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/BundleManifestProcessor.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/BundleManifestProcessor.java
new file mode 100644
index 00000000..81d36217
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/BundleManifestProcessor.java
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.util.common.StringUtils;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+import org.eclipse.virgo.util.osgi.manifest.ExportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+/**
+ * Provides a number of helper methods for processing {@link BundleManifest BundleManifests}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe
+ *
+ */
+class BundleManifestProcessor {
+
+ private static final String BUNDLE_SYMBOLIC_NAME_ATTRIBUTE_NAME = "bundle-symbolic-name";
+
+ private static final String BUNDLE_VERSION_ATTRIBUTE_NAME = "bundle-version";
+
+ /**
+ * Creates a {@link ImportedPackage} for each of the supplied {@link ExportedPackage ExportedPackages} and returns a
+ * {@link Result} containing them. The <code>ImportedPackages</code> will be versioned with a range that matches the
+ * corresponding <code>ExportedPackage</code>'s version and above. I.e an export with version x will result in an
+ * import with ;version="x". The result also contains any warnings that were generated during the processing of the
+ * manifests.
+ *
+ * @param packageExports the <code>ExportedPackages</code> to process
+ * @return a Result containing the package imports and any warnings
+ */
+ static List<ImportedPackage> createImportedPackageForEachExportedPackage(List<ExportedPackage> packageExports, String bundleSymbolicName,
+ String bundleVersion) {
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+
+ doCreateImportedPackageForEachExportedPackage(packageExports, bundleSymbolicName, bundleVersion, manifest);
+
+ return manifest.getImportPackage().getImportedPackages();
+ }
+
+ /**
+ * Creates a {@link ImportedPackage} for each package export found in the given {@link BundleManifest
+ * BundleManifests} and returns a {@link Result} containing the package imports. The package imports will be
+ * versioned with a range that matches the corresponding package export's version and above. I.e an export with
+ * version x will result in an import with ;version="x". The result also contains any warnings that were generated
+ * during the processing of the manifests.
+ *
+ * @param bundleManifests the bundle manifests to process
+ * @return a Result containing the package imports and any warnings
+ */
+ static Result<ImportedPackage[]> createImportedPackageForEachExportedPackage(BundleManifest[] bundleManifests) {
+
+ List<ImportedPackage> allImportedPackages = new ArrayList<ImportedPackage>();
+ List<Warning> warnings = new ArrayList<Warning>();
+
+ for (BundleManifest bundleManifest : bundleManifests) {
+ List<ExportedPackage> packageExports = bundleManifest.getExportPackage().getExportedPackages();
+ BundleManifest resultManifest = BundleManifestFactory.createBundleManifest();
+ doCreateImportedPackageForEachExportedPackage(packageExports, null, null, resultManifest);
+
+ List<ImportedPackage> packageImports = resultManifest.getImportPackage().getImportedPackages();
+
+ for (ImportedPackage packageImport : packageImports) {
+ String name = packageImport.getPackageName();
+ if (!containsImport(allImportedPackages, name)) {
+ allImportedPackages.add(packageImport);
+ } else {
+ warnings.add(new Warning(Code.DUPLICATE_PACKAGE_EXPORTS, name));
+ }
+ }
+ }
+
+ return new Result<ImportedPackage[]>(allImportedPackages.toArray(new ImportedPackage[allImportedPackages.size()]),
+ warnings.toArray(new Warning[warnings.size()]));
+ }
+
+ private static void doCreateImportedPackageForEachExportedPackage(List<ExportedPackage> packageExports, String bundleSymbolicName, String version,
+ BundleManifest bundleManifest) {
+ for (ExportedPackage packageExport : packageExports) {
+ String name = packageExport.getPackageName();
+ List<ImportedPackage> packageImports = bundleManifest.getImportPackage().getImportedPackages();
+ ImportedPackage packageImport = findImport(packageImports, name);
+ if (packageImport == null) {
+ packageImport = bundleManifest.getImportPackage().addImportedPackage(name);
+ }
+
+ List<String> mandatory = packageExport.getMandatory();
+ Map<String, String> attributes = new HashMap<String, String>();
+ if (mandatory != null) {
+ for (String attrName : mandatory) {
+ if (packageExport.getAttributes().containsKey(attrName)) {
+ attributes.put(attrName, packageExport.getAttributes().get(attrName));
+ }
+ }
+ }
+
+ if (bundleSymbolicName != null) {
+ attributes.put(BUNDLE_SYMBOLIC_NAME_ATTRIBUTE_NAME, bundleSymbolicName);
+ }
+
+ if (StringUtils.hasText(version)) {
+ VersionRange vr;
+ try {
+ vr = VersionRange.createExactRange(new Version(version));
+ } catch (IllegalArgumentException e) {
+ vr = new VersionRange(version);
+ }
+ attributes.put(BUNDLE_VERSION_ATTRIBUTE_NAME, vr.toString());
+ }
+
+ packageImport.getAttributes().putAll(attributes);
+ }
+ }
+
+ private static boolean containsImport(List<ImportedPackage> imports, String packageName) {
+ for (ImportedPackage packageImport : imports) {
+ if (packageImport.getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static ImportedPackage findImport(List<ImportedPackage> imports, String packageName) {
+ for (ImportedPackage packageImport : imports) {
+ if (packageImport.getPackageName().equals(packageName)) {
+ return packageImport;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Encapsulates the output from bundle manifest processing processing along with any {@link Warning Warnings} that
+ * were generated during that processing.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ * @param <T> output type
+ */
+ public static class Result<T> {
+
+ private final T output;
+
+ private final Warning[] warnings;
+
+ private Result(T output, Warning[] warnings) {
+ this.output = output;
+ this.warnings = warnings;
+ }
+
+ /**
+ * Returns the output of this result
+ *
+ * @return the output
+ */
+ public T getOutput() {
+ return this.output;
+ }
+
+ /**
+ * Returns any warnings generated while producing the result. If no warnings were generated an empty array will
+ * be returned.
+ *
+ * @return any generated warnings
+ */
+ public Warning[] getWarnings() {
+ return this.warnings.clone();
+ }
+
+ /**
+ * Returns whether this result was successful. A result is deemed to be successful if it does not contain any
+ * warnings
+ *
+ * @return <code>true</code> if the result is successful
+ */
+ public boolean success() {
+ return this.warnings.length == 0;
+ }
+ }
+
+ /**
+ * A Warning describes a problem that was encountered during BundleManifestHelper processing
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+ public static class Warning {
+
+ private final Code code;
+
+ private final String reason;
+
+ private Warning(Code code, String reason) {
+ this.code = code;
+ this.reason = reason;
+ }
+
+ /**
+ * Returns the {@link Code} that identifies the nature of this warning
+ *
+ * @return the warning's code
+ */
+ public Code getCode() {
+ return this.code;
+ }
+
+ /**
+ * Returns the reason for the generation of this warning
+ *
+ * @return the warning's reason
+ */
+ public String getReason() {
+ return this.reason;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Warning)) {
+ return false;
+ }
+
+ Warning that = (Warning) other;
+ return this.getCode().equals(that.getCode()) && this.getReason().equals(that.getReason());
+ }
+
+ @Override
+ public int hashCode() {
+ return this.code.hashCode();
+ }
+ }
+
+ /**
+ * Defines a unique code for each of the warnings that may be generated as a result of bundle manifest processing.
+ * <p />
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+ public enum Code {
+ /**
+ * More than one of the input {@link BundleManifest BundleManifests} exported the same package.
+ */
+ DUPLICATE_PACKAGE_EXPORTS;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/BundleTrackedPackageImports.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/BundleTrackedPackageImports.java
new file mode 100644
index 00000000..db4419d8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/BundleTrackedPackageImports.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+
+import org.eclipse.virgo.kernel.serviceability.Assert;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+/**
+ * {@link BundleTrackedPackageImports} provides merging and tracking for package imports originating in bundle
+ * manifests.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class BundleTrackedPackageImports extends AbstractTrackedPackageImports {
+
+ private final BundleManifest bundleManifest;
+
+ /**
+ * Construct a {@link TrackedPackageImports} sourced from a bundle manifest.
+ *
+ * @param importPackageHeader
+ */
+ BundleTrackedPackageImports(BundleManifest bundleManifest) {
+ super(getInitialImportedPackages(bundleManifest));
+ this.bundleManifest = bundleManifest;
+ }
+
+ /**
+ * Extract a map of package name to {@link ImportedPackage} from the given bundle manifest.
+ * <p />
+ * Each <code>ImportedPackage</code> in the map contains a single package name.
+ *
+ * @param bundleManifest the bundle manifest containing the package imports
+ * @return a map of package name to {@link ImportedPackage}
+ */
+ private static Map<String, ImportedPackage> getInitialImportedPackages(BundleManifest bundleManifest) {
+ Assert.notNull(bundleManifest, "bundleManifest must be non-null");
+ List<ImportedPackage> importedPackages = bundleManifest.getImportPackage().getImportedPackages();
+ return convertImportedPackageListToMap(importedPackages);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getSource(String pkg) {
+ Assert.notNull(this.bundleManifest.getBundleSymbolicName(), "bundleManifest must have a symbolic name");
+ return !getInitialImportedPackages(this.bundleManifest).containsKey(pkg) ? null
+ : ((this.bundleManifest.getFragmentHost().getBundleSymbolicName() == null ? "bundle " : "fragment ") + this.bundleManifest.getBundleSymbolicName().getSymbolicName());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void merge(TrackedPackageImports importsToMerge) throws ImportMergeException {
+ super.merge(importsToMerge);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/CollectingTrackedPackageImports.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/CollectingTrackedPackageImports.java
new file mode 100644
index 00000000..b5d1dcb9
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/CollectingTrackedPackageImports.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.ArrayList;
+
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+
+/**
+ * {@link CollectingTrackedPackageImports} is used to merge up a series of {@link TrackedPackageImports}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+class CollectingTrackedPackageImports extends AdditionalTrackedPackageImports {
+
+ private static final String SOURCE = "CollectingTrackedPackageImports";
+
+ CollectingTrackedPackageImports() {
+ super(new ArrayList<ImportedPackage>(), SOURCE);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ContainingTrackedPackageImports.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ContainingTrackedPackageImports.java
new file mode 100644
index 00000000..29fc37b8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ContainingTrackedPackageImports.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.ArrayList;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+/**
+ * {@link ContainingTrackedPackageImports} is used to collect a series of {@link TrackedPackageImports} into a
+ * containing source. Only contained <code>TrackedPackageImports</code> should be merged into the containing
+ * <code>ContainingTrackedPackageImports</code>.
+ * <p />
+ * The contained sources are wrapped in the containing source name to show the nesting.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class ContainingTrackedPackageImports extends AdditionalTrackedPackageImports {
+
+ private final String containingSource;
+
+ /**
+ * Construct a {@link ContainingTrackedPackageImports}.
+ *
+ * @param containingSource
+ */
+ ContainingTrackedPackageImports(String containingSource) {
+ super(new ArrayList<ImportedPackage>(), containingSource);
+ this.containingSource = containingSource;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSources(String pkg) {
+ return this.containingSource + "(" + super.getSources(pkg) + ")";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void merge(TrackedPackageImports importsToMerge) throws ImportMergeException {
+ super.merge(importsToMerge);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/EmptyTrackedPackageImports.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/EmptyTrackedPackageImports.java
new file mode 100644
index 00000000..32e289d9
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/EmptyTrackedPackageImports.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+
+/**
+ * {@link EmptyTrackedPackageImports} is an immutable, empty collection of package imports.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+final class EmptyTrackedPackageImports extends CollectingTrackedPackageImports {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void merge(TrackedPackageImports importsToMerge) throws ImportMergeException {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ImportExpansionHandler.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ImportExpansionHandler.java
new file mode 100644
index 00000000..ae6a2ea4
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ImportExpansionHandler.java
@@ -0,0 +1,562 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.osgi.framework.internal.core.BundleRepository;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportExpander;
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyBundleDependenciesException;
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyDependenciesException;
+
+import org.eclipse.virgo.kernel.artifact.bundle.BundleBridge;
+import org.eclipse.virgo.kernel.artifact.library.LibraryDefinition;
+import org.eclipse.virgo.kernel.serviceability.Assert;
+import org.eclipse.virgo.kernel.userregion.internal.UserRegionLogEvents;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.repository.ArtifactDescriptor;
+import org.eclipse.virgo.repository.Attribute;
+import org.eclipse.virgo.repository.Repository;
+import org.eclipse.virgo.util.math.OrderedPair;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+import org.eclipse.virgo.util.osgi.manifest.BundleSymbolicName;
+import org.eclipse.virgo.util.osgi.manifest.ExportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.ImportedBundle;
+import org.eclipse.virgo.util.osgi.manifest.ImportedLibrary;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.Resolution;
+
+/**
+ * A helper class for handling the expansion of <code>Import-Library</code> and <code>Import-Bundle</code> headers in a
+ * bundle manifest into <code>Import-Package</code> header entries.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * This class is <strong>thread-safe</strong>.
+ *
+ */
+public final class ImportExpansionHandler implements ImportExpander {
+
+ private static final String IMPORT_SCOPE_APPLICATION = "application";
+
+ private static final String IMPORT_SCOPE_DIRECTIVE = "import-scope";
+
+ private static final String MISSING_BUNDLE_SYMBOLIC_NAME = "<bundle symbolic name not present>";
+
+ private static final String INSTRUMENTED_SUFFIX = ".instrumented";
+
+ private static final String SYNTHETIC_CONTEXT_SUFFIX = "-synthetic.context";
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final Repository repository;
+
+ private final TrackedPackageImportsFactory trackedPackageImportsFactory = new StandardTrackedPackageImportsFactory();
+
+ private final BundleContext bundleContext;
+
+ private final Set<String> packagesExportedBySystemBundle;
+
+ private final EventLogger eventLogger;
+
+ public ImportExpansionHandler(Repository repository, Set<String> packagesExportedBySystemBundle, EventLogger eventLogger) {
+ this(repository, null, packagesExportedBySystemBundle, eventLogger);
+ }
+
+ public ImportExpansionHandler(Repository repository, BundleContext bundleContext, Set<String> packagesExportedBySystemBundle,
+ EventLogger eventLogger) {
+ this.repository = repository;
+ this.bundleContext = bundleContext;
+ this.packagesExportedBySystemBundle = packagesExportedBySystemBundle;
+ this.eventLogger = eventLogger;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws ImportMergeException
+ */
+ public ImportPromotionVector expandImports(List<BundleManifest> bundleManifests) throws UnableToSatisfyDependenciesException,
+ ImportMergeException {
+ StandardImportPromotionVector importPromotionVector = new StandardImportPromotionVector(this.trackedPackageImportsFactory);
+ TrackedPackageImports packageImportsToBePromoted = this.trackedPackageImportsFactory.createCollector();
+
+ for (BundleManifest bundleManifest : bundleManifests) {
+ TrackedPackageImports bundlePackageImportsToBePromoted = this.trackedPackageImportsFactory.createCollector();
+
+ detectPromotedPackageImports(bundleManifest, bundlePackageImportsToBePromoted);
+
+ expandImportsIfNecessary(bundleManifest, bundlePackageImportsToBePromoted, bundleManifests);
+ if (!bundlePackageImportsToBePromoted.isEmpty()) {
+ packageImportsToBePromoted.merge(bundlePackageImportsToBePromoted);
+ BundleSymbolicName bundleSymbolicNameHeader = bundleManifest.getBundleSymbolicName();
+ Assert.notNull(bundleSymbolicNameHeader, "Bundle-SymbolicName must be present for import promotion tracking");
+ importPromotionVector.put(bundleSymbolicNameHeader.getSymbolicName(), bundlePackageImportsToBePromoted);
+ }
+ }
+
+ mergePromotedImports(packageImportsToBePromoted, bundleManifests);
+ return importPromotionVector;
+ }
+
+ /**
+ * Detect package imports with application import scope and add these to the package imports to be promoted.
+ */
+ private void detectPromotedPackageImports(BundleManifest bundleManifest, TrackedPackageImports bundlePackageImportsToBePromoted) {
+ List<ImportedPackage> importedPackages = bundleManifest.getImportPackage().getImportedPackages();
+ List<ImportedPackage> importedPackagesToPromote = new ArrayList<ImportedPackage>();
+ for (ImportedPackage importedPackage : importedPackages) {
+ if (IMPORT_SCOPE_APPLICATION.equals(importedPackage.getDirectives().get(IMPORT_SCOPE_DIRECTIVE))) {
+ importedPackagesToPromote.add(importedPackage);
+ }
+ }
+ TrackedPackageImports trackedPackageImportsToPromote = this.trackedPackageImportsFactory.create(importedPackagesToPromote, "Import-Package in '" + bundleManifest.getBundleSymbolicName().getSymbolicName()
+ + "' version '" + bundleManifest.getBundleVersion() + "'");
+ bundlePackageImportsToBePromoted.merge(trackedPackageImportsToPromote);
+ }
+
+ private void mergePromotedImports(TrackedPackageImports importsToBemerged, List<BundleManifest> bundleManifests) throws ImportMergeException {
+ for (BundleManifest bundleManifest : bundleManifests) {
+ mergePromotedImports(importsToBemerged, bundleManifest);
+ }
+ }
+
+ private void mergePromotedImports(TrackedPackageImports importsToBemerged, BundleManifest bundleManifest) throws ImportMergeException {
+ BundleSymbolicName bundleSymbolicNameHeader = bundleManifest.getBundleSymbolicName();
+ if (bundleSymbolicNameHeader.getSymbolicName() == null || !bundleSymbolicNameHeader.getSymbolicName().endsWith(SYNTHETIC_CONTEXT_SUFFIX)) {
+ mergeImports(importsToBemerged, bundleManifest);
+ }
+ }
+
+ /**
+ * @param importsToBemerged
+ * @param bundleManifest
+ */
+ private void mergeImports(TrackedPackageImports importsToBemerged, BundleManifest bundleManifest) throws ImportMergeException {
+ TrackedPackageImports bundleTrackedPackageImports = this.trackedPackageImportsFactory.create(bundleManifest);
+ bundleTrackedPackageImports.merge(importsToBemerged);
+ setMergedImports(bundleManifest, bundleTrackedPackageImports);
+ }
+
+ private boolean expandImportsIfNecessary(BundleManifest manifest, TrackedPackageImports packageImportsToBePromoted,
+ List<BundleManifest> bundleManifests) throws UnableToSatisfyDependenciesException {
+ boolean expanded = false;
+
+ List<ImportedBundle> directlyImportedBundles = manifest.getImportBundle().getImportedBundles();
+ List<ImportedLibrary> importedLibraries = manifest.getImportLibrary().getImportedLibraries();
+
+ if (directlyImportedBundles.size() > 0 || importedLibraries.size() > 0) {
+ this.logger.info("Import-Library and/or Import-Bundle header found. Original manifest: \n{}", manifest);
+ expandImports(importedLibraries, directlyImportedBundles, manifest, packageImportsToBePromoted, bundleManifests);
+ this.logger.info("Updated manifest: \n{}", manifest);
+ expanded = true;
+ }
+ return expanded;
+ }
+
+ UnableToSatisfyBundleDependenciesException createExceptionForMissingLibrary(String name, VersionRange versionRange, BundleManifest bundleManifest) {
+ String description = String.format("A library with the name '%s' and a version within the range '%s' could not be found", name, versionRange);
+ BundleSymbolicName bundleSymbolicName = bundleManifest.getBundleSymbolicName();
+ return new UnableToSatisfyBundleDependenciesException(bundleSymbolicName != null ? bundleSymbolicName.getSymbolicName()
+ : MISSING_BUNDLE_SYMBOLIC_NAME, bundleManifest.getBundleVersion(), description);
+ }
+
+ @SuppressWarnings("unchecked")
+ void expandImports(List<ImportedLibrary> libraryImports, List<ImportedBundle> directlyImportedBundles, BundleManifest bundleManifest)
+ throws UnableToSatisfyDependenciesException {
+ expandImports(libraryImports, directlyImportedBundles, bundleManifest, this.trackedPackageImportsFactory.createCollector(),
+ Collections.EMPTY_LIST);
+ }
+
+ void expandImports(List<ImportedLibrary> libraryImports, List<ImportedBundle> directlyImportedBundles, BundleManifest bundleManifest,
+ List<BundleManifest> bundleManifests) throws UnableToSatisfyDependenciesException {
+ expandImports(libraryImports, directlyImportedBundles, bundleManifest, this.trackedPackageImportsFactory.createCollector(),
+ bundleManifests);
+ }
+
+ private void expandImports(List<ImportedLibrary> libraryImports, List<ImportedBundle> directlyImportedBundles, BundleManifest bundleManifest,
+ TrackedPackageImports packageImportsToBePromoted, List<BundleManifest> bundleManifests)
+ throws UnableToSatisfyDependenciesException {
+ Assert.notNull(libraryImports, "Library imports must be non-null");
+ Assert.notNull(directlyImportedBundles, "Direct bundle imports must be non-null");
+
+ mergeImports(getAdditionalPackageImports(libraryImports, directlyImportedBundles, bundleManifest, packageImportsToBePromoted,
+ bundleManifests), bundleManifest);
+
+ bundleManifest.getImportBundle().getImportedBundles().clear();
+ bundleManifest.getImportLibrary().getImportedLibraries().clear();
+ }
+
+ /**
+ * The bundle with the given bundle manifest imports the given list of libraries and the given list of bundles.
+ * Dereference these imports and return a collection of the corresponding {@link TrackedPackageImports}. Any
+ * promoted imports are added to the given <code>TrackedPackageImports</code> of imports to be promoted.
+ *
+ * @param importedLibraries
+ * @param directlyImportedBundles
+ * @param bundleManifest
+ * @param bundleRepository
+ * @param packageImportsToBePromoted
+ * @return
+ * @throws UnableToSatisfyDependenciesException
+ * @throws UnableToSatisfyBundleDependenciesException
+ */
+ private TrackedPackageImports getAdditionalPackageImports(List<ImportedLibrary> importedLibraries, List<ImportedBundle> directlyImportedBundles,
+ BundleManifest bundleManifest, TrackedPackageImports packageImportsToBePromoted, List<BundleManifest> additionalManifests)
+ throws UnableToSatisfyDependenciesException, UnableToSatisfyBundleDependenciesException {
+ TrackedPackageImports additionalPackageImports = this.trackedPackageImportsFactory.createCollector();
+ TrackedPackageImports libraryPackageImports = getLibraryPackageImports(importedLibraries, packageImportsToBePromoted, bundleManifest,
+ additionalManifests);
+ additionalPackageImports.merge(libraryPackageImports);
+ for (ImportedBundle directlyImportedBundle : directlyImportedBundles) {
+ additionalPackageImports.merge(getBundlePackageImports(directlyImportedBundle, packageImportsToBePromoted, bundleManifest,
+ additionalManifests));
+ }
+ return additionalPackageImports;
+ }
+
+ /**
+ * Get a {@link TrackedPackageImports} instance representing the package imports that correspond to the given bundle
+ * import. The imported bundle is looked up in the given {@link BundleRepository} and, if it is not found,
+ * {@link UnableToSatisfiedBundleDependenciesException} is thrown. If the bundle import is to be promoted, then the
+ * result is also merged into the given <code>TrackedPackageImports</code> instance representing the package imports
+ * to be promoted.
+ *
+ * @param importedBundle
+ * @param bundleRepository
+ * @param packageImportsToBePromoted
+ * @param importingBundle
+ * @return
+ * @throws UnableToSatisfyBundleDependenciesException
+ */
+ private TrackedPackageImports getBundlePackageImports(ImportedBundle importedBundle, TrackedPackageImports packageImportsToBePromoted,
+ BundleManifest importingBundle, List<BundleManifest> additionalManifests) throws UnableToSatisfyBundleDependenciesException {
+ String bundleSymbolicName = importedBundle.getBundleSymbolicName();
+ VersionRange importVersionRange = importedBundle.getVersion();
+ boolean mandatory = importedBundle.getResolution().equals(Resolution.MANDATORY);
+ if (bundleSymbolicName.equals(importingBundle.getBundleSymbolicName().getSymbolicName())
+ && importVersionRange.includes(importingBundle.getBundleVersion())) {
+ throw new UnableToSatisfyBundleDependenciesException(importingBundle.getBundleSymbolicName().getSymbolicName(),
+ importingBundle.getBundleVersion(), "Import-Bundle must not import the importing bundle");
+ }
+
+ OrderedPair<BundleManifest, Boolean> bundleManifestHolder = findBundle(bundleSymbolicName, importVersionRange, additionalManifests);
+ if (bundleManifestHolder != null && bundleManifestHolder.getFirst() != null) {
+ return createTrackedPackageImportsFromImportedBundle(bundleManifestHolder, importedBundle.isApplicationImportScope(),
+ packageImportsToBePromoted);
+ } else if (mandatory) {
+ throw new UnableToSatisfyBundleDependenciesException(
+ importingBundle.getBundleSymbolicName() != null ? importingBundle.getBundleSymbolicName().getSymbolicName()
+ : MISSING_BUNDLE_SYMBOLIC_NAME, importingBundle.getBundleVersion(), "Import-Bundle with symbolic name '" + bundleSymbolicName
+ + "' in version range '" + importVersionRange + "' could not be satisfied");
+ } else {
+ return this.trackedPackageImportsFactory.createEmpty();
+ }
+ }
+
+ /**
+ * Return the {@link TrackedPackageImports} corresponding to importing the given {@link BundleManifest} and, if
+ * appropriate, merge them into the given imports to be promoted.
+ *
+ * @param bundleManifest
+ * @param promoteExports
+ * @param packageImportsToBePromoted
+ * @return
+ */
+ private TrackedPackageImports createTrackedPackageImportsFromImportedBundle(OrderedPair<BundleManifest, Boolean> bundleManifest,
+ boolean promoteExports, TrackedPackageImports packageImportsToBePromoted) {
+ TrackedPackageImports bundlePackageImports = createPackageImportsFromPackageExports(bundleManifest, promoteExports);
+ if (promoteExports) {
+ packageImportsToBePromoted.merge(bundlePackageImports);
+ }
+ return bundlePackageImports;
+ }
+
+ /**
+ * Create a {@link TrackedPackageImports} instance corresponding to the given package exports from the given bundle
+ * manifest.
+ * <p />
+ * Importing fragment bundles is not encouraged but sometimes it's the only way out when reusing poorly packaged
+ * bundles. Packages exported by a fragment are imported with the host's bundle symbolic name, an exact range
+ * matching only the exported package version, and a bundle version range corresponding to that of the fragment host
+ * header. Since the generated imports do not include a bundle version matching attribute with a value of the form
+ * [v,v], there is a risk that the package import will wire to the wrong version of the host. This risk seems very
+ * small and not worth the extra complexity of matching up hosts and fragments (some of which would need searching
+ * for in the repository) during import expansion.
+ *
+ */
+ private TrackedPackageImports createPackageImportsFromPackageExports(OrderedPair<BundleManifest, Boolean> bundleManifestHolder,
+ boolean promoteExports) {
+ BundleManifest bundleManifest = bundleManifestHolder.getFirst();
+ List<ExportedPackage> exportedPackages = bundleManifest.getExportPackage().getExportedPackages();
+ if (exportedPackages.isEmpty()) {
+ return this.trackedPackageImportsFactory.createEmpty();
+ }
+ String bundleVersion = bundleManifest.getBundleVersion().toString();
+ String bundleSymbolicName = bundleManifest.getBundleSymbolicName().getSymbolicName();
+
+ if (bundleManifest.getFragmentHost().getBundleSymbolicName() != null) {
+ bundleSymbolicName = bundleManifest.getFragmentHost().getBundleSymbolicName();
+ bundleVersion = bundleManifest.getFragmentHost().getBundleVersion().toParseString();
+ }
+
+ List<ImportedPackage> packageImports = BundleManifestProcessor.createImportedPackageForEachExportedPackage(exportedPackages,
+ bundleSymbolicName, bundleVersion);
+
+ if (promoteExports) {
+ tagImportsAsPromoted(packageImports);
+ }
+
+ if (bundleManifestHolder.getSecond()) {
+ diagnoseSystemBundleOverlap(packageImports, bundleSymbolicName, bundleVersion);
+ }
+
+ return this.trackedPackageImportsFactory.create(packageImports, "Import-Bundle '" + bundleManifest.getBundleSymbolicName().getSymbolicName()
+ + "' version '" + bundleManifest.getBundleVersion() + "'");
+ }
+
+ private void tagImportsAsPromoted(List<ImportedPackage> packageImports) {
+ for (ImportedPackage importedPackage : packageImports) {
+ importedPackage.getDirectives().put(IMPORT_SCOPE_DIRECTIVE, IMPORT_SCOPE_APPLICATION);
+ }
+ }
+
+ /**
+ * Check whether the packages imported by importing a bundle are exported by the system bundle and, if they are,
+ * issue a warning.
+ *
+ * @param importedPackages the imported packages
+ * @param bundleSymbolicNameString the symbolic name of the imported bundle
+ * @param bundleVersion the version of the imported bundle
+ */
+ private void diagnoseSystemBundleOverlap(List<ImportedPackage> importedPackages, String bundleSymbolicNameString, String bundleVersion) {
+ boolean overlap = false;
+ for (ImportedPackage importedPackage : importedPackages) {
+ if (this.packagesExportedBySystemBundle.contains(importedPackage.getPackageName())) {
+ overlap = true;
+ break;
+ }
+ }
+ if (overlap) {
+ StringBuilder imports = new StringBuilder();
+ boolean first = true;
+ for (ImportedPackage packageImport : importedPackages) {
+ if (!first) {
+ imports.append(",");
+ }
+ first = false;
+ imports.append(packageImport.getPackageName());
+ }
+ this.eventLogger.log(UserRegionLogEvents.SYSTEM_BUNDLE_OVERLAP, bundleSymbolicNameString, bundleVersion, imports.toString());
+ }
+ }
+
+ /**
+ * Get a {@link TrackedPackageImports} instance representing the package imports that correspond to the given
+ * library imports. Each imported library is looked up in the given {@link BundleRepository} and, if it is not
+ * found, {@link UnableToSatisfiedBundleDependenciesException} is thrown. If any package imports are to be promoted,
+ * then the result is also merged into the given <code>TrackedPackageImports</code> instance representing the
+ * package imports to be promoted.
+ *
+ * @param importedLibraries
+ * @param bundleRepository
+ * @param packageImportsToBePromoted
+ * @param importingBundle
+ * @return
+ * @throws UnableToSatisfyBundleDependenciesException
+ */
+ private TrackedPackageImports getLibraryPackageImports(List<ImportedLibrary> importedLibraries, TrackedPackageImports packageImportsToBePromoted,
+ BundleManifest importingBundle, List<BundleManifest> additionalManifests) throws UnableToSatisfyBundleDependenciesException {
+ TrackedPackageImports allLibraryPackageImports = this.trackedPackageImportsFactory.createCollector();
+ for (ImportedLibrary importedLibrary : importedLibraries) {
+ VersionRange libraryVersionRange = importedLibrary.getVersion();
+ String libraryName = importedLibrary.getLibrarySymbolicName();
+
+ ArtifactDescriptor libraryArtefact = findArtifactDescriptorForLibrary(libraryName, libraryVersionRange);
+
+ if (libraryArtefact != null) {
+
+ if (!libraryName.endsWith(INSTRUMENTED_SUFFIX) && libraryArtefact.getName().endsWith(INSTRUMENTED_SUFFIX)) {
+ this.eventLogger.log(UserRegionLogEvents.ALTERNATE_INSTRUMENTED_LIBRARY_FOUND, importingBundle.getBundleSymbolicName(),
+ libraryName, libraryVersionRange.toString(), libraryArtefact.getName());
+ }
+
+ Version libraryVersion = libraryArtefact.getVersion();
+ TrackedPackageImports libraryPackageImports = this.trackedPackageImportsFactory.createContainer("Import-Library '"
+ + importedLibrary.getLibrarySymbolicName() + "' version '" + libraryVersion + "'");
+
+ Set<Attribute> importedBundles = libraryArtefact.getAttribute("Import-Bundle");
+
+ for (Attribute importedBundle : importedBundles) {
+ String bundleSymbolicName = importedBundle.getValue();
+ Map<String, Set<String>> properties = importedBundle.getProperties();
+ Set<String> versionSet = properties.get("version");
+ VersionRange bundleVersionRange;
+ if (versionSet != null && !versionSet.isEmpty()) {
+ bundleVersionRange = new VersionRange(versionSet.iterator().next());
+ } else {
+ bundleVersionRange = VersionRange.NATURAL_NUMBER_RANGE;
+ }
+ OrderedPair<BundleManifest, Boolean> bundleManifest = findBundle(bundleSymbolicName, bundleVersionRange,
+ additionalManifests);
+ if (bundleManifest.getFirst() != null) {
+ boolean applicationImportScope = false;
+ Set<String> importScopeSet = properties.get(IMPORT_SCOPE_DIRECTIVE);
+ if (importScopeSet != null && !importScopeSet.isEmpty()) {
+ applicationImportScope = IMPORT_SCOPE_APPLICATION.equals(importScopeSet.iterator().next());
+ }
+ libraryPackageImports.merge(createTrackedPackageImportsFromImportedBundle(bundleManifest, applicationImportScope,
+ packageImportsToBePromoted));
+ } else {
+ Resolution importedBundleResolution = Resolution.MANDATORY;
+ Set<String> resolutionSet = properties.get("resolution");
+ if (resolutionSet != null && !resolutionSet.isEmpty()) {
+ importedBundleResolution = Resolution.valueOf(resolutionSet.iterator().next().toUpperCase(Locale.ENGLISH));
+ }
+
+ if (importedBundleResolution.equals(Resolution.MANDATORY)) {
+ throw new UnableToSatisfyBundleDependenciesException(
+ importingBundle.getBundleSymbolicName() != null ? importingBundle.getBundleSymbolicName().getSymbolicName()
+ : MISSING_BUNDLE_SYMBOLIC_NAME, importingBundle.getBundleVersion(), "Imported library '" + libraryName
+ + "' version '" + libraryVersion + "' contains Import-Bundle for bundle '" + bundleSymbolicName
+ + "' in version range '" + bundleVersionRange + "' which could not be satisfied");
+ }
+ }
+ }
+ allLibraryPackageImports.merge(libraryPackageImports);
+ } else if (importedLibrary.getResolution().equals(Resolution.MANDATORY)) {
+ throw createExceptionForMissingLibrary(libraryName, libraryVersionRange, importingBundle);
+ }
+ }
+ return allLibraryPackageImports;
+ }
+
+ /**
+ * Set the package imports of the given {@link BundleManifest} to the merged imports of the given
+ * {@link TrackedPackageImports}.
+ *
+ * @param bundleManifest the bundle manifest to be modified
+ * @param bundleTrackedPackageImports the <code>TrackedPackageImports</code> containing the merged imports
+ */
+ private void setMergedImports(BundleManifest bundleManifest, TrackedPackageImports bundleTrackedPackageImports) {
+ bundleManifest.getImportPackage().getImportedPackages().clear();
+ bundleManifest.getImportPackage().getImportedPackages().addAll(bundleTrackedPackageImports.getMergedImports());
+ }
+
+ /**
+ * Find the bundle with the given symbolic name and version range in the given bundle repository. If the given
+ * scoper is non-null, scope the symbolic name before searching the repository. Return the {@link BundleDefinition}
+ * of the bundle if it was found.
+ *
+ * @param bundleSymbolicName
+ * @param versionRange
+ * @param bundleRepository
+ * @param scoper
+ * @return
+ */
+ private OrderedPair<BundleManifest, Boolean> findBundle(String bundleSymbolicName, VersionRange versionRange,
+ List<BundleManifest> additionalManifests) {
+
+ boolean diagnose = false;
+
+ // prefer bundles from the supplied list
+ BundleManifest bundleManifest = findMatchingManifest(bundleSymbolicName, versionRange, additionalManifests);
+
+ if (bundleManifest == null && this.bundleContext != null) {
+ Bundle[] installedBundles = this.bundleContext.getBundles();
+ for (Bundle bundle : installedBundles) {
+ if (bundleSymbolicName.equals(bundle.getSymbolicName()) && versionRange.includes(bundle.getVersion())) {
+ bundleManifest = getBundleManifest(bundle);
+ if (bundleManifest != null) {
+ diagnose = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (bundleManifest == null) {
+ ArtifactDescriptor artefact = findArtifactDescriptorForBundle(bundleSymbolicName, versionRange);
+ if (artefact != null) {
+ diagnose = true;
+ bundleManifest = BundleManifestFactory.createBundleManifest(BundleBridge.convertToDictionary(artefact));
+ }
+ }
+
+ if (bundleManifest != null) {
+ this.logger.info("Found definition for bundle with symbolic name '{}' and version range '{}': {}", new Object[] { bundleSymbolicName,
+ versionRange, bundleManifest });
+ } else {
+ this.logger.info("Could not find definition for bundle with symbolic name '{}' and version range '{}'", bundleSymbolicName, versionRange);
+ }
+
+ return new OrderedPair<BundleManifest, Boolean>(bundleManifest, diagnose);
+ }
+
+ /**
+ * Finds the manifest in the supplied list that matches the given bundle symbolic name and version range. If no
+ * match is found, null is returned. If many matches are found, the one with the highest version range is selected.
+ */
+ private BundleManifest findMatchingManifest(String bundleSymbolicName, VersionRange versionRange, List<BundleManifest> additionalManifests) {
+ Version selectedVersion = null;
+ BundleManifest selectedManifest = null;
+
+ for (final BundleManifest manifest : additionalManifests) {
+ BundleSymbolicName bsn = manifest.getBundleSymbolicName();
+ if (bsn != null && bundleSymbolicName.equals(bsn.getSymbolicName())) {
+ Version version = manifest.getBundleVersion();
+ if (versionRange.includes(version)) {
+ if (selectedVersion == null || version.compareTo(selectedVersion) > 0) {
+ selectedVersion = version;
+ selectedManifest = manifest;
+ }
+ }
+ }
+ }
+ return selectedManifest;
+ }
+
+ private ArtifactDescriptor findArtifactDescriptorForBundle(String bundleSymbolicName, VersionRange versionRange) {
+ return this.repository.get(BundleBridge.BRIDGE_TYPE, bundleSymbolicName, versionRange);
+ }
+
+ private ArtifactDescriptor findArtifactDescriptorForLibrary(String librarySymbolicName, VersionRange versionRange) {
+ return this.repository.get(LibraryDefinition.LIBRARY_TYPE, librarySymbolicName, versionRange);
+ }
+
+ /**
+ * Get a {@link BundleDefinition} for the given {@link Bundle}. If a definition cannot be created, return
+ * <code>null</code>.
+ *
+ * @param bundle the bundle whose definition is required
+ * @return the bundle definition or <code>null</code> if no definition can be created
+ */
+ @SuppressWarnings("unchecked")
+ private BundleManifest getBundleManifest(Bundle bundle) {
+ return BundleManifestFactory.createBundleManifest(bundle.getHeaders());
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/StandardImportPromotionVector.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/StandardImportPromotionVector.java
new file mode 100644
index 00000000..8a0c2de2
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/StandardImportPromotionVector.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportExpander.ImportPromotionVector;
+
+/**
+ * {@link StandardImportPromotionVector} maintains a vector of promoted TrackedPackageImports indexed by bundle symbolic
+ * name.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class StandardImportPromotionVector implements ImportPromotionVector {
+
+ private final TrackedPackageImportsFactory trackedPackageImportsFactory;
+
+ private final Map<String, TrackedPackageImports> imports = new ConcurrentHashMap<String, TrackedPackageImports>();
+
+ private final Object monitor = new Object();
+
+ /**
+ * Construct a {@link StandardImportPromotionVector} with the given {@link TrackedPackageImportsFactory}.
+ *
+ * @param trackedPackageImportsFactory a {@link TrackedPackageImportsFactory}
+ */
+ StandardImportPromotionVector(TrackedPackageImportsFactory trackedPackageImportsFactory) {
+ this.trackedPackageImportsFactory = trackedPackageImportsFactory;
+ }
+
+ /**
+ * Add the given {@link TrackedPackageImports} at the index of the given bundle symbolic name.
+ * <p />
+ * Any entry at the given index is replaced.
+ *
+ * @param bundleSymbolicName the index into the vector
+ * @param promotedImports the promoted imports to be added at the given index
+ */
+ void put(String bundleSymbolicName, TrackedPackageImports promotedImports) {
+ synchronized (this.monitor) {
+ this.imports.put(bundleSymbolicName, promotedImports);
+ }
+ }
+
+ /**
+ * Get the {@link TrackedPackageImports} at the index of the given bundle symbolic name.
+ *
+ * @param bundleSymbolicName the index into the vector
+ * @return the promoted imports at the given index or <code>null</code> if there are no such promoted imports
+ */
+ TrackedPackageImports get(String bundleSymbolicName) {
+ return this.imports.get(bundleSymbolicName);
+ }
+
+ /**
+ * Get a merged form of the promoted imports in this vector.
+ *
+ * @return a merged {@link TrackedPackageImports}
+ */
+ TrackedPackageImports getPromotedImports() {
+ synchronized (this.monitor) {
+ TrackedPackageImports merged = this.trackedPackageImportsFactory.createCollector();
+ for (TrackedPackageImports promotedImports : this.imports.values()) {
+ merged.merge(promotedImports);
+ }
+ return merged;
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/StandardTrackedPackageImportsFactory.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/StandardTrackedPackageImportsFactory.java
new file mode 100644
index 00000000..89386cd1
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/StandardTrackedPackageImportsFactory.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.List;
+
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+/**
+ * {@link StandardTrackedPackageImportsFactory} provides a standard way of creating {@link TrackedPackageImports}
+ * instances.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public final class StandardTrackedPackageImportsFactory implements TrackedPackageImportsFactory {
+
+ private static final EmptyTrackedPackageImports EMPTY_TRACKED_PACKAGE_IMPORTS = new EmptyTrackedPackageImports();
+
+ /**
+ * {@inheritDoc}
+ */
+ public TrackedPackageImports create(BundleManifest bundleManifest) {
+ return new BundleTrackedPackageImports(bundleManifest);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TrackedPackageImports create(List<ImportedPackage> importedPackages, String source) {
+ return new AdditionalTrackedPackageImports(importedPackages, source);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TrackedPackageImports createCollector() {
+ return new CollectingTrackedPackageImports();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TrackedPackageImports createContainer(String containingSource) {
+ return new ContainingTrackedPackageImports(containingSource);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TrackedPackageImports createEmpty() {
+ return EMPTY_TRACKED_PACKAGE_IMPORTS;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImports.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImports.java
new file mode 100644
index 00000000..7863a32d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImports.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.List;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+/**
+ * {@link TrackedPackageImports} is a collection of package imports which supports merging. Merge conflicts result in an
+ * {@link ImportMergeException} being thrown which can be tracked back to its sources for diagnosis of the reason for the
+ * clash.
+ * <p />
+ * Various implementations of <code>TrackedPackageImports</code> are available and instance of these should be created
+ * using the {@link TrackedPackageImportsFactory} interface.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this class must be thread safe.
+ *
+ * @see TrackedPackageImportsFactory
+ */
+public interface TrackedPackageImports {
+
+ /**
+ * Merge the given package imports into this collection of package imports. If there is a conflict, issue
+ * diagnostics and throw {@link ImportMergeException}.
+ * <p />
+ * A merge conflict is caused by disjoint version ranges or incompatible matching attributes.
+ *
+ * @param importsToMerge the package imports to be merged
+ * @throws ImportMergeException thrown if and only if there is a conflict
+ */
+ void merge(TrackedPackageImports importsToMerge) throws ImportMergeException;
+
+ /**
+ * Determine whether there are any merged imports.
+ *
+ * @return <code>true</code> if and only if there are merged imports
+ */
+ boolean isEmpty();
+
+ /**
+ * Determine whether or not this {@link TrackedPackageImports} has equivalent merged imports to the given
+ * {@link TrackedPackageImports}. The sources of the merged imports are disregarded in this comparison.
+ *
+ * @param otherTrackedPackageImports
+ * @return <code>true</code> if and only if this and the given {@link TrackedPackageImports} have equivalent
+ * merged imports
+ */
+ boolean isEquivalent(TrackedPackageImports otherTrackedPackageImports);
+
+ /**
+ * Get the merged package imports. If there has been a conflict, throw {@link ImportMergeException}.
+ *
+ * @return the merged package imports
+ * @throws ImportMergeException thrown if and only if there has been a conflict
+ */
+ List<ImportedPackage> getMergedImports() throws ImportMergeException;
+
+ /**
+ * Get any sources of the given package in this merged collection. Return <code>null</code> if there are no
+ * sources in this collection.
+ *
+ * @param pkg the name of the package whose sources are required
+ * @return a description of the sources in this collection or <code>null</code> if there are no sources in this
+ * collection
+ */
+ String getSources(String pkg);
+
+ /**
+ * If the given package name was in the original collection, return the source of this {@link TrackedPackageImports}.
+ * Otherwise return <code>null</code>.
+ *
+ * @param pkg the name of the package whose source is required
+ * @return a description of this source or <code>null</code> if the given package was not in the original
+ * collection
+ */
+ String getSource(String pkg);
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImportsFactory.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImportsFactory.java
new file mode 100644
index 00000000..ed339b28
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImportsFactory.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.util.List;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+/**
+ * {@link TrackedPackageImportsFactory} is an interface for creating {@link TrackedPackageImports} instances.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ * @see TrackedPackageImports
+ */
+public interface TrackedPackageImportsFactory {
+
+ /**
+ * Create a {@link TrackedPackageImports} from the given {@link BundleManifest}.
+ * <p />
+ * This is typically used to take the imports of a bundle and merge in imports from other sources.
+ *
+ * @param bundleManifest the bundle manifest
+ * @return a <code>TrackedPackageImports</code> instance
+ */
+ TrackedPackageImports create(BundleManifest bundleManifest);
+
+ /**
+ * Create a {@link TrackedPackageImports} from the given list of {@link ImportedPackage}s and given source.
+ * <p />
+ * This is typically used to represent a collection of imports which are to be added to one or more bundles.
+ *
+ * @param importedPackages a list of <code>ImportedPackage</code>s which must not contain duplicate packages
+ * @param source the source of the given package imports
+ * @return a <code>TrackedPackageImports</code> instance
+ */
+ TrackedPackageImports create(List<ImportedPackage> importedPackages, String source);
+
+ /**
+ * Create a {@link TrackedPackageImports} with no {@link ImportedPackage}s of its own.
+ * <p />
+ * This is typically used to gather together a collection of imports from multiple sources which are then to be
+ * added to one or more bundles.
+ *
+ * @return a <code>TrackedPackageImports</code> instance
+ */
+ TrackedPackageImports createCollector();
+
+ /**
+ * Create a <i>container</i> {@link TrackedPackageImports} which contains any {@link ImportedPackage}s which are
+ * subsequently merged into it but which has no {@link ImportedPackage}s of its own. If the container is involved in
+ * producing an {@link ImportMergeException}, the sources of the {@link ImportedPackage}s merged into the container
+ * are wrapped in the given containing source.
+ * <p />
+ * This is typically used for named groups of imports such as the group of package imports corresponding to a
+ * library (in which case the name of the group could be the library name).
+ * @param containingSource
+ * @return a <code>TrackedPackageImports</code> instance
+ */
+ TrackedPackageImports createContainer(String containingSource);
+
+ /**
+ * Create an empty, immutable {@link TrackedPackageImports}.
+ *
+ * @return a <code>TrackedPackageImports</code> instance
+ */
+ TrackedPackageImports createEmpty();
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/BundleDescriptionComparator.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/BundleDescriptionComparator.java
new file mode 100644
index 00000000..0eb4ef1e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/BundleDescriptionComparator.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+
+class BundleDescriptionComparator implements Comparator<BundleDescription>, Serializable {
+
+ private static final long serialVersionUID = 1360592459106362605L;
+
+ public int compare(BundleDescription bd1, BundleDescription bd2) {
+ int comparison = bd1.getSymbolicName().compareTo(bd2.getSymbolicName());
+
+ if (comparison == 0) {
+ comparison = bd1.getVersion().compareTo(bd2.getVersion());
+ }
+
+ return comparison;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/DependencyCalculator.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/DependencyCalculator.java
new file mode 100644
index 00000000..58882ad0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/DependencyCalculator.java
@@ -0,0 +1,376 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.osgi.service.resolver.BundleDelta;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.HostSpecification;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.ResolverError;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.osgi.service.resolver.StateDelta;
+import org.eclipse.osgi.service.resolver.StateObjectFactory;
+import org.eclipse.osgi.service.resolver.VersionConstraint;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyBundleDependenciesException;
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyDependenciesException;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.ResolutionDumpContributor;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.ResolutionFailureDetective.ResolverErrorsHolder;
+
+import org.eclipse.virgo.kernel.artifact.bundle.BundleBridge;
+import org.eclipse.virgo.medic.dump.DumpGenerator;
+import org.eclipse.virgo.repository.ArtifactDescriptor;
+import org.eclipse.virgo.repository.Attribute;
+import org.eclipse.virgo.repository.Query;
+import org.eclipse.virgo.repository.Repository;
+
+/**
+ * Calculates the dependencies of a given set of {@link BundleDescription BundleDescriptions}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+public final class DependencyCalculator {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final ResolutionFailureDetective detective;
+
+ private final AtomicLong bundleId = new AtomicLong(System.currentTimeMillis());
+
+ private final Repository repository;
+
+ private final Object monitor = new Object();
+
+ private final StateObjectFactory stateObjectFactory;
+
+ private final DumpGenerator dumpGenerator;
+
+ public DependencyCalculator(StateObjectFactory stateObjectFactory, ResolutionFailureDetective detective, Repository repository,
+ BundleContext bundleContext) {
+ this.repository = repository;
+ this.detective = detective;
+ this.stateObjectFactory = stateObjectFactory;
+ this.dumpGenerator = (DumpGenerator) bundleContext.getService(bundleContext.getServiceReference(DumpGenerator.class.getName()));
+ }
+
+ /**
+ * Calculates the dependencies of the supplied set of {@link BundleDescription bundles}.
+ * <p/>
+ *
+ * Callers must supply a {@link State} against which dependency satisfaction is executed. The supplied State is
+ * destructively modified during constraint satisfaction so it <strong>must</strong> not be the system state.
+ * <p/>
+ *
+ * In a successful invocation, any new bundles that need to be installed are returned, and the supplied
+ * <code>State</code> is transformed to reflect the newly resolved state of the supplied bundles. Callers can query
+ * the <code>State</code> to find the fully wiring graph of the supplied bundles after successful constraint
+ * satisfaction.
+ * <p/>
+ *
+ * If diagnostics are forced, then an {@link UnableToSatisfyDependenciesException} is thrown if the constraints
+ * cannot be satisfied. If diagnostics are not forced, then either an
+ * <code>UnableToSatisfyDependenciesException</code> is thrown or the new bundles that need to be installed are
+ * returned depending on whether cloning some bundles may improve the chances of satisfying the constraints.
+ *
+ * @param state the <code>State</code> to satisfy against.
+ * @param bundles the bundles to calculate dependencies for.
+ * @return an array of descriptions of bundles that need to be added to the state to satisfy constraints.
+ * @throws BundleException
+ * @throws UnableToSatisfyDependenciesException
+ */
+ public BundleDescription[] calculateDependencies(State state, BundleDescription[] bundles) throws BundleException,
+ UnableToSatisfyDependenciesException {
+ this.logger.info("Calculating missing dependencies of bundle(s) '{}'", bundles);
+ synchronized (this.monitor) {
+
+ doSatisfyConstraints(bundles, state);
+
+ StateDelta delta = state.resolve(bundles);
+
+ for (BundleDescription description : bundles) {
+ if (!description.isResolved()) {
+ generateDump(state);
+
+ ResolverErrorsHolder reh = new ResolverErrorsHolder();
+ String failure = this.detective.generateFailureDescription(state, description, reh);
+
+ ResolverError[] resolverErrors = reh.getResolverErrors();
+ if (resolverErrors != null) {
+ for (ResolverError resolverError : resolverErrors) {
+ if (resolverError.getType() == ResolverError.IMPORT_PACKAGE_USES_CONFLICT) {
+ VersionConstraint unsatisfiedConstraint = resolverError.getUnsatisfiedConstraint();
+ if ((unsatisfiedConstraint instanceof ImportPackageSpecification)) {
+ ImportPackageSpecification importPackageSpecification = (ImportPackageSpecification) unsatisfiedConstraint;
+ this.logger.debug("Uses conflict: package '{}' version '{}' bundle '{}' version '{}'", new Object[] {
+ importPackageSpecification.getName(), importPackageSpecification.getVersionRange(),
+ importPackageSpecification.getBundleSymbolicName(), importPackageSpecification.getBundleVersionRange() });
+ }
+ }
+ }
+ }
+
+ throw new UnableToSatisfyBundleDependenciesException(description.getSymbolicName(), description.getVersion(), failure, state,
+ reh.getResolverErrors());
+ }
+ }
+
+ BundleDelta[] deltas = delta.getChanges(BundleDelta.ADDED, false);
+ Set<BundleDescription> newBundles = new HashSet<BundleDescription>();
+
+ for (BundleDelta bundleDelta : deltas) {
+ newBundles.add(bundleDelta.getBundle());
+ }
+
+ Set<BundleDescription> dependenciesSet = getNewTransitiveDependencies(new HashSet<BundleDescription>(Arrays.asList(bundles)), newBundles);
+
+ List<BundleDescription> dependencies = new ArrayList<BundleDescription>(dependenciesSet);
+ this.logger.info("The dependencies of '{}' are '{}'", Arrays.toString(bundles), dependencies);
+
+ Collections.sort(dependencies, new BundleDescriptionComparator());
+
+ BundleDescription[] dependencyDescriptions = dependencies.toArray(new BundleDescription[dependencies.size()]);
+ return dependencyDescriptions;
+ }
+ }
+
+ private Set<BundleDescription> getNewTransitiveDependencies(Set<BundleDescription> dependingBundles, Collection<BundleDescription> newBundles) {
+ Set<BundleDescription> transitiveDependencies = new HashSet<BundleDescription>();
+
+ while (!dependingBundles.isEmpty()) {
+ Set<BundleDescription> newDependencies = new HashSet<BundleDescription>();
+ for (BundleDescription bundle : dependingBundles) {
+ newDependencies.addAll(getNewImmediateDependencies(bundle, newBundles));
+ }
+
+ // Next time round the loop, check new dependencies that we haven't already processed
+ newDependencies.removeAll(transitiveDependencies);
+ dependingBundles = newDependencies;
+
+ transitiveDependencies.addAll(newDependencies);
+ }
+ return transitiveDependencies;
+ }
+
+ private Set<BundleDescription> getNewImmediateDependencies(BundleDescription bundle, Collection<BundleDescription> newBundles) {
+ Set<BundleDescription> immediateDependencies = new HashSet<BundleDescription>();
+ immediateDependencies.addAll(Arrays.asList(bundle.getFragments()));
+ immediateDependencies.addAll(Arrays.asList(bundle.getResolvedRequires()));
+ immediateDependencies.addAll(getPackageProviders(bundle));
+
+ HostSpecification hostSpecification = bundle.getHost();
+ if (hostSpecification != null) {
+ for (BundleDescription host : hostSpecification.getHosts()) {
+ immediateDependencies.add(host);
+ }
+ }
+ immediateDependencies.retainAll(newBundles);
+ return immediateDependencies;
+ }
+
+ private Set<BundleDescription> getPackageProviders(BundleDescription bundleDescription) {
+ Set<BundleDescription> packageProviders = new HashSet<BundleDescription>();
+ ExportPackageDescription[] resolvedImports = bundleDescription.getResolvedImports();
+ for (ExportPackageDescription resolvedImport : resolvedImports) {
+ packageProviders.add(resolvedImport.getExporter());
+ }
+ return packageProviders;
+ }
+
+ private void doSatisfyConstraints(BundleDescription description, State state) throws BundleException {
+ doSatisfyConstraints(new BundleDescription[] { description }, state);
+ }
+
+ private void doSatisfyConstraints(BundleDescription[] descriptions, State state) throws BundleException {
+
+ VersionConstraint[] unsatisfiedConstraints = findUnsatisfiedConstraints(descriptions, state);
+
+ List<BundleDescription> constraintsSatisfiers = new ArrayList<BundleDescription>();
+
+ for (VersionConstraint versionConstraint : unsatisfiedConstraints) {
+ boolean found = false;
+ for (BundleDescription description : descriptions) {
+ if (description == versionConstraint.getBundle()) {
+ found = true;
+ }
+ }
+ if (!found) {
+ continue;
+ }
+ if (versionConstraint instanceof ImportPackageSpecification) {
+ satisfyImportPackage((ImportPackageSpecification) versionConstraint, state, constraintsSatisfiers);
+ } else if (versionConstraint instanceof BundleSpecification) {
+ satisfyRequireBundle(versionConstraint, state, constraintsSatisfiers);
+ } else if (versionConstraint instanceof HostSpecification) {
+ satisfyFragmentHost(versionConstraint, state, constraintsSatisfiers);
+ }
+ }
+
+ for (BundleDescription description : descriptions) {
+ satisfyFragments(description, state, constraintsSatisfiers);
+ }
+
+ Collections.sort(constraintsSatisfiers, new BundleDescriptionComparator());
+
+ for (BundleDescription constraintSatisfier : constraintsSatisfiers) {
+ if (!isBundlePresentInState(constraintSatisfier.getName(), constraintSatisfier.getVersion(), state)) {
+ state.addBundle(constraintSatisfier);
+ doSatisfyConstraints(constraintSatisfier, state);
+ }
+ }
+ }
+
+ private void satisfyFragments(BundleDescription description, State state, List<BundleDescription> constraintSatisfiers) throws BundleException {
+ Set<? extends ArtifactDescriptor> fragmentArtefacts = this.repository.createQuery("type", BundleBridge.BRIDGE_TYPE).addFilter(
+ "Fragment-Host", description.getSymbolicName()).run();
+ for (ArtifactDescriptor fragmentArtefact : fragmentArtefacts) {
+ addBundle(fragmentArtefact, state, constraintSatisfiers);
+ }
+ }
+
+ private void satisfyFragmentHost(VersionConstraint constraint, State state, List<BundleDescription> constraintSatisfiers) throws BundleException {
+ Set<? extends ArtifactDescriptor> hostArtefacts = this.repository.createQuery("type", BundleBridge.BRIDGE_TYPE).addFilter(
+ "Bundle-SymbolicName", constraint.getName()).run();
+ for (ArtifactDescriptor hostArtefact : hostArtefacts) {
+ addBundle(hostArtefact, state, constraintSatisfiers);
+ }
+ }
+
+ private void satisfyRequireBundle(VersionConstraint constraint, State state, List<BundleDescription> constraintSatisfiers) throws BundleException {
+ Set<? extends ArtifactDescriptor> requiredBundleArtefacts = this.repository.createQuery("type", BundleBridge.BRIDGE_TYPE).addFilter(
+ "Bundle-SymbolicName", constraint.getName()).run();
+ for (ArtifactDescriptor requiredBundleArtefact : requiredBundleArtefacts) {
+ addBundle(requiredBundleArtefact, state, constraintSatisfiers);
+ }
+ }
+
+ private void satisfyImportPackage(ImportPackageSpecification constraint, State state, List<BundleDescription> constraintSatisfiers)
+ throws BundleException {
+ VersionRange packageVersionRange = constraint.getVersionRange();
+ Query query = this.repository.createQuery("type", BundleBridge.BRIDGE_TYPE);
+ boolean loosePackageVersionRange = false;
+ if (packageVersionRange != null && packageVersionRange.getMaximum().equals(packageVersionRange.getMinimum())) {
+ Map<String, Set<String>> properties = new HashMap<String, Set<String>>();
+ properties.put("version", new HashSet<String>(Arrays.asList(packageVersionRange.getMaximum().toString())));
+ query.addFilter("Export-Package", constraint.getName(), properties);
+ } else {
+ query.addFilter("Export-Package", constraint.getName());
+ loosePackageVersionRange = packageVersionRange != null;
+ }
+
+ String bundleSymbolicName = constraint.getBundleSymbolicName();
+ if (bundleSymbolicName != null) {
+ query.addFilter("Bundle-SymbolicName", bundleSymbolicName);
+ }
+
+ VersionRange bundleVersionRange = constraint.getBundleVersionRange();
+ boolean looseBundleVersionRange = false;
+ if (bundleVersionRange != null && bundleVersionRange.getMaximum().equals(bundleVersionRange.getMinimum())) {
+ query.addFilter("Bundle-Version", bundleVersionRange.getMaximum().toString());
+ } else {
+ looseBundleVersionRange = bundleVersionRange != null;
+ }
+
+ Set<? extends ArtifactDescriptor> packageExportingArtefacts = query.run();
+
+ for (ArtifactDescriptor packageExportingArtefact : packageExportingArtefacts) {
+ if ((!loosePackageVersionRange || packageVersionInRange(packageExportingArtefact, packageVersionRange, constraint.getName()))
+ && (!looseBundleVersionRange || bundleVersionInRange(packageExportingArtefact, bundleVersionRange))) {
+ addBundle(packageExportingArtefact, state, constraintSatisfiers);
+ }
+ }
+ }
+
+ private boolean packageVersionInRange(ArtifactDescriptor packageExportingArtefact, VersionRange packageVersionRange, String packageName) {
+ for (Attribute attribute : packageExportingArtefact.getAttribute("Export-Package")) {
+ if (attribute.getValue().equals(packageName)) {
+ Set<String> versions = attribute.getProperties().get("version");
+ Version version = new org.osgi.framework.Version(versions == null || versions.isEmpty() ? "0" : versions.iterator().next());
+ if (packageVersionRange.isIncluded(version)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean bundleVersionInRange(ArtifactDescriptor packageExportingArtefact, VersionRange bundleVersionRange) {
+ return bundleVersionRange.isIncluded(packageExportingArtefact.getVersion());
+ }
+
+ private void addBundle(ArtifactDescriptor artefact, State state, List<BundleDescription> constraintSatisfiers) throws BundleException {
+ if (!isBundlePresentInState(artefact.getName(), artefact.getVersion(), state)) {
+ BundleDescription description = createBundleDescription(artefact, state);
+ constraintSatisfiers.add(description);
+ }
+ }
+
+ private boolean isBundlePresentInState(String bundleSymbolicName, Version version, State state) {
+ return state.getBundle(bundleSymbolicName, version) != null;
+ }
+
+ private BundleDescription createBundleDescription(ArtifactDescriptor artifact, State state) throws BundleException {
+ Dictionary<?, ?> manifest = BundleBridge.convertToDictionary(artifact);
+ try {
+ URI uri = artifact.getUri();
+ if ("file".equals(uri.getScheme())) {
+ return this.stateObjectFactory.createBundleDescription(state, manifest, new File(uri).getAbsolutePath(),
+ this.bundleId.getAndIncrement());
+ } else {
+ return this.stateObjectFactory.createBundleDescription(state, manifest, uri.toString(), this.bundleId.getAndIncrement());
+ }
+ } catch (RuntimeException e) {
+ throw new BundleException("Unable to read bundle at '" + artifact.getUri() + "'", e);
+ } catch (BundleException be) {
+ throw new BundleException("Failed to create BundleDescriptor for artifact at '" + artifact.getUri() + "'", be);
+ }
+ }
+
+ private VersionConstraint[] findUnsatisfiedConstraints(BundleDescription[] bundles, State state) {
+ return state.getStateHelper().getUnsatisfiedLeaves(bundles);
+ }
+
+ private void generateDump(State state) {
+ Map<String, Object> context = new HashMap<String, Object>();
+ context.put(ResolutionDumpContributor.RESOLUTION_STATE_KEY, state);
+ this.dumpGenerator.generateDump("resolutionFailure", context);
+ }
+
+ public long getNextBundleId() {
+ return this.bundleId.getAndIncrement();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/GenericQuasiResolutionFailure.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/GenericQuasiResolutionFailure.java
new file mode 100644
index 00000000..31995334
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/GenericQuasiResolutionFailure.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiResolutionFailure;
+
+class GenericQuasiResolutionFailure implements QuasiResolutionFailure {
+
+ private final String description;
+
+ private final QuasiBundle quasiBundle;
+
+ public GenericQuasiResolutionFailure(QuasiBundle quasiBundle, String description) {
+ this.description = description;
+ this.quasiBundle = quasiBundle;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getDescription() {
+ return this.description;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle getUnresolvedQuasiBundle() {
+ return this.quasiBundle;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/PackageQuasiResolutionFailure.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/PackageQuasiResolutionFailure.java
new file mode 100644
index 00000000..0990a826
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/PackageQuasiResolutionFailure.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiPackageResolutionFailure;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+/**
+ * <p>
+ * TODO Document PackageQuasiResolutionFailure
+ * </p>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * TODO Document concurrent semantics of PackageQuasiResolutionFailure
+ *
+ */
+class PackageQuasiResolutionFailure extends GenericQuasiResolutionFailure implements QuasiPackageResolutionFailure {
+
+ private final String bundleSymbolicName;
+
+ private final VersionRange bundleVersionRange;
+
+ private final String pkg;
+
+ private final VersionRange pkgVersionRange;
+
+ public PackageQuasiResolutionFailure(String description, QuasiBundle quasiBundle, String pkg,
+ VersionRange pkgVersionRange, String bundleSymbolicName, VersionRange bundleVersionRange) {
+ super(quasiBundle, description);
+ this.bundleSymbolicName = bundleSymbolicName;
+ this.bundleVersionRange = bundleVersionRange;
+ this.pkg = pkg;
+ this.pkgVersionRange = pkgVersionRange;
+ }
+
+ public String getPackageBundleSymbolicName() {
+ return this.bundleSymbolicName;
+ }
+
+ public VersionRange getPackageBundleVersionRange() {
+ return this.bundleVersionRange;
+ }
+
+ public String getPackage() {
+ return this.pkg;
+ }
+
+ public VersionRange getPackageVersionRange() {
+ return this.pkgVersionRange;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/PackageUsesQuasiResolutionFailure.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/PackageUsesQuasiResolutionFailure.java
new file mode 100644
index 00000000..403ef8c0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/PackageUsesQuasiResolutionFailure.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiPackageUsesResolutionFailure;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+/**
+ * <p>
+ * TODO Document PackageUsesQuasiResolutionFailure
+ * </p>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * TODO Document concurrent semantics of PackageUsesQuasiResolutionFailure
+ *
+ */
+class PackageUsesQuasiResolutionFailure extends PackageQuasiResolutionFailure implements QuasiPackageUsesResolutionFailure {
+
+ public PackageUsesQuasiResolutionFailure(String description, QuasiBundle quasiBundle, String pkg,
+ VersionRange pkgVersionRange, String bundleSymbolicName, VersionRange bundleVersionRange) {
+ super(description, quasiBundle, pkg, pkgVersionRange, bundleSymbolicName, bundleVersionRange);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/ResolutionFailureDetective.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/ResolutionFailureDetective.java
new file mode 100644
index 00000000..24aefe7a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/ResolutionFailureDetective.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ResolverError;
+import org.eclipse.osgi.service.resolver.State;
+
+/**
+ *
+ */
+public interface ResolutionFailureDetective {
+
+ String generateFailureDescription(State state, BundleDescription bundleDescription, ResolverErrorsHolder resolverErrors);
+
+ public static class ResolverErrorsHolder {
+
+ private ResolverError[] resolverErrors = null;
+
+ void setResolverErrors(ResolverError[] resolverErrors) {
+ this.resolverErrors = resolverErrors;
+ }
+
+ public ResolverError[] getResolverErrors() {
+ // Prevent findbugs complaining by returning a copy of this object's internal state.
+ ResolverError[] copy = new ResolverError[this.resolverErrors.length];
+ System.arraycopy(this.resolverErrors, 0, copy, 0, this.resolverErrors.length);
+ return copy;
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiBundle.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiBundle.java
new file mode 100644
index 00000000..b3e2f988
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiBundle.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.internal.core.BundleHost;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.HostSpecification;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.StateHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiExportPackage;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiImportPackage;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiRequiredBundle;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link StandardQuasiBundle} is the default implementation of {@link QuasiBundle}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class StandardQuasiBundle implements QuasiBundle {
+
+ private final BundleDescription bundleDescription;
+
+ private final BundleManifest bundleManifest;
+
+ private volatile Bundle bundle;
+
+ private final String bsn;
+
+ private Version bv;
+
+ private final StateHelper stateHelper;
+
+ /**
+ * Constructs an unresolved, uncommitted {@link QuasiBundle} with the given {@link BundleDescription}.
+ *
+ * @param bundleDescription the <code>BundleDescription</code> for this <code>QuasiBundle</code>
+ * @param bundleManifest
+ * @param stateHelper a {@link StateHelper} for analysing wiring
+ */
+ public StandardQuasiBundle(BundleDescription bundleDescription, BundleManifest bundleManifest, StateHelper stateHelper) {
+ this.bundleDescription = bundleDescription;
+ this.bundleManifest = bundleManifest;
+ this.bsn = bundleDescription.getSymbolicName();
+ this.bv = bundleDescription.getVersion();
+ this.stateHelper = stateHelper;
+ }
+
+ BundleDescription getBundleDescription() {
+ return this.bundleDescription;
+ }
+
+ BundleManifest getBundleManifest() {
+ return this.bundleManifest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getSymbolicName() {
+ return this.bundleDescription.getSymbolicName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Version getVersion() {
+ return this.bundleDescription.getVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolved() {
+ return this.bundleDescription.isResolved();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void uninstall() {
+ throw new UnsupportedOperationException("not implemented yet");
+ }
+
+ public void setBundle(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Bundle getBundle() {
+ return this.bundle;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "QuasiBundle(" + getSymbolicName() + ", " + getVersion() + ")";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getBundleId() {
+ return this.bundleDescription.getBundleId();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiBundle> getFragments() {
+ BundleDescription[] fragments = this.bundleDescription.getFragments();
+ return this.wrapBundleDescriptions(fragments);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiBundle> getHosts() {
+ HostSpecification hostSpecification = this.bundleDescription.getHost();
+ return hostSpecification == null ? null : this.wrapBundleDescriptions(hostSpecification.getHosts());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiExportPackage> getExportPackages() {
+ ExportPackageDescription[] exportPackages = this.bundleDescription.getExportPackages();
+ return this.wrapExportPackageDescriptions(exportPackages);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiImportPackage> getImportPackages() {
+ return this.wrapImportPackageSpecifications(this.bundleDescription.getImportPackages());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiRequiredBundle> getRequiredBundles() {
+ return this.wrapBundleSpecificationsAsRequiredBundles(this.bundleDescription.getRequiredBundles());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiBundle> getDependents() {
+ BundleDescription[] dependents = this.bundleDescription.getDependents();
+ return this.wrapBundleDescriptions(dependents);
+ }
+
+ /**
+ * Utility method to wrap a list of {@link BundleDescription} in QuasiBundle.
+ *
+ * @param bundleDescriptions
+ * @return
+ */
+ private List<QuasiBundle> wrapBundleDescriptions(BundleDescription[] bundleDescriptions) {
+ List<QuasiBundle> quasiBundles = new ArrayList<QuasiBundle>();
+ for (BundleDescription bundleDescription : bundleDescriptions) {
+ quasiBundles.add(new StandardQuasiBundle(bundleDescription, null, this.stateHelper));
+ }
+ return Collections.unmodifiableList(quasiBundles);
+ }
+
+ /**
+ * Utility method to wrap a list of {@link BundleDescription} in QuasiBundle.
+ *
+ * @param bundleDescriptions
+ * @return
+ */
+ private List<QuasiRequiredBundle> wrapBundleSpecificationsAsRequiredBundles(BundleSpecification[] bundleDescriptions) {
+ List<QuasiRequiredBundle> quasiRequiredBundles = new ArrayList<QuasiRequiredBundle>();
+ for (BundleSpecification bundleSpecification : bundleDescriptions) {
+ quasiRequiredBundles.add(new StandardQuasiRequiredBundle(bundleSpecification, this));
+ }
+ return Collections.unmodifiableList(quasiRequiredBundles);
+ }
+
+ /**
+ * Utility method to wrap a list of {@link BundleDescription} in {@link QuasiExportPackage}.
+ *
+ * @param exportPackageDescriptions
+ * @param quasiBundle
+ * @return
+ */
+ private List<QuasiExportPackage> wrapExportPackageDescriptions(ExportPackageDescription[] exportPackageDescriptions) {
+ List<QuasiExportPackage> quasiExportPackages = new ArrayList<QuasiExportPackage>();
+ for (ExportPackageDescription exportPackageDescription : exportPackageDescriptions) {
+ quasiExportPackages.add(new StandardQuasiExportPackage(exportPackageDescription, this));
+ }
+ return Collections.unmodifiableList(quasiExportPackages);
+ }
+
+ /**
+ * Utility method to wrap a list of {@link BundleDescription} in {@link QuasiImportPackage}.
+ *
+ * @param bundleDescriptions
+ * @return
+ */
+ private List<QuasiImportPackage> wrapImportPackageSpecifications(ImportPackageSpecification[] importPackageSpecifications) {
+ List<QuasiImportPackage> quasiImportPackages = new ArrayList<QuasiImportPackage>();
+ for (ImportPackageSpecification importPackageSpecification : importPackageSpecifications) {
+ quasiImportPackages.add(new StandardQuasiImportPackage(importPackageSpecification, this));
+ }
+ return Collections.unmodifiableList(quasiImportPackages);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((bsn == null) ? 0 : bsn.hashCode());
+ result = prime * result + ((bv == null) ? 0 : bv.hashCode());
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ StandardQuasiBundle other = (StandardQuasiBundle) obj;
+ if (bsn == null) {
+ if (other.bsn != null)
+ return false;
+ } else if (!bsn.equals(other.bsn))
+ return false;
+ if (bv == null) {
+ if (other.bv != null)
+ return false;
+ } else if (!bv.equals(other.bv))
+ return false;
+ return true;
+ }
+
+ public StateHelper getStateHelper() {
+ return this.stateHelper;
+ }
+
+ public File getBundleFile() {
+ if (bundle instanceof BundleHost) {
+ BundleHost bh = (BundleHost) bundle;
+ BundleData bundleData = bh.getBundleData();
+ if (bundleData instanceof BaseData) {
+ File file = ((BaseData) bundleData).getBundleFile().getBaseFile();
+ return file;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiExportPackage.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiExportPackage.java
new file mode 100644
index 00000000..5a489c12
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiExportPackage.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.StateHelper;
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiExportPackage;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiImportPackage;
+
+/**
+ * {@link StandardQuasiExportPackage} is the default implementation of {@link QuasiExportPackage}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class StandardQuasiExportPackage extends StandardQuasiParameterised implements QuasiExportPackage {
+
+ private final ExportPackageDescription exportPackageDescription;
+
+ private final QuasiBundle exporter;
+
+ public StandardQuasiExportPackage(ExportPackageDescription exportPackageDescription, QuasiBundle exporter) {
+ super(exportPackageDescription);
+ this.exportPackageDescription = exportPackageDescription;
+ this.exporter = exporter;
+ }
+
+ private StateHelper getStateHelper() {
+ return ((StandardQuasiBundle) exporter).getStateHelper();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getPackageName() {
+ return this.exportPackageDescription.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Version getVersion() {
+ return this.exportPackageDescription.getVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle getExportingBundle() {
+ return this.exporter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiImportPackage> getConsumers() {
+ List<QuasiImportPackage> consumers = new ArrayList<QuasiImportPackage>();
+ StateHelper stateHelper = getStateHelper();
+ for (BundleDescription dependentBundle : getDependentBundles(stateHelper)) {
+ if (isConsumer(dependentBundle)) {
+ addConsumer(dependentBundle, consumers, stateHelper);
+ }
+ }
+ return consumers;
+ }
+
+ private BundleDescription[] getDependentBundles(StateHelper stateHelper) {
+ return stateHelper.getDependentBundles(new BundleDescription[] { this.exportPackageDescription.getExporter() });
+ }
+
+ private boolean isConsumer(BundleDescription dependentBundle) {
+ ImportPackageSpecification[] importedPackages = dependentBundle.getImportPackages();
+
+ for (ImportPackageSpecification importedPackage : importedPackages) {
+ if (this.exportPackageDescription.equals(importedPackage.getSupplier())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void addConsumer(BundleDescription dependentBundle, List<QuasiImportPackage> consumers, StateHelper stateHelper) {
+ ImportPackageSpecification[] dependentImportPackages = dependentBundle.getImportPackages();
+ for (ImportPackageSpecification dependentImportPackage : dependentImportPackages) {
+ if (matches(dependentImportPackage, this.exportPackageDescription)) {
+ consumers.add(new StandardQuasiImportPackage(dependentImportPackage, new StandardQuasiBundle(dependentBundle, null,
+ stateHelper)));
+ break;
+ }
+ }
+ }
+
+ private boolean matches(ImportPackageSpecification i, ExportPackageDescription e) {
+ return i.isSatisfiedBy(e);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "StandardQuasiExportPackage(" + getPackageName() + ", " + getVersion().toString() + ", " + super.toString() +")";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((exportPackageDescription == null) ? 0 : exportPackageDescription.hashCode());
+ result = prime * result + ((exporter == null) ? 0 : exporter.hashCode());
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ StandardQuasiExportPackage other = (StandardQuasiExportPackage) obj;
+ if (exportPackageDescription == null) {
+ if (other.exportPackageDescription != null)
+ return false;
+ } else if (!exportPackageDescription.equals(other.exportPackageDescription))
+ return false;
+ if (exporter == null) {
+ if (other.exporter != null)
+ return false;
+ } else if (!exporter.equals(other.exporter))
+ return false;
+ return true;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiFramework.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiFramework.java
new file mode 100644
index 00000000..edb8fe90
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiFramework.java
@@ -0,0 +1,415 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.ResolverError;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.osgi.service.resolver.StateHelper;
+import org.eclipse.osgi.service.resolver.StateObjectFactory;
+import org.eclipse.osgi.service.resolver.VersionConstraint;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.osgi.framework.ManifestTransformer;
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyDependenciesException;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiFramework;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiResolutionFailure;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.TransformedManifestProvidingBundleFileWrapper;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.ResolutionFailureDetective.ResolverErrorsHolder;
+import org.eclipse.virgo.repository.Repository;
+import org.eclipse.virgo.util.common.StringUtils;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link StandardQuasiFramework} is the default implementation of {@link QuasiFramework}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class StandardQuasiFramework implements QuasiFramework {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final Object monitor = new Object();
+
+ private final State state;
+
+ private final StateObjectFactory stateObjectFactory;
+
+ /*
+ * Track the bundles which are explicitly installed. These are input to the resolve method.
+ */
+ private final List<StandardQuasiBundle> installedQuasiBundles = new ArrayList<StandardQuasiBundle>();
+
+ private volatile BundleDescription[] otherBundles;
+
+ private final ResolutionFailureDetective detective;
+
+ private final BundleContext bundleContext;
+
+ private final DependencyCalculator dependencyCalculator;
+
+ private final StateHelper stateHelper;
+
+ private final TransformedManifestProvidingBundleFileWrapper bundleTransformationHandler;
+
+ StandardQuasiFramework(BundleContext bundleContext, State state, PlatformAdmin platformAdmin, ResolutionFailureDetective detective, Repository repository, TransformedManifestProvidingBundleFileWrapper bundleTransformationHandler) {
+ this.bundleContext = bundleContext;
+ this.state = state;
+ this.stateObjectFactory = platformAdmin.getFactory();
+ this.detective = detective;
+ this.dependencyCalculator = new DependencyCalculator(platformAdmin.getFactory(), this.detective, repository, this.bundleContext);
+ this.stateHelper = platformAdmin.getStateHelper();
+ this.bundleTransformationHandler = bundleTransformationHandler;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle install(URI location, BundleManifest bundleManifest) throws BundleException {
+ synchronized (this.monitor) {
+ StandardQuasiBundle qb = doInstall(location, bundleManifest);
+ this.installedQuasiBundles.add(qb);
+ return qb;
+ }
+ }
+
+ private StandardQuasiBundle doInstall(URI location, BundleManifest bundleManifest) throws BundleException {
+ try {
+ Dictionary<String, String> manifest = bundleManifest.toDictionary();
+ String installLocation = "file".equals(location.getScheme()) ? new File(location).getAbsolutePath() : location.toString();
+ BundleDescription bundleDescription = this.stateObjectFactory.createBundleDescription(this.state, manifest, installLocation,
+ nextBundleId());
+ this.state.addBundle(bundleDescription);
+ return new StandardQuasiBundle(bundleDescription, bundleManifest, this.stateHelper);
+ } catch (RuntimeException e) {
+ throw new BundleException("Unable to read bundle at '" + location + "'", e);
+ }
+ }
+
+ /**
+ * @return
+ */
+ private long nextBundleId() {
+ return this.dependencyCalculator.getNextBundleId();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiBundle> getBundles() {
+ BundleDescription[] bundleDescriptions = this.state.getBundles();
+ List<QuasiBundle> result = new ArrayList<QuasiBundle>();
+ QuasiBundle quasiBundle;
+ for (BundleDescription bundleDescription : bundleDescriptions) {
+ quasiBundle = new StandardQuasiBundle(bundleDescription, null, this.stateHelper);
+ result.add(quasiBundle);
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle getBundle(long bundleId) {
+ QuasiBundle quasiBundle = null;
+ BundleDescription bundleDescription = this.state.getBundle(bundleId);
+ if (bundleDescription!= null) {
+ quasiBundle = new StandardQuasiBundle(bundleDescription, null, this.stateHelper);
+ }
+ return quasiBundle;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle getBundle(String name, Version version) {
+ QuasiBundle quasiBundle = null;
+ BundleDescription bundleDescription = this.state.getBundle(name, version);
+ if (bundleDescription!= null) {
+ quasiBundle = new StandardQuasiBundle(bundleDescription, null, this.stateHelper);
+ }
+ return quasiBundle;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiResolutionFailure> resolve() {
+ synchronized (this.monitor) {
+ BundleDescription[] bundles = getBundleDescriptionArray();
+ BundleDescription[] dependencies = getDependencies(bundles);
+
+ this.otherBundles = dependencies;
+
+ List<QuasiResolutionFailure> failures = getFailures();
+ if (!failures.isEmpty()) {
+ this.otherBundles = null;
+ }
+
+ return failures;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<QuasiResolutionFailure> diagnose(long bundleId) {
+ BundleDescription bundleDescription = this.state.getBundle(bundleId);
+ ResolverErrorsHolder reh = new ResolverErrorsHolder();
+ String failureDescription = this.detective.generateFailureDescription(this.state, bundleDescription, reh);
+ return this.processResolverErrors(reh.getResolverErrors(), new StandardQuasiBundle(bundleDescription, null, this.stateHelper), failureDescription);
+ }
+
+ private BundleDescription[] getDependencies(BundleDescription[] bundles) {
+ try {
+ return this.dependencyCalculator.calculateDependencies(this.state, bundles);
+ } catch (BundleException e) {
+ return new BundleDescription[0];
+ } catch (UnableToSatisfyDependenciesException utsde) {
+ return new BundleDescription[0];
+ }
+ }
+
+ private List<QuasiResolutionFailure> getFailures() {
+ List<QuasiResolutionFailure> failures = new ArrayList<QuasiResolutionFailure>();
+ ResolverErrorsHolder reh;
+ String failureDescription;
+ for (StandardQuasiBundle quasiBundle : this.installedQuasiBundles) {
+ if (!quasiBundle.isResolved()) {
+ reh = new ResolverErrorsHolder();
+ failureDescription = this.detective.generateFailureDescription(this.state, quasiBundle.getBundleDescription(), reh);
+ failures.addAll(this.processResolverErrors(reh.getResolverErrors(), quasiBundle, failureDescription));
+ }
+ }
+ return failures;
+ }
+
+ private List<QuasiResolutionFailure> processResolverErrors(ResolverError[] resolverErrors, QuasiBundle quasiBundle, String failureDescription){
+ List<QuasiResolutionFailure> processedResolverErrors = new ArrayList<QuasiResolutionFailure>();
+ boolean added = false;
+ if (resolverErrors != null) {
+ for (ResolverError resolverError : resolverErrors) {
+ if (resolverError.getType() == ResolverError.IMPORT_PACKAGE_USES_CONFLICT) {
+ VersionConstraint unsatisfiedConstraint = resolverError.getUnsatisfiedConstraint();
+ if (unsatisfiedConstraint instanceof ImportPackageSpecification) {
+ processedResolverErrors.add(createPackagesUsesResolutionFailure(quasiBundle, failureDescription, unsatisfiedConstraint));
+ added = true;
+ }
+ } else if (resolverError.getType() == ResolverError.MISSING_IMPORT_PACKAGE) {
+ VersionConstraint unsatisfiedConstraint = resolverError.getUnsatisfiedConstraint();
+ if (unsatisfiedConstraint instanceof ImportPackageSpecification) {
+ processedResolverErrors.add(createPackageResolutionFailure(quasiBundle, failureDescription, unsatisfiedConstraint));
+ added = true;
+ }
+ }
+ }
+ }
+ if (!added) {
+ processedResolverErrors.add(new GenericQuasiResolutionFailure(quasiBundle, failureDescription));
+ }
+ return processedResolverErrors;
+ }
+
+ private PackageQuasiResolutionFailure createPackageResolutionFailure(QuasiBundle quasiBundle, String failureDescription, VersionConstraint unsatisfiedConstraint) {
+ ImportPackageSpecification importPackageSpecification = (ImportPackageSpecification) unsatisfiedConstraint;
+ String pkgName = importPackageSpecification.getName();
+ VersionRange pkgVersionRange = convertVersionRange(importPackageSpecification.getVersionRange());
+ String bundleSymbolicName = importPackageSpecification.getBundleSymbolicName();
+ VersionRange bundleVersionRange = convertVersionRange(importPackageSpecification.getBundleVersionRange());
+ this.logger.debug("Missing import: package '{}' version '{}' bundle '{}' version '{}'", new Object[] { pkgName, pkgVersionRange,
+ bundleSymbolicName, bundleVersionRange });
+ return new PackageQuasiResolutionFailure(failureDescription, quasiBundle, pkgName, pkgVersionRange,
+ bundleSymbolicName, bundleVersionRange);
+ }
+
+ private PackageUsesQuasiResolutionFailure createPackagesUsesResolutionFailure(QuasiBundle quasiBundle, String failureDescription,
+ VersionConstraint unsatisfiedConstraint) {
+ ImportPackageSpecification importPackageSpecification = (ImportPackageSpecification) unsatisfiedConstraint;
+ String pkgName = importPackageSpecification.getName();
+ VersionRange pkgVersionRange = convertVersionRange(importPackageSpecification.getVersionRange());
+ String bundleSymbolicName = importPackageSpecification.getBundleSymbolicName();
+ VersionRange bundleVersionRange = convertVersionRange(importPackageSpecification.getBundleVersionRange());
+ this.logger.debug("Uses conflict: package '{}' version '{}' bundle '{}' version '{}'", new Object[] { pkgName, pkgVersionRange,
+ bundleSymbolicName, bundleVersionRange });
+ return new PackageUsesQuasiResolutionFailure(failureDescription, quasiBundle, pkgName,
+ pkgVersionRange, bundleSymbolicName, bundleVersionRange);
+ }
+
+ private static VersionRange convertVersionRange(org.eclipse.osgi.service.resolver.VersionRange versionRange) {
+ return new VersionRange(versionRange.toString());
+ }
+
+ private BundleDescription[] getBundleDescriptionArray() {
+ BundleDescription[] bd;
+ int n = this.installedQuasiBundles.size();
+ bd = new BundleDescription[n];
+ for (int i = 0; i < n; i++) {
+ bd[i] = this.installedQuasiBundles.get(i).getBundleDescription();
+ }
+ return bd;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void commit() throws BundleException {
+ synchronized (this.monitor) {
+ if (this.otherBundles == null) {
+ List<QuasiResolutionFailure> failures = resolve();
+ if (!failures.isEmpty()) {
+ throw new BundleException("Commit resolution failed: '" + failures.toString() + "'");
+ }
+ } else {
+ try {
+ Set<Long> installedQuasiBundles = installQuasiBundles();
+ List<Bundle> installedDependencies = installOtherBundles(installedQuasiBundles);
+ startBundles(installedDependencies);
+ } catch (BundleException e) {
+ uninstallQuasiBundles();
+ throw e;
+ }
+ }
+ }
+ }
+
+ private void startBundles(List<Bundle> bundles) throws BundleException {
+ for (Bundle bundle : bundles) {
+ startBundle(bundle);
+ }
+ }
+
+ private void startBundle(Bundle bundle) throws BundleException {
+ if (!isFragmentBundle(bundle)) {
+ String bundleActivationPolicy = (String) bundle.getHeaders().get(Constants.BUNDLE_ACTIVATIONPOLICY);
+ if (bundleActivationPolicy == null) {
+ try {
+ bundle.start();
+ } catch (BundleException be) {
+ throw new BundleException("Failed to start bundle '" + bundle.getSymbolicName() + "' version '" + bundle.getVersion() + "'", be);
+ }
+ }
+ }
+ }
+
+
+ // TODO Move this method into utils project
+ private static boolean isFragmentBundle(Bundle bundle) {
+ String fragmentHostHeader = (String) bundle.getHeaders().get(Constants.FRAGMENT_HOST);
+ return StringUtils.hasText(fragmentHostHeader);
+ }
+
+ private List<Bundle> installOtherBundles(Set<Long> installedQuasiBundles) throws BundleException {
+ List<Bundle> installedBundles = new ArrayList<Bundle>();
+ for (BundleDescription otherBundle : otherBundles) {
+ if (!installedQuasiBundles.contains(otherBundle.getBundleId())) {
+ try {
+ Bundle bundle = installBundleDescription(otherBundle);
+ installedBundles.add(bundle);
+ } catch (BundleException e) {
+ for (Bundle bundle : installedBundles) {
+ try {
+ bundle.uninstall();
+ } catch (BundleException be) {
+ this.logger.error("Uninstall of '{}' failed", be, bundle);
+ }
+ }
+ throw e;
+ }
+ }
+ }
+ return installedBundles;
+ }
+
+ private Set<Long> installQuasiBundles() throws BundleException {
+ Set<Long> installed = new HashSet<Long>();
+ for (StandardQuasiBundle quasiBundle : this.installedQuasiBundles) {
+ BundleDescription description = quasiBundle.getBundleDescription();
+ String location = description.getLocation();
+ ManifestTransformer manifestTransformer = new QuasiManifestTransformer(quasiBundle.getBundleManifest());
+ this.bundleTransformationHandler.pushManifestTransformer(manifestTransformer);
+
+ try {
+ URI locationUri = new File(location).toURI();
+ Bundle bundle = this.bundleContext.installBundle(locationUri.toString());
+ quasiBundle.setBundle(bundle);
+ installed.add(description.getBundleId());
+ } finally {
+ this.bundleTransformationHandler.popManifestTransformer();
+ }
+
+ }
+ return installed;
+ }
+
+ private static final class QuasiManifestTransformer implements ManifestTransformer {
+
+ private final BundleManifest bundleManifest;
+
+ public QuasiManifestTransformer(BundleManifest bundleManifest) {
+ this.bundleManifest = bundleManifest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleManifest transform(BundleManifest bundleManifest) {
+ return this.bundleManifest;
+ }
+ }
+
+ private Bundle installBundleDescription(BundleDescription description) throws BundleException {
+ String location = description.getLocation();
+ String installLocation = location.startsWith("http:") ? location : new File(location).toURI().toString();
+ return doInstallBundleInternal(installLocation);
+ }
+
+ private Bundle doInstallBundleInternal(String location) throws BundleException {
+ return this.bundleContext.installBundle(location);
+ }
+
+ private void uninstallQuasiBundles() {
+ for (StandardQuasiBundle quasiBundle : this.installedQuasiBundles) {
+ Bundle bundle = quasiBundle.getBundle();
+ if (bundle != null) {
+ try {
+ bundle.uninstall();
+ } catch (BundleException e) {
+ this.logger.error("Uninstall of '{}' failed", e, quasiBundle);
+ }
+ quasiBundle.setBundle(null);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiFrameworkFactory.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiFrameworkFactory.java
new file mode 100644
index 00000000..14b803ac
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiFrameworkFactory.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.osgi.internal.baseadaptor.StateManager;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.osgi.service.resolver.StateObjectFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkUtils;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiServiceHolder;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiFramework;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiFrameworkFactory;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.TransformedManifestProvidingBundleFileWrapper;
+import org.eclipse.virgo.repository.Repository;
+
+/**
+ * {@link StandardQuasiFrameworkFactory} is the default implementation of {@link QuasiFrameworkFactory}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public final class StandardQuasiFrameworkFactory implements QuasiFrameworkFactory {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final BundleContext bundleContext;
+
+ private final PlatformAdmin platformAdmin;
+
+ private final StateManager stateManager;
+
+ private ResolutionFailureDetective detective;
+
+ private final Repository repository;
+
+ private final TransformedManifestProvidingBundleFileWrapper bundleTransformationHandler;
+
+ public StandardQuasiFrameworkFactory(BundleContext bundleContext, ResolutionFailureDetective detective, Repository repository, TransformedManifestProvidingBundleFileWrapper bundleTransformationHandler) {
+ this.bundleContext = bundleContext;
+ this.platformAdmin = getPlatformAdminService(bundleContext);
+ this.detective = detective;
+ this.repository = repository;
+ ServiceReference platformAdminServiceReference = bundleContext.getServiceReference(PlatformAdmin.class.getName());
+ this.stateManager = (StateManager) bundleContext.getService(platformAdminServiceReference);
+ this.bundleTransformationHandler = bundleTransformationHandler;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiFramework create() {
+ return new StandardQuasiFramework(this.bundleContext, createState(), this.platformAdmin, this.detective, this.repository, this.bundleTransformationHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiFramework create(File stateDump) {
+ return new StandardQuasiFramework(this.bundleContext, readStateDump(stateDump), this.platformAdmin, this.detective, this.repository, this.bundleTransformationHandler);
+ }
+
+ @SuppressWarnings("deprecation")
+ private State createState() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ State state;
+
+ try {
+ this.platformAdmin.getFactory().writeState(this.stateManager.getSystemState(), baos);
+ state = this.platformAdmin.getFactory().readState(new ByteArrayInputStream(baos.toByteArray()));
+ } catch (IOException ioe) {
+ throw new RuntimeException("Failed to create a copy of the OSGi state", ioe);
+ }
+
+ if (state.getResolver() == null) {
+ state.setResolver(this.platformAdmin.createResolver());
+ }
+
+ if (!state.isResolved()) {
+ state.resolve(true);
+ }
+
+ return state;
+ }
+
+ private State readStateDump(File outdir) {
+ State state = null;
+
+ try {
+ StateObjectFactory sof = this.platformAdmin.getFactory();
+ state = sof.readState(outdir);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to read resolver state", e);
+ } finally {
+ try { // delete all state files written to this directory
+ String[] files = outdir.list();
+ if (files != null) {
+ for (int i = 0; i < files.length; ++i) {
+ File file = new File(outdir, files[i]);
+ if (!file.delete()) {
+ this.logger.warn("Temporary file '{}' not deleted", file.getAbsolutePath());
+ }
+ }
+ }
+ } finally {
+ if (!outdir.delete() && outdir.exists()) {
+ this.logger.warn("Temporary state directory '{}' was not removed after use.", outdir.getAbsolutePath());
+ }
+ }
+ }
+
+ if (state.getResolver() == null) {
+ state.setResolver(this.platformAdmin.createResolver());
+ }
+
+ if (!state.isResolved()) {
+ state.resolve(true);
+ }
+
+ return state;
+ }
+
+ /**
+ * Gets the {@link PlatformAdmin} service.
+ *
+ * @param bundleContext the {@link BundleContext} to use for lookup.
+ * @return the <code>PlatformAdmin</code> service.
+ */
+ private static PlatformAdmin getPlatformAdminService(BundleContext bundleContext) {
+ OsgiServiceHolder<PlatformAdmin> service = OsgiFrameworkUtils.getService(bundleContext, PlatformAdmin.class);
+ PlatformAdmin platformAdmin = service.getService();
+ if (platformAdmin == null) {
+ throw new IllegalStateException("Unable to locate PlatformAdmin service");
+ }
+ return platformAdmin;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiImportPackage.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiImportPackage.java
new file mode 100644
index 00000000..1b3e1217
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiImportPackage.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import static org.osgi.framework.Constants.RESOLUTION_DIRECTIVE;
+import static org.osgi.framework.Constants.RESOLUTION_MANDATORY;
+import static org.osgi.framework.Constants.RESOLUTION_OPTIONAL;
+
+import org.eclipse.osgi.service.resolver.BaseDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.StateHelper;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiExportPackage;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiImportPackage;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+/**
+ * {@link StandardQuasiImportPackage} is the default implementation of {@link QuasiImportPackage}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class StandardQuasiImportPackage extends StandardQuasiParameterised implements QuasiImportPackage {
+
+ private final ImportPackageSpecification importPackageSpecification;
+
+ private final QuasiBundle importingBundle;
+
+ public StandardQuasiImportPackage(ImportPackageSpecification importPackageSpecification, QuasiBundle importingBundle) {
+ super(importPackageSpecification);
+ this.importPackageSpecification = importPackageSpecification;
+ this.importingBundle = importingBundle;
+ }
+
+ private StateHelper getStateHelper() {
+ return ((StandardQuasiBundle) importingBundle).getStateHelper();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getPackageName() {
+ return this.importPackageSpecification.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VersionRange getVersionConstraint() {
+ org.eclipse.osgi.service.resolver.VersionRange resolverVersionRange = this.importPackageSpecification.getVersionRange();
+ VersionRange versionRange;
+ if (resolverVersionRange == null) {
+ versionRange = new VersionRange(null); // The range of all possible versions
+ } else {
+ versionRange = new VersionRange(resolverVersionRange.toString());
+ }
+ return versionRange;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle getImportingBundle() {
+ return this.importingBundle;
+ }
+
+ public boolean isOptional() {
+ Object resolutionObject = this.importPackageSpecification.getDirective(RESOLUTION_DIRECTIVE);
+ if (resolutionObject == null) {
+ resolutionObject = RESOLUTION_MANDATORY;
+ }
+ return resolutionObject.equals(RESOLUTION_OPTIONAL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiExportPackage getProvider() {
+ QuasiExportPackage provider = null;
+ if (isResolved()) {
+ ExportPackageDescription providerDescription = getProviderDescription();
+ if (providerDescription != null) {
+ provider = constructProvider(providerDescription);
+ }
+ }
+ return provider;
+ }
+
+ private QuasiExportPackage constructProvider(ExportPackageDescription providerDescription) {
+ StandardQuasiBundle quasiExporter = new StandardQuasiBundle(providerDescription.getExporter(), null, getStateHelper());
+ return new StandardQuasiExportPackage(providerDescription, quasiExporter);
+ }
+
+ private ExportPackageDescription getProviderDescription() {
+ BaseDescription supplier = this.importPackageSpecification.getSupplier();
+ if (supplier instanceof ExportPackageDescription) {
+ return (ExportPackageDescription) supplier;
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolved() {
+ return this.importPackageSpecification.isResolved();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "StandardQuasiImportPackage(" + getPackageName() + ", " + getVersionConstraint().toString() + ", " + super.toString() + ")";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((importPackageSpecification == null) ? 0 : importPackageSpecification.hashCode());
+ result = prime * result + ((importingBundle == null) ? 0 : importingBundle.hashCode());
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ StandardQuasiImportPackage other = (StandardQuasiImportPackage) obj;
+ if (importPackageSpecification == null) {
+ if (other.importPackageSpecification != null)
+ return false;
+ } else if (!importPackageSpecification.equals(other.importPackageSpecification))
+ return false;
+ if (importingBundle == null) {
+ if (other.importingBundle != null)
+ return false;
+ } else if (!importingBundle.equals(other.importingBundle))
+ return false;
+ return true;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiParameterised.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiParameterised.java
new file mode 100644
index 00000000..9dd2224d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiParameterised.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.VersionRange;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiParameterised;
+import org.eclipse.virgo.util.common.StringUtils;
+
+/**
+ * {@link StandardQuasiParameterised} is the default implementation of {@link QuasiParameterised}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class StandardQuasiParameterised implements QuasiParameterised {
+
+ private static final String VERSION_ATTRIBUTE = "version";
+
+ private static final String BUNDLE_SYMBOLIC_NAME_ATTRIBUTE = "bundle-symbolic-name";
+
+ private static final String BUNDLE_VERSION_ATTRIBUTE = "bundle-version";
+
+ private final Map<String, Object> attributes;
+
+ private final Map<String, Object> directives;
+
+
+ /**
+ * Will specifically add attributes for version, bundle-version
+ *
+ * @param importPackageSpecification
+ */
+ @SuppressWarnings("unchecked")
+ public StandardQuasiParameterised(ImportPackageSpecification importPackageSpecification) {
+ this.attributes = normalise(importPackageSpecification.getAttributes());
+ this.directives = normalise(importPackageSpecification.getDirectives());
+ VersionRange bundleVersionRange = importPackageSpecification.getBundleVersionRange();
+ if(bundleVersionRange != null) {
+ this.attributes.put(BUNDLE_VERSION_ATTRIBUTE, bundleVersionRange);
+ }
+ String bundleSymbolicName = importPackageSpecification.getBundleSymbolicName();
+ if(StringUtils.hasLength(bundleSymbolicName)) {
+ this.attributes.put(BUNDLE_SYMBOLIC_NAME_ATTRIBUTE, bundleSymbolicName);
+ }
+ VersionRange versionRange = importPackageSpecification.getVersionRange();
+ if(versionRange != null) {
+ this.attributes.put(VERSION_ATTRIBUTE, versionRange);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public StandardQuasiParameterised(ExportPackageDescription exportPackageDescription) {
+ this.attributes = normalise(exportPackageDescription.getAttributes());
+ this.directives = normalise(exportPackageDescription.getDirectives());
+ }
+
+ /**
+ * Coerce the given map into an immutable map of String to String.
+ */
+ private static Map<String, Object> normalise(final Map<String, Object> map) {
+ if (map!=null) {
+ return new HashMap<String, Object>(map);
+ }
+ return new HashMap<String, Object>();
+ }
+
+ public Map<String, Object> getAttributes() {
+ return this.attributes;
+ }
+
+ public Map<String, Object> getDirectives() {
+ return this.directives;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("[");
+ boolean first = true;
+ if (this.directives != null) {
+ for (String directiveName : this.directives.keySet()) {
+ if (!first) {
+ result.append(", ");
+ }
+ first = false;
+ result.append(directiveName);
+ result.append(":=");
+ result.append(this.directives.get(directiveName));
+ }
+ }
+ if (this.attributes != null) {
+ for (String attributeName : this.attributes.keySet()) {
+ if (!first) {
+ result.append(", ");
+ }
+ first = false;
+ result.append(attributeName);
+ result.append("=");
+ result.append(this.attributes.get(attributeName));
+ }
+ }
+ result.append("]");
+ return result.toString();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiRequiredBundle.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiRequiredBundle.java
new file mode 100644
index 00000000..60b4fe3e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiRequiredBundle.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.StateHelper;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiRequiredBundle;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+/**
+ * {@link StandardQuasiRequiredBundle} is the default implementation of {@link QuasiRequiredBundle}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class StandardQuasiRequiredBundle implements QuasiRequiredBundle {
+
+ private final BundleSpecification bundleSpecification;
+
+ private final QuasiBundle requiringBundle;
+
+ public StandardQuasiRequiredBundle(BundleSpecification bundleSpecification, QuasiBundle requiringBundle) {
+ this.bundleSpecification = bundleSpecification;
+ this.requiringBundle = requiringBundle;
+ }
+
+ private StateHelper getStateHelper() {
+ return ((StandardQuasiBundle) requiringBundle).getStateHelper();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getRequiredBundleName() {
+ return this.bundleSpecification.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VersionRange getVersionConstraint() {
+ org.eclipse.osgi.service.resolver.VersionRange resolverVersionRange = this.bundleSpecification.getVersionRange();
+ VersionRange versionRange;
+ if (resolverVersionRange == null) {
+ versionRange = new VersionRange(null); // The range of all possible versions
+ } else {
+ versionRange = new VersionRange(resolverVersionRange.toString());
+ }
+ return versionRange;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle getRequiringBundle() {
+ return this.requiringBundle;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle getProvider() {
+ if (isResolved()) {
+ return new StandardQuasiBundle(this.bundleSpecification.getSupplier().getSupplier(), null, getStateHelper());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolved() {
+ return this.bundleSpecification.isResolved();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, Object> getAttributes() {
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ return Collections.unmodifiableMap(attributes);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, Object> getDirectives() {
+ Map<String, Object> directives = new HashMap<String, Object>();
+ return Collections.unmodifiableMap(directives);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardResolutionFailureDetective.java b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardResolutionFailureDetective.java
new file mode 100644
index 00000000..638b4eda
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardResolutionFailureDetective.java
@@ -0,0 +1,392 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.resolver.ResolverError;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.osgi.service.resolver.VersionConstraint;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.UsesAnalyser;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.UsesAnalyser.AnalysedUsesConflict;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+
+/**
+ * Helper class that analyses resolution failures and generates a human-readable failure description.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br/>
+ *
+ * Threadsafe.
+ *
+ */
+public final class StandardResolutionFailureDetective implements ResolutionFailureDetective {
+
+ private final UsesAnalyser usesAnalyser = new UsesAnalyser();
+
+ private final PlatformAdmin platformAdmin;
+
+ /**
+ * Constructor for a new {@link ResolutionFailureDetective}.
+ *
+ * @param platformAdmin the {@link org.eclipse.osgi.launch.Equinox Equinox} {@link PlatformAdmin} service.
+ */
+ public StandardResolutionFailureDetective(PlatformAdmin platformAdmin) {
+ this.platformAdmin = platformAdmin;
+ }
+
+ /**
+ * Generates a description of all the resolver errors for the supplied {@link Bundle} in the supplied {@link State}.
+ */
+ public String generateFailureDescription(State state, BundleDescription bundleDescription, ResolverErrorsHolder resolverErrorsHolder) {
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("Cannot resolve: ").append(bundleDescription.getSymbolicName()).append("\n");
+
+ // these resolver errors are for all unresolved bundles in the state:
+ ResolverError[] resolverErrors = gatherResolverErrors(bundleDescription, state);
+ resolverErrorsHolder.setResolverErrors(resolverErrors);
+ if (resolverErrors.length > 0) {
+ indent(sb, 1);
+ sb.append("Resolver report:\n");
+
+ for (ResolverError resolverError : resolverErrors) {
+ indent(sb, 2);
+ formatResolverError(resolverError, sb, state);
+ sb.append("\n");
+ }
+ } else {
+ VersionConstraint[] unsatisfiedLeaves = this.platformAdmin.getStateHelper().getUnsatisfiedLeaves(
+ new BundleDescription[] { bundleDescription });
+ if (unsatisfiedLeaves.length > 0) {
+ indent(sb, 1);
+ sb.append("Unsatisfied leaf constraints:\n");
+
+ for (VersionConstraint versionConstraint : unsatisfiedLeaves) {
+ if (!isOptional(versionConstraint)) {
+ indent(sb, 2);
+ formatConstraint(versionConstraint, sb);
+ sb.append("\n");
+ }
+ }
+ }
+ }
+
+ return sb.toString();
+
+ }
+
+ private ResolverError[] gatherResolverErrors(BundleDescription bundleDescription, State state) {
+ Set<ResolverError> resolverErrors = new LinkedHashSet<ResolverError>();
+ Collections.addAll(resolverErrors, state.getResolverErrors(bundleDescription));
+ BundleDescription[] bundles = state.getBundles();
+ for (BundleDescription bd : bundles) {
+ if (!bd.isResolved()) {
+ Collections.addAll(resolverErrors, state.getResolverErrors(bd));
+ }
+ }
+ return resolverErrors.toArray(new ResolverError[resolverErrors.size()]);
+ }
+
+ /**
+ * Finds the member of <code>candidates</code> that is the nearest match to <code>match</code>.
+ *
+ * @param match the string to match against.
+ * @param candidates the candidates to search.
+ * @return the nearest match.
+ */
+ private String nearestMatch(String match) {
+ Set<String> candidates = gatherExports();
+ int nearestDistance = Integer.MAX_VALUE;
+ String nearestMatch = null;
+
+ for (String candidate : candidates) {
+ int distance = calculateStringDistance(match, candidate);
+ if (distance < nearestDistance) {
+ nearestDistance = distance;
+ nearestMatch = candidate;
+ }
+ }
+
+ return nearestMatch;
+ }
+
+ /**
+ * Calculate the distance between the given two Strings according to the Levenshtein algorithm.
+ *
+ * @param s1 the first String
+ * @param s2 the second String
+ * @return the distance value
+ */
+ private final static int calculateStringDistance(String s1, String s2) {
+ if (s1.isEmpty()) {
+ return s2.length();
+ }
+ if (s2.isEmpty()) {
+ return s1.length();
+ }
+
+ final int s2len = s2.length();
+ final int s1len = s1.length();
+
+ int d[][] = new int[s1len + 1][s2len + 1];
+
+ for (int i = 0; i <= s1len; i++) {
+ d[i][0] = i;
+ }
+ for (int j = 0; j <= s2len; j++) {
+ d[0][j] = j;
+ }
+
+ for (int i = 1; i <= s1len; i++) {
+ char s_i = s1.charAt(i - 1);
+ for (int j = 1; j <= s2len; j++) {
+ int cost;
+ char t_j = s2.charAt(j - 1);
+ if (Character.toLowerCase(s_i) == Character.toLowerCase(t_j)) {
+ cost = 0;
+ } else {
+ cost = 1;
+ }
+ d[i][j] = Math.min(Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1), d[i - 1][j - 1] + cost);
+ }
+ }
+
+ return d[s1len][s2len];
+ }
+
+ /**
+ * Gathers all the package exports.
+ *
+ * @return the exported packages.
+ */
+ private Set<String> gatherExports() {
+ State state = this.platformAdmin.getState(false);
+ ExportPackageDescription[] exportedPackages = state.getExportedPackages();
+ Set<String> exports = new HashSet<String>(exportedPackages.length);
+ for (ExportPackageDescription epd : exportedPackages) {
+ exports.add(epd.getName());
+ }
+ return exports;
+ }
+
+ private void formatResolverError(ResolverError resolverError, StringBuilder sb, State state) {
+ if (resolverError.getType() == ResolverError.IMPORT_PACKAGE_USES_CONFLICT) {
+ formatUsesConflict(resolverError, sb, state);
+ } else if (resolverError.getType() == ResolverError.MISSING_FRAGMENT_HOST) {
+ formatMissingFragment(resolverError, sb);
+ } else if (resolverError.getType() == ResolverError.FRAGMENT_CONFLICT) {
+ formatFragmentConflict(resolverError, sb, state);
+ } else {
+ formatBasicResolverError(resolverError, sb);
+ }
+ }
+
+ private void formatBasicResolverError(ResolverError resolverError, StringBuilder sb) {
+ sb.append(this.getTypeDescription(resolverError.getType()));
+ VersionConstraint unsatisfiedConstraint = resolverError.getUnsatisfiedConstraint();
+ if(unsatisfiedConstraint != null){
+ formatMissingConstraintWithAttributes(resolverError, sb, unsatisfiedConstraint);
+ } else {
+ sb.append(" In bundle <").append(resolverError.getBundle()).append(">");
+ }
+ }
+
+ private void formatMissingFragment(ResolverError resolverError, StringBuilder sb) {
+ sb.append(this.getTypeDescription(resolverError.getType()));
+ sb.append(" The affected fragment is ").append(resolverError.getBundle());
+ }
+
+ private void formatUsesConflict(ResolverError resolverError, StringBuilder sb, State state) {
+ VersionConstraint unsatisfiedConstraint = resolverError.getUnsatisfiedConstraint();
+ sb.append("Uses violation: <").append(unsatisfiedConstraint)
+ .append("> in bundle <").append(resolverError.getBundle()).append(">\n");
+
+ AnalysedUsesConflict[] usesConflicts = this.usesAnalyser.getUsesConflicts(state, resolverError);
+ if (usesConflicts==null || usesConflicts.length==0) {
+ indent(sb,3);
+ sb.append(" Resolver reported uses conflict for import");
+ formatConstraintAttributes(sb, unsatisfiedConstraint);
+ } else {
+ formatConflictsFound(sb, usesConflicts);
+ }
+ }
+
+ private void formatMissingConstraintWithAttributes(ResolverError resolverError, StringBuilder sb, VersionConstraint unsatisfiedConstraint) {
+ sb.append(" Caused by missing constraint in bundle <").append(resolverError.getBundle()).append(">\n");
+ indent(sb, 3); sb.append(" constraint: <").append(unsatisfiedConstraint).append(">");
+
+ formatConstraintAttributes(sb, unsatisfiedConstraint);
+ }
+
+ private void formatConstraintAttributes(StringBuilder sb, VersionConstraint unsatisfiedConstraint) {
+ if (unsatisfiedConstraint instanceof ImportPackageSpecification) {
+ ImportPackageSpecification importPackageSpecification = (ImportPackageSpecification) unsatisfiedConstraint;
+ formatConstrainedBundleAttributes(sb, importPackageSpecification);
+ Map<?,?> attributes = importPackageSpecification.getAttributes();
+ if (attributes!=null && !attributes.isEmpty()) {
+ sb.append("\n");
+ indent(sb, 3); sb.append("with attributes ").append(attributes).append("\n");
+ }
+ }
+ }
+
+ private void formatConstrainedBundleAttributes(StringBuilder sb, ImportPackageSpecification importPackageSpecification) {
+ String bundleSymbolicName = importPackageSpecification.getBundleSymbolicName();
+ if (null!=bundleSymbolicName) {
+ sb.append(" constrained to bundle <").append(bundleSymbolicName).append(">");
+ VersionRange versionRange = importPackageSpecification.getBundleVersionRange();
+ if (null!=versionRange) {
+ sb.append(" constrained bundle version range \"").append(versionRange).append("\"");
+ }
+ }
+ }
+
+ private void formatConflictsFound(StringBuilder sb, AnalysedUsesConflict[] usesConflicts) {
+ indent(sb, 3);
+ sb.append("Found conflicts:\n");
+ for (AnalysedUsesConflict conflict : usesConflicts) {
+ for (String line : conflict.getConflictStatement()) {
+ indent(sb,4); sb.append(line).append("\n");
+ }
+ }
+ }
+
+ private void formatFragmentConflict(ResolverError resolverError, StringBuilder sb, State state) {
+ formatMissingFragment(resolverError, sb);
+ List<BundleDescription> possibleHosts = findPossibleHosts(resolverError.getUnsatisfiedConstraint().getBundle().getHost(), state);
+ if (!possibleHosts.isEmpty()) {
+ sb.append("\n");
+ indent(sb, 3);
+ sb.append("Possible hosts:\n");
+ for (BundleDescription possibleHost : possibleHosts) {
+ indent(sb, 4);
+ sb.append(possibleHost).append(" ").append(possibleHost.isResolved() ? "(resolved)" : "(not resolved)").append("\n");
+ }
+ indent(sb, 3);
+ sb.append("Constraint conflict:\n");
+ indent(sb, 4);
+ sb.append(resolverError.getUnsatisfiedConstraint());
+ }
+ }
+
+ private List<BundleDescription> findPossibleHosts(VersionConstraint hostSpecification, State state) {
+ List<BundleDescription> possibleHosts = new ArrayList<BundleDescription>();
+
+ BundleDescription[] bundles = state.getBundles(hostSpecification.getName());
+ if (bundles != null) {
+ for (BundleDescription bundle : bundles) {
+ if (hostSpecification.getVersionRange().isIncluded(bundle.getVersion())) {
+ possibleHosts.add(bundle);
+ }
+ }
+ }
+
+ return possibleHosts;
+ }
+
+
+ private void formatConstraint(VersionConstraint versionConstraint, StringBuilder sb) {
+ String constraintInformation = versionConstraint.toString();
+ String bundleInQuestion = versionConstraint.getBundle().toString();
+ sb.append("Bundle: ").append(bundleInQuestion).append(" - ").append(constraintInformation);
+ if (versionConstraint instanceof ImportPackageSpecification) {
+ sb.append("\n");
+ indent(sb, 3);
+ sb.append("Did you mean: '").append(nearestMatch(versionConstraint.getName())).append("'?");
+ }
+ }
+
+ private boolean isOptional(VersionConstraint versionConstraint) {
+ if (versionConstraint instanceof ImportPackageSpecification) {
+ ImportPackageSpecification ips = (ImportPackageSpecification) versionConstraint;
+ return !ImportPackageSpecification.RESOLUTION_STATIC.equals(ips.getDirective(Constants.RESOLUTION_DIRECTIVE));
+ }
+ return false;
+ }
+
+ /**
+ * Indent the supplied {@link StringBuilder} to the supplied <code>level</code>.
+ *
+ * @param out the <code>StringBuilder</code> to indent.
+ * @param level the indentation level.
+ */
+ private void indent(StringBuilder out, int level) {
+ for (int n = 0; n < level; n++) {
+ out.append(" ");
+ }
+ }
+
+ /**
+ * Provides a human readable string for any type of equinox resolver error.
+ * Errors are defined in {@link org.eclipse.osgi.service.resolver.ResolverError}
+ *
+ * @param type
+ * @return
+ */
+ private String getTypeDescription(int type) {
+ switch(type){
+ case ResolverError.MISSING_IMPORT_PACKAGE: return "An Import-Package could not be resolved.";
+
+ case ResolverError.MISSING_REQUIRE_BUNDLE: return "A Require-Bundle could not be resolved.";
+
+ case ResolverError.MISSING_FRAGMENT_HOST: return "A Fragment-Host could not be resolved.";
+
+ case ResolverError.SINGLETON_SELECTION: return "The bundle could not be resolved because another singleton bundle was selected.";
+
+ case ResolverError.FRAGMENT_CONFLICT: return "The fragment could not be resolved because of a constraint conflict with a host, possibly because the host is already resolved.";
+
+ case ResolverError.IMPORT_PACKAGE_USES_CONFLICT: return "An Import-Package could not be resolved because of a uses directive conflict.";
+
+ case ResolverError.REQUIRE_BUNDLE_USES_CONFLICT: return "A Require-Bundle could not be resolved because of a uses directive conflict.";
+
+ case ResolverError.IMPORT_PACKAGE_PERMISSION: return "An Import-Package could not be resolved because the importing bundle does not have the correct permissions to import the package.";
+
+ case ResolverError.EXPORT_PACKAGE_PERMISSION: return "An Import-Package could not be resolved because no exporting bundle has the correct permissions to export the package.";
+
+ case ResolverError.REQUIRE_BUNDLE_PERMISSION: return "A Require-Bundle could not be resolved because the requiring bundle does not have the correct permissions to require the bundle.";
+
+ case ResolverError.PROVIDE_BUNDLE_PERMISSION: return "A Require-Bundle could not be resolved because no bundle with the required symbolic name has the correct permissions to provied the required symbolic name.";
+
+ case ResolverError.HOST_BUNDLE_PERMISSION: return "A Fragment-Host could not be resolved because no bundle with the required symbolic name has the correct permissions to host a fragment.";
+
+ case ResolverError.FRAGMENT_BUNDLE_PERMISSION: return "A Fragment-Host could not be resolved because the fragment bundle does not have the correct permissions to be a fragment.";
+
+ case ResolverError.PLATFORM_FILTER: return "A bundle could not be resolved because a platform filter did not match the runtime environment.";
+
+ case ResolverError.MISSING_EXECUTION_ENVIRONMENT: return "A bundle could not be resolved because the required execution enviroment did not match the runtime environment.";
+
+ case ResolverError.MISSING_GENERIC_CAPABILITY: return "A bundle could not be resolved because the required generic capability could not be resolved.";
+
+ case ResolverError.NO_NATIVECODE_MATCH: return "A bundle could not be resolved because no match was found for the native code specification.";
+
+ case ResolverError.INVALID_NATIVECODE_PATHS: return "A bundle could not be resolved because the matching native code paths are invalid.";
+
+ case ResolverError.DISABLED_BUNDLE: return "A bundle could not be resolved because the bundle was disabled.";
+
+ default: return "Unknown Error.";
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/main/resources/EventLogMessages.properties b/org.eclipse.virgo.kernel.userregion/src/main/resources/EventLogMessages.properties
new file mode 100644
index 00000000..c00b4919
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/main/resources/EventLogMessages.properties
@@ -0,0 +1,5 @@
+#Enter log event codes to short messages - XX9999(E,W,I) = This is the short code
+UR0001I = User region ready.
+UR0002E = User region failed while deploying initial artifacts. Shutting down.
+UR0003W = Detected Import-Bundle of bundle '{}' version '{}' which exports packages that are exported by the system bundle: consider importing the packages explicitly. For example, use Import-Package: {}.
+UR0004W = Bundle '{}' imports library '{}' version '{}' but an instrumented library '{}' will be used instead.
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/InitialArtifactDeployerTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/InitialArtifactDeployerTests.java
new file mode 100644
index 00000000..ab9e6854
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/InitialArtifactDeployerTests.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+
+import org.eclipse.virgo.kernel.core.Shutdown;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer;
+import org.eclipse.virgo.kernel.deployer.core.DeployUriNormaliser;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+import org.eclipse.virgo.kernel.userregion.internal.InitialArtifactDeployer;
+import org.eclipse.virgo.kernel.userregion.internal.KernelStartedAwaiter;
+import org.eclipse.virgo.medic.test.eventlog.MockEventLogger;
+
+public class InitialArtifactDeployerTests {
+
+ private ApplicationDeployer deployer = createMock(ApplicationDeployer.class);
+
+ private KernelStartedAwaiter startedAwaiter = new KernelStartedAwaiter();
+
+ private StubEventAdmin eventAdmin = new StubEventAdmin();
+
+ private MockEventLogger eventLogger = new MockEventLogger();
+
+ private Shutdown shutdown = createMock(Shutdown.class);
+
+ @Test
+ public void basicEventingWithNoArtifactDeployment() throws InterruptedException {
+ replay(this.deployer);
+
+ InitialArtifactDeployer initialArtifactDeployer = new InitialArtifactDeployer(this.startedAwaiter, this.deployer, "", "", new StubDeployUriNormaliser(), this.eventAdmin, this.eventLogger, this.shutdown);
+ this.startedAwaiter.handleEvent(new Event("org/eclipse/virgo/kernel/STARTED", null));
+
+ initialArtifactDeployer.deployArtifacts();
+
+ Event eventSystemArtifactsDeployed = new Event("org/eclipse/virgo/kernel/userregion/systemartifacts/DEPLOYED", null);
+ this.eventAdmin.awaitPostingOfEvent(eventSystemArtifactsDeployed);
+
+ initialArtifactDeployer.handleEvent(eventSystemArtifactsDeployed);
+ this.eventAdmin.awaitPostingOfEvent(new Event("org/eclipse/virgo/kernel/userregion/userartifacts/DEPLOYED", null));
+
+ verify(this.deployer);
+ }
+
+ @Test
+ public void artifactDeployment() throws DeploymentException, InterruptedException {
+
+ InitialArtifactDeployer initialArtifactDeployer = new InitialArtifactDeployer(this.startedAwaiter, this.deployer, "repository:alpha/bravo/1, repository:alpha/bravo/2", "repository:charlie/delta,repository:echo/foxtrot/2", new StubDeployUriNormaliser(), this.eventAdmin, this.eventLogger, this.shutdown);
+
+ expect(this.deployer.deploy(URI.create("repository:alpha/bravo/1"), new DeploymentOptions(false, false, true))).andReturn(null);
+ expect(this.deployer.deploy(URI.create("repository:alpha/bravo/2"), new DeploymentOptions(false, false, true))).andReturn(null);
+ replay(this.deployer);
+
+ this.startedAwaiter.handleEvent(new Event("org/eclipse/virgo/kernel/STARTED", null));
+ initialArtifactDeployer.deployArtifacts();
+
+ Event eventSystemArtifactsDeployed = new Event("org/eclipse/virgo/kernel/userregion/systemartifacts/DEPLOYED", null);
+ this.eventAdmin.awaitPostingOfEvent(eventSystemArtifactsDeployed);
+
+ verify(this.deployer);
+
+ reset(this.deployer);
+
+ expect(this.deployer.deploy(URI.create("repository:charlie/delta"), new DeploymentOptions(false, false, true))).andReturn(null);
+ expect(this.deployer.deploy(URI.create("repository:echo/foxtrot/2"), new DeploymentOptions(false, false, true))).andReturn(null);
+
+ replay(this.deployer);
+
+ initialArtifactDeployer.handleEvent(eventSystemArtifactsDeployed);
+ this.eventAdmin.awaitPostingOfEvent(new Event("org/eclipse/virgo/kernel/userregion/userartifacts/DEPLOYED", null));
+
+ verify(this.deployer);
+ }
+
+ @Test
+ public void failedDeploymentLogsMessageAndTriggersShutdown() throws DeploymentException, InterruptedException {
+ InitialArtifactDeployer initialArtifactDeployer = new InitialArtifactDeployer(this.startedAwaiter, this.deployer, "repository:alpha/bravo/1", null, new StubDeployUriNormaliser(), this.eventAdmin, this.eventLogger, this.shutdown);
+
+ expect(this.deployer.deploy(URI.create("repository:alpha/bravo/1"), new DeploymentOptions(false, false, true))).andThrow(new DeploymentException("Deployment failed"));
+ replay(this.deployer);
+
+ this.shutdown.shutdown();
+ replay(this.shutdown);
+
+ this.startedAwaiter.handleEvent(new Event("org/eclipse/virgo/kernel/STARTED", null));
+
+ initialArtifactDeployer.deployArtifacts();
+
+ while (!this.eventLogger.isLogged("UR0002E")) {
+ Thread.sleep(100);
+ }
+
+ Thread.sleep(100);
+
+ verify(this.deployer, this.shutdown);
+ }
+
+ private final class StubEventAdmin implements EventAdmin {
+
+ private final List<Event> postedEvents = new ArrayList<Event>();
+
+ private final List<Event> sentEvents = new ArrayList<Event>();
+
+ private final Object monitor = new Object();
+
+ public void postEvent(Event event) {
+ synchronized (this.monitor) {
+ this.postedEvents.add(event);
+ }
+ }
+
+ public void sendEvent(Event event) {
+ synchronized (this.monitor) {
+ this.sentEvents.add(event);
+ }
+ }
+
+ public void awaitPostingOfEvent(Event event) {
+ boolean eventSent = false;
+ while (!eventSent) {
+ synchronized (this.monitor) {
+ eventSent = this.postedEvents.contains(event);
+ }
+ }
+ }
+ }
+
+ private final class StubDeployUriNormaliser implements DeployUriNormaliser {
+ public URI normalise(URI uri) throws DeploymentException {
+ return uri;
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingStrategyTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingStrategyTests.java
new file mode 100644
index 00000000..4b21d1b5
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/ServiceScopingStrategyTests.java
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+
+import org.eclipse.virgo.kernel.shim.scope.Scope;
+import org.eclipse.virgo.kernel.shim.scope.ScopeFactory;
+import org.eclipse.virgo.kernel.userregion.internal.ServiceScopingStrategy;
+import org.eclipse.virgo.teststubs.osgi.framework.StubBundleContext;
+
+/**
+ */
+public class ServiceScopingStrategyTests {
+
+ private static final String SCOPE_NAME = "application scope";
+
+ private static final String CLASS_NAME = "Class";
+
+ private static final String FILTER = "Filter";
+
+ private ServiceScopingStrategy serviceScopingStrategy;
+
+ private ScopeFactory scopeFactory;
+
+ private ScopeServiceRepository scopeServiceRepository;
+
+ private Scope globalScope;
+
+ private ServiceReference unscopedServiceReference;
+
+ private StubBundleContext unscopedBundleContext;
+
+ private Scope appScope;
+
+ private ServiceReference scopedServiceReference;
+
+ private StubBundleContext scopedBundleContext;
+
+ @Before
+ public void setUp() throws Exception {
+ this.scopeFactory = createMock(ScopeFactory.class);
+ this.scopeServiceRepository = createMock(ScopeServiceRepository.class);
+ this.serviceScopingStrategy = new ServiceScopingStrategy(this.scopeFactory, this.scopeServiceRepository);
+
+ this.globalScope = createMock(Scope.class);
+ expect(this.globalScope.isGlobal()).andReturn(true).anyTimes();
+ expect(this.scopeFactory.getGlobalScope()).andReturn(this.globalScope).anyTimes();
+
+ this.appScope = createMock(Scope.class);
+ expect(this.appScope.isGlobal()).andReturn(false).anyTimes();
+ expect(this.appScope.getScopeName()).andReturn(SCOPE_NAME).anyTimes();
+
+ replay(this.globalScope, this.appScope);
+ }
+
+ private void setUpScopedBundleContext() {
+ this.scopedBundleContext = new StubBundleContext();
+ expect(this.scopeFactory.getBundleScope(eq(this.scopedBundleContext.getBundle()))).andReturn(this.appScope).anyTimes();
+ }
+
+ private void setUpScopedServiceReference() {
+ this.scopedServiceReference = createMock(ServiceReference.class);
+ expect(this.scopeFactory.getServiceScope(eq(this.scopedServiceReference))).andReturn(this.appScope).anyTimes();
+ }
+
+ private void setUpUnscopedBundleContext() {
+ this.unscopedBundleContext = new StubBundleContext();
+ expect(this.scopeFactory.getBundleScope(eq(this.unscopedBundleContext.getBundle()))).andReturn(this.globalScope).anyTimes();
+ }
+
+ private void setUpUnscopedServiceReference() {
+ this.unscopedServiceReference = createMock(ServiceReference.class);
+ expect(this.scopeFactory.getServiceScope(eq(this.unscopedServiceReference))).andReturn(this.globalScope).anyTimes();
+ }
+
+ @After
+ public void tearDown() {
+ verify(this.globalScope, this.appScope);
+ }
+
+ @Test
+ public void testMatchesScopeUnscopedServiceUnscopedApplication() {
+ setUpUnscopedServiceReference();
+ setUpUnscopedBundleContext();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+ assertTrue(this.serviceScopingStrategy.isPotentiallyVisible(this.unscopedServiceReference, this.unscopedBundleContext));
+ verify(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+ }
+
+ @Test
+ public void testMatchesScopeScopedServiceScopedApplication() {
+ setUpScopedServiceReference();
+ setUpScopedBundleContext();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+ assertTrue(this.serviceScopingStrategy.isPotentiallyVisible(this.scopedServiceReference, this.scopedBundleContext));
+ verify(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+ }
+
+ @Test
+ public void testMatchesScopeScopedServiceUnscopedApplication() {
+ setUpScopedServiceReference();
+ setUpUnscopedBundleContext();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+ assertFalse(this.serviceScopingStrategy.isPotentiallyVisible(this.scopedServiceReference, this.unscopedBundleContext));
+ verify(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+ }
+
+ @Test
+ public void testMatchesScopeUnscopedServiceScopedApplication() {
+ setUpUnscopedServiceReference();
+ setUpScopedBundleContext();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+ assertTrue(this.serviceScopingStrategy.isPotentiallyVisible(this.unscopedServiceReference, this.scopedBundleContext));
+ verify(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testScopeReferencesUnscopedServiceUnscopedApplication() throws InvalidSyntaxException {
+ setUpUnscopedServiceReference();
+ setUpUnscopedBundleContext();
+ expect(this.scopeServiceRepository.scopeHasMatchingService(eq(SCOPE_NAME), eq(CLASS_NAME), eq(FILTER))).andReturn(false).anyTimes();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+
+ Collection references = new ShrinkableSet(this.unscopedServiceReference);
+ this.serviceScopingStrategy.scopeReferences(references, this.unscopedBundleContext, CLASS_NAME, FILTER);
+ assertTrue(references.contains(this.unscopedServiceReference));
+
+ verify(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testScopeReferencesScopedServiceUnscopedApplication() throws InvalidSyntaxException {
+ setUpScopedServiceReference();
+ setUpUnscopedBundleContext();
+ expect(this.scopeServiceRepository.scopeHasMatchingService(eq(SCOPE_NAME), eq(CLASS_NAME), eq(FILTER))).andReturn(false).anyTimes();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+
+ Collection references = new ShrinkableSet(this.scopedServiceReference);
+ this.serviceScopingStrategy.scopeReferences(references, this.unscopedBundleContext, CLASS_NAME, FILTER);
+ assertFalse(references.contains(this.unscopedServiceReference));
+
+ verify(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testScopeReferencesScopedServiceInModelScopedApplication() throws InvalidSyntaxException {
+ setUpScopedServiceReference();
+ setUpScopedBundleContext();
+ expect(this.scopeServiceRepository.scopeHasMatchingService(eq(SCOPE_NAME), eq(CLASS_NAME), eq(FILTER))).andReturn(true).anyTimes();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+
+ Collection references = new ShrinkableSet(this.scopedServiceReference);
+ this.serviceScopingStrategy.scopeReferences(references, this.scopedBundleContext, CLASS_NAME, FILTER);
+ assertTrue(references.contains(this.scopedServiceReference));
+
+ verify(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testScopeReferencesScopedServiceNotInModelScopedApplication() throws InvalidSyntaxException {
+ setUpScopedServiceReference();
+ setUpScopedBundleContext();
+ expect(this.scopeServiceRepository.scopeHasMatchingService(eq(SCOPE_NAME), eq(CLASS_NAME), eq(FILTER))).andReturn(false).anyTimes();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+
+ Collection references = new ShrinkableSet(this.scopedServiceReference);
+ this.serviceScopingStrategy.scopeReferences(references, this.scopedBundleContext, CLASS_NAME, FILTER);
+ assertTrue(references.contains(this.scopedServiceReference));
+
+ verify(this.scopeFactory, this.scopeServiceRepository, this.scopedServiceReference);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testScopeReferencesUnscopedUnshadowedServiceScopedApplication() throws InvalidSyntaxException {
+ setUpUnscopedServiceReference();
+ setUpScopedBundleContext();
+ expect(this.scopeServiceRepository.scopeHasMatchingService(eq(SCOPE_NAME), eq(CLASS_NAME), eq(FILTER))).andReturn(false).anyTimes();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+
+ Collection references = new ShrinkableSet(this.unscopedServiceReference);
+ this.serviceScopingStrategy.scopeReferences(references, this.scopedBundleContext, CLASS_NAME, FILTER);
+ assertTrue(references.contains(this.unscopedServiceReference));
+
+ verify(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testScopeReferencesUnscopedShadowedServiceScopedApplication() throws InvalidSyntaxException {
+ setUpUnscopedServiceReference();
+ setUpScopedBundleContext();
+ expect(this.scopeServiceRepository.scopeHasMatchingService(eq(SCOPE_NAME), eq(CLASS_NAME), eq(FILTER))).andReturn(true).anyTimes();
+ replay(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+
+ Collection references = new ShrinkableSet(this.unscopedServiceReference);
+ this.serviceScopingStrategy.scopeReferences(references, this.scopedBundleContext, CLASS_NAME, FILTER);
+ assertFalse(references.contains(this.unscopedServiceReference));
+
+ verify(this.scopeFactory, this.scopeServiceRepository, this.unscopedServiceReference);
+ }
+
+ /**
+ * This test uses a collection that does not support addition in order to place the
+ * same constraints on the implementation as the service registry find hook.
+ */
+ private final class ShrinkableSet extends HashSet<ServiceReference> {
+
+ private static final long serialVersionUID = 1L;
+
+ public ShrinkableSet(ServiceReference e) {
+ super();
+ super.add(e);
+ }
+
+ @Override
+ public boolean add(ServiceReference e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends ServiceReference> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/AbstractOsgiFrameworkLaunchingTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/AbstractOsgiFrameworkLaunchingTests.java
new file mode 100644
index 00000000..44374583
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/AbstractOsgiFrameworkLaunchingTests.java
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileReader;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.osgi.launch.Equinox;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.junit.After;
+import org.junit.Before;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportExpander;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiFramework;
+import org.eclipse.virgo.kernel.services.repository.internal.RepositoryFactoryBean;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.EquinoxOsgiFramework;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.KernelClassLoaderCreator;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.TransformedManifestProvidingBundleFileWrapper;
+import org.eclipse.virgo.kernel.userregion.internal.importexpansion.ImportExpansionHandler;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiFrameworkFactory;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardResolutionFailureDetective;
+
+import org.eclipse.virgo.osgi.extensions.equinox.EquinoxLauncherConfiguration;
+import org.eclipse.virgo.osgi.extensions.equinox.ExtendedEquinoxLauncher;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.PluggableClassLoadingHook;
+import org.eclipse.virgo.kernel.artifact.bundle.BundleBridge;
+import org.eclipse.virgo.kernel.artifact.library.LibraryBridge;
+import org.eclipse.virgo.medic.dump.DumpGenerator;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.medic.test.eventlog.MockEventLogger;
+import org.eclipse.virgo.repository.ArtifactBridge;
+import org.eclipse.virgo.repository.Repository;
+import org.eclipse.virgo.repository.RepositoryFactory;
+import org.eclipse.virgo.repository.internal.RepositoryBundleActivator;
+import org.eclipse.virgo.util.io.FileSystemUtils;
+
+public abstract class AbstractOsgiFrameworkLaunchingTests {
+
+ protected EquinoxOsgiFramework framework;
+
+ protected PlatformAdmin platformAdmin;
+
+ protected Repository repository;
+
+ private RepositoryBundleActivator repositoryBundleActivator;
+
+ private BundleContext bundleContext;
+
+ private ServiceRegistration repositoryRegistration;
+
+ private ServiceRegistration eventLoggerRegistration;
+
+ private ServiceRegistration dumpGeneratorRegistration;
+
+ private Equinox equinox;
+
+ protected QuasiFramework quasiFramework;
+
+ @Before
+ public void setUp() throws Exception {
+
+ final File workDir = new File("target/work");
+
+ if (workDir.exists()) {
+ assertTrue(FileSystemUtils.deleteRecursively(new File("target/work")));
+ }
+
+ // Uncomment this line to enable Equinox debugging
+ // FrameworkProperties.setProperty("osgi.debug", "src/test/resources/debug.options");
+ EquinoxLauncherConfiguration launcherConfiguration = new EquinoxLauncherConfiguration();
+ launcherConfiguration.setClean(true);
+ URI targetURI = new File("./target").toURI();
+ launcherConfiguration.setConfigPath(targetURI);
+ launcherConfiguration.setInstallPath(targetURI);
+
+ equinox = ExtendedEquinoxLauncher.launch(launcherConfiguration);
+
+ this.bundleContext = equinox.getBundleContext();
+
+ DumpGenerator dumpGenerator = new DumpGenerator() {
+
+ public void generateDump(String cause, Throwable... throwables) {
+ }
+
+ public void generateDump(String cause, Map<String, Object> context, Throwable... throwables) {
+ }
+
+ };
+
+ final EventLogger mockEventLogger = new MockEventLogger();
+
+ eventLoggerRegistration = bundleContext.registerService(EventLogger.class.getName(), mockEventLogger, null);
+ dumpGeneratorRegistration = bundleContext.registerService(DumpGenerator.class.getName(), dumpGenerator, null);
+
+ this.repositoryBundleActivator = new RepositoryBundleActivator();
+ this.repositoryBundleActivator.start(bundleContext);
+
+ ServiceReference serviceReference = bundleContext.getServiceReference(RepositoryFactory.class.getName());
+ RepositoryFactory repositoryFactory = (RepositoryFactory) bundleContext.getService(serviceReference);
+
+ Properties repositoryProperties = new Properties();
+ repositoryProperties.load(new FileReader(new File(getRepositoryConfigDirectory(), "repository.properties")));
+
+ Set<ArtifactBridge> artifactBridges = new HashSet<ArtifactBridge>();
+ artifactBridges.add(new BundleBridge(new StubHashGenerator()));
+ artifactBridges.add(new LibraryBridge(new StubHashGenerator()));
+
+ RepositoryFactoryBean bean = new RepositoryFactoryBean(repositoryProperties, mockEventLogger, repositoryFactory, new File("target/work"),
+ artifactBridges, null);
+ repository = bean.getObject();
+
+ repositoryRegistration = bundleContext.registerService(Repository.class.getName(), repository, null);
+
+ serviceReference = bundleContext.getServiceReference(PlatformAdmin.class.getName());
+ this.platformAdmin = (PlatformAdmin) bundleContext.getService(serviceReference);
+
+ serviceReference = bundleContext.getServiceReference(PackageAdmin.class.getName());
+ PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(serviceReference);
+
+ ImportExpander importExpander = createImportExpander(packageAdmin);
+ TransformedManifestProvidingBundleFileWrapper bundleFileWrapper = new TransformedManifestProvidingBundleFileWrapper(importExpander);
+ this.framework = new EquinoxOsgiFramework(equinox.getBundleContext(), packageAdmin, bundleFileWrapper);
+
+ PluggableClassLoadingHook.getInstance().setClassLoaderCreator(new KernelClassLoaderCreator());
+ StandardResolutionFailureDetective detective = new StandardResolutionFailureDetective(platformAdmin);
+ this.quasiFramework = new StandardQuasiFrameworkFactory(bundleContext, detective, repository, bundleFileWrapper).create();
+ }
+
+ private ImportExpander createImportExpander(PackageAdmin packageAdmin) {
+ Set<String> packagesExportedBySystemBundle = new HashSet<String>(30);
+ ExportedPackage[] exportedPackages = packageAdmin.getExportedPackages(bundleContext.getBundle(0));
+
+ for (ExportedPackage exportedPackage : exportedPackages) {
+ packagesExportedBySystemBundle.add(exportedPackage.getName());
+ }
+
+ return new ImportExpansionHandler(repository, bundleContext, packagesExportedBySystemBundle, new MockEventLogger());
+ }
+
+ @After
+ public void stop() throws Exception {
+
+ if (this.repositoryRegistration != null) {
+ this.repositoryRegistration.unregister();
+ this.repositoryRegistration = null;
+ }
+
+ if (this.dumpGeneratorRegistration != null) {
+ this.dumpGeneratorRegistration.unregister();
+ this.dumpGeneratorRegistration = null;
+ }
+
+ if (this.eventLoggerRegistration != null) {
+ this.eventLoggerRegistration.unregister();
+ this.eventLoggerRegistration = null;
+ }
+
+ if (this.repositoryBundleActivator != null) {
+ this.repositoryBundleActivator.stop(this.bundleContext);
+ this.repositoryBundleActivator = null;
+ }
+
+ if (this.framework != null) {
+ this.framework.stop();
+ this.framework = null;
+ }
+
+ if (this.equinox != null) {
+ this.equinox.stop();
+ this.equinox.waitForStop(30000);
+ }
+ }
+
+ protected abstract String getRepositoryConfigDirectory();
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/BundleInstallationTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/BundleInstallationTests.java
new file mode 100644
index 00000000..57c60415
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/BundleInstallationTests.java
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URI;
+
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+
+/**
+ */
+public class BundleInstallationTests extends AbstractOsgiFrameworkLaunchingTests {
+
+ @Override
+ protected String getRepositoryConfigDirectory() {
+ return new File("src/test/resources/config/BundleInstallationTests").getAbsolutePath();
+ }
+
+ @Test(expected = BundleException.class)
+ public void testFailedInstall() throws Exception {
+ installBundle("fail.parent");
+ }
+
+ @Test
+ public void testInstallFragHost() throws Exception {
+ Bundle bundle = installBundle("frag.host");
+ assertNotNull(bundle);
+ assertEquals(Bundle.INSTALLED, bundle.getState());
+
+ Bundle[] bundles = this.framework.getBundleContext().getBundles();
+ boolean foundChild = false;
+ for (Bundle b : bundles) {
+ if ("frag.child".equals(b.getSymbolicName())) {
+ foundChild = true;
+ break;
+ }
+ }
+ assertTrue(foundChild);
+ }
+
+ @Test
+ public void testInstallFragChild() throws Exception {
+ Bundle bundle = installBundle("frag.child");
+ assertNotNull(bundle);
+ assertEquals(Bundle.RESOLVED, bundle.getState());
+ }
+
+ @Test
+ public void testInstallBundleWithNoDependencies() throws Exception {
+ Bundle bundle = installBundle("org.eclipse.virgo.server.mock.bundle");
+ assertNotNull(bundle);
+ bundle.start();
+ assertEquals(Bundle.ACTIVE, bundle.getState());
+ }
+
+ @Test
+ public void testInstallBundleWithMultipleDependencies() throws Exception {
+ Bundle bundle = installBundle("install.three");
+ assertNotNull(bundle);
+ bundle.start();
+ assertEquals(Bundle.ACTIVE, bundle.getState());
+ }
+
+ @Test
+ public void testInstallBundleWithTransitiveDependencies() throws Exception {
+ Bundle bundle = installBundle("install.four");
+ assertNotNull(bundle);
+ assertEquals(Bundle.RESOLVED, bundle.getState());
+ }
+
+ @Test
+ public void testInstallBundleWithRequireBundle() throws Exception {
+ Bundle bundle = installBundle("install.five");
+ assertNotNull(bundle);
+ assertEquals(Bundle.RESOLVED, bundle.getState());
+ }
+
+ @Test
+ public void testInstallBundleWithCircle() throws Exception {
+ Bundle bundle = installBundle("install.six");
+ assertNotNull(bundle);
+ assertEquals(Bundle.RESOLVED, bundle.getState());
+ }
+
+ @Test
+ public void platform170() throws Exception {
+ Bundle b = installBundle(new File("./src/test/resources/platform170/simpleosgiservice-1.0.0.jar"));
+ b.start();
+
+ b = installBundle(new File("./src/test/resources/platform170/simpleosgiservice-2.0.0.jar"));
+ b.start();
+
+ b = installBundle(new File("./src/test/resources/platform170/simpleosgiapp-1.0.0.jar"));
+ b.start();
+
+ assertEquals(Bundle.ACTIVE, b.getState());
+ }
+
+ @Test
+ public void testInstallWithOptionalImportNotSatisfied() throws Exception {
+ Bundle b = installBundle("install.optional.ns");
+ b.start();
+ assertEquals(Bundle.ACTIVE, b.getState());
+ }
+
+ @Test
+ public void testInstallWithOptionalImportWithNotSatisfiedDependencyInOptional() throws Exception {
+ Bundle b = installBundle("install.optional.dep.bundle");
+ b.start();
+ assertEquals(Bundle.ACTIVE, b.getState());
+ }
+
+ @Test
+ public void testMultipleOptionsChoosesOnlyOneOption() throws Exception {
+ Bundle b = installBundle("install.multi.bundle");
+ b.start();
+ assertEquals(Bundle.ACTIVE, b.getState());
+ Bundle[] bundles = b.getBundleContext().getBundles();
+ for (Bundle bundle : bundles) {
+ if ("install.multi.a".equals(bundle.getSymbolicName())) {
+ fail("Bundle install.multi.a should not have been installed into the framework");
+ }
+ }
+ }
+
+ @Test
+ public void testSatisfyAgainstBundleNotInRepo() throws Exception {
+ installBundle(new File("./src/test/resources/bit/standalone"));
+ Bundle bundle = installBundle("install.six");
+ bundle.start();
+ assertEquals(Bundle.ACTIVE, bundle.getState());
+ }
+
+ @Test(expected = BundleException.class)
+ public void testFailedDueToMissingImport() throws Exception {
+ installBundle("install.error.import");
+ }
+
+ @Test
+ public void testUnresolvableFragmentIsIgnored() throws Exception {
+ Bundle b = installBundle("fragments.unresolvable.host");
+ b.start();
+ assertEquals(Bundle.ACTIVE, b.getState());
+ }
+
+ @Test(expected=BundleException.class)
+ public void testFailDueToUses() throws Exception {
+ installBundle("install.uses.hibernate325");
+ installBundle("install.uses.hibernate326");
+ Bundle b = installBundle("install.uses.spring");
+ b.start();
+
+ assertEquals(Bundle.ACTIVE, b.getState());
+
+ installBundle("install.uses.bundle");
+ }
+
+ private Bundle installBundle(String symbolicName) throws BundleException, IOException {
+ URI bundleLocation = this.repository.get("bundle", symbolicName, VersionRange.NATURAL_NUMBER_RANGE).getUri();
+ File bundleFile = new File(bundleLocation);
+ return installBundle(bundleFile);
+ }
+
+ private Bundle installBundle(File bundleFile) throws BundleException, IOException {
+ Reader manifest;
+ if (bundleFile.isDirectory()) {
+ manifest = ManifestUtils.manifestReaderFromExplodedDirectory(bundleFile);
+ } else {
+ manifest = ManifestUtils.manifestReaderFromJar(bundleFile);
+ }
+
+ QuasiBundle quasiBundle = this.quasiFramework.install(bundleFile.toURI(), BundleManifestFactory.createBundleManifest(manifest));
+ this.quasiFramework.resolve();
+ this.quasiFramework.commit();
+ return quasiBundle.getBundle();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/BundleUpdateTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/BundleUpdateTests.java
new file mode 100644
index 00000000..4d9e482c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/BundleUpdateTests.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.util.io.PathReference;
+
+/**
+ */
+public class BundleUpdateTests extends AbstractOsgiFrameworkLaunchingTests {
+
+ @Override
+ protected String getRepositoryConfigDirectory() {
+ return new File("src/test/resources/config/BundleUpdateTests").getAbsolutePath();
+ }
+
+ /*
+ * This test checks the behaviour of class loading during a bundle update operation. The test passes on Mac OS X, but
+ * it seems unlikely to pass on some other operating systems. The basic question is how can classes be loaded from the
+ * old version of a bundle after the bundle has been updated if install by reference is used.
+ *
+ * The test installs two bundles but.B and but.C and starts but.C. but.C loads class but.A which is exported
+ * by but.B and listens synchronously for an update event for bundle but.B. This test method overwrites bundle but.B
+ * and issues an update for the bundle. The new version of but.B contains a new version of class but.A.
+ *
+ * The old version of bundle but.B also contains a class but.B which depends on the class but.A. The new version
+ * of bundle but.B contains a new version of class but.B which depends on the new version of but.A and which will fail
+ * if it is loaded with the old version of but.A.
+ *
+ * When the synchronous bundle listener in bundle but.C is notified of the update of bundle but.B, it loads the class
+ * but.B. This will succeed if it is loads the old version of the class but will fail if it loads the new version of the
+ * class.
+ */
+ @Test
+ @Ignore("See DMS-284")
+ public void testUpdate() throws Exception {
+ PathReference bBeforeSrc = new PathReference("src/test/resources/but/but.B.before.jar");
+ PathReference b = new PathReference("target/but.B.jar");
+ b.delete();
+ bBeforeSrc.copy(b);
+ Bundle bBundle = this.framework.getBundleContext().installBundle(b.toFile().toURI().toString());
+ assertNotNull(bBundle);
+
+ PathReference cSrc = new PathReference("src/test/resources/but/but.C.jar");
+ PathReference c = new PathReference("target/but.C.jar");
+ c.delete();
+ cSrc.copy(c);
+ Bundle cBundle = this.framework.getBundleContext().installBundle(c.toFile().toURI().toString());
+ assertNotNull(cBundle);
+ cBundle.start();
+
+ PathReference bAfterSrc = new PathReference("src/test/resources/but/but.B.after.jar");
+ b.delete();
+ bAfterSrc.copy(b);
+ bBundle.update();
+
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxBootDelegationHelperTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxBootDelegationHelperTests.java
new file mode 100644
index 00000000..a1db742c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxBootDelegationHelperTests.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.virgo.kernel.userregion.internal.equinox.EquinoxBootDelegationHelper;
+import org.junit.Test;
+
+
+/**
+ */
+public class EquinoxBootDelegationHelperTests {
+
+ @Test public void isBootDelegated() {
+ EquinoxBootDelegationHelper helper = new EquinoxBootDelegationHelper("a.b.c.*, d.e.f");
+
+ assertFalse(helper.isBootDelegated("g.h.MyClass"));
+ assertTrue(helper.isBootDelegated("d.e.f.MyClass"));
+ assertFalse(helper.isBootDelegated("d.e.f.g.MyClass"));
+ assertTrue(helper.isBootDelegated("a.b.c.d.MyClass"));
+
+ // I believe there's a bug in Equinox which we need to mirror. The boot delegation is
+ // specified as (e.g.) org.eclipse.virgo.server.osgi.* which should mean that all classes that
+ // reside in a subpackage of org.eclipse.virgo.server.osgi are boot delegated but those
+ // that reside directly in org.eclipse.virgo.server.osgi are not boot delegated. However,
+ // Equinox treats org.eclipse.virgo.server.osgi.* as meaning that everything in
+ // org.eclipse.virgo.server.osgi and its subpackages is boot delegated.
+ assertTrue(helper.isBootDelegated("a.b.c.MyClass"));
+ assertFalse(helper.isBootDelegated("d.e.MyClass"));
+ assertFalse(helper.isBootDelegated("a.b.MyClass"));
+ }
+
+ @Test public void testDelegationOfAllPackages() {
+ EquinoxBootDelegationHelper helper = new EquinoxBootDelegationHelper("a.b.c.*, *");
+ assertTrue(helper.isBootDelegated("d.e.f.MyClass"));
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxOsgiFrameworkTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxOsgiFrameworkTests.java
new file mode 100644
index 00000000..460cfb60
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/EquinoxOsgiFrameworkTests.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+import org.eclipse.virgo.kernel.osgi.framework.BundleClassLoaderUnavailableException;
+import org.eclipse.virgo.kernel.osgi.framework.InstrumentableClassLoader;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.EquinoxOsgiFramework;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.KernelBundleClassLoader;
+
+/**
+ */
+public class EquinoxOsgiFrameworkTests extends AbstractOsgiFrameworkLaunchingTests {
+
+ @Override
+ protected String getRepositoryConfigDirectory() {
+ return new File("src/test/resources/config/EquinoxOsgiFrameworkTests").getAbsolutePath();
+ }
+
+ @Test
+ public void testStartAndStop() throws Exception {
+ assertNotNull(this.framework.getBundleContext());
+ assertEquals(Bundle.ACTIVE, this.framework.getBundleContext().getBundle().getState());
+ }
+
+ @Test
+ public void testGetClassBundle() throws Exception {
+ Class<?> c = this.framework.getBundleContext().getBundle().loadClass("org.osgi.framework.Bundle");
+ assertNotNull(c);
+ Bundle b = this.framework.getClassBundle(c);
+ assertNotNull(b);
+ assertEquals(0, b.getBundleId());
+ }
+
+ @Test(expected = BundleClassLoaderUnavailableException.class)
+ public void testGetClassLoaderFromUnresolved() throws Exception {
+ Bundle faultyBundle = this.framework.getBundleContext().installBundle(new File("src/test/resources/EquinoxOsgiFrameworkTests/faulty").toURI().toString());
+ assertEquals(Bundle.INSTALLED, faultyBundle.getState());
+ this.framework.getBundleClassLoader(faultyBundle);
+ }
+
+ @Test
+ public void testLoadClassAndGetClassLoader() throws Exception {
+ Bundle bundle = installSpringCore(this.framework);
+ assertEquals("incorrect bundle loaded", "org.springframework.core", bundle.getSymbolicName());
+ Class<?> cls = bundle.loadClass("org.springframework.core.JdkVersion");
+ assertNotNull(cls);
+ assertTrue(cls.getClassLoader() instanceof KernelBundleClassLoader);
+ assertTrue("classloader is screwed", cls.getClassLoader().toString().contains("org.springframework.core"));
+ }
+
+ @Test
+ public void testAddClassFileTransformer() throws Exception {
+ Bundle bundle = installSpringCore(this.framework);
+ ClassLoader bundleClassLoader = this.framework.getBundleClassLoader(bundle);
+ assertNotNull(bundleClassLoader);
+ InstrumentableClassLoader icl = (InstrumentableClassLoader) bundleClassLoader;
+ final AtomicInteger count = new AtomicInteger(0);
+ icl.addClassFileTransformer(new ClassFileTransformer() {
+
+ public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) throws IllegalClassFormatException {
+ count.incrementAndGet();
+ return null;
+ }
+
+ });
+ bundle.loadClass("org.springframework.core.JdkVersion");
+ assertEquals(1, count.get());
+ }
+
+ /**
+ * @param osgi
+ * @return
+ * @throws BundleException
+ */
+ private Bundle installSpringCore(EquinoxOsgiFramework osgi) throws BundleException {
+ osgi.getBundleContext().installBundle("file:///" + new File("../ivy-cache/repository/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar").getAbsolutePath());
+ return osgi.getBundleContext().installBundle("file:///" + new File("../ivy-cache/repository/org.springframework/org.springframework.core/2.5.6.SEC01/org.springframework.core-2.5.6.SEC01.jar").getAbsolutePath());
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelBundleClassLoaderTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelBundleClassLoaderTests.java
new file mode 100644
index 00000000..e871c0b4
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/KernelBundleClassLoaderTests.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyDependenciesException;
+
+/**
+ *
+ */
+public class KernelBundleClassLoaderTests extends AbstractOsgiFrameworkLaunchingTests {
+
+ private Bundle dependant;
+
+ @Override
+ protected String getRepositoryConfigDirectory() {
+ return new File("src/test/resources/config/KernelBundleClassLoaderTests").getAbsolutePath();
+ }
+
+ /**
+ * Test method for {@link org.eclipse.virgo.kernel.userregion.internal.equinox.KernelBundleClassLoader#getResources(java.lang.String)}.
+ * @throws UnableToSatisfyDependenciesException
+ * @throws Exception
+ */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testGetResourcesStringFromBundle() throws Exception {
+ Enumeration<URL> resources = this.dependant.getResources("/META-INF/GET_ME");
+
+ assertNotNull(resources);
+ assertTrue(resources.hasMoreElements());
+ assertTrue(resources.nextElement().getPath().endsWith("bundlefile!/META-INF/GET_ME"));
+
+ }
+
+ /**
+ * Test method for {@link org.eclipse.virgo.kernel.userregion.internal.equinox.KernelBundleClassLoader#getResource(java.lang.String)}.
+ * @throws UnableToSatisfyDependenciesException
+ * @throws Exception
+ */
+ @Test
+ public void testGetResourceStringFromBundle() throws Exception {
+ URL resource = this.dependant.getResource("/META-INF/GET_ME");
+
+ assertNotNull(resource);
+ assertTrue(resource.getPath().endsWith("bundlefile!/META-INF/GET_ME"));
+ }
+
+ /**
+ * Test method for {@link org.eclipse.virgo.kernel.userregion.internal.equinox.KernelBundleClassLoader#getResources(java.lang.String)}.
+ * @throws UnableToSatisfyDependenciesException
+ * @throws Exception
+ */
+ @Test
+ public void testGetResourcesStringFromBundleClassLoader() throws Exception {
+ Bundle bundle = this.dependant;
+ ClassLoader loader = this.framework.getBundleClassLoader(bundle);
+ Enumeration<URL> resources = loader.getResources("/META-INF/GET_ME");
+
+ assertNotNull(resources);
+ assertTrue(resources.hasMoreElements());
+ assertTrue(resources.nextElement().getPath().endsWith("bundlefile!/META-INF/GET_ME"));
+
+ }
+
+ /**
+ * Test method for {@link org.eclipse.virgo.kernel.userregion.internal.equinox.KernelBundleClassLoader#getResource(java.lang.String)}.
+ * @throws UnableToSatisfyDependenciesException
+ * @throws Exception
+ */
+ @Test
+ public void testGetResourceStringFromBundleClassLoader() throws Exception {
+ ClassLoader loader = this.framework.getBundleClassLoader(this.dependant);
+ URL resource = loader.getResource("/META-INF/GET_ME");
+
+ assertNotNull(resource);
+ assertTrue(resource.getPath().endsWith("bundlefile!/META-INF/GET_ME"));
+ }
+
+
+ @Before
+ public void installDependant() throws BundleException {
+ this.dependant = this.framework.getBundleContext().installBundle(new File("src/test/resources/KernelBundleClassLoaderTests/dependant.jar").toURI().toString());
+ }
+
+ @After
+ public void uninstallDependant() throws BundleException {
+ this.dependant.uninstall();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/LoadTimeWeavingTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/LoadTimeWeavingTests.java
new file mode 100644
index 00000000..5570fa02
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/LoadTimeWeavingTests.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+
+import java.io.File;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.virgo.kernel.userregion.internal.equinox.KernelBundleClassLoader;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+
+
+/**
+ */
+public class LoadTimeWeavingTests extends AbstractOsgiFrameworkLaunchingTests {
+
+ private static final String DOMAIN_TYPE_NAME = "ltw.domain.DomainType";
+
+ private static final String A_TYPE_NAME = "app.a.A";
+
+ private static final String B_TYPE_NAME = "app.b.B";
+
+ private Bundle emBundle;
+
+ private Bundle includeBundle;
+
+ @Override
+ protected String getRepositoryConfigDirectory() {
+ return new File("src/test/resources/config/LoadTimeWeavingTests").getAbsolutePath();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ this.framework.getBundleContext().installBundle(new File("src/test/resources/ltw/ltw-domain.jar").toURI().toString());
+ this.includeBundle = this.framework.getBundleContext().installBundle(new File("src/test/resources/ltw/ltw-include.jar").toURI().toString());
+ this.emBundle = this.framework.getBundleContext().installBundle(new File("src/test/resources/ltw/ltw-em.jar").toURI().toString());
+ }
+
+ @Test public void instrumentPackageIncludes() throws ClassNotFoundException {
+ KernelBundleClassLoader incClassLoader = (KernelBundleClassLoader) this.framework.getBundleClassLoader(this.includeBundle);
+ assertNotNull(incClassLoader);
+
+ ClassLoader throwAway = incClassLoader.createThrowAway();
+ assertEquals(throwAway, throwAway.loadClass(A_TYPE_NAME).getClassLoader());
+ assertEquals(incClassLoader, throwAway.loadClass(B_TYPE_NAME).getClassLoader());
+ }
+
+ @Test public void throwawayAcrossBundles() throws ClassNotFoundException {
+ KernelBundleClassLoader emClassLoader = (KernelBundleClassLoader) this.framework.getBundleClassLoader(this.emBundle);
+ assertNotNull(emClassLoader);
+ ClassLoader throwAway = emClassLoader.createThrowAway();
+ Class<?> domainTypeClass = throwAway.loadClass(DOMAIN_TYPE_NAME);
+ assertNotNull(domainTypeClass);
+ assertNotSame(emClassLoader.loadClass(DOMAIN_TYPE_NAME), domainTypeClass);
+ assertSame(domainTypeClass, throwAway.loadClass(DOMAIN_TYPE_NAME));
+ }
+
+ @Test public void weaveAcrossBundles() throws ClassNotFoundException {
+ KernelBundleClassLoader emClassLoader = (KernelBundleClassLoader) this.framework.getBundleClassLoader(this.emBundle);
+ assertNotNull(emClassLoader);
+ final AtomicInteger counter = new AtomicInteger(0);
+ emClassLoader.addClassFileTransformer(new ClassFileTransformer() {
+
+ public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) throws IllegalClassFormatException {
+ counter.incrementAndGet();
+ return null;
+ }
+
+ });
+ Class<?> domainTypeClass = emClassLoader.loadClass(DOMAIN_TYPE_NAME);
+ assertNotNull(domainTypeClass);
+ assertEquals(1, counter.get());
+ }
+
+ @Test public void testRefreshWithNoPropagation() throws Exception {
+ Class<?> before = this.emBundle.loadClass(DOMAIN_TYPE_NAME);
+ this.framework.refresh(this.emBundle);
+ waitUntilResolved(3000);
+ assertSame(this.emBundle.loadClass(DOMAIN_TYPE_NAME), before);
+ }
+
+ @Test public void testRefreshWithPropagation() throws Exception {
+ KernelBundleClassLoader emClassLoader = (KernelBundleClassLoader) this.framework.getBundleClassLoader(this.emBundle);
+ emClassLoader.addClassFileTransformer(new ClassFileTransformer() {
+
+ public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
+ byte[] classfileBuffer) throws IllegalClassFormatException {
+ return null;
+ }
+
+ });
+ Class<?> before = this.emBundle.loadClass(DOMAIN_TYPE_NAME);
+ this.framework.refresh(this.emBundle);
+ waitUntilResolved(3000);
+ assertNotSame(this.emBundle.loadClass(DOMAIN_TYPE_NAME), before);
+ }
+
+ private void waitUntilResolved(int maxWaitInMillis) {
+ boolean resolved = this.emBundle.getState() == Bundle.RESOLVED;
+ while (!resolved && maxWaitInMillis>0) {
+ try {
+ Thread.sleep(50); maxWaitInMillis-=50;
+ } catch (InterruptedException e) {
+ continue;
+ }
+ resolved = this.emBundle.getState() == Bundle.RESOLVED;
+ }
+ }
+}
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ManifestUtils.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ManifestUtils.java
new file mode 100644
index 00000000..48203421
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ManifestUtils.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.eclipse.virgo.util.io.FileCopyUtils;
+
+/**
+ * Utility class for extracting a {@link Reader} for manifest data in a JAR file and in exploded JAR directories.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+public final class ManifestUtils {
+
+ private static final String MANIFEST_DIRECTORY_LOCATION = "META-INF" + File.separator + "MANIFEST.MF";
+
+ private static final String MANIFEST_ENTRY = "META-INF/MANIFEST.MF";
+
+ /**
+ * Creates a {@link Reader} for the manifest in the supplied exploded JAR directory.
+ *
+ * @param directory the exploded JAR directory.
+ * @return the <code>Reader</code> or <code>null</code> if the manifest cannot be found.
+ */
+ public static final Reader manifestReaderFromExplodedDirectory(File directory) {
+ if (directory == null || !directory.isDirectory()) {
+ throw new IllegalArgumentException("Must supply a valid directory");
+ }
+ try {
+ File manifestFile = new File(directory.getAbsolutePath() + File.separator + MANIFEST_DIRECTORY_LOCATION);
+ if (manifestFile.exists()) {
+ return new FileReader(manifestFile);
+ } else {
+ return null;
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to read MANIFEST for exploded directory '" + directory.getAbsolutePath() + "'.", e);
+ }
+ }
+
+ /**
+ * Creates a {@link Reader} for the manifest in the supplied JAR file.
+ *
+ * @param file the JAR file.
+ * @return the <code>Reader</code> or <code>null</code> if the manifest cannot be found.
+ */
+ public static final Reader manifestReaderFromJar(File file) {
+ JarFile jar = null;
+ try {
+ jar = new JarFile(file);
+ JarEntry entry = jar.getJarEntry(MANIFEST_ENTRY);
+ if (entry != null) {
+ StringWriter writer = new StringWriter();
+ FileCopyUtils.copy(new InputStreamReader(jar.getInputStream(entry)), writer);
+ jar.close();
+ return new StringReader(writer.toString());
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot read MANIFEST.MF from jar '" + file.getAbsolutePath() + "'.", e);
+ } finally {
+ if (jar != null) {
+ try {
+ jar.close();
+ } catch (IOException ioe) {
+ throw new RuntimeException("Failed to close jar '" + file.getAbsolutePath() + "'.", ioe);
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionStateDumperTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionStateDumperTests.java
new file mode 100644
index 00000000..7e801cd8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/ResolutionStateDumperTests.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.osgi.service.resolver.State;
+import org.junit.Test;
+
+import org.eclipse.virgo.kernel.userregion.internal.equinox.ResolutionStateDumper;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.StateWriter;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.SystemStateAccessor;
+import org.eclipse.virgo.util.io.PathReference;
+import org.eclipse.virgo.util.io.ZipUtils;
+
+/**
+ */
+public class ResolutionStateDumperTests {
+
+ @Test
+ public void createDump() throws Exception {
+
+ State state = createNiceMock(State.class);
+ StubStateWriter writer = new StubStateWriter();
+
+ byte[] bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ writer.addBytes(state, bytes);
+
+ ResolutionStateDumper dumper = new ResolutionStateDumper(new StubSystemStateAccessor(state), writer);
+
+ File outputFile = new File("./target/dump.zip");
+ if (outputFile.exists()) {
+ assertTrue(outputFile.delete());
+ }
+
+ dumper.dump(outputFile);
+
+ assertTrue(outputFile.exists());
+
+ PathReference unzipLocation = new PathReference("target/dump");
+ if (unzipLocation.exists()) {
+ assertTrue(unzipLocation.delete(true));
+ }
+
+ ZipUtils.unzipTo(new PathReference(outputFile), unzipLocation);
+ File stateFile = new File("target/dump/state/state");
+
+ assertTrue(stateFile.exists());
+ assertEquals(10, stateFile.length());
+
+ byte[] actualBytes = new byte[10];
+
+ new FileInputStream(stateFile).read(actualBytes);
+
+ assertArrayEquals(bytes, actualBytes);
+ }
+
+ private static final class StubSystemStateAccessor implements SystemStateAccessor {
+
+ private final State systemState;
+
+ private StubSystemStateAccessor(State systemState) {
+ this.systemState = systemState;
+ }
+
+ public State getSystemState() {
+ return this.systemState;
+ }
+ }
+
+ private static final class StubStateWriter implements StateWriter {
+
+ private final Map<State, byte[]> stateBytes = new HashMap<State, byte[]>();
+
+ private void addBytes(State state, byte[] bytes) {
+ this.stateBytes.put(state, bytes);
+ }
+
+ public void writeState(State state, File outputDir) throws IOException {
+ byte[] bytes = this.stateBytes.get(state);
+ if (bytes == null) {
+ throw new IOException();
+ } else {
+ FileOutputStream fos = null;
+
+ try {
+ fos = new FileOutputStream(new File(outputDir, "state"));
+ fos.write(bytes);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StubHashGenerator.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StubHashGenerator.java
new file mode 100644
index 00000000..375d3d3c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/StubHashGenerator.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.File;
+
+import org.eclipse.virgo.repository.HashGenerator;
+import org.eclipse.virgo.repository.builder.ArtifactDescriptorBuilder;
+
+public class StubHashGenerator implements HashGenerator {
+
+ public void generateHash(ArtifactDescriptorBuilder artifactDescriptorBuilder, File artifactFile) {
+ // Do nothing
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/TestUtils.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/TestUtils.java
new file mode 100644
index 00000000..78eff8bb
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/TestUtils.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import java.io.File;
+
+/**
+ */
+public class TestUtils {
+
+ public static boolean deleteRecursively(File root) {
+ if (root.exists()) {
+ if (root.isDirectory()) {
+ File[] children = root.listFiles();
+ for (File file : children) {
+ deleteRecursively(file);
+ }
+ }
+ return root.delete();
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/UsesAnalyserTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/UsesAnalyserTests.java
new file mode 100644
index 00000000..84701566
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/equinox/UsesAnalyserTests.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.equinox;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+
+import org.eclipse.osgi.internal.baseadaptor.StateManager;
+import org.eclipse.osgi.service.resolver.ResolverError;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.UsesAnalyser;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.UsesAnalyser.AnalysedUsesConflict;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+import org.osgi.framework.Version;
+
+
+/**
+ */
+public class UsesAnalyserTests extends AbstractOsgiFrameworkLaunchingTests {
+
+ @Override
+ protected String getRepositoryConfigDirectory() {
+ return new File("src/test/resources/config/UsesAnalyserTests").getAbsolutePath();
+ }
+
+ @Test
+ public void testDependentConstraints() throws Exception {
+ Bundle p = install("dependent/bundles/p");
+ install("dependent/bundles/q");
+ install("dependent/bundles/r1");
+ install("dependent/bundles/r2");
+ install("dependent/bundles/s1");
+ install("dependent/bundles/s2");
+
+ try {
+ p.start();
+ } catch (BundleException ex) {
+ }
+
+ State systemState = ((StateManager) this.platformAdmin).getSystemState();
+
+ UsesAnalyser analyser = new UsesAnalyser();
+
+ ResolverError[] resolverErrors = analyser.getUsesResolverErrors(systemState, systemState.getBundle(p.getBundleId()));
+ assertNotNull("No uses errors found for bundle '" + p + "'.", resolverErrors);
+
+ AnalysedUsesConflict[] usesConflicts = analyser.getUsesConflicts(systemState, resolverErrors[0]);
+ assertNotNull("No conflicts found for bundle '" + p + "'.", usesConflicts);
+ printUsesConflicts(usesConflicts);
+
+ assertEquals("No, or more than one conflict discovered.", 1, usesConflicts.length);
+
+ assertEquals("q",usesConflicts[0].getUsesRootPackage().getName());
+
+ assertEquals("r",usesConflicts[0].getPackage().getName());
+ assertEquals(new Version("1.1.0"), usesConflicts[0].getPackage().getVersion());
+
+ assertEquals("r",usesConflicts[0].getConflictingPackage().getName());
+ assertEquals(new Version("1.0.0"), usesConflicts[0].getConflictingPackage().getVersion());
+ }
+
+ @Test
+ public void testInstallOrder() throws Exception {
+ install("install/bundles/s1");
+ install("install/bundles/s2");
+ Bundle q = install("install/bundles/q");
+ install("install/bundles/r1");
+ q.start();
+
+ Bundle p = install("install/bundles/p");
+ install("install/bundles/r2");
+ try {
+ p.start();
+ } catch (BundleException ex) {
+ }
+
+ State systemState = ((StateManager) this.platformAdmin).getSystemState();
+ UsesAnalyser analyser = new UsesAnalyser();
+
+ ResolverError[] resolverErrors = analyser.getUsesResolverErrors(systemState, systemState.getBundle(p.getBundleId()));
+ assertNotNull("No uses errors found for bundle '" + p + "'.", resolverErrors);
+
+ AnalysedUsesConflict[] usesConflicts = analyser.getUsesConflicts(systemState, resolverErrors[0]);
+
+ assertNotNull("No conflicts found for bundle '" + p + "'.", usesConflicts);
+ printUsesConflicts(usesConflicts);
+
+ assertEquals("No, or more than one conflict discovered.", 1, usesConflicts.length);
+
+ assertEquals("q",usesConflicts[0].getUsesRootPackage().getName());
+
+ assertEquals("r",usesConflicts[0].getPackage().getName());
+ assertEquals(new Version("1.1.0"), usesConflicts[0].getPackage().getVersion());
+
+ assertEquals("r",usesConflicts[0].getConflictingPackage().getName());
+ assertEquals(new Version("1.0.0"), usesConflicts[0].getConflictingPackage().getVersion());
+
+ }
+
+ @Test
+ public void transitiveUsesConstraint() throws Exception {
+ install("transitiveconstraint/tmD.jar");
+ install("transitiveconstraint/tmC.jar");
+ install("transitiveconstraint/tmB.jar");
+ Bundle a = install("transitiveconstraint/tmA.jar");
+ try {
+ a.start();
+ } catch (BundleException _) {
+ }
+
+ State systemState = ((StateManager) this.platformAdmin).getSystemState();
+ UsesAnalyser analyser = new UsesAnalyser();
+
+ ResolverError[] resolverErrors = analyser.getUsesResolverErrors(systemState, systemState.getBundle(a.getBundleId()));
+ assertNotNull("No uses errors found for bundle '" + a + "'.", resolverErrors);
+
+ AnalysedUsesConflict[] usesConflicts = analyser.getUsesConflicts(systemState, resolverErrors[0]);
+
+ assertNotNull("No conflicts found for bundle '" + a + "'.", usesConflicts);
+ printUsesConflicts(usesConflicts);
+
+ assertEquals("No, or more than one conflict discovered.", 1, usesConflicts.length);
+
+ assertEquals("p",usesConflicts[0].getUsesRootPackage().getName());
+
+ assertEquals("r",usesConflicts[0].getPackage().getName());
+ assertEquals(new Version("1.0.0"), usesConflicts[0].getPackage().getVersion());
+
+ assertEquals("r",usesConflicts[0].getConflictingPackage().getName());
+ assertEquals(new Version("0.0.0"), usesConflicts[0].getConflictingPackage().getVersion());
+ }
+
+ private static final void printUsesConflicts(AnalysedUsesConflict[] usesConflicts) {
+ int count = 0;
+ for (AnalysedUsesConflict a : usesConflicts) {
+ System.out.println("AnalysedUsesConflict element " + (count++));
+ for (String s : a.getConflictStatement()) {
+ System.out.println(" " + s);
+ }
+ }
+ }
+
+ private Bundle install(String subPath) throws BundleException {
+ String fullPath = "src/test/resources/uat/" + subPath;
+ String location = "reference:file:/" + new File(fullPath).getAbsolutePath();
+ return this.framework.getBundleContext().installBundle(location);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ImportExpansionHandlerTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ImportExpansionHandlerTests.java
new file mode 100644
index 00000000..2fbccb49
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/ImportExpansionHandlerTests.java
@@ -0,0 +1,751 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyBundleDependenciesException;
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyDependenciesException;
+import org.eclipse.virgo.kernel.userregion.internal.equinox.StubHashGenerator;
+import org.eclipse.virgo.kernel.userregion.internal.importexpansion.ImportExpansionHandler;
+
+
+import org.eclipse.virgo.kernel.artifact.bundle.BundleBridge;
+import org.eclipse.virgo.kernel.artifact.library.LibraryBridge;
+import org.eclipse.virgo.medic.test.eventlog.MockEventLogger;
+import org.eclipse.virgo.repository.ArtifactDescriptor;
+import org.eclipse.virgo.repository.ArtifactGenerationException;
+import org.eclipse.virgo.repository.Attribute;
+import org.eclipse.virgo.repository.Query;
+import org.eclipse.virgo.repository.Repository;
+import org.eclipse.virgo.repository.RepositoryAwareArtifactDescriptor;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+import org.eclipse.virgo.util.osgi.manifest.ImportedBundle;
+import org.eclipse.virgo.util.osgi.manifest.ImportedLibrary;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.Resolution;
+
+/**
+ */
+public class ImportExpansionHandlerTests {
+
+ private StubRepository repository = new StubRepository();
+
+ private static Set<String> packagesExportedBySystemBundle = new HashSet<String>();
+
+ static {
+ packagesExportedBySystemBundle.add("javax.crypto.spec");
+ packagesExportedBySystemBundle.add("javax.imageio");
+ packagesExportedBySystemBundle.add("javax.imageio.event");
+ }
+
+ @Before
+ public void populateRepository() throws ArtifactGenerationException {
+ BundleBridge bundleBridge = new BundleBridge(new StubHashGenerator());
+ LibraryBridge libraryBridge = new LibraryBridge(new StubHashGenerator());
+
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("../ivy-cache/repository/org.springframework/org.springframework.core/2.5.6.SEC01/org.springframework.core-2.5.6.SEC01.jar")));
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("../ivy-cache/repository/org.springframework/org.springframework.beans/2.5.6.SEC01/org.springframework.beans-2.5.6.SEC01.jar")));
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("src/test/resources/silht/bundles/fragmentOne")));
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("src/test/resources/silht/bundles/fragmentTwo")));
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("src/test/resources/silht/bundles/noexports")));
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("src/test/resources/silht/bundles/fragmentwithnoexports")));
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("src/test/resources/silht/bundles/host")));
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("src/test/resources/silht/bundles/overlapper")));
+ this.repository.addArtifactDescriptor(bundleBridge.generateArtifactDescriptor(new File("src/test/resources/silht/bundles/multi-version-export")));
+ this.repository.addArtifactDescriptor(libraryBridge.generateArtifactDescriptor(new File("src/test/resources/silht/libraries/spring.libd")));
+ this.repository.addArtifactDescriptor(libraryBridge.generateArtifactDescriptor(new File("src/test/resources/silht/libraries/com.foo.libd")));
+ this.repository.addArtifactDescriptor(libraryBridge.generateArtifactDescriptor(new File("src/test/resources/silht/libraries/missing.optional.bundle.libd")));
+ }
+
+ @Test
+ public void basicImportBundle() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport.getBundleSymbolicName()).andReturn("org.springframework.core").atLeastOnce();
+ expect(bundleImport.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(bundleImport.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(bundleImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(new ArrayList<ImportedLibrary>(), Arrays.asList(new ImportedBundle[] { bundleImport }), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertEquals(19, bundleManifest.getImportPackage().getImportedPackages().size());
+
+ List<ImportedPackage> packageImports = bundleManifest.getImportPackage().getImportedPackages();
+ for (ImportedPackage packageImport : packageImports) {
+ Map<String, String> attributes = packageImport.getAttributes();
+ assertTrue("org.springframework.core".equals(attributes.get("bundle-symbolic-name")));
+ assertEquals(new VersionRange("[2.5.6.SEC01,2.5.6.SEC01]"), new VersionRange(attributes.get("bundle-version")));
+ }
+ }
+
+ @Test
+ public void basicImportLibrary() throws UnableToSatisfyDependenciesException {
+
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("org.springframework").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(libraryImport.getResolution()).andReturn(Resolution.MANDATORY).anyTimes();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[0]), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertEquals(34, bundleManifest.getImportPackage().getImportedPackages().size());
+
+ List<ImportedPackage> packageImports = bundleManifest.getImportPackage().getImportedPackages();
+ for (ImportedPackage packageImport : packageImports) {
+ Map<String, String> attributes = packageImport.getAttributes();
+ if (packageImport.getPackageName().startsWith("org.springframework.beans")) {
+ assertTrue("org.springframework.beans".equals(attributes.get("bundle-symbolic-name")));
+ } else {
+ assertTrue("org.springframework.core".equals(attributes.get("bundle-symbolic-name")));
+ }
+ assertEquals(new VersionRange("[2.5.6.SEC01,2.5.6.SEC01]"), new VersionRange(attributes.get("bundle-version")));
+ }
+
+ }
+
+ @Test
+ public void basicImportFragmentBundle() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport.getBundleSymbolicName()).andReturn("com.foo.fragment.one").atLeastOnce();
+ expect(bundleImport.getVersion()).andReturn(new VersionRange("[1,1]")).atLeastOnce();
+ expect(bundleImport.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(bundleImport.getResolution()).andReturn(Resolution.MANDATORY).anyTimes();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[0]), Arrays.asList(new ImportedBundle[] { bundleImport }), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertEquals(1, bundleManifest.getImportPackage().getImportedPackages().size());
+
+ ImportedPackage packageImport = bundleManifest.getImportPackage().getImportedPackages().get(0);
+ Map<String, String> attributes = packageImport.getAttributes();
+ assertEquals("com.foo.host", attributes.get("bundle-symbolic-name"));
+ assertEquals(new VersionRange("[1.0, 2.0)"), new VersionRange(attributes.get("bundle-version")));
+ assertEquals("com.foo.host", packageImport.getPackageName());
+ }
+
+ @Test(expected = UnableToSatisfyBundleDependenciesException.class)
+ public void importLibraryReferringToNonExistentBundle() throws UnableToSatisfyDependenciesException, IOException {
+
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("bad.bundle").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[9,9]")).atLeastOnce();
+ expect(libraryImport.getResolution()).andReturn(Resolution.MANDATORY).anyTimes();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest(new StringReader(
+ "Manifest-Version: 1.0\nBundle-SymbolicName: test.bundle"));
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[0]), bundleManifest);
+ }
+
+ @Test
+ public void optionalImportBundle() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport.getBundleSymbolicName()).andReturn("org.springframework.dosnt.exist").atLeastOnce();
+ expect(bundleImport.getVersion()).andReturn(new VersionRange("[6.5,7.0)")).atLeastOnce();
+ expect(bundleImport.isApplicationImportScope()).andReturn(false).anyTimes();
+ expect(bundleImport.getResolution()).andReturn(Resolution.OPTIONAL).anyTimes();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[0]), Arrays.asList(new ImportedBundle[] { bundleImport }), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertTrue("" + bundleManifest.getImportPackage().getImportedPackages().size(),
+ bundleManifest.getImportPackage().getImportedPackages().size() == 0);
+ }
+
+ @Test
+ public void optionalImportLibrary() throws UnableToSatisfyDependenciesException {
+
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("org.springframework.dosnt.exist").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[6.5,7.0)")).atLeastOnce();
+ expect(libraryImport.getResolution()).andReturn(Resolution.OPTIONAL).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[0]), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertTrue(bundleManifest.getImportPackage().getImportedPackages().size() == 0);
+ }
+
+ @Test(expected = UnableToSatisfyDependenciesException.class)
+ public void optionalImportLibraryException() throws UnableToSatisfyDependenciesException {
+
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("org.springframework.dosnt.exist").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[6.5,7.0)")).atLeastOnce();
+ expect(libraryImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[0]), bundleManifest);
+ }
+
+ /**
+ * Test the expansion of the following import:
+ *
+ * Import-Library: com.foo;version="[1.0,2.0)"
+ *
+ * @throws UnableToSatisfyDependenciesException
+ */
+ @Test
+ public void importLibraryWithFragment() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("com.foo").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[1.0,2.0)")).atLeastOnce();
+ expect(libraryImport.getResolution()).andReturn(Resolution.MANDATORY).anyTimes();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[0]), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertImported(bundleManifest, Arrays.asList(new String[] { "com.foo.host", "com.foo.host.a", "com.foo.host.b", "com.foo.fragment.two" }),
+ Arrays.asList(new String[] { "1.5.0", "1.0.0", "1.0.0", "1.0.0" }));
+ }
+
+ @Test(expected = UnableToSatisfyDependenciesException.class)
+ public void incompatibleBundleVersions() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport1 = createAndStoreMock(ImportedLibrary.class, mocks);
+ expect(libraryImport1.getLibrarySymbolicName()).andReturn("org.springframework").atLeastOnce();
+ expect(libraryImport1.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(libraryImport1.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportedLibrary libraryImport2 = createAndStoreMock(ImportedLibrary.class, mocks);
+ expect(libraryImport2.getLibrarySymbolicName()).andReturn("org.springframework").atLeastOnce();
+ expect(libraryImport2.getVersion()).andReturn(new VersionRange("[2.0,2.5)")).atLeastOnce();
+ expect(libraryImport2.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+ try {
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport1, libraryImport2 }), Arrays.asList(new ImportedBundle[0]),
+ bundleManifest);
+ } catch (UnableToSatisfyDependenciesException e) {
+ throw e;
+ }
+ }
+
+ @Test(expected = UnableToSatisfyDependenciesException.class)
+ public void incompatibleIntersection() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport1 = createAndStoreMock(ImportedLibrary.class, mocks);
+ expect(libraryImport1.getLibrarySymbolicName()).andReturn("com.intersect.one").atLeastOnce();
+ expect(libraryImport1.getVersion()).andReturn(new VersionRange("[1.0,2.0)")).atLeastOnce();
+ expect(libraryImport1.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportedLibrary libraryImport2 = createAndStoreMock(ImportedLibrary.class, mocks);
+ expect(libraryImport2.getLibrarySymbolicName()).andReturn("com.intersect.two").atLeastOnce();
+ expect(libraryImport2.getVersion()).andReturn(new VersionRange("[1.0,2.0)")).atLeastOnce();
+ expect(libraryImport2.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+ try {
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport1, libraryImport2 }), Arrays.asList(new ImportedBundle[0]),
+ bundleManifest);
+ } catch (UnableToSatisfyDependenciesException e) {
+ throw e;
+ }
+ }
+
+ public void disjointImportedPackageAndImportedLibraryVersionRanges() throws UnableToSatisfyDependenciesException, IOException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("org.springframework").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(libraryImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest(new StringReader(
+ "Bundle-SymbolicName: B\nImport-Package: org.springframework.core;version=\"[1,2]\""));
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[0]), bundleManifest);
+ }
+
+ public void disjointImportedPackageAndImportedBundleVersionRanges() throws UnableToSatisfyDependenciesException, IOException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport.getBundleSymbolicName()).andReturn("org.springframework.bundle.spring.core").atLeastOnce();
+ expect(bundleImport.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(bundleImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+ expect(bundleImport.isApplicationImportScope()).andReturn(false);
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest(new StringReader(
+ "Bundle-SymbolicName: B\nImport-Package: org.springframework.core;version=\"[1,2]\""));
+ handler.expandImports(Arrays.asList(new ImportedLibrary[0]), Arrays.asList(new ImportedBundle[] { bundleImport }), bundleManifest);
+ }
+
+ @Test
+ public void packageImportAndImportedBundleVersionRangeIntersection() throws UnableToSatisfyDependenciesException, IOException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport.getBundleSymbolicName()).andReturn("org.springframework.core").atLeastOnce();
+ expect(bundleImport.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(bundleImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+ expect(bundleImport.isApplicationImportScope()).andReturn(false);
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest(new StringReader(
+ "Manifest-Version: 1.0, Bundle-SymbolicName: B\nImport-Package: org.springframework.core;version=\"[2.5,2.6)\""));
+ handler.expandImports(Arrays.asList(new ImportedLibrary[0]), Arrays.asList(new ImportedBundle[] { bundleImport }), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertEquals(19, bundleManifest.getImportPackage().getImportedPackages().size());
+ }
+
+ @Test
+ public void packageImportAndImportedLibraryVersionRangeIntersection() throws UnableToSatisfyDependenciesException, IOException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("org.springframework").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest(new StringReader(
+ "Manifest-Version: 1.0\nBundle-SymbolicName: B\nImport-Package: org.springframework.core;version=\"[2.5,2.6)\""));
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[0]), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertEquals(34, bundleManifest.getImportPackage().getImportedPackages().size());
+ }
+
+ @Test(expected = UnableToSatisfyDependenciesException.class)
+ public void disjointImportedBundleVersionRangeIntersection() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport1 = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport1.getBundleSymbolicName()).andReturn("org.springframework.core").atLeastOnce();
+ expect(bundleImport1.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(bundleImport1.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(bundleImport1.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportedBundle bundleImport2 = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport2.getBundleSymbolicName()).andReturn("org.springframework.core").atLeastOnce();
+ expect(bundleImport2.getVersion()).andReturn(new VersionRange("[2.0,2.5)")).atLeastOnce();
+ expect(bundleImport2.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(bundleImport2.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ try {
+ handler.expandImports(Arrays.asList(new ImportedLibrary[0]), Arrays.asList(new ImportedBundle[] { bundleImport1, bundleImport2 }),
+ bundleManifest);
+ } catch (UnableToSatisfyDependenciesException utsde) {
+ throw utsde;
+ }
+ }
+
+ @Test
+ public void overlappingBundleAndImportedLibrarys() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport.getBundleSymbolicName()).andReturn("org.springframework.core").atLeastOnce();
+ expect(bundleImport.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(bundleImport.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(bundleImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("org.springframework").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[2.5,3.0)")).atLeastOnce();
+ expect(libraryImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[] { bundleImport }),
+ bundleManifest);
+ }
+
+ @Test
+ public void importBundleWithNoExports() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport.getBundleSymbolicName()).andReturn("silht.bundles.noexports").atLeastOnce();
+ expect(bundleImport.getVersion()).andReturn(new VersionRange("[1.0,1.0]")).atLeastOnce();
+ expect(bundleImport.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(bundleImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+ handler.expandImports(Arrays.asList(new ImportedLibrary[0]), Arrays.asList(new ImportedBundle[] { bundleImport }), bundleManifest);
+ }
+
+ @Test
+ public void importBundleWithFragmentWithNoExports() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle hostImportedBundle = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(hostImportedBundle.getBundleSymbolicName()).andReturn("silht.bundles.noexports").atLeastOnce();
+ expect(hostImportedBundle.getVersion()).andReturn(new VersionRange("[1.0,1.0]")).atLeastOnce();
+ expect(hostImportedBundle.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(hostImportedBundle.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportedBundle fragmentImportedBundle = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(fragmentImportedBundle.getBundleSymbolicName()).andReturn("silht.bundles.fragmentwithnoexports").atLeastOnce();
+ expect(fragmentImportedBundle.getVersion()).andReturn(new VersionRange("[1.0,1.0]")).atLeastOnce();
+ expect(fragmentImportedBundle.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(fragmentImportedBundle.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+ handler.expandImports(Arrays.asList(new ImportedLibrary[0]),
+ Arrays.asList(new ImportedBundle[] { hostImportedBundle, fragmentImportedBundle }), bundleManifest);
+ assertTrue(bundleManifest.getImportPackage().getImportedPackages().size() == 0);
+ }
+
+ @Test
+ public void importBundleBetweenManifests() throws Exception {
+ List<BundleManifest> manifests = new ArrayList<BundleManifest>();
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest(new StringReader("Manifest-Version: 1.0\nImport-Bundle: com.foo"));
+ manifests.add(manifest);
+ manifests.add(BundleManifestFactory.createBundleManifest(new StringReader(
+ "Manifest-Version: 1.0\nExport-Package: com.foo;version=1.0\nBundle-SymbolicName: com.foo\n")));
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(this.repository, packagesExportedBySystemBundle, new MockEventLogger());
+ handler.expandImports(manifests);
+ assertImported(manifest, Arrays.asList(new String[] { "com.foo" }), Arrays.asList(new String[] { "1.0.0" }));
+ }
+
+ @Test
+ public void importBundleExportingPackagesExportedBySystemBundle() throws Exception {
+ List<BundleManifest> manifests = new ArrayList<BundleManifest>();
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest(new StringReader("Manifest-Version: 1.0\nImport-Bundle: overlapper"));
+ manifests.add(manifest);
+
+ MockEventLogger eventLogger = new MockEventLogger();
+ ImportExpansionHandler handler = new ImportExpansionHandler(this.repository, packagesExportedBySystemBundle, eventLogger);
+ handler.expandImports(manifests);
+ assertImported(manifest, Arrays.asList(new String[] { "javax.crypto.spec", "javax.imageio", "javax.imageio.event", "overlapper.pkg" }),
+ Arrays.asList(new String[] { "0.0.0", "0.0.0", "0.0.0", "0.0.0" }));
+
+ Assert.assertTrue("No events were logged.", eventLogger.getCalled());
+ Assert.assertTrue("The correct event was not logged.", eventLogger.containsLogged("UR0003W"));
+ }
+
+ @Test
+ public void importLibraryThatImportsMissingOptionalBundle() throws Exception {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedLibrary libraryImport = createAndStoreMock(ImportedLibrary.class, mocks);
+
+ expect(libraryImport.getLibrarySymbolicName()).andReturn("missing.optional.bundle").atLeastOnce();
+ expect(libraryImport.getVersion()).andReturn(new VersionRange("[1.0,1.0]")).atLeastOnce();
+ expect(libraryImport.getResolution()).andReturn(Resolution.MANDATORY).anyTimes();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[] { libraryImport }), Arrays.asList(new ImportedBundle[0]), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertEquals(0, bundleManifest.getImportPackage().getImportedPackages().size());
+ }
+
+ @Test
+ public void importBundleThatExportsPackageAtMultipleVersions() throws UnableToSatisfyDependenciesException {
+ List<Object> mocks = new ArrayList<Object>();
+
+ ImportedBundle bundleImport = createAndStoreMock(ImportedBundle.class, mocks);
+ expect(bundleImport.getBundleSymbolicName()).andReturn("multi.version.export").atLeastOnce();
+ expect(bundleImport.getVersion()).andReturn(new VersionRange("[1.0,2.0)")).atLeastOnce();
+ expect(bundleImport.isApplicationImportScope()).andReturn(false).atLeastOnce();
+ expect(bundleImport.getResolution()).andReturn(Resolution.MANDATORY).atLeastOnce();
+
+ ImportExpansionHandler handler = new ImportExpansionHandler(repository, packagesExportedBySystemBundle, new MockEventLogger());
+
+ replayMocks(mocks);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ handler.expandImports(Arrays.asList(new ImportedLibrary[0]), Arrays.asList(new ImportedBundle[] { bundleImport }), bundleManifest);
+
+ verifyMocks(mocks);
+
+ assertImported(bundleManifest, Arrays.asList(new String[] { "a" }), Arrays.asList(new String[] { "1.0.0" }));
+ }
+
+ private static <T> T createAndStoreMock(Class<T> classToMock, List<Object> mocks) {
+ T mock = createMock(classToMock);
+ mocks.add(mock);
+ return mock;
+ }
+
+ private static void replayMocks(List<Object> mocks) {
+ Object[] mocksArray = mocks.toArray(new Object[mocks.size()]);
+ replay(mocksArray);
+ }
+
+ private static void verifyMocks(List<Object> mocks) {
+ Object[] mocksArray = mocks.toArray(new Object[mocks.size()]);
+ verify(mocksArray);
+ }
+
+ private static void assertImported(BundleManifest bundleManifest, List<String> packages, List<String> versions) {
+ List<ImportedPackage> packageImports = bundleManifest.getImportPackage().getImportedPackages();
+ List<String> expectedPackages = new ArrayList<String>(packages);
+ List<String> expectedVersions = new ArrayList<String>(versions);
+ for (ImportedPackage packageImport : packageImports) {
+
+ String packageName = packageImport.getPackageName();
+ int index = expectedPackages.indexOf(packageName);
+ if (index > -1) {
+ Version expected = new Version(expectedVersions.get(index));
+ VersionRange actualRange = new VersionRange(packageImport.getAttributes().get("version"));
+
+ if (actualRange.includes(expected)) {
+ expectedPackages.remove(packageName);
+ expectedVersions.remove(index);
+ }
+ }
+ }
+
+ if (expectedPackages.size() > 0) {
+ fail("No import(s) were found for package(s) " + expectedPackages + " with version(s) " + expectedVersions + " in manifest: \n"
+ + bundleManifest);
+ }
+ }
+
+ private static final class StubRepository implements Repository {
+
+ private final List<RepositoryAwareArtifactDescriptor> artifactDescriptors = new ArrayList<RepositoryAwareArtifactDescriptor>();
+
+ /**
+ * {@inheritDoc}
+ */
+ public Query createQuery(String key, String value) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Query createQuery(String key, String value, Map<String, Set<String>> properties) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public RepositoryAwareArtifactDescriptor get(String type, String name, VersionRange versionRange) {
+ RepositoryAwareArtifactDescriptor bestMatch = null;
+
+ for (RepositoryAwareArtifactDescriptor candidate : this.artifactDescriptors) {
+ if (type.equals(candidate.getType()) && name.equals(candidate.getName()) && versionRange.includes(candidate.getVersion())) {
+ if (bestMatch == null || bestMatch.getVersion().compareTo(candidate.getVersion()) < 0) {
+ bestMatch = candidate;
+ }
+ }
+ }
+ return bestMatch;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop() {
+ throw new UnsupportedOperationException();
+ }
+
+ private void addArtifactDescriptor(ArtifactDescriptor descriptor) {
+ this.artifactDescriptors.add(new StubRepositoryAwareArtifactDescriptor(descriptor));
+ }
+
+ private static final class StubRepositoryAwareArtifactDescriptor implements RepositoryAwareArtifactDescriptor {
+
+ private final ArtifactDescriptor delegate;
+
+ /**
+ * @param delegate
+ */
+ private StubRepositoryAwareArtifactDescriptor(ArtifactDescriptor delegate) {
+ this.delegate = delegate;
+ }
+
+ public Set<Attribute> getAttribute(String name) {
+ return delegate.getAttribute(name);
+ }
+
+ public Set<Attribute> getAttributes() {
+ return delegate.getAttributes();
+ }
+
+ public String getFilename() {
+ return delegate.getFilename();
+ }
+
+ public String getName() {
+ return delegate.getName();
+ }
+
+ public String getType() {
+ return delegate.getType();
+ }
+
+ public java.net.URI getUri() {
+ return delegate.getUri();
+ }
+
+ public Version getVersion() {
+ return delegate.getVersion();
+ }
+
+ public String getRepositoryName() {
+ return "Unit test repository";
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImportsTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImportsTests.java
new file mode 100644
index 00000000..ec4e7d4c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/importexpansion/TrackedPackageImportsTests.java
@@ -0,0 +1,405 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.importexpansion;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+import org.eclipse.virgo.kernel.userregion.internal.importexpansion.StandardTrackedPackageImportsFactory;
+import org.eclipse.virgo.kernel.userregion.internal.importexpansion.TrackedPackageImports;
+import org.eclipse.virgo.kernel.userregion.internal.importexpansion.TrackedPackageImportsFactory;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.Resolution;
+
+/**
+ */
+public class TrackedPackageImportsTests {
+
+ private static final String TEST_SOURCE = "test source";
+
+ private final TrackedPackageImportsFactory trackedPackageImportsFactory = new StandardTrackedPackageImportsFactory();
+
+ @Test public void testTrivialMerge() throws ImportMergeException, IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader("bundle-symbolicname: A\nimport-package: p"));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader("bundle-symbolicname: B\nimport-package: p"));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ TrackedPackageImports empty = this.trackedPackageImportsFactory.createEmpty();
+
+ Assert.assertTrue(empty.isEmpty());
+ Assert.assertFalse(tpiB.isEmpty());
+ Assert.assertTrue(tpiA.isEquivalent(tpiB));
+ Assert.assertFalse(tpiA.isEquivalent(empty));
+
+ tpiB.merge(tpiA);
+ tpiB.merge(empty);
+ Map<String, ImportedPackage> mergedImports = convertImportedPackageListToMap(tpiB.getMergedImports());
+ ImportedPackage pImport = mergedImports.get("p");
+ Assert.assertNotNull("Missing merged import", pImport);
+ }
+
+ @Test public void testOverlappingVersionRanges() throws ImportMergeException, IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;version=\"[1,3]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;version=\"2\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ tpiB.merge(tpiA);
+ Map<String, ImportedPackage> mergedImports = convertImportedPackageListToMap(tpiB.getMergedImports());
+ ImportedPackage pImport = mergedImports.get("p");
+ Assert.assertNotNull("Missing merged import", pImport);
+ VersionRange v = new VersionRange(pImport.getAttributes().get("version"));
+ Assert.assertTrue("Incorrectly merged version", v.isFloorInclusive() && v.isCeilingInclusive() && v.getFloor().getMajor() == 2
+ && v.getCeiling().getMajor() == 3);
+ }
+
+ @Test public void testDisjointVersionRanges() throws IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;version=\"[1,2]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;version=\"[3,4]\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ try {
+ tpiB.merge(tpiA);
+ Assert.assertTrue("Exception should be thrown", false);
+ } catch (ImportMergeException e) {
+ System.out.println(e);
+ Assert.assertEquals("Incorrect conflicting package name", "p", e.getConflictingPackageName());
+ Assert.assertEquals("Incorrect sources", "bundle B, bundle A", e.getSources());
+ }
+ }
+
+ @Test public void testOverlappingBundleVersionRanges() throws ImportMergeException, IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;bundle-version=\"[1,3]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;bundle-version=\"2\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ tpiB.merge(tpiA);
+ Map<String, ImportedPackage> mergedImports = convertImportedPackageListToMap(tpiB.getMergedImports());
+ ImportedPackage pImport = mergedImports.get("p");
+ Assert.assertNotNull("Missing merged import", pImport);
+ VersionRange v = new VersionRange(pImport.getAttributes().get("bundle-version"));
+ Assert.assertTrue("Incorrectly merged version", v.isFloorInclusive() && v.isCeilingInclusive() && v.getFloor().getMajor() == 2
+ && v.getCeiling().getMajor() == 3);
+ }
+
+ @Test public void testDisjointBundleVersionRanges() throws IOException {
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;bundle-version=\"[1,2]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;bundle-version=\"[3,4]\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ try {
+ tpiB.merge(tpiA);
+ Assert.assertTrue("Exception should be thrown", false);
+ } catch (ImportMergeException e) {
+ System.out.println(e);
+ Assert.assertEquals("Incorrect conflicting package name", "p", e.getConflictingPackageName());
+ Assert.assertEquals("Incorrect sources", "bundle B, bundle A", e.getSources());
+ }
+ }
+
+ @Test public void testResolution() throws ImportMergeException, IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;resolution:=optional"));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;resolution:=mandatory"));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ tpiB.merge(tpiA);
+ Map<String, ImportedPackage> mergedImports = convertImportedPackageListToMap(tpiB.getMergedImports());
+ ImportedPackage pImport = mergedImports.get("p");
+ Assert.assertNotNull("Missing merged import", pImport);
+ Assert.assertTrue("Incorrectly merged resolution", pImport.getResolution() == Resolution.MANDATORY);
+ }
+
+ @Test public void testConsistentAttributes() throws ImportMergeException, IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;a1=v1;a2=v2"));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;a1=v1;a3=v3"));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ tpiB.merge(tpiA);
+ Map<String, ImportedPackage> mergedImports = convertImportedPackageListToMap(tpiB.getMergedImports());
+ ImportedPackage pImport = mergedImports.get("p");
+ Assert.assertNotNull("Missing merged import", pImport);
+ Map<String, String> pAttrs = pImport.getAttributes();
+ Assert.assertEquals("Incorrectly merged attribute for a1", "v1", pAttrs.get("a1"));
+ Assert.assertEquals("Incorrectly merged attribute for a2", "v2", pAttrs.get("a2"));
+ Assert.assertEquals("Incorrectly merged attribute for a3", "v3", pAttrs.get("a3"));
+ }
+
+ @Test public void testInconsistentAttributes() throws ImportMergeException, IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader("bundle-symbolicname: A\nimport-package: p;a1=v1"));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader("bundle-symbolicname: B\nimport-package: p;a1=v2"));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ BundleManifest manifestC = BundleManifestFactory.createBundleManifest(new StringReader("bundle-symbolicname: C\nimport-package: q"));
+ TrackedPackageImports tpiC = this.trackedPackageImportsFactory.create(manifestC);
+ tpiB.merge(tpiC);
+
+ try {
+ tpiB.merge(tpiA);
+ Assert.assertTrue("Exception should be thrown", false);
+ } catch (ImportMergeException e) {
+ System.out.println(e);
+ Assert.assertEquals("Incorrect conflicting package name", "p", e.getConflictingPackageName());
+ Assert.assertEquals("Incorrect sources", "bundle B, bundle A", e.getSources());
+ }
+ }
+
+ @Test public void testTrivialAddition() throws ImportMergeException, IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader("bundle-symbolicname: A\nimport-package: p"));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ ImportedPackage packageImport = BundleManifestFactory.createBundleManifest().getImportPackage().addImportedPackage("p");
+ List<ImportedPackage> packageImports = new ArrayList<ImportedPackage>();
+ packageImports.add(packageImport);
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(packageImports, TEST_SOURCE);
+
+ tpiA.merge(tpiB);
+ Map<String, ImportedPackage> mergedImports = convertImportedPackageListToMap(tpiA.getMergedImports());
+ ImportedPackage pImport = mergedImports.get("p");
+ Assert.assertNotNull("Missing merged import", pImport);
+ }
+
+ @Test public void testClashingAddition() throws IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;attr=x"));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ ImportedPackage packageImport = BundleManifestFactory.createBundleManifest().getImportPackage().addImportedPackage("p");
+ packageImport.getAttributes().put("attr", "y");
+ List<ImportedPackage> packageImports = new ArrayList<ImportedPackage>();
+ packageImports.add(packageImport);
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(packageImports, TEST_SOURCE);
+
+ try {
+ tpiA.merge(tpiB);
+ Assert.assertTrue("Exception should be thrown", false);
+ } catch (ImportMergeException e) {
+ System.out.println(e);
+ Assert.assertEquals("Incorrect conflicting package name", "p", e.getConflictingPackageName());
+ Assert.assertEquals("Incorrect sources", "bundle A, " + TEST_SOURCE, e.getSources());
+ }
+ }
+
+ @Test public void testOverlappingVersionRangesInCollection() throws ImportMergeException, IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;version=\"[1,3]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;version=\"2\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ TrackedPackageImports tpiC = this.trackedPackageImportsFactory.createCollector();
+
+ tpiC.merge(tpiB);
+ tpiC.merge(tpiA);
+
+ Map<String, ImportedPackage> mergedImports = convertImportedPackageListToMap(tpiC.getMergedImports());
+ ImportedPackage pImport = mergedImports.get("p");
+ Assert.assertNotNull("Missing merged import", pImport);
+ VersionRange v = new VersionRange(pImport.getAttributes().get("version"));
+ Assert.assertTrue("Incorrectly merged version", v.isFloorInclusive() && v.isCeilingInclusive() && v.getFloor().getMajor() == 2
+ && v.getCeiling().getMajor() == 3);
+ }
+
+ @Test public void testDisjointVersionRangesInCollection() throws IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;version=\"[1,2]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;version=\"[3,4]\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ TrackedPackageImports tpiC = this.trackedPackageImportsFactory.createCollector();
+
+ try {
+ tpiC.merge(tpiB);
+ tpiC.merge(tpiA);
+ Assert.assertTrue("Exception should be thrown", false);
+ } catch (ImportMergeException e) {
+ System.out.println(e);
+ Assert.assertEquals("Incorrect conflicting package name", "p", e.getConflictingPackageName());
+ Assert.assertEquals("Incorrect sources", "bundle B, bundle A", e.getSources());
+ }
+ }
+
+ @Test public void testThreeWayClash() throws IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;version=\"[1,2]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;version=\"[2,3]\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ TrackedPackageImports tpiC = this.trackedPackageImportsFactory.createCollector();
+
+ BundleManifest manifestD = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: D\nimport-package: p;version=\"[3,3]\""));
+ TrackedPackageImports tpiD = this.trackedPackageImportsFactory.create(manifestD);
+
+ try {
+ tpiC.merge(tpiB);
+ tpiC.merge(tpiA);
+ tpiD.merge(tpiC);
+ Assert.assertTrue("Exception should be thrown", false);
+ } catch (ImportMergeException e) {
+ System.out.println(e);
+ Assert.assertEquals("Incorrect conflicting package name", "p", e.getConflictingPackageName());
+ /*
+ * Note that in a N-way clash of version ranges, there must be a pair of version ranges that clash, but the
+ * current implementation simply reports all N.
+ */
+ Assert.assertEquals("Incorrect sources", "bundle D, bundle B, bundle A", e.getSources());
+ }
+ }
+
+ @Test public void testThreeWayNestedClash() throws IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;version=\"[1,2]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;version=\"[2,3]\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ BundleManifest manifestD = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: D\nimport-package: p;version=\"[3,3]\""));
+ TrackedPackageImports tpiD = this.trackedPackageImportsFactory.create(manifestD);
+
+ TrackedPackageImports tpiContainer = this.trackedPackageImportsFactory.createContainer("container");
+
+ try {
+ tpiContainer.merge(tpiA);
+ tpiContainer.merge(tpiB);
+ tpiD.merge(tpiContainer);
+ Assert.assertTrue("Exception should be thrown", false);
+ } catch (ImportMergeException e) {
+ System.out.println(e);
+ Assert.assertEquals("Incorrect conflicting package name", "p", e.getConflictingPackageName());
+ /*
+ * Note that in a N-way clash of version ranges, there must be a pair of version ranges that clash, but the
+ * current implementation simply reports all N.
+ */
+ Assert.assertEquals("Incorrect sources", "bundle D, container(bundle A, bundle B)", e.getSources());
+ }
+ }
+
+ @Test public void testFourWayNestedClashInACollector() throws IOException {
+
+ BundleManifest manifestA = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: A\nimport-package: p;version=\"[1,2]\""));
+ TrackedPackageImports tpiA = this.trackedPackageImportsFactory.create(manifestA);
+
+ BundleManifest manifestB = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: B\nimport-package: p;version=\"[2,3]\""));
+ TrackedPackageImports tpiB = this.trackedPackageImportsFactory.create(manifestB);
+
+ BundleManifest manifestC = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: C\nimport-package: p;version=\"[3,4]\""));
+ TrackedPackageImports tpiC = this.trackedPackageImportsFactory.create(manifestC);
+
+ BundleManifest manifestD = BundleManifestFactory.createBundleManifest(new StringReader(
+ "bundle-symbolicname: D\nimport-package: p;version=\"[4,5]\""));
+ TrackedPackageImports tpiD = this.trackedPackageImportsFactory.create(manifestD);
+
+ TrackedPackageImports tpiContainerX = this.trackedPackageImportsFactory.createContainer("containerX");
+
+ TrackedPackageImports tpiContainerY = this.trackedPackageImportsFactory.createContainer("containerY");
+
+ TrackedPackageImports tpiCollector = this.trackedPackageImportsFactory.createCollector();
+
+ try {
+ tpiContainerX.merge(tpiA);
+ tpiContainerX.merge(tpiB);
+ tpiContainerY.merge(tpiC);
+ tpiContainerY.merge(tpiD);
+ tpiCollector.merge(tpiContainerX);
+ tpiCollector.merge(tpiContainerY);
+ Assert.assertTrue("Exception should be thrown", false);
+ } catch (ImportMergeException e) {
+ System.out.println(e);
+ Assert.assertEquals("Incorrect conflicting package name", "p", e.getConflictingPackageName());
+ /*
+ * Note that in a N-way clash of version ranges, there must be a pair of version ranges that clash, but the
+ * current implementation simply reports all N.
+ */
+ Assert.assertEquals("Incorrect sources", "containerX(bundle A, bundle B), containerY(bundle C, bundle D)", e.getSources());
+ }
+ }
+
+ /**
+ * Convert a given list of package imports with no duplicate package names to a map of package name to
+ * {@link ImportedPackage}.
+ *
+ * @param importedPackages a list of <code>PackageImport</code>
+ * @return a map of package name to <code>PackageImport</code>
+ */
+ private static Map<String, ImportedPackage> convertImportedPackageListToMap(List<ImportedPackage> importedPackages) {
+ Map<String, ImportedPackage> initialPackageImports = new HashMap<String, ImportedPackage>();
+ for (ImportedPackage importedPackage : importedPackages) {
+ Assert.assertNull(initialPackageImports.put(importedPackage.getPackageName(), importedPackage));
+ }
+ return initialPackageImports;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/BundleDescriptionComparatorTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/BundleDescriptionComparatorTests.java
new file mode 100644
index 00000000..ea5f1da6
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/BundleDescriptionComparatorTests.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.GenericDescription;
+import org.eclipse.osgi.service.resolver.GenericSpecification;
+import org.eclipse.osgi.service.resolver.HostSpecification;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.NativeCodeSpecification;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.BundleDescriptionComparator;
+import org.junit.Test;
+import org.osgi.framework.Version;
+
+
+public class BundleDescriptionComparatorTests {
+
+ private final BundleDescriptionComparator bundleDescriptionComparator = new BundleDescriptionComparator();
+
+ @Test
+ public void matchingBundleSymbolicNameAndVersion() {
+ StubBundleDescription bd1 = new StubBundleDescription("foo", new Version(1,2,3));
+ StubBundleDescription bd2 = new StubBundleDescription("foo", new Version(1,2,3));
+ assertEquals(0, bundleDescriptionComparator.compare(bd1, bd2));
+ }
+
+ @Test
+ public void differingBundleSymbolicName() {
+ StubBundleDescription bd1 = new StubBundleDescription("foo", new Version(1,2,3));
+ StubBundleDescription bd2 = new StubBundleDescription("bar", new Version(1,2,3));
+
+ assertDifferent(bd1, bd2);
+ }
+
+ @Test
+ public void differingVersion() {
+ StubBundleDescription bd1 = new StubBundleDescription("foo", new Version(1,2,3));
+ StubBundleDescription bd2 = new StubBundleDescription("foo", new Version(2,3,4));
+
+ assertDifferent(bd1, bd2);
+ }
+
+ @Test
+ public void differingBundleSymbolicNameAndVersion() {
+ StubBundleDescription bd1 = new StubBundleDescription("foo", new Version(1,2,3));
+ StubBundleDescription bd2 = new StubBundleDescription("bar", new Version(2,3,4));
+
+ assertDifferent(bd1, bd2);
+ }
+
+ private void assertDifferent(StubBundleDescription bd1, StubBundleDescription bd2) {
+ int bd1ToBd2Comparison = this.bundleDescriptionComparator.compare(bd1, bd2);
+ assertTrue(bd1ToBd2Comparison != 0);
+
+ int bd2ToBd1Comparison = this.bundleDescriptionComparator.compare(bd2, bd1);
+ assertTrue(bd2ToBd1Comparison != 0);
+
+ assertFalse(bd2ToBd1Comparison < 0 && bd1ToBd2Comparison < 0);
+ }
+
+ private static final class StubBundleDescription implements BundleDescription {
+
+ private final String symbolicName;
+
+ private final Version version;
+
+ StubBundleDescription(String symbolicName, Version version) {
+ this.symbolicName = symbolicName;
+ this.version = version;
+ }
+
+ public boolean attachFragments() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean dynamicFragments() {
+ throw new UnsupportedOperationException();
+ }
+
+ public long getBundleId() {
+ throw new UnsupportedOperationException();
+ }
+
+ public State getContainingState() {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleDescription[] getDependents() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String[] getExecutionEnvironments() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ExportPackageDescription[] getExportPackages() {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleDescription[] getFragments() {
+ throw new UnsupportedOperationException();
+ }
+
+ public GenericDescription[] getGenericCapabilities() {
+ throw new UnsupportedOperationException();
+ }
+
+ public GenericSpecification[] getGenericRequires() {
+ throw new UnsupportedOperationException();
+ }
+
+ public HostSpecification getHost() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ImportPackageSpecification[] getImportPackages() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getLocation() {
+ throw new UnsupportedOperationException();
+ }
+
+ public NativeCodeSpecification getNativeCodeSpecification() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getPlatformFilter() {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleSpecification[] getRequiredBundles() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ExportPackageDescription[] getResolvedImports() {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleDescription[] getResolvedRequires() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ExportPackageDescription[] getSelectedExports() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ExportPackageDescription[] getSubstitutedExports() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getSymbolicName() {
+ return this.symbolicName;
+ }
+
+ public Object getUserObject() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean hasDynamicImports() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isRemovalPending() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isResolved() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isSingleton() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setUserObject(Object userObject) {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleDescription getSupplier() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Version getVersion() {
+ return this.version;
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiBundleTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiBundleTests.java
new file mode 100644
index 00000000..cd61e36e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiBundleTests.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiExportPackage;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiImportPackage;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiRequiredBundle;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiBundle;
+
+/**
+ */
+public class StandardQuasiBundleTests {
+
+ private static final String BSN = "bsn";
+
+ private static final Version BV = new Version("4.3.2.ga");
+
+ private static final long BID = 2341341;
+
+ private StubBundleDescription bundleDescription;
+
+ @Before
+ public void setUp() {
+ bundleDescription = new StubBundleDescription();
+ }
+
+ @Test
+ public void testSymbolicName() {
+ bundleDescription.setBundleSymbolicName(BSN);
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ Assert.assertEquals(BSN, qb.getSymbolicName());
+ }
+
+ @Test
+ public void testVersion() {
+ bundleDescription.setVersion(BV);
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ Assert.assertEquals(BV, qb.getVersion());
+ }
+
+ @Test
+ public void testIsResolved() {
+ bundleDescription.setResolved(true);
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ Assert.assertEquals(true, qb.isResolved());
+ }
+
+ @Test
+ public void testBundleId() {
+ bundleDescription.setBundleId(BID);
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ Assert.assertEquals(BID, qb.getBundleId());
+ }
+
+ @Test
+ public void testFragments() {
+ bundleDescription.addFragment(new StubBundleDescription("f1"));
+ bundleDescription.addFragment(new StubBundleDescription("f2"));
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ List<QuasiBundle> fragments = qb.getFragments();
+ Assert.assertEquals(2, fragments.size());
+ Assert.assertEquals("f1", fragments.get(0).getSymbolicName());
+ Assert.assertEquals("f2", fragments.get(1).getSymbolicName());
+ }
+
+ @Test
+ public void testNoFragments() {
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ List<QuasiBundle> fragments = qb.getFragments();
+ Assert.assertEquals(0, fragments.size());
+ }
+
+ @Test
+ public void testHosts() {
+ bundleDescription.addHost(new StubBundleDescription("h1"));
+ bundleDescription.addHost(new StubBundleDescription("h2"));
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ List<QuasiBundle> hosts = qb.getHosts();
+ Assert.assertEquals(2, hosts.size());
+ Assert.assertEquals("h1", hosts.get(0).getSymbolicName());
+ Assert.assertEquals("h2", hosts.get(1).getSymbolicName());
+ }
+
+ @Test
+ public void testNoHosts() {
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ List<QuasiBundle> hosts = qb.getHosts();
+ Assert.assertNull(hosts);
+ }
+
+ @Test
+ public void testExportPackages() {
+ bundleDescription.addExportPackage(new StubExportPackageDescription("e1"));
+ bundleDescription.addExportPackage(new StubExportPackageDescription("e2"));
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ List<QuasiExportPackage> exportPackages = qb.getExportPackages();
+ Assert.assertEquals(2, exportPackages.size());
+ Assert.assertEquals("e1", exportPackages.get(0).getPackageName());
+ Assert.assertEquals("e2", exportPackages.get(1).getPackageName());
+ }
+
+ @Test
+ public void testImportPackages() {
+ bundleDescription.addImportPackage(new StubImportPackageSpecification("i1"));
+ bundleDescription.addImportPackage(new StubImportPackageSpecification("i2"));
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ List<QuasiImportPackage> importPackages = qb.getImportPackages();
+ Assert.assertEquals(2, importPackages.size());
+ Assert.assertEquals("i1", importPackages.get(0).getPackageName());
+ Assert.assertEquals("i2", importPackages.get(1).getPackageName());
+ }
+
+ @Test
+ public void testRequiredBundles() {
+ bundleDescription.addRequiredBundle(new StubBundleSpecification("b1"));
+ bundleDescription.addRequiredBundle(new StubBundleSpecification("b2"));
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ List<QuasiRequiredBundle> requiredBundles = qb.getRequiredBundles();
+ Assert.assertEquals(2, requiredBundles.size());
+ Assert.assertEquals("b1", requiredBundles.get(0).getRequiredBundleName());
+ Assert.assertEquals("b2", requiredBundles.get(1).getRequiredBundleName());
+ }
+
+ @Test
+ public void testDependents() {
+ bundleDescription.addDependent(new StubBundleDescription("b1"));
+ bundleDescription.addDependent(new StubBundleDescription("b2"));
+ QuasiBundle qb = new StandardQuasiBundle(bundleDescription, null, null);
+ List<QuasiBundle> requiredBundles = qb.getDependents();
+ Assert.assertEquals(2, requiredBundles.size());
+ Assert.assertEquals("b1", requiredBundles.get(0).getSymbolicName());
+ Assert.assertEquals("b2", requiredBundles.get(1).getSymbolicName());
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiExportPackageTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiExportPackageTests.java
new file mode 100644
index 00000000..1fb2e508
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiExportPackageTests.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.util.List;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiExportPackage;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiImportPackage;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiBundle;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiExportPackage;
+
+/**
+ */
+public class StandardQuasiExportPackageTests {
+
+ private static final String BSN = "bsn";
+
+ private static final String PN = "p";
+
+ private static final Version VERSION = new Version("5.4.3");
+
+ private StubBundleDescription bundleDescription;
+
+ private StubExportPackageDescription exportPackage;
+
+ private QuasiBundle qb;
+
+ private StubStateHelper stateHelper;
+
+ @Before
+ public void setUp() {
+ this.bundleDescription = new StubBundleDescription();
+ this.bundleDescription.setBundleSymbolicName(BSN);
+ this.stateHelper = new StubStateHelper();
+ this.qb = new StandardQuasiBundle(this.bundleDescription, null, this.stateHelper);
+ this.exportPackage = new StubExportPackageDescription(PN);
+ }
+
+ @Test
+ public void testPackageName() {
+ QuasiExportPackage qep = new StandardQuasiExportPackage(this.exportPackage, this.qb);
+ Assert.assertEquals(PN, qep.getPackageName());
+ }
+
+ @Test
+ public void testVersion() {
+ this.exportPackage.setVersion(VERSION);
+ QuasiExportPackage qep = new StandardQuasiExportPackage(this.exportPackage, this.qb);
+ Assert.assertEquals(PN, qep.getPackageName());
+ }
+
+ @Test
+ public void testExportingBundle() {
+ QuasiExportPackage qep = new StandardQuasiExportPackage(this.exportPackage, this.qb);
+ Assert.assertEquals(BSN, qep.getExportingBundle().getSymbolicName());
+ }
+
+ @Test
+ public void testConsumers() {
+ StubBundleDescription dependentBundle = new StubBundleDescription();
+ StubImportPackageSpecification ips = new StubImportPackageSpecification(PN);
+ dependentBundle.addImportPackage(ips);
+ ips.setSupplier(this.exportPackage);
+ this.stateHelper.setDependentBundles(new BundleDescription[] { dependentBundle });
+ this.exportPackage.setExporter(this.bundleDescription);
+ QuasiExportPackage qep = new StandardQuasiExportPackage(this.exportPackage, this.qb);
+ List<QuasiImportPackage> consumers = qep.getConsumers();
+ Assert.assertEquals(1, consumers.size());
+ Assert.assertEquals(PN, consumers.get(0).getPackageName());
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiImportPackageTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiImportPackageTests.java
new file mode 100644
index 00000000..eac79807
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiImportPackageTests.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiImportPackage;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiBundle;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiImportPackage;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+/**
+ */
+public class StandardQuasiImportPackageTests {
+
+ private static final String STRING_VERSION_RANGE = "[2,4)";
+
+ private static final org.eclipse.osgi.service.resolver.VersionRange RESOLVER_VERSION_RANGE = new org.eclipse.osgi.service.resolver.VersionRange(
+ STRING_VERSION_RANGE);
+
+ private static final VersionRange VERSION_RANGE = new VersionRange(STRING_VERSION_RANGE);
+
+ private static final String BSN = "bsn";
+
+ private static final String PROVIDER_BSN = "provider-bsn";
+
+ private static final String CONSUMER_BSN = "consumer-bsn";
+
+ private static final String PN = "p";
+
+ private StubBundleDescription bundleDescription;
+
+ private StubImportPackageSpecification importPackage;
+
+ private QuasiBundle qb;
+
+ private StubStateHelper stateHelper;
+
+ private StubExportPackageDescription exportPackage;
+
+ @Before
+ public void setUp() {
+ this.bundleDescription = new StubBundleDescription();
+ this.bundleDescription.setBundleSymbolicName(BSN);
+ this.stateHelper = new StubStateHelper();
+ this.qb = new StandardQuasiBundle(this.bundleDescription, null, this.stateHelper);
+ this.importPackage = new StubImportPackageSpecification(PN);
+ this.exportPackage = new StubExportPackageDescription(PN);
+ }
+
+ @Test
+ public void testPackageName() {
+ QuasiImportPackage qip = new StandardQuasiImportPackage(this.importPackage, this.qb);
+ Assert.assertEquals(PN, qip.getPackageName());
+ }
+
+ @Test
+ public void testVersionConstraint() {
+ org.eclipse.osgi.service.resolver.VersionRange versionRange = RESOLVER_VERSION_RANGE;
+ this.importPackage.setVersionRange(versionRange);
+ QuasiImportPackage qip = new StandardQuasiImportPackage(this.importPackage, qb);
+ Assert.assertEquals(VERSION_RANGE, qip.getVersionConstraint());
+ }
+
+ @Test
+ public void testResolved() {
+ this.importPackage.setResolved(true);
+ QuasiImportPackage qip = new StandardQuasiImportPackage(this.importPackage, this.qb);
+ Assert.assertTrue(qip.isResolved());
+ }
+
+ @Test
+ public void testNotResolved() {
+ QuasiImportPackage qip = new StandardQuasiImportPackage(this.importPackage, this.qb);
+ Assert.assertFalse(qip.isResolved());
+ }
+
+ @Test
+ public void testResolvedProvider() {
+ StubBaseDescription supplier = new StubBaseDescription();
+ StubBundleDescription bundleSupplier = new StubBundleDescription();
+ bundleSupplier.setBundleSymbolicName(PROVIDER_BSN);
+ supplier.setSupplier(bundleSupplier);
+ this.importPackage.setSupplier(supplier);
+ this.importPackage.setResolved(true);
+ StubBundleDescription bundleConsumer = new StubBundleDescription();
+ bundleConsumer.setBundleSymbolicName(CONSUMER_BSN);
+ this.importPackage.setBundle(bundleConsumer);
+ this.importPackage.setSupplier(this.exportPackage);
+ QuasiImportPackage qip = new StandardQuasiImportPackage(this.importPackage, qb);
+ this.exportPackage.setExporter(bundleSupplier);
+ Assert.assertEquals(PN, qip.getProvider().getPackageName());
+ }
+
+ @Test
+ public void testUnresolvedProvider() {
+ QuasiImportPackage qip = new StandardQuasiImportPackage(this.importPackage, qb);
+ Assert.assertNull(qip.getProvider());
+ }
+
+ @Test
+ public void testImportingBundle() {
+ QuasiImportPackage qip = new StandardQuasiImportPackage(this.importPackage, qb);
+ Assert.assertEquals(qb, qip.getImportingBundle());
+ }
+
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiRequiredBundleTests.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiRequiredBundleTests.java
new file mode 100644
index 00000000..1d8b7bfb
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StandardQuasiRequiredBundleTests.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiRequiredBundle;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiBundle;
+import org.eclipse.virgo.kernel.userregion.internal.quasi.StandardQuasiRequiredBundle;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+/**
+ */
+public class StandardQuasiRequiredBundleTests {
+
+ private static final String STRING_VERSION_RANGE = "[2,4)";
+
+ private static final org.eclipse.osgi.service.resolver.VersionRange RESOLVER_VERSION_RANGE = new org.eclipse.osgi.service.resolver.VersionRange(
+ STRING_VERSION_RANGE);
+
+ private static final VersionRange VERSION_RANGE = new VersionRange(STRING_VERSION_RANGE);
+
+ private static final String BSN = "bsn";
+
+ private static final String REQUIRED_BSN = "required-bsn";
+
+ private StubBundleDescription bundleDescription;
+
+ private QuasiBundle qb;
+
+ private StubStateHelper stateHelper;
+
+ private StubBundleSpecification bundleSpecification;
+
+ @Before
+ public void setUp() {
+ this.bundleDescription = new StubBundleDescription();
+ this.bundleDescription.setBundleSymbolicName(BSN);
+ this.bundleSpecification = new StubBundleSpecification(REQUIRED_BSN);
+ this.stateHelper = new StubStateHelper();
+ this.qb = new StandardQuasiBundle(this.bundleDescription, null, this.stateHelper);
+ }
+
+ @Test
+ public void testRequiredBundleName() {
+ QuasiRequiredBundle qrb = new StandardQuasiRequiredBundle(this.bundleSpecification, this.qb);
+ Assert.assertEquals(REQUIRED_BSN, qrb.getRequiredBundleName());
+ }
+
+ @Test
+ public void testVersionConstraint() {
+ this.bundleSpecification.setVersionRange(RESOLVER_VERSION_RANGE);
+ QuasiRequiredBundle qrb = new StandardQuasiRequiredBundle(this.bundleSpecification, this.qb);
+ Assert.assertEquals(VERSION_RANGE, qrb.getVersionConstraint());
+ }
+
+ @Test
+ public void testResolved() {
+ this.bundleSpecification.setResolved(true);
+ QuasiRequiredBundle qrb = new StandardQuasiRequiredBundle(this.bundleSpecification, this.qb);
+ Assert.assertTrue(qrb.isResolved());
+ }
+
+ @Test
+ public void testUnresolved() {
+ QuasiRequiredBundle qrb = new StandardQuasiRequiredBundle(this.bundleSpecification, this.qb);
+ Assert.assertFalse(qrb.isResolved());
+ }
+
+ @Test
+ public void testUnresolvedProvider() {
+ QuasiRequiredBundle qrb = new StandardQuasiRequiredBundle(this.bundleSpecification, this.qb);
+ Assert.assertNull(qrb.getProvider());
+ }
+
+ @Test
+ public void testResolvedProvider() {
+ StubBundleDescription requiredBundleDescription = new StubBundleDescription();
+ requiredBundleDescription.setBundleSymbolicName(REQUIRED_BSN);
+ QuasiBundle rqb = new StandardQuasiBundle(requiredBundleDescription, null, this.stateHelper);
+ QuasiRequiredBundle qrb = new StandardQuasiRequiredBundle(this.bundleSpecification, this.qb);
+ this.bundleSpecification.setResolved(true);
+ StubBaseDescription supplier = new StubBaseDescription();
+ supplier.setSupplier(requiredBundleDescription);
+ this.bundleSpecification.setSupplier(supplier);
+ Assert.assertEquals(rqb, qrb.getProvider());
+ }
+
+ @Test
+ public void testRequiringBundle() {
+ QuasiRequiredBundle qrb = new StandardQuasiRequiredBundle(this.bundleSpecification, this.qb);
+ Assert.assertEquals(this.qb, qrb.getRequiringBundle());
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBaseDescription.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBaseDescription.java
new file mode 100644
index 00000000..e7a6479b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBaseDescription.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.osgi.service.resolver.BaseDescription;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.osgi.framework.Version;
+
+
+/**
+ * TODO Document StubBaseDescription
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * TODO Document concurrent semantics of StubBaseDescription
+ *
+ */
+public class StubBaseDescription implements BaseDescription {
+
+ private BundleDescription supplier;
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription getSupplier() {
+ return this.supplier;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Version getVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setSupplier(BundleDescription supplier) {
+ this.supplier = supplier;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBundleDescription.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBundleDescription.java
new file mode 100644
index 00000000..a5834a95
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBundleDescription.java
@@ -0,0 +1,348 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.osgi.service.resolver.BaseDescription;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.GenericDescription;
+import org.eclipse.osgi.service.resolver.GenericSpecification;
+import org.eclipse.osgi.service.resolver.HostSpecification;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.NativeCodeSpecification;
+import org.eclipse.osgi.service.resolver.State;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.osgi.framework.Version;
+
+/**
+ */
+public class StubBundleDescription implements BundleDescription {
+
+ private String bsn;
+
+ private Version bv;
+
+ private boolean resolved;
+
+ private long bid;
+
+ private List<BundleDescription> fragments = new ArrayList<BundleDescription>();
+
+ private List<BundleDescription> hosts = null;
+
+ private List<ExportPackageDescription> epds = new ArrayList<ExportPackageDescription>();
+
+ private List<ImportPackageSpecification> ipss = new ArrayList<ImportPackageSpecification>();
+
+ private List<BundleSpecification> rbs = new ArrayList<BundleSpecification>();
+
+ private List<BundleDescription> dependents = new ArrayList<BundleDescription>();
+
+ public StubBundleDescription() {
+ }
+
+ public StubBundleDescription(String bsn) {
+ this.bsn = bsn;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean attachFragments() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean dynamicFragments() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getBundleId() {
+ return this.bid;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public State getContainingState() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription[] getDependents() {
+ return this.dependents.toArray(new BundleDescription[this.dependents.size()]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getExecutionEnvironments() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ExportPackageDescription[] getExportPackages() {
+ return this.epds.toArray(new ExportPackageDescription[this.epds.size()]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription[] getFragments() {
+ return this.fragments.toArray(new BundleDescription[this.fragments.size()]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public GenericDescription[] getGenericCapabilities() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public GenericSpecification[] getGenericRequires() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public HostSpecification getHost() {
+ return StubBundleDescription.this.hosts == null ? null : new HostSpecification() {
+
+ public BundleDescription[] getHosts() {
+ return StubBundleDescription.this.hosts.toArray(new BundleDescription[StubBundleDescription.this.hosts.size()]);
+ }
+
+ public boolean isMultiHost() {
+ throw new UnsupportedOperationException();
+ }
+
+ public BundleDescription getBundle() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ public BaseDescription getSupplier() {
+ throw new UnsupportedOperationException();
+ }
+
+ public VersionRange getVersionRange() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isResolved() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ throw new UnsupportedOperationException();
+ }
+
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ImportPackageSpecification[] getImportPackages() {
+ return this.ipss.toArray(new ImportPackageSpecification[this.ipss.size()]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLocation() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public NativeCodeSpecification getNativeCodeSpecification() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getPlatformFilter() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleSpecification[] getRequiredBundles() {
+ return this.rbs.toArray(new BundleSpecification[this.rbs.size()]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ExportPackageDescription[] getResolvedImports() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription[] getResolvedRequires() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ExportPackageDescription[] getSelectedExports() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ExportPackageDescription[] getSubstitutedExports() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getSymbolicName() {
+ return this.bsn;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object getUserObject() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasDynamicImports() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isRemovalPending() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolved() {
+ return this.resolved;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isSingleton() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setUserObject(Object userObject) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription getSupplier() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Version getVersion() {
+ return this.bv;
+ }
+
+ public void setBundleSymbolicName(String bsn) {
+ this.bsn = bsn;
+ }
+
+ public void setVersion(Version bv) {
+ this.bv = bv;
+ }
+
+ public void setResolved(boolean resolved) {
+ this.resolved = resolved;
+ }
+
+ public void setBundleId(long bid) {
+ this.bid = bid;
+ }
+
+ public void addFragment(BundleDescription f) {
+ this.fragments.add(f);
+ }
+
+ public void addHost(StubBundleDescription bd) {
+ if (this.hosts == null) {
+ this.hosts = new ArrayList<BundleDescription>();
+ }
+ this.hosts.add(bd);
+ }
+
+ public void addExportPackage(ExportPackageDescription epd) {
+ this.epds.add(epd);
+ }
+
+ public void addImportPackage(ImportPackageSpecification ips) {
+ this.ipss.add(ips);
+ }
+
+ public void addRequiredBundle(BundleSpecification bs) {
+ this.rbs.add(bs);
+ }
+
+ public void addDependent(BundleDescription d) {
+ this.dependents.add(d);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBundleSpecification.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBundleSpecification.java
new file mode 100644
index 00000000..23aacf92
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubBundleSpecification.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.osgi.service.resolver.BaseDescription;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.VersionRange;
+
+/**
+ */
+public class StubBundleSpecification implements BundleSpecification {
+
+ private String name;
+
+ private VersionRange versionRange;
+
+ private boolean resolved = false;
+
+ private BaseDescription supplier;
+
+ public StubBundleSpecification(String name) {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isExported() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isOptional() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription getBundle() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BaseDescription getSupplier() {
+ return this.supplier;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VersionRange getVersionRange() {
+ return this.versionRange;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolved() {
+ return this.resolved;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setVersionRange(VersionRange versionRange) {
+ this.versionRange = versionRange;
+ }
+
+ public void setResolved(boolean resolved) {
+ this.resolved = resolved;
+ }
+
+ public void setSupplier(BaseDescription supplier) {
+ this.supplier = supplier;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubExportPackageDescription.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubExportPackageDescription.java
new file mode 100644
index 00000000..c09bcaf0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubExportPackageDescription.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.osgi.framework.Version;
+
+/**
+ */
+public class StubExportPackageDescription extends StubParameterised implements ExportPackageDescription {
+
+ private String name;
+
+ private Version version;
+
+ private BundleDescription exporter;
+
+ public StubExportPackageDescription(String name) {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription getExporter() {
+ return this.exporter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isRoot() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription getSupplier() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Version getVersion() {
+ return this.version;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+
+ public void setExporter(BundleDescription exporter) {
+ this.exporter = exporter;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubImportPackageSpecification.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubImportPackageSpecification.java
new file mode 100644
index 00000000..bb1c3edc
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubImportPackageSpecification.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.osgi.service.resolver.BaseDescription;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.VersionRange;
+
+/**
+ */
+public class StubImportPackageSpecification extends StubParameterised implements ImportPackageSpecification {
+
+ private String name;
+
+ private VersionRange versionRange;
+
+ private boolean resolved = false;
+
+ private BaseDescription supplier;
+
+ private BundleDescription bundle;
+
+ public StubImportPackageSpecification(String name) {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getBundleSymbolicName() {
+ return "supplierBundle";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VersionRange getBundleVersionRange() {
+ return new VersionRange("[2.0.0, 4.0.0)");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription getBundle() {
+ return this.bundle;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BaseDescription getSupplier() {
+ return this.supplier;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VersionRange getVersionRange() {
+ return this.versionRange;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolved() {
+ return this.resolved ;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isSatisfiedBy(BaseDescription supplier) {
+ return this.name.equals(supplier.getName());
+ }
+
+ public void setVersionRange(VersionRange versionRange) {
+ this.versionRange = versionRange;
+ }
+
+ public void setResolved(boolean b) {
+ this.resolved = b;
+ }
+
+ public void setBundle(BundleDescription bundle) {
+ this.bundle = bundle;
+ }
+
+ public void setSupplier(BaseDescription supplier) {
+ this.supplier = supplier;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubParameterised.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubParameterised.java
new file mode 100644
index 00000000..437fbdaa
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubParameterised.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ */
+public abstract class StubParameterised {
+
+ @SuppressWarnings("unchecked")
+ Map attributes = new HashMap();
+
+ @SuppressWarnings("unchecked")
+ Map directives = new HashMap();
+
+ @SuppressWarnings("unchecked")
+ public Map getAttributes() {
+ return this.attributes;
+ }
+
+ public Object getDirective(String key) {
+ return this.directives.get(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map getDirectives() {
+ return this.directives;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubStateHelper.java b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubStateHelper.java
new file mode 100644
index 00000000..9b4fdae5
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/java/org/eclipse/virgo/kernel/userregion/internal/quasi/StubStateHelper.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.userregion.internal.quasi;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.BundleSpecification;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.eclipse.osgi.service.resolver.HostSpecification;
+import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
+import org.eclipse.osgi.service.resolver.StateHelper;
+import org.eclipse.osgi.service.resolver.VersionConstraint;
+
+/**
+ */
+public class StubStateHelper implements StateHelper {
+
+ private BundleDescription[] dependentBundles;
+
+ private ExportPackageDescription[] visiblePackages;
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getAccessCode(BundleDescription bundle, ExportPackageDescription export) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription[] getDependentBundles(BundleDescription[] bundles) {
+ return this.dependentBundles;
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleDescription[] getPrerequisites(BundleDescription[] bundles) {
+ throw new UnsupportedOperationException();
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VersionConstraint[] getUnsatisfiedConstraints(BundleDescription bundle) {
+ throw new UnsupportedOperationException();
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VersionConstraint[] getUnsatisfiedLeaves(BundleDescription[] bundles) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ExportPackageDescription[] getVisiblePackages(BundleDescription bundle) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ExportPackageDescription[] getVisiblePackages(BundleDescription bundle, int options) {
+ return this.visiblePackages;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolvable(ImportPackageSpecification specification) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolvable(BundleSpecification specification) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isResolvable(HostSpecification specification) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object[][] sortBundles(BundleDescription[] toSort) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setDependentBundles(BundleDescription[] dependentBundles) {
+ this.dependentBundles = dependentBundles;
+ }
+
+ public void setVisiblePackages(ExportPackageDescription[] visiblePackages) {
+ this.visiblePackages = visiblePackages;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/EquinoxOsgiFrameworkTests/faulty/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/EquinoxOsgiFrameworkTests/faulty/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..ba572dfe
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/EquinoxOsgiFrameworkTests/faulty/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Bundle-SymbolicName: faulty
+Bundle-ManifestVersion: 2
+Bundle-Version: 1
+Import-Package: alpha
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/KernelBundleClassLoaderTests/dependant.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/KernelBundleClassLoaderTests/dependant.jar
new file mode 100644
index 00000000..54b40064
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/KernelBundleClassLoaderTests/dependant.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/KernelBundleClassLoaderTests/depender.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/KernelBundleClassLoaderTests/depender.jar
new file mode 100644
index 00000000..e522f570
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/KernelBundleClassLoaderTests/depender.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/eight/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/eight/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..b0b00a3c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/eight/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.eight
+Export-Package: install.eight
+Import-Package: install.six, install.seven
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/error/missingimport/bundle/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/error/missingimport/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..e34088cc
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/error/missingimport/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.error.import
+Import-Package: does.not.exist
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/five/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/five/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..30bb1e39
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/five/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.five
+Export-Package: install.five
+Require-Bundle: install.three
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/four/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/four/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..cd43c08c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/four/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Import-Package: install.three
+Bundle-SymbolicName: install.four
+Export-Package: install.four
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/fragments/unresolvable/bundle/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/fragments/unresolvable/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..9b7951f0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/fragments/unresolvable/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: fragments.unresolvable.host
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/fragments/unresolvable/fragment/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/fragments/unresolvable/fragment/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..8f3a9850
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/fragments/unresolvable/fragment/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: fragments.unresolvable.frag
+Fragment-Host: fragments.unresolvable.host
+Import-Package: no.chance
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/a/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/a/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..002aa5b4
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/a/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.multi.a
+Export-Package: multi;version="1.2"
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/b/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/b/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..a2397014
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/b/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.multi.b
+Export-Package: multi;version="1.3"
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/bundle/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..214cb9cf
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/multi/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.multi.bundle
+Import-Package: multi;version="[1.2,1.4)"
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/nine/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/nine/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..9b7affdf
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/nine/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Import-Package: install.one
+Bundle-SymbolicName: install.nine
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/one/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/one/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..7eccd98e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/one/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.one
+Export-Package: install.one
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatindep/bundle/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatindep/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..3545444f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatindep/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.optional.dep.bundle
+Import-Package: pqr;resolution:=optional
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatindep/dep/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatindep/dep/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..15c08075
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatindep/dep/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.optional.dep.dep
+Export-Package: pqr
+Import-Package: foo
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatisfied/bundle/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatisfied/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..f8f126c8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/optional/notsatisfied/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.optional.ns
+Import-Package: p;resolution:=optional
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/outsiderepo/bundle/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/outsiderepo/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..42820160
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/outsiderepo/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.outsiderepo
+Import-Package: install.standalone
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/seven/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/seven/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..18490ccf
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/seven/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.seven
+Export-Package: install.seven
+Import-Package: install.six
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/six/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/six/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..e5ff528f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/six/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Import-Package: install.seven,install.eight
+Bundle-SymbolicName: install.six
+Export-Package: install.six
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/three/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/three/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..c5a43e25
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/three/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Import-Package: install.two,install.one
+Bundle-SymbolicName: install.three
+Export-Package: install.three
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/two/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/two/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..1573224d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/two/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.two
+Export-Package: install.two
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/bundle/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..fd0eb33a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.uses.bundle
+Import-Package: uses.spring;version="[2.5.5, 2.5.5]",uses.hibernate;version="[3.2.6, 3.2.6]"
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/hibernate325/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/hibernate325/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..e68b24a7
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/hibernate325/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: install.uses.hibernate325
+Export-Package: uses.hibernate;version="3.2.5"
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/hibernate326/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/hibernate326/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..53d31a25
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/hibernate326/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: install.uses.hibernate326
+Export-Package: uses.hibernate;version="3.2.6"
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/spring/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/spring/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..365df33d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/uses/spring/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: install.uses.spring
+Export-Package: uses.spring;version="2.5.5";uses:="uses.hibernate"
+Import-Package: uses.hibernate;version="[3.2.5, 3.2.5]"
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/x/high/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/x/high/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..f48164d0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/x/high/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.x
+Bundle-Version: 1.4.6
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/x/low/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/x/low/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..195710c2
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/install/x/low/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.x
+Bundle-Version: 1.4.7
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/standalone/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/standalone/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..b703d84a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/bit/standalone/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Bundle-SymbolicName: install.standalone
+Export-Package: install.standalone
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/BundleInstallationTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/BundleInstallationTests/repository.properties
new file mode 100644
index 00000000..55c3ddbc
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/BundleInstallationTests/repository.properties
@@ -0,0 +1,43 @@
+bundles.type=external
+bundles.searchPattern=../{bundle}/target/classes
+
+ivy-cache-bundles.type=external
+ivy-cache-bundles.searchPattern=../${ivy.cache::ivy-cache}/repository/{org}/{name}/{version}/{bundle}.jar
+
+ivy-cache-libraries.type=external
+ivy-cache-libraries.searchPattern=../${ivy.cache::ivy-cache}/repository/{org}/{name}/{version}/{library}.libd
+
+src-test-resources-frag.type=external
+src-test-resources-frag.searchPattern=src/test/resources/frag/*
+
+src-test-resources-jars.type=external
+src-test-resources-jars.searchPattern=src/test/resources/jars/*.jar
+
+src-test-resources-bit-install-x.type=external
+src-test-resources-bit-install-x.searchPattern=src/test/resources/bit/install/x/*
+
+src-test-resources-bit-install.type=external
+src-test-resources-bit-install.searchPattern=src/test/resources/bit/install/*
+
+src-test-resources-fail.type=external
+src-test-resources-fail.searchPattern=src/test/resources/fail/*
+
+src-test-resources-bit-install-optional.type=external
+src-test-resources-bit-install-optional.searchPattern=src/test/resources/bit/install/optional/*/*
+
+src-test-resources-bit-install-error.type=external
+src-test-resources-bit-install-error.searchPattern=src/test/resources/bit/install/error/*/*
+
+src-test-resources-bit-install-fragments.type=external
+src-test-resources-bit-install-fragments.searchPattern=src/test/resources/bit/install/fragments/*/*
+
+src-test-resources-bit-install-outsiderepo.type=external
+src-test-resources-bit-install-outsiderepo.searchPattern=src/test/resources/bit/install/outsiderepo/*
+
+src-test-resources-bit-install-multi.type=external
+src-test-resources-bit-install-multi.searchPattern=src/test/resources/bit/install/multi/*
+
+src-test-resources-bit-install-uses.type=external
+src-test-resources-bit-install-uses.searchPattern=src/test/resources/bit/install/uses/*
+
+chain=bundles,ivy-cache-bundles,ivy-cache-libraries,src-test-resources-frag,src-test-resources-jars,src-test-resources-bit-install-x,src-test-resources-bit-install,src-test-resources-fail,src-test-resources-bit-install-optional,src-test-resources-bit-install-error,src-test-resources-bit-install-fragments,src-test-resources-bit-install-outsiderepo,src-test-resources-bit-install-multi,src-test-resources-bit-install-uses
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/BundleUpdateTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/BundleUpdateTests/repository.properties
new file mode 100644
index 00000000..55c3ddbc
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/BundleUpdateTests/repository.properties
@@ -0,0 +1,43 @@
+bundles.type=external
+bundles.searchPattern=../{bundle}/target/classes
+
+ivy-cache-bundles.type=external
+ivy-cache-bundles.searchPattern=../${ivy.cache::ivy-cache}/repository/{org}/{name}/{version}/{bundle}.jar
+
+ivy-cache-libraries.type=external
+ivy-cache-libraries.searchPattern=../${ivy.cache::ivy-cache}/repository/{org}/{name}/{version}/{library}.libd
+
+src-test-resources-frag.type=external
+src-test-resources-frag.searchPattern=src/test/resources/frag/*
+
+src-test-resources-jars.type=external
+src-test-resources-jars.searchPattern=src/test/resources/jars/*.jar
+
+src-test-resources-bit-install-x.type=external
+src-test-resources-bit-install-x.searchPattern=src/test/resources/bit/install/x/*
+
+src-test-resources-bit-install.type=external
+src-test-resources-bit-install.searchPattern=src/test/resources/bit/install/*
+
+src-test-resources-fail.type=external
+src-test-resources-fail.searchPattern=src/test/resources/fail/*
+
+src-test-resources-bit-install-optional.type=external
+src-test-resources-bit-install-optional.searchPattern=src/test/resources/bit/install/optional/*/*
+
+src-test-resources-bit-install-error.type=external
+src-test-resources-bit-install-error.searchPattern=src/test/resources/bit/install/error/*/*
+
+src-test-resources-bit-install-fragments.type=external
+src-test-resources-bit-install-fragments.searchPattern=src/test/resources/bit/install/fragments/*/*
+
+src-test-resources-bit-install-outsiderepo.type=external
+src-test-resources-bit-install-outsiderepo.searchPattern=src/test/resources/bit/install/outsiderepo/*
+
+src-test-resources-bit-install-multi.type=external
+src-test-resources-bit-install-multi.searchPattern=src/test/resources/bit/install/multi/*
+
+src-test-resources-bit-install-uses.type=external
+src-test-resources-bit-install-uses.searchPattern=src/test/resources/bit/install/uses/*
+
+chain=bundles,ivy-cache-bundles,ivy-cache-libraries,src-test-resources-frag,src-test-resources-jars,src-test-resources-bit-install-x,src-test-resources-bit-install,src-test-resources-fail,src-test-resources-bit-install-optional,src-test-resources-bit-install-error,src-test-resources-bit-install-fragments,src-test-resources-bit-install-outsiderepo,src-test-resources-bit-install-multi,src-test-resources-bit-install-uses
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/EquinoxOsgiFrameworkTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/EquinoxOsgiFrameworkTests/repository.properties
new file mode 100644
index 00000000..58341ec0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/EquinoxOsgiFrameworkTests/repository.properties
@@ -0,0 +1,4 @@
+src-test-resources-faulty.type=external
+src-test-resources-faulty.searchPattern=src/test/resources/faulty/*.jar
+
+chain=src-test-resources-faulty
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/ImportExpansionHandlerTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/ImportExpansionHandlerTests/repository.properties
new file mode 100644
index 00000000..f5854cdf
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/ImportExpansionHandlerTests/repository.properties
@@ -0,0 +1,13 @@
+bundles.type=external
+bundles.searchPattern=../{bundle}/target/classes
+
+src-test-resources-silht-libraries.type=external
+src-test-resources-silht-libraries.searchPattern=src/test/resources/silht/libraries/{name}.libd
+
+src-test-resources-silht-bundles.type=external
+src-test-resources-silht-bundles.searchPattern=src/test/resources/silht/bundles/{name}
+
+src-test-resources-ieh.type=external
+src-test-resources-ieh.searchPattern=src/test/resources/ieh/*
+
+chain=bundles,src-test-resources-silht-libraries,src-test-resources-silht-bundles,src-test-resources-ieh
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/InstallFromLocationTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/InstallFromLocationTests/repository.properties
new file mode 100644
index 00000000..4b83bd7d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/InstallFromLocationTests/repository.properties
@@ -0,0 +1,4 @@
+src-test-resources-empty.type=external
+src-test-resources-empty.searchPattern=src/test/resources/empty
+
+chain=src-test-resources-empty
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/KernelBundleClassLoaderTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/KernelBundleClassLoaderTests/repository.properties
new file mode 100644
index 00000000..a5e712c4
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/KernelBundleClassLoaderTests/repository.properties
@@ -0,0 +1,13 @@
+bundles.type=external
+bundles.searchPattern=../{bundle}/target/classes
+
+ivy-cache-bundles.type=external
+ivy-cache-bundles.searchPattern=../${ivy.cache::ivy-cache}/repository/{org}/{name}/{version}/{bundle}.jar
+
+ivy-cache-libraries.type=external
+ivy-cache-libraries.searchPattern=../${ivy.cache::ivy-cache}/repository/{org}/{name}/{version}/{library}.libd
+
+src-test-resources-resources.type=external
+src-test-resources-resources.searchPattern=src/test/resources/KernelBundleClassLoaderTests/*
+
+chain=bundles,ivy-cache-bundles,ivy-cache-libraries,src-test-resources-resources
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/LoadTimeWeavingTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/LoadTimeWeavingTests/repository.properties
new file mode 100644
index 00000000..611e33e5
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/LoadTimeWeavingTests/repository.properties
@@ -0,0 +1,4 @@
+src-test-resources-ltw.type=external
+src-test-resources-ltw.searchPattern=src/test/resources/ltw/{bundle}.jar
+
+chain=src-test-resources-ltw
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/ResolutionStateDumperTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/ResolutionStateDumperTests/repository.properties
new file mode 100644
index 00000000..ca067ea3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/ResolutionStateDumperTests/repository.properties
@@ -0,0 +1,4 @@
+src-test-resources-rfd.type=external
+src-test-resources-rfd.searchPattern=src/test/resources/rfd/{name}
+
+chain=src-test-resources-rfd
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/config/UsesAnalyserTests/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/UsesAnalyserTests/repository.properties
new file mode 100644
index 00000000..ca067ea3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/config/UsesAnalyserTests/repository.properties
@@ -0,0 +1,4 @@
+src-test-resources-rfd.type=external
+src-test-resources-rfd.searchPattern=src/test/resources/rfd/{name}
+
+chain=src-test-resources-rfd
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/config/repository.properties b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/config/repository.properties
new file mode 100644
index 00000000..22f6874e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/config/repository.properties
@@ -0,0 +1,7 @@
+bundles.type=external
+bundles.searchPattern=src/test/resources/dependency-locator/repository/bundles/{bundle}
+
+libraries.type=external
+libraries.searchPattern=src/test/resources/dependency-locator/repository/libraries/{library}
+
+chain=bundles,libraries
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/java5-server.profile b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/java5-server.profile
new file mode 100644
index 00000000..4fc9e2bb
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/java5-server.profile
@@ -0,0 +1,152 @@
+org.osgi.framework.system.packages = \
+ org.eclipse.virgo.server.serviceability.logging;version="1.0",\
+ org.eclipse.virgo.server.serviceability.output;version="1.0",\
+ org.eclipse.virgo.server.serviceability.tracing;version="1.0",\
+ javax.accessibility,\
+ javax.activity,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml,\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.xpath,\
+ org.apache.commons.logging;version="1.1.1",\
+ org.apache.commons.logging.impl;version="1.1.1",\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.slf4j;version="1.5.0",\
+ org.slf4j.helpers;version="1.5.0",\
+ org.slf4j.impl;version="1.5.0",\
+ org.slf4j.spi;version="1.5.0",\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ com_cenqua_clover,\
+ com.cenqua.*,\
+ com.yourkit.*,\
+ org.eclipse.virgo.server.bootstrap,\
+ org.eclipse.virgo.server.kernel.bootstrap,\
+ org.eclipse.virgo.server.osgi.*,\
+ com.sun.*,\
+ javax.xml.*,\
+ org.apache.xerces.jaxp.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5
+osgi.java.profile.name = SpringSource-AP-Java5
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/java6-server.profile b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/java6-server.profile
new file mode 100644
index 00000000..5e41d4fe
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/java6-server.profile
@@ -0,0 +1,216 @@
+org.osgi.framework.system.packages = \
+ org.eclipse.virgo.server.serviceability.logging;version="1.0",\
+ org.eclipse.virgo.server.serviceability.output;version="1.0",\
+ org.eclipse.virgo.server.serviceability.tracing;version="1.0",\
+ javax.accessibility,\
+ javax.activation,\
+ javax.activation;version="1.1.0",\
+ javax.activity,\
+ javax.annotation,\
+ javax.annotation;version="1.0.0",\
+ javax.annotation.processing,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.jws,\
+ javax.jws;version="2.0",\
+ javax.jws.soap,\
+ javax.jws.soap;version="2.0",\
+ javax.lang.model,\
+ javax.lang.model.element,\
+ javax.lang.model.type,\
+ javax.lang.model.util,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.script,\
+ javax.script;version="1.1",\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.tools,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml,\
+ javax.xml;version="1.0.1",\
+ javax.xml.bind,\
+ javax.xml.bind;version="2.0",\
+ javax.xml.bind.annotation,\
+ javax.xml.bind.annotation;version="2.0",\
+ javax.xml.bind.annotation.adapters,\
+ javax.xml.bind.annotation.adapters;version="2.0",\
+ javax.xml.bind.attachment,\
+ javax.xml.bind.attachment;version="2.0",\
+ javax.xml.bind.helpers,\
+ javax.xml.bind.helpers;version="2.0",\
+ javax.xml.bind.util,\
+ javax.xml.bind.util;version="2.0",\
+ javax.xml.crypto,\
+ javax.xml.crypto;version="1.0",\
+ javax.xml.crypto.dom,\
+ javax.xml.crypto.dom;version="1.0",\
+ javax.xml.crypto.dsig,\
+ javax.xml.crypto.dsig;version="1.0",\
+ javax.xml.crypto.dsig.dom,\
+ javax.xml.crypto.dsig.dom;version="1.0",\
+ javax.xml.crypto.dsig.keyinfo,\
+ javax.xml.crypto.dsig.keyinfo;version="1.0",\
+ javax.xml.crypto.dsig.spec,\
+ javax.xml.crypto.dsig.spec;version="1.0",\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.soap,\
+ javax.xml.soap;version="1.3.0",\
+ javax.xml.stream,\
+ javax.xml.stream;version="1.0.1",\
+ javax.xml.stream.events,\
+ javax.xml.stream.events;version="1.0.1",\
+ javax.xml.stream.util,\
+ javax.xml.stream.util;version="1.0.1",\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.ws,\
+ javax.xml.ws;version="2.1.1",\
+ javax.xml.ws.handler,\
+ javax.xml.ws.handler;version="2.1.1",\
+ javax.xml.ws.handler.soap,\
+ javax.xml.ws.handler.soap;version="2.1.1",\
+ javax.xml.ws.http,\
+ javax.xml.ws.http;version="2.1.1",\
+ javax.xml.ws.soap,\
+ javax.xml.ws.soap;version="2.1.1",\
+ javax.xml.ws.spi,\
+ javax.xml.ws.spi;version="2.1.1",\
+ javax.xml.xpath,\
+ org.apache.commons.logging;version="1.1.1",\
+ org.apache.commons.logging.impl;version="1.1.1",\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.slf4j;version="1.5.0",\
+ org.slf4j.helpers;version="1.5.0",\
+ org.slf4j.impl;version="1.5.0",\
+ org.slf4j.spi;version="1.5.0",\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ com_cenqua_clover,\
+ com.cenqua.*,\
+ com.yourkit.*,\
+ org.eclipse.virgo.server.bootstrap,\
+ org.eclipse.virgo.server.bootstrap.*,\
+ org.eclipse.virgo.server.kernel.bootstrap,\
+ org.eclipse.virgo.server.osgi.*,\
+ com.sun.*,\
+ javax.xml.*,\
+ org.apache.xerces.jaxp.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5,\
+ JavaSE-1.6
+osgi.java.profile.name = SpringSource-AP-Java6
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/org.eclipse.osgi-3.4.0.v20080529-1200.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/org.eclipse.osgi-3.4.0.v20080529-1200.jar
new file mode 100644
index 00000000..3d1f125b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/org.eclipse.osgi-3.4.0.v20080529-1200.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/server.profile b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/server.profile
new file mode 100644
index 00000000..2f28c085
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/dependency-locator/lib/server.profile
@@ -0,0 +1,146 @@
+org.osgi.framework.system.packages = \
+ org.eclipse.virgo.server.serviceability.logging;version="1.0",\
+ org.eclipse.virgo.server.serviceability.output;version="1.0",\
+ org.eclipse.virgo.server.serviceability.tracing;version="1.0",\
+ javax.accessibility,\
+ javax.activity,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml,\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.xpath,\
+ org.apache.commons.logging;version="1.1.1",\
+ org.apache.commons.logging.impl;version="1.1.1",\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.slf4j;version="1.5.0",\
+ org.slf4j.helpers;version="1.5.0",\
+ org.slf4j.impl;version="1.5.0",\
+ org.slf4j.spi;version="1.5.0",\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.events,\
+ org.w3c.dom.ls,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ com_cenqua_clover,\
+ com.cenqua.*,\
+ com.yourkit.*,\
+ org.eclipse.virgo.server.bootstrap,\
+ org.eclipse.virgo.server.kernel.bootstrap,\
+ org.eclipse.virgo.server.osgi.*,\
+ com.sun.*,\
+ javax.xml.*,\
+ org.apache.xerces.jaxp.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5
+osgi.java.profile.name = SpringSource-AP
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/fail/child/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/fail/child/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..c97a4806
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/fail/child/META-INF/MANIFEST.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: fail.child
+Bundle-Version: 1.0.0
+Export-Package: fail.child;version="1.2.3"
+Import-Package: fail.bar;version="1.2.3.";resolution:=optional
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/fail/parent/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/fail/parent/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..3c8b0e6b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/fail/parent/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: fail.parent
+Bundle-Version: 1.0.0
+Import-Package: fail.child
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/frag/child/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/frag/child/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..2795c80c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/frag/child/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: frag.child
+Bundle-Version: 1.0.0
+Fragment-Host: frag.host
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/frag/host/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/frag/host/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..47a5db64
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/frag/host/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: frag.host
+Bundle-Version: 1.0.0
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/four/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/four/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..8076c3c4
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/four/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: bundle.four
+Export-Package: bundle.four
+Import-Package: bundle.one
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/one/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/one/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..16e0d176
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/one/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: bundle.one
+Export-Package: bundle.one
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/three/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/three/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..62270044
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/three/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: bundle.three
+Export-Package: bundle.three
+Import-Package: bundle.two
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/two/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/two/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..334988a5
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/iflt/bundles/two/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: bundle.two
+Export-Package: bundle.two
+Import-Package: bundle.three
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/jars/dummy.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/jars/dummy.jar
new file mode 100644
index 00000000..7ab5fb31
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/jars/dummy.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/jars/mockbundle.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/jars/mockbundle.jar
new file mode 100644
index 00000000..d6c3b93a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/jars/mockbundle.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-domain.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-domain.jar
new file mode 100644
index 00000000..3adc23fd
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-domain.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-em.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-em.jar
new file mode 100644
index 00000000..22342b32
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-em.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-include.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-include.jar
new file mode 100644
index 00000000..06c0b6cd
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/ltw/ltw-include.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiapp-1.0.0.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiapp-1.0.0.jar
new file mode 100644
index 00000000..bb696600
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiapp-1.0.0.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiapp-2.0.0.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiapp-2.0.0.jar
new file mode 100644
index 00000000..30a81280
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiapp-2.0.0.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiservice-1.0.0.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiservice-1.0.0.jar
new file mode 100644
index 00000000..0e844c8a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiservice-1.0.0.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiservice-2.0.0.jar b/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiservice-2.0.0.jar
new file mode 100644
index 00000000..d1d6e2d7
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/platform170/simpleosgiservice-2.0.0.jar
Binary files differ
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd-other/other/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd-other/other/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..3169dff0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd-other/other/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: rfd.az
+Bundle-Version: 1.0
+Export-Package: rfd.az
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/a/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/a/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..deb3204b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/a/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: rfd.a
+Bundle-Version: 1.0
+Import-Package: rfd.b, rfd.az
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/app/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/app/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..13791715
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/app/META-INF/MANIFEST.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: rfd.app
+Bundle-Version: 1.0
+Export-Package: rfd.app
+Import-Package: rfd.hibernate;version="2.0",rfd.spring.orm
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/b/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/b/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..df7b55c9
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/b/META-INF/MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: rfd.b
+Bundle-Version: 1.0
+Import-Package: rfd.c
+Export-Package: rfd.b
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/c/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/c/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..2b732322
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/c/META-INF/MANIFEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: rfd.c
+Bundle-Version: 1.0
+Import-Package: rfd.d
+Export-Package: rfd.c
+
diff --git a/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/hibernate/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/hibernate/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..1ad8417f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.userregion/src/test/resources/rfd/hibernate/META-INF/MANIFEST.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-S