Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.virgo.kernel.deployer/src')
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/ArtifactIdentity.java75
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/DeployedArtefactInfo.java60
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/Deployer.java230
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/RecoveryMonitor.java39
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ApplicationDeployer.java307
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployUriNormaliser.java39
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployerConfiguration.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployerLogEvents.java104
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeploymentException.java89
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeploymentIdentity.java48
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/FatalDeploymentException.java47
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ServerApplicationInfo.java64
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ServerModuleInfo.java47
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeployed.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeploying.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeploymentEvent.java61
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStarted.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStarting.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStopped.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStopping.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleUndeployed.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleUndeploying.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeployed.java38
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploying.java38
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploymentEvent.java68
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploymentEventNotifier.java81
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationUndeployed.java38
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationUndeploying.java38
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/DeploymentListener.java43
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ApplicationRecoverer.java41
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ArtefactNotFoundException.java36
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/BlockingSignal.java121
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/BundleDeploymentPropertiesTransformer.java88
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleManifest.java255
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleSymbolicName.java125
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ImportExpandingTransformer.java119
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ManifestUpgrader.java118
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/PipelinedApplicationDeployer.java532
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/PlanResolver.java115
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/Plumber.java164
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/RegionAwarePackageAdminAccessor.java25
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/SignalJunction.java140
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StandardDeployerConfiguration.java65
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StandardDeploymentIdentity.java109
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StateCleanupInstallArtifactLifecycleListener.java57
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/TreeUtils.java61
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/event/DeploymentListener.java36
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLog.java270
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLogDeploymentListener.java58
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/RecoveryAgent.java106
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/CompoundDeployUriNormaliser.java53
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/FileDeployUriNormaliser.java42
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/RepositoryDeployUriNormaliser.java112
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployer.java133
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployerLogEvents.java47
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeploymentFileSystemListener.java199
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/WatchTask.java64
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/management/StandardDeployer.java133
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/management/StandardRecoveryMonitor.java100
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateDeploymentIdentityException.java26
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateFileNameException.java26
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateLocationException.java26
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/RuntimeArtifactModel.java91
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/internal/StandardRuntimeArtifactModel.java199
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactIdentity.java130
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactIdentityDeterminer.java45
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactState.java145
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/BundleInstallArtifact.java88
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifact.java178
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactLifecycleListener.java181
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactLifecycleListenerSupport.java123
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactTreeFactory.java45
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactTreeInclosure.java85
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/PlanInstallArtifact.java70
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ScopeServiceRepository.java29
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/AbstractInstallArtifact.java456
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStateMonitor.java324
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStorage.java31
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStorageFactory.java52
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/AtomicInstallArtifactLifecycleListener.java189
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigInstallArtifact.java90
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigInstallArtifactTreeFactory.java73
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigLifecycleEngine.java91
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/DelegatingServiceRegistryBackedArtifactIdentityDeterminer.java70
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/InstallArtifactRefreshHandler.java37
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/LoggingInstallArtifactLifecycleListener.java160
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifact.java279
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifactFactory.java78
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanInstallArtifactTreeFactory.java131
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanMemberCollector.java83
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanScoper.java159
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/RefreshEngine.java22
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/RefreshException.java28
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ServiceScoper.java194
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/SpringConfigServiceModelScanner.java222
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactIdentityDeterminer.java119
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorage.java125
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorageFactory.java72
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactRefreshHandler.java58
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactTreeInclosure.java205
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardPlanInstallArtifact.java226
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardScopeServiceRepository.java162
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StartEngine.java21
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StartException.java28
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StopEngine.java21
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StopException.java28
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/TreeRestrictingInstallArtifactLifecycleListener.java107
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriver.java105
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverBundleListener.java108
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverFactory.java58
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverManifestTransformer.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactFactory.java119
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactTreeFactory.java83
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleThreadContextManager.java130
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleDriver.java308
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifact.java519
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/ArtifactIdentityScoper.java64
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/ScopeNameFactory.java42
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/Scoper.java403
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/environment/InstallEnvironment.java51
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/environment/InstallEnvironmentFactory.java35
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/environment/InstallLog.java49
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/environment/internal/StandardInstallEnvironment.java64
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/environment/internal/StandardInstallEnvironmentFactory.java48
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/environment/internal/StandardInstallLog.java106
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/Pipeline.java48
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/PipelineFactory.java42
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/internal/CompensatingPipeline.java98
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/internal/StandardPipeline.java72
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/internal/StandardPipelineFactory.java43
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/AbstractPipelineStage.java85
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/Operator.java39
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/PipelineStage.java43
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/resolve/internal/CommitStage.java45
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/resolve/internal/QuasiInstallStage.java85
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/resolve/internal/QuasiResolveStage.java50
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/resolve/internal/ResolveStage.java102
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/Transformer.java36
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/BundleInstallArtifactGatheringTreeVisitor.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/ScopedPlanIdentifyingTreeVisitor.java47
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/ScopedPlanInstallArtifactProcessor.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/ScopingTransformer.java53
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/SyntheticContextBundleCreatingTransformer.java164
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/TransformationStage.java59
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/UserInstalledTaggingTransformer.java42
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/visit/Visitor.java34
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/visit/internal/VisitationStage.java69
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/ManagementExporter.java101
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/SystemDump.java34
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/StandardSystemDump.java42
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/AbstractMultiBeanSystemManagementExporter.java66
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/AbstractSystemManagementExporter.java66
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/ClassLoadingSystemManagementExporter.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/EnvironmentVariablesSystemManagementExporter.java96
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/GarbageCollectorSystemManagementExporter.java49
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/JmxSystemExporter.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/MemoryPoolSystemManagementExporter.java48
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/MemorySystemManagementExporter.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/OperatingSystemManagementExporter.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/RuntimeSystemManagementExporter.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/SystemManagementExporter.java28
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/management/internal/system/ThreadingSystemManagementExporter.java40
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/BundleDelegatingClassLoaderFactory.java36
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/Component.java53
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ComponentClassLoadingException.java45
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ComponentException.java46
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/InvalidComponentPropertyException.java46
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ModuleContext.java56
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ModuleContextAccessor.java32
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ModuleContextEvent.java57
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ModuleContextEventListener.java31
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ModuleContextEventPublisher.java25
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ModuleContextFailedEvent.java44
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/NoSuchComponentException.java32
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/PropertyTypeMismatchException.java57
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/ServiceProxyInspector.java33
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/module/internal/StandardModuleContextEventPublisher.java128
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/EventLogMessages.properties61
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/META-INF/spring/api-context.xml9
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/META-INF/spring/configuration-context.xml16
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/META-INF/spring/core-context.xml10
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/META-INF/spring/deployer-context.xml313
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/META-INF/spring/deployer-hot-context.xml24
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/META-INF/spring/internal-osgi-context.xml58
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/META-INF/spring/management-context.xml25
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/main/resources/META-INF/spring/serviceability-context.xml9
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/ArtifactIdentityTests.java38
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/DeploymentOptionsTests.java65
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/StubInstallArtifactLifecycleListener.java262
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/internal/BundleDeploymentPropertiesTransformerTests.java92
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/internal/DeployerBlockingSignalTests.java142
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleManifestTests.java275
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/internal/ImportExpandingTransformerTests.java178
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/internal/ManifestUpgraderTests.java166
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/internal/SignalJunctionTests.java164
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLogTests.java85
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/RepositoryDeployUriNormalizerTests.java231
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployerFileSystemListenerTests.java105
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployerTests.java123
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/deployer/management/RecoveryMonitorTests.java69
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/AbstractInstallArtifactTests.java127
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStateMonitorTests.java150
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/AtomicInstallArtifactLifecycleListenerTests.java380
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanMemberCollectorTests.java105
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/ScopeServiceRepositoryTests.java84
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/SpringConfigServiceModelScannerTests.java63
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactIdentityDeterminerTests.java109
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactTreeInclosureTests.java243
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/StubArtifactBridge.java93
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/StubEventLogger.java32
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/StubInstallArtifactRefreshHandler.java55
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/TreeRestrictingInstallArtifactLifecycleListenerTests.java79
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifactTests.java143
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/environment/internal/StandardInstallEnvironmentTests.java43
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/pipeline/AbstractPipelineStageTests.java102
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/pipeline/internal/CompensatingPipelineTests.java358
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/pipeline/internal/StandardPipelineTests.java269
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/TransformationStageTests.java102
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/SyntheticContextBundleCreatingTransformerTests.java198
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/UserInstalledTaggingTransformerTests.java49
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artefacts/exploded/META-INF/MANIFEST.MF0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artefacts/exploded/META-INF/web/index.jsp12
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artefacts/exploded/somefile.txt1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artefacts/exploded/somefolder/.gitignore0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artifacts/malformed-application-context.jarbin0 -> 713 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artifacts/nobsn.jarbin0 -> 545 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artifacts/rawfile1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artifacts/simple.jarbin0 -> 576 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artifacts/simple/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artifacts/simple/test/rawfile1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/artifacts/test.properties1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/but/but.B.after.jarbin0 -> 1589 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/but/but.B.before.jarbin0 -> 1331 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/but/but.C.jarbin0 -> 1641 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/cloning/app/1.0/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/cloning/hibernate/3.2.5/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/cloning/hibernate/3.2.6/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/cloning/spring/2.5.3/META-INF/MANIFEST.MF6
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/config/BundleUpdateTests/repository.properties7
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/dat/one/bundle/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/dat/two/bundle/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/debug.options4
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/different-hash-test/jar1.jarbin0 -> 456 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/different-hash-test/jar2.jarbin0 -> 456 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/different-hash-test/jar2a.jarbin0 -> 456 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/dummy/other/ignore.txt0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/dummy/other/jar2.jarbin0 -> 715 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/exploded/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/export-same-package/root-a/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/export-same-package/root-b/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/faulty/imp/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/file-system-searcher/a/1/a1.txt0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/file-system-searcher/a/1/z/a1z.txt0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/file-system-searcher/a/1/z/file.wont.be.found0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/file-system-searcher/b/1/b1.txt0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/file-system-searcher/b/1/file.wont.be.found0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/file-system-searcher/c/1/c1.txt0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/file-system-searcher/c/c.txt0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/invalid/bundle/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/jars/dummy.jarbin0 -> 774 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/jars/mockbundle.jarbin0 -> 419 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/libraries/exploded/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/libraries/exploded/bundle.jarbin0 -> 673 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/libraries/spring-instrumented.libd4
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/libraries/spring.libd5
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/manifests/invalid-import-package.MF11
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/multi-bundle-application-tests/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/multi-bundle-application-tests/one.jarbin0 -> 374 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/multi-bundle-application-tests/two.jarbin0 -> 374 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/multi-root/root-a/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/multi-root/root-b/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/org/eclipse/virgo/kernel/install/artifact/internal/sbiat-bundle/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-configuration/duplicate-repository-in-chain.properties7
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-configuration/missing-chain.properties2
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-configuration/missing-repositories.properties1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-configuration/repository.properties12
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-populator-tests/corrupt-jar-test/a.jarbin0 -> 391 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-populator-tests/corrupt-jar-test/b.jar0
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-populator-tests/corrupt-jar-test/c.jarbin0 -> 391 bytes
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-populator-tests/malformed-manifest-test/a/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-populator-tests/malformed-manifest-test/b/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/repository-populator-tests/malformed-manifest-test/c/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/scoping/complexService.xml18
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/scoping/simpleService.xml12
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/test/dummy.txt1
-rw-r--r--org.eclipse.virgo.kernel.deployer/src/test/resources/unit.test.subsystem/subsystem.xml4
286 files changed, 21507 insertions, 0 deletions
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/ArtifactIdentity.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/ArtifactIdentity.java
new file mode 100644
index 00000000..664a49b5
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/ArtifactIdentity.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.deployer;
+
+import java.beans.ConstructorProperties;
+
+/**
+ * Identifies an artifact as a type, name, and version tuple.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+public final class ArtifactIdentity {
+
+ private final String type;
+
+ private final String name;
+
+ private final String version;
+
+ /**
+ * Create a new <code>ArtifactIdentity</code>.
+ *
+ * @param type The type of the artifact
+ * @param name The name of the artifact
+ * @param version The version of the artifact
+ */
+ @ConstructorProperties( { "type", "name", "version" })
+ public ArtifactIdentity(String type, String name, String version) {
+ this.type = type;
+ this.name = name;
+ this.version = version;
+ }
+
+ /**
+ * Returns the type of the artifact
+ *
+ * @return the artifact's type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Returns the name of the artifact
+ *
+ * @return the artifact's name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the version of the artifact
+ *
+ * @return the artifact's version
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ public String toString() {
+ return String.format("%s:%s:%s", type, name, version);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/DeployedArtefactInfo.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/DeployedArtefactInfo.java
new file mode 100644
index 00000000..b9e26575
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/DeployedArtefactInfo.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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.deployer;
+
+import java.util.Date;
+import java.util.Map;
+
+import javax.management.MXBean;
+
+/**
+ * Interface used to expose deployed artefact information
+ */
+@MXBean
+public interface DeployedArtefactInfo {
+
+ /**
+ * @return The type of the deployed artefact
+ */
+ String getType();
+
+ /**
+ * @return The name of the deployed artefact
+ */
+ String getName();
+
+ /**
+ * @return The version of the deployed artefact
+ */
+ String getVersion();
+
+ /**
+ * @return The time when this artefact was deployed
+ */
+ Date getDeployTime();
+
+ /**
+ * @return The source URI of the deployed artefact
+ */
+ String getSourceUri();
+
+ /**
+ * Returns the properties of the deployed artefact
+ * @return the artefact's properties.
+ */
+ Map<String, String> getProperties();
+
+ /**
+ * @return The local name of the repository from which the artefact was deployed
+ */
+ String getRepositoryName();
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/Deployer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/Deployer.java
new file mode 100644
index 00000000..db4ca2fe
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/Deployer.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * 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.deployer;
+
+import javax.management.MXBean;
+import javax.management.openmbean.CompositeData;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
+
+
+/**
+ * Definition of the Deployer control used to allow the Server to support remote deployment.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Implementations must be thread-safe.
+ *
+ */
+@MXBean
+public interface Deployer {
+
+ /**
+ * Deploy an application located at the given URI.
+ *
+ * @param uri the URI string of the location of the application
+ * @return the {@link DeploymentIdentity} of the deployed application as a {@link CompositeData} object.
+ * @throws DeploymentException
+ */
+ DeploymentIdentity deploy(String uri) throws DeploymentException;
+
+ /**
+ * Deploy an application located at the given URI.
+ *
+ * @param uri the URI string of the location of the application.
+ * @param recoverable whether or not the application should be recovered on warm restart.
+ * @return the {@link DeploymentIdentity} of the deployed application as a {@link CompositeData} object.
+ * @throws DeploymentException
+ */
+ DeploymentIdentity deploy(String uri, boolean recoverable) throws DeploymentException;
+
+ /**
+ * Undeploy an application with a given symbolic name and version.
+ *
+ * @param applicationSymbolicName the symbolic name of the application
+ * @param version the version of the application in string form
+ * @throws DeploymentException
+ */
+ void undeploy(String applicationSymbolicName, String version) throws DeploymentException;
+
+ /**
+ * Refresh a single module of the application which was deployed from the given URI.
+ *
+ * @param uri the URI string from which the application was deployed
+ * @param symbolicName the bundle symbolic name of the module to be refreshed
+ * @throws DeploymentException if the refresh failed
+ */
+ void refresh(String uri, String symbolicName) throws DeploymentException;
+
+ /**
+ * Refresh any bundle with the given symbolic name and version and any bundles cloned from a bundle with the given
+ * symbolic name and version. If no bundles or cloned bundles match the given symbolic name and version, simply
+ * return and do not throw an exception. <br>
+ * <p/> Certain bundles which are critical to the operation of the system may not be refreshed. If an attempt is
+ * made to refresh one of these bundles, a warning message is logged, the bundle is not refreshed, but any clones of
+ * the bundle are refreshed.
+ *
+ * @param bundleSymbolicName the symbolic name of the bundle
+ * @param bundleVersion the version of the bundle
+ * @throws DeploymentException
+ */
+ void refreshBundle(String bundleSymbolicName, String bundleVersion) throws DeploymentException;
+
+ // TODO: Implement install, start, stop and uninstall on Deployer
+ // The following methods are a draft for the new methods to allow install, start, stop, and
+ // uninstall to be driven separately over JMX, as opposed to deploy (install and start),
+ // and undeploy (stop and uninstall).
+ //
+ // NOTE: These methods have not yet been implemented, calling them will result in an
+ // UnsupportedOperationException being thrown.
+ //
+
+ /**
+ * Installs the artifact identified by the supplied <code>artifactUri</code>. The artifact
+ * will be recovered upon warm restart, i.e. equivalent to calling {@link #install(String, boolean)
+ * install(artifactUri, true)}. If the artifact is already present this method has no effect, and
+ * the identity of the existing artifact is returned.
+ *
+ * @param artifactUri The uri, as a <code>String</code>, of the artifact to be installed.
+ *
+ * @return The {@link ArtifactIdentity} of the installed artifact as {@link CompositeData}.
+ *
+ * @throws DeploymentException if the install fails
+ */
+ ArtifactIdentity install(String artifactUri) throws DeploymentException;
+
+ /**
+ * Installs the artifact identified by the supplied <code>artifactUri</code>, telling the
+ * deployer whether or not the artifact should be recovered upon warm restart. If the artifact is
+ * already present this method has no effect, and the identity of the existing artifact
+ * is returned.
+ *
+ * @param artifactUri The uri, as a <code>String</code>, of the artifact to be installed.
+ * @param recover <code>true</code> if the artifact should be recovered, <code>false</code> if it should not.
+ *
+ * @return The {@link ArtifactIdentity} of the installed artifact as {@link CompositeData}.
+ *
+ * @throws DeploymentException if the install fails
+ */
+ ArtifactIdentity install(String artifactUri, boolean recover) throws DeploymentException;
+
+ /**
+ * Installs the artifact identified by the supplied <code>type</code>, <code>name</code>, and
+ * <code>version</code>. The artifact will be recovered upon warm restart, i.e. equivalent to calling
+ * {@link #install(String, String, String, boolean) install(type, name, version, true)}. If the
+ * artifact is already present this method has no effect, and the identity of the existing
+ * artifact is returned.
+ *
+ * @param artifactUri The uri, as a <code>String</code>, of the artifact to be installed.
+ * @param type The type of the artifact to be installed
+ * @param name The name of the artifact to be installed
+ * @param version The version of the artifact to be installed
+ *
+ * @return The {@link ArtifactIdentity} of the installed artifact as {@link CompositeData}.
+ *
+ * @throws DeploymentException if the install fails
+ */
+ ArtifactIdentity install(String type, String name, String version) throws DeploymentException;
+
+ /**
+ * Installs the artifact identified by the supplied <code>type</code>, <code>name</code>, and
+ * <code>version</code>, telling the deployer whether or not the artifact should be recovered
+ * upon warm restart. If the artifact is already present this method has no effect, and the
+ * identity of the existing artifact is returned.
+ *
+ * @param type The type of the artifact to be installed
+ * @param name The name of the artifact to be installed
+ * @param version The version of the artifact to be installed
+ * @param recover <code>true</code> if the artifact should be recovered upon warm restart,
+ * <code>false</code> if it should not.
+ *
+ * @return The {@link ArtifactIdentity} of the installed artifact as {@link CompositeData}.
+ *
+ * @throws DeploymentException if the install fails
+ */
+ ArtifactIdentity install(String type, String name, String version, boolean recover) throws DeploymentException;
+
+ /**
+ * Starts the artifact identified by the supplied <code>artifactIdentity</code>. If the artifact
+ * is already active, this method has no effect.
+ *
+ * @param artifactIdentity The {@link ArtifactIdentity} of the artifact that is to be started.
+ *
+ * @throws DeploymentException if the start fails
+ * @throws IllegalStateException If the identified artifact is not present
+ */
+ void start(ArtifactIdentity artifactIdentity) throws DeploymentException, IllegalStateException;
+
+ /**
+ * Starts the artifact identified by the supplied <code>type</code>, <code>name</code>, and
+ * <code>version</code>. If the artifact is already active, this method has no effect.
+ *
+ * @param type The type of the artifact to be started
+ * @param name The name of the artifact to be started
+ * @param version The version of the artifact to be started
+ *
+ * @throws DeploymentException if the start fails
+ * @throws IllegalStateException If the artifact is not present
+ */
+ void start(String type, String name, String version) throws DeploymentException, IllegalStateException;
+
+ /**
+ * Stops the artifact identified by the supplied <code>artifactIdentity</code>. If the artifact is
+ * not active, this method has no effect.
+ *
+ * @param artifactIdentity The {@link ArtifactIdentity} of the artifact that is to be started.
+ *
+ * @throws DeploymentException if the stop fails
+ * @throws IllegalStateException If the artifact is not present
+ */
+ void stop(ArtifactIdentity artifactIdentity) throws DeploymentException, IllegalStateException;
+
+ /**
+ * Stops the artifact identified by the supplied <code>type</code>, <code>name</code>, and
+ * <code>version</code>. If the artifact is not active, this method has no effect.
+ *
+ * @param type The type of the artifact to be stopped
+ * @param name The name of the artifact to be stopped
+ * @param version The version of the artifact to be stopped
+ *
+ * @throws DeploymentException if the stop fails
+ * @throws IllegalStateException If the identified artifact is not present
+ */
+ void stop(String type, String name, String version) throws DeploymentException, IllegalStateException;
+
+ /**
+ * Uninstalls the artifact identified by the supplied <code>artifactIdentity</code>. If
+ * the artifact is not present, this method has no effect. If the artifact is active an
+ * {@link IllegalStateException} is thrown, i.e. an active artifact cannot be uninstalled.
+ *
+ * @param artifactIdentity The {@link ArtifactIdentity} of the artifact that is to be uninstalled.
+ *
+ * @throws DeploymentException if the uninstall fails
+ * @throws IllegalStateException if the artifact is present and is active
+ */
+ void uninstall(ArtifactIdentity artifactIdentity) throws DeploymentException, IllegalStateException;
+
+ /**
+ * Uninstalls the artifact identified by the supplied <code>type</code>, <code>name</code>,
+ * and <code>version</code>. If the artifact is not present, this method has no effect. If the
+ * artifact is active an {@link IllegalStateException} is thrown, i.e. an active artifact cannot
+ * be uninstalled.
+ *
+ * @param type The type of the artifact to be uninstalled
+ * @param name The name of the artifact to be uninstalled
+ * @param version The version of the artifact to be uninstalled
+ *
+ * @throws DeploymentException if the uninstall fails
+ * @throws IllegalStateException if the artifact is present and is started
+ */
+ void uninstall(String type, String name, String version) throws DeploymentException, IllegalStateException;
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/RecoveryMonitor.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/RecoveryMonitor.java
new file mode 100644
index 00000000..7dcd6e2e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/RecoveryMonitor.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.deployer;
+
+import javax.management.MXBean;
+
+/**
+ * MBean for tracking deployer recovery. A notification of type {@value #NOTIFICATION_TYPE} is broadcasted when recovery
+ * completes. <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations <code>must</code> be threadsafe.
+ *
+ */
+@MXBean
+public interface RecoveryMonitor {
+
+ /**
+ * The type of notification sent when recovery completes.
+ */
+ public static final String NOTIFICATION_TYPE = "org.eclipse.virgo.server.recovery";
+
+ /**
+ * Indicates whether or not recovery is complete.
+ *
+ * @return <code>true</code> if recovery is complete, otherwise <code>false</code>.
+ */
+ boolean isRecoveryComplete();
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ApplicationDeployer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ApplicationDeployer.java
new file mode 100644
index 00000000..b1b5f3d1
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ApplicationDeployer.java
@@ -0,0 +1,307 @@
+/*******************************************************************************
+ * 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.deployer.core;
+
+import java.net.URI;
+
+import org.osgi.framework.Version;
+
+/**
+ * The ApplicationDeployer interface is the programmatic interface to the deployer subsystem for deploying applications
+ * and libraries.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface ApplicationDeployer {
+
+ /**
+ * {@link DeploymentOptions} provides a collection of deployment options.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ */
+ public class DeploymentOptions {
+
+ public static final DeploymentOptions DEFAULT_DEPLOYMENT_OPTIONS = new DeploymentOptions();
+
+ private final boolean recoverable;
+
+ private final boolean deployerOwned;
+
+ private final boolean synchronous;
+
+ /**
+ * Create default deployment options.
+ */
+ public DeploymentOptions() {
+ this.recoverable = true;
+ this.deployerOwned = false;
+ this.synchronous = true;
+ }
+
+ /**
+ * Create deployment options with the given recoverability, ownership, and synchronisation.
+ *
+ * @param recoverable <code>true</code> if and only if the application is to persist across Server restarts
+ * @param deployerOwned <code>true</code> if and only if the application at the location specified on deployment
+ * is to be deleted when the application is undeployed
+ * @param synchronous <code>true</code> if and only if the application should be deployed synchronously
+ */
+ public DeploymentOptions(boolean recoverable, boolean deployerOwned, boolean synchronous) {
+ this.recoverable = recoverable;
+ this.deployerOwned = deployerOwned;
+ this.synchronous = synchronous;
+ }
+
+ /**
+ * Get the recoverability option.
+ *
+ * @return <code>true</code> if and only if the application is to persist across Server restarts
+ */
+ public boolean getRecoverable() {
+ return this.recoverable;
+ }
+
+ /**
+ * Get the ownership option.
+ *
+ * @return <code>true</code> if and only if the application at the location specified on deployment is to be
+ * deleted when the application is undeployed
+ */
+ public boolean getDeployerOwned() {
+ return this.deployerOwned;
+ }
+
+ /**
+ * Get the synchronisation option which is <code>true</code> if and only if the application should be deployed
+ * synchronously.
+ * <p/>
+ * Deploying synchronously means that control does not return to the caller of the
+ * {@link org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer#deploy(URI, DeploymentOptions) deploy}
+ * method until any application contexts for the application have been built and published in the service
+ * registry or deployment fails or times out.
+ * <p/>
+ * Deploying asynchronously means that control returns to the caller of the
+ * {@link org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer#deploy(URI, DeploymentOptions) deploy}
+ * method once the application has been started, but not necessarily before any application contexts have been
+ * built and published.
+ *
+ * @return <code>true</code> if and only if the application should be deployed synchronously
+ */
+ public boolean getSynchronous() {
+ return this.synchronous;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (deployerOwned ? 1231 : 1237);
+ result = prime * result + (recoverable ? 1231 : 1237);
+ result = prime * result + (synchronous ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ DeploymentOptions other = (DeploymentOptions) obj;
+ if (deployerOwned != other.deployerOwned)
+ return false;
+ if (recoverable != other.recoverable)
+ return false;
+ if (synchronous != other.synchronous)
+ return false;
+ return true;
+ }
+ }
+
+ /**
+ * Deploys the artifact are the supplied <code>location</code>. The supplied <code>options</code> govern how the
+ * installed artifact is handled by the deployed, e.g. if it is recovered and re-installed upon warm restart.
+ *
+ * @param uri The location of the artifact
+ * @param options The options for the installation
+ * @return The identity of the installed artifact
+ * @throws DeploymentException if installation fails.
+ */
+ DeploymentIdentity install(URI uri, DeploymentOptions options) throws DeploymentException;
+
+ /**
+ * Deploys the artifact are the supplied <code>location</code>. The supplied <code>options</code> govern how the
+ * installed artifact is handled by the deployed, e.g. if it is recovered and re-installed upon warm restart.
+ * <p />
+ * This method is equivalent to calling <code>install(location, new DeploymentOptions())</code>.
+ *
+ * @param uri The location of the artifact
+ * @param options The options for the installation
+ * @return The identity of the installed artifact
+ * @throws DeploymentException if installation fails.
+ */
+ DeploymentIdentity install(URI uri) throws DeploymentException;
+
+ /**
+ * Deploy an application which may be either an OSGi application or a legacy application such as a WAR. This is used
+ * by admin. and hot deployment. A successfully deployed application is always recovered on warm restart but the
+ * application at the given location is not deleted when the application is undeployed. <br/>
+ * <p />
+ * This method is equivalent to calling <code>deploy(location, new DeploymentOptions())</code>.
+ *
+ * @param uri the location of the application JAR file
+ * @return the {@link DeploymentIdentity} of the deployed application
+ * @throws DeploymentException
+ */
+ DeploymentIdentity deploy(URI uri) throws DeploymentException;
+
+ /**
+ * Deploy an application which may be either an OSGi application or a legacy application such as a WAR. This is used
+ * by admin. and hot deployment.
+ *
+ * @param uri location of the artifact
+ * @param options the options for this deployment
+ * @return the {@link DeploymentIdentity} of the deployed application
+ * @throws DeploymentException
+ */
+ DeploymentIdentity deploy(URI uri, DeploymentOptions options) throws DeploymentException;
+
+ /**
+ * Deploy an artifact from the repository with the given type, name, and version. A successfully deployed
+ * application is always recovered on warm restart. <br/>
+ * <p />
+ * This method is equivalent to calling <code>deploy(type, name, version, new DeploymentOptions())</code>.
+ *
+ * @param type the type of the artifact to deploy
+ * @param name the name of the artifact to deploy
+ * @param version the {@link Version} of the artifact to deploy
+ * @return the {@link DeploymentIdentity} of the deployed application
+ * @throws DeploymentException
+ */
+ DeploymentIdentity deploy(String type, String name, Version version) throws DeploymentException;
+
+ /**
+ * Deploy an artifact from the repository with the given type, name, and version.<br/>
+ * <p />
+ * If the given {@link DeploymentOptions} specify the artifact is owned by the deployer, then deployment fails and a
+ * deployment exception is thrown. This is to avoid the deployer deleting the artifact from the repository store.
+ *
+ * @param type the type of the artifact to deploy
+ * @param name the name of the artifact to deploy
+ * @param version the {@link Version} of the artifact to deploy
+ * @param options of the deployment
+ * @return the {@link DeploymentIdentity} of the deployed application which must return <code>false</code> from
+ * <code>getDeployerOwned</code>
+ * @throws DeploymentException
+ */
+ DeploymentIdentity deploy(String type, String name, Version version, DeploymentOptions options) throws DeploymentException;
+
+ /**
+ * Undeploy an application with a given symbolic name and version.
+ *
+ * WARNING: there can be ambiguity if applications of distinct types have the same symbolic name and version, so
+ * this method is deprecated.
+ *
+ * @param applicationSymbolicName the symbolic name of the application
+ * @param version the version of the application in string form
+ * @throws DeploymentException
+ * @deprecated in favour of the undeploy(String, String, String) which takes artifact type as first parameter
+ */
+ void undeploy(String applicationSymbolicName, String version) throws DeploymentException;
+
+ /**
+ * Undeploy an application with a given type, name, and version.
+ *
+ * @param type the type of the application
+ * @param name the symbolic name of the application
+ * @param version the version of the application in string form
+ * @throws DeploymentException
+ */
+ void undeploy(String type, String name, String version) throws DeploymentException;
+
+ /**
+ * Undeploy an application with a given {@link DeploymentIdentity}. The application may be either an OSGi
+ * application or a legacy application such as a WAR.
+ *
+ * @param deploymentIdentity the <code>DeploymentIdentity</code> of the application
+ * @throws DeploymentException
+ */
+ void undeploy(DeploymentIdentity deploymentIdentity) throws DeploymentException;
+
+ /**
+ * Refresh a single bundle of the application (PAR or bundle) which was deployed from the given URI and return the
+ * deployment identity of the deployed application. <br/>
+ * If the refresh is promoted to a redeploy then the DeploymentIdentity of the newly deployed application is
+ * returned.
+ *
+ * @param uri location of the artifact
+ * @param symbolicName the bundle symbolic name of the bundle to be refreshed
+ * @return the {@link DeploymentIdentity} of the deployed application
+ * @throws DeploymentException
+ */
+ DeploymentIdentity refresh(URI uri, String symbolicName) throws DeploymentException;
+
+ /**
+ * Refresh any bundle with the given symbolic name and version and any bundles cloned from a bundle with the given
+ * symbolic name and version. If no bundles or cloned bundles match the given symbolic name and version, simply
+ * return and do not throw an exception. <br/>
+ * <p/>
+ * Certain bundles which are critical to the operation of the system may not be refreshed. If an attempt is made to
+ * refresh one of these bundles, a warning message is logged, the bundle is not refreshed, but any clones of the
+ * bundle are refreshed.
+ *
+ * @param bundleSymbolicName the symbolic name of the bundle
+ * @param bundleVersion the version of the bundle
+ * @throws DeploymentException
+ */
+ void refreshBundle(String bundleSymbolicName, String bundleVersion) throws DeploymentException;
+
+ /**
+ * Get a list of deployed applications.
+ *
+ * @return Array of deployed application identities.
+ */
+ DeploymentIdentity[] getDeploymentIdentities();
+
+ /**
+ * Get the {@link DeploymentIdentity} of an application deployed from the given location. If no such application was
+ * found, return <code>null</code>. <br/>
+ * <p />
+ * Although the deployer currently prevents two applications being deployed from the same location without
+ * un-deploying the first application, this restriction may be lifted in the future in which case this method will
+ * return one of potentially many applications deployed from the given location.
+ *
+ * @param uri location of the artifact
+ * @return a <code>DeploymentIdentity</code> or <code>null</code>
+ */
+ DeploymentIdentity getDeploymentIdentity(URI uri);
+
+ /**
+ * Determine whether or not the given artifact is already deployed. Return <code>true</code> if the given artefact
+ * at its file's last modified time is already deployed. The last modified time is only taken into account if the
+ * file was deployed on a previous run of the Server.
+ *
+ * @param uri location of the artifact
+ * @return <code>true</code> if and only if the given artifact at its file's last modified time is already deployed
+ */
+ boolean isDeployed(URI uri);
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployUriNormaliser.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployUriNormaliser.java
new file mode 100644
index 00000000..eaede2d1
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployUriNormaliser.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.deployer.core;
+
+import java.net.URI;
+
+
+/**
+ * A <code>DeployUriNormaliser</code> is used to normalise deploy {@link URI URIs} such that they're suitable for
+ * consumption by the deployer's internals.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations <strong>must</strong> be thread-safe.
+ *
+ */
+public interface DeployUriNormaliser {
+
+ /**
+ * Normalises the supplied <code>uri</code> such that it is suitable for consumption by the deployer. If the
+ * <code>uri</code> is not understood, <code>null</code> is returned.
+ *
+ * @param uri The {@link URI} to normalise
+ * @return The normalised <code>URI</code>.
+ *
+ * @throws DeploymentException if the <code>uri</code> is understood but it cannot be normalised.
+ */
+ URI normalise(URI uri) throws DeploymentException;
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployerConfiguration.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployerConfiguration.java
new file mode 100644
index 00000000..60513a66
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployerConfiguration.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.deployer.core;
+
+import org.eclipse.virgo.util.io.PathReference;
+
+/**
+ * {@link DeployerConfiguration} provides access to the deployer configuration values.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface DeployerConfiguration {
+
+ /**
+ * Get the configured deployment timeout.
+ *
+ * @return the timeout in seconds
+ */
+ int getDeploymentTimeoutSeconds();
+
+ /**
+ * Get the configured deployment pickup directory.
+ *
+ * @return the pickup directory as a {@link PathReference}.
+ */
+ PathReference getDeploymentPickupDirectory();
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployerLogEvents.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployerLogEvents.java
new file mode 100644
index 00000000..230040c3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeployerLogEvents.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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.deployer.core;
+
+import org.eclipse.virgo.kernel.serviceability.LogEventDelegate;
+import org.eclipse.virgo.medic.eventlog.Level;
+import org.eclipse.virgo.medic.eventlog.LogEvent;
+
+/**
+ * Defines all the {@link LogEvent LogEvents} for the deployer subsystem.
+ *
+ * <strong>Concurrent Semantics</strong><br/>
+ *
+ * Implementation is immutable.
+ *
+ */
+public enum DeployerLogEvents implements LogEvent {
+
+ INSTALLING(0, Level.INFO), //
+ INSTALLED(1, Level.INFO), //
+ INSTALL_FAILURE(2, Level.ERROR), //
+ INSTALL_FAILED(3, Level.ERROR), //
+ STARTING(4, Level.INFO), //
+ STARTED(5, Level.INFO), //
+ START_FAILED(6, Level.ERROR), //
+ REFRESHING(7, Level.INFO), //
+ REFRESHED(8, Level.INFO), //
+ REFRESH_FAILED(9, Level.ERROR), //
+ STOPPING(10, Level.INFO), //
+ STOPPED(11, Level.INFO), //
+ STOP_FAILED(12, Level.ERROR), //
+ UNINSTALLING(13, Level.INFO), //
+ UNINSTALLED(14, Level.INFO), //
+ UNINSTALL_FAILED(15, Level.ERROR), //
+
+ INSTALL_ARTIFACT_REFRESH_NOT_SUPPORTED(50, Level.WARNING), //
+
+ NESTED_SCOPES_NOT_SUPPORTED(60, Level.ERROR), //
+
+ CANNOT_REFRESH_BUNDLE_IDENTITY_CHANGED(70, Level.WARNING), //
+ CANNOT_REFRESH_BUNDLE_AS_SCOPED_AND_EXPORTS_CHANGED(71, Level.WARNING),
+
+ INSTALL_ARTIFACT_DAG_NOT_SUPPORTED(80, Level.ERROR), //
+
+ RECOVERY_FAILED(200, Level.ERROR), //
+
+ DUPLICATE_PACKAGE_DURING_SCOPING(300, Level.ERROR), //
+ DUPLICATE_BSN_IN_SCOPE(301, Level.ERROR), //
+ CONFIG_FILE_ERROR(302, Level.ERROR), //
+
+ DISCARDING_BUNDLE_UPDATE_LOCATION(400, Level.WARNING), //
+ MISSING_BUNDLE_SYMBOLIC_NAME(401, Level.ERROR), //
+ BUNDLE_MANIFEST_NOT_FOUND(402, Level.ERROR), //
+
+ UNABLE_TO_SATISFY_CONSTRAINTS(500, Level.ERROR), //
+ UNSUPPORTED_URI_SCHEME(501, Level.ERROR), //
+ START_TIMED_OUT(502, Level.ERROR), //
+ REFRESH_REQUEST_URI_NOT_FOUND(503, Level.ERROR), //
+ REFRESH_REQUEST_COMPLETED(504, Level.INFO), //
+ REFRESH_REQUEST_FAILED(505, Level.ERROR), //
+ UNDEPLOY_ARTEFACT_NOT_FOUND(506, Level.ERROR), //
+ REFRESH_ARTEFACT_NOT_FOUND(507, Level.ERROR), //
+
+ JAR_UNPACK_ERROR(600, Level.ERROR), //
+
+ ARTIFACT_NOT_FOUND(700, Level.ERROR), //
+ INDETERMINATE_ARTIFACT_TYPE(701, Level.ERROR), //
+ MISSING_ARTIFACT_FACTORY(702, Level.ERROR), //
+
+ REPOSITORY_DEPLOYMENT_URI_MALFORMED(800, Level.ERROR), //
+ REPOSITORY_DEPLOYMENT_INVALID_VERSION(801, Level.ERROR);
+
+ private static final String PREFIX = "DE";
+
+ private final LogEventDelegate delegate;
+
+ private DeployerLogEvents(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.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeploymentException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeploymentException.java
new file mode 100644
index 00000000..9dbbffde
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeploymentException.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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.deployer.core;
+
+/**
+ * Signals a checked exception to the caller of the deployer subsystem.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br/>
+ *
+ * This class is thread safe.
+ *
+ */
+public class DeploymentException extends Exception {
+
+ private static final long serialVersionUID = -6809659761040724153L;
+
+ private final boolean diagnosed;
+
+ /**
+ * Creates a new <code>DeploymentException</code> with the supplied error message that has not already been
+ * diagnosed. Equivalent to calling {@link #DeploymentException(String, boolean) DeploymentException(message,
+ * false)}.
+ *
+ * @param message The exception's message
+ */
+ public DeploymentException(String message) {
+ this(message, false);
+
+ }
+
+ /**
+ * Creates a new <code>DeploymentException</code> with the supplied error message. <code>diagnosed</code> can be
+ * used to indicate whether or not this problem has already been diagnosed. If <code>true</code> the caller
+ * <strong>must</strong> ensure that the user has already been given sufficient information to diagnose the problem.
+ *
+ * @param message The exception's message
+ * @param diagnosed <code>true</code> if already diagnosed, otherwise <code>false</code>.
+ */
+ public DeploymentException(String message, boolean diagnosed) {
+ super(message);
+ this.diagnosed = diagnosed;
+ }
+
+ /**
+ * Creates a new <code>DeploymentException</code>, with the supplied error message and cause, that has not already
+ * been diagnosed. Equivalent to calling {@link #DeploymentException(String, Throwable, boolean)
+ * DeploymentException(message, cause, false)}.
+ *
+ * @param message The exception's message.
+ * @param cause The exception's cause.
+ */
+ public DeploymentException(String message, Throwable cause) {
+ this(message, cause, false);
+ }
+
+ /**
+ * Creates a new <code>DeploymentException</code> with the supplied error message and cause. <code>diagnosed</code>
+ * can be used to indicate whether or not this problem has already been diagnosed. If <code>true</code> the caller
+ * <strong>must</strong> ensure that the user has already been given sufficient information to diagnose the problem.
+ *
+ * @param message The exception's message.
+ * @param cause The exception's cause.
+ * @param diagnosed <code>true</code> if already diagnosed, otherwise <code>false</code>.
+ */
+ public DeploymentException(String message, Throwable cause, boolean diagnosed) {
+ super(message, cause);
+ this.diagnosed = diagnosed;
+ }
+
+ /**
+ * Returns <code>true</code> if this exception has already been diagnosed and no further information relating to
+ * this exception needs to be conveyed to the user.
+ *
+ * @return <code>true</code> if already diagnosed, otherwise <code>false</code>.
+ */
+ public boolean isDiagnosed() {
+ return diagnosed;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeploymentIdentity.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeploymentIdentity.java
new file mode 100644
index 00000000..77a674b4
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/DeploymentIdentity.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.deployer.core;
+
+import java.io.Serializable;
+
+/**
+ * {@link DeploymentIdentity} is an interface for the serializable objects returned when an artefact is deployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface DeploymentIdentity extends Serializable {
+
+ /**
+ * Get the type of the deployed artefact with this {@link DeploymentIdentity}.
+ *
+ * @return the type of the deployed artefact
+ */
+ String getType();
+
+ /**
+ * Get the name of the deployed artefact with this {@link DeploymentIdentity}.
+ *
+ * @return the symbolic name of the deployed artefact
+ */
+ String getSymbolicName();
+
+ /**
+ * Get the version of the deployed artefact with this {@link DeploymentIdentity}.
+ *
+ * @return the version of the deployed artefact
+ */
+ String getVersion();
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/FatalDeploymentException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/FatalDeploymentException.java
new file mode 100644
index 00000000..70d0ed95
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/FatalDeploymentException.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.deployer.core;
+
+import org.eclipse.virgo.kernel.serviceability.FatalServerException;
+
+/**
+ * Signals a fatal exception in the deployer subsystem.<p/>
+ *
+ * <strong>Concurrent Semantics</strong><br/>
+ *
+ * Threadsafe.
+ *
+ */
+public final class FatalDeploymentException extends FatalServerException {
+
+ private static final long serialVersionUID = 8857189976248973019L;
+
+ /**
+ * Creates a new <code>FatalProfileException</code> with the supplied error message.
+ *
+ * @param message The exception's message
+ */
+ public FatalDeploymentException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new <code>FatalProfileException</code> with the supplied error message and cause.
+ *
+ * @param message The exception's message
+ * @param cause The exception's cause
+ */
+ public FatalDeploymentException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ServerApplicationInfo.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ServerApplicationInfo.java
new file mode 100644
index 00000000..6ad893d3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ServerApplicationInfo.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.deployer.core;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * <p>
+ * Used to expose management information about a deployed application, and it's
+ * constituent modules/deployed bundles. See {@link ServerModuleInfo ServerModuleInfo}
+ * </p>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be Thread Safe
+ *
+ */
+public interface ServerApplicationInfo {
+
+ /**
+ * @return The name of the application as specified in the manifest, otherwise something sensible
+ */
+ String getName();
+
+ /**
+ * @return The specified version of the application or '0' if unknown
+ */
+ String getVersion();
+
+ /**
+ * @return The number of milliseconds since midnight, January 1, 1970 UTC, when this application was deployed
+ */
+ Date getDeployTime();
+
+ /**
+ * @return The type of the application, OSGi/non-OSGi etc...
+ */
+ String getType();
+
+ /**
+ * Get the {@link ServerModuleInfo ServerModuleInfos} of all the modules of this application.
+ *
+ * @return this application's {@link ServerModuleInfo ServerModuleInfos}
+ */
+ List<ServerModuleInfo> getServerModuleInfo();
+
+ /**
+ * Get the URI used to deploy this application.
+ *
+ * @return the string form of the URI
+ */
+ String getURI();
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ServerModuleInfo.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ServerModuleInfo.java
new file mode 100644
index 00000000..d9859f73
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/ServerModuleInfo.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.deployer.core;
+
+/**
+ * {@link ServerModuleInfo} provides management information for a deployed module of an application.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface ServerModuleInfo extends Comparable<ServerModuleInfo>{
+
+ /**
+ * Get the personality of this module.
+ *
+ * @return the personality of this module or <code>null</code> if this module is devoid of personality
+ */
+ String getPersonality();
+
+ /**
+ * Get an identifier of this module which uniquely identifies the module among all modules of the same personality.
+ *
+ * @return the personality identifier of this module or <code>null</code> if this module is devoid of personality
+ */
+ String getPersonalityIdentifier();
+
+ /**
+ * Get the symbolic name that this bundle is based on.
+ * @return the symbolic bundle name as specified in the manifest header
+ */
+ String getBundleSymbolicName();
+
+}
+
+
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeployed.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeployed.java
new file mode 100644
index 00000000..ab4c60f7
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeployed.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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationBundleDeployed} is an event which is broadcast when an application bundle has been successfully deployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationBundleDeployed extends ApplicationBundleDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationBundleDeployed} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName of bundle
+ * @param applicationVersion of bundle
+ * @param bundle object deployed
+ */
+ public ApplicationBundleDeployed(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion, bundle);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeploying.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeploying.java
new file mode 100644
index 00000000..d64843e8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeploying.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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationBundleDeploying} is an event which is broadcast when an application bundle is about to be deployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationBundleDeploying extends ApplicationBundleDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationBundleDeploying} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName of bundle
+ * @param applicationVersion of bundle
+ * @param bundle object deploying
+ */
+ public ApplicationBundleDeploying(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion, bundle);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeploymentEvent.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeploymentEvent.java
new file mode 100644
index 00000000..14a8e648
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleDeploymentEvent.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+/**
+ * {@link ApplicationBundleDeploymentEvent} is the base class for all events relating to deployment of bundles of
+ * applications.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public abstract class ApplicationBundleDeploymentEvent extends ApplicationDeploymentEvent {
+
+ private final Bundle bundle;
+
+ /**
+ * Construct a {@link ApplicationBundleDeploymentEvent} event with the given application symbolic name, application
+ * version, and bundle.
+ *
+ * @param applicationSymbolicName
+ * @param applicationVersion
+ */
+ protected ApplicationBundleDeploymentEvent(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion);
+ this.bundle = bundle;
+ }
+
+ /**
+ * Get this event's {@link Bundle}.
+ *
+ * @return the bundle
+ */
+ public Bundle getBundle() {
+ return this.bundle;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override public String toString() {
+ return this.getClass().getName() + " for bundle " + this.bundle.getSymbolicName() + " of application " + this.getApplicationSymbolicName()
+ + " version " + this.getApplicationVersion();
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStarted.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStarted.java
new file mode 100644
index 00000000..b1903233
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStarted.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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationBundleStarted} is an event which is broadcast when an application bundle has been started.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationBundleStarted extends ApplicationBundleDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationBundleStarted} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName of bundle
+ * @param applicationVersion of bundle
+ * @param bundle object started
+ */
+ public ApplicationBundleStarted(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion, bundle);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStarting.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStarting.java
new file mode 100644
index 00000000..3d79b6ed
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStarting.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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationBundleStarting} is an event which is broadcast when an application bundle is being started.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationBundleStarting extends ApplicationBundleDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationBundleStarting} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName of bundle
+ * @param applicationVersion of bundle
+ * @param bundle object starting
+ */
+ public ApplicationBundleStarting(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion, bundle);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStopped.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStopped.java
new file mode 100644
index 00000000..248ee5d8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStopped.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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationBundleStopped} is an event which is broadcast when an application bundle has been stopped.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationBundleStopped extends ApplicationBundleDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationBundleStopped} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName of bundle
+ * @param applicationVersion of bundle
+ * @param bundle object stopped
+ */
+ public ApplicationBundleStopped(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion, bundle);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStopping.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStopping.java
new file mode 100644
index 00000000..6cd1a224
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleStopping.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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationBundleStopping} is an event which is broadcast when an application bundle is being stopped.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationBundleStopping extends ApplicationBundleDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationBundleStopping} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName of bundle
+ * @param applicationVersion of bundle
+ * @param bundle object stopping
+ */
+ public ApplicationBundleStopping(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion, bundle);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleUndeployed.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleUndeployed.java
new file mode 100644
index 00000000..9c20a260
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleUndeployed.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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationBundleUndeployed} is an event which is broadcast when an application bundle has been undeployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationBundleUndeployed extends ApplicationBundleDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationBundleUndeployed} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName of bundle
+ * @param applicationVersion of bundle
+ * @param bundle object undeployed
+ */
+ public ApplicationBundleUndeployed(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion, bundle);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleUndeploying.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleUndeploying.java
new file mode 100644
index 00000000..309a6a95
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationBundleUndeploying.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.deployer.core.event;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationBundleUndeploying} is an event which is broadcast when an application bundle is about to be undeployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationBundleUndeploying extends ApplicationBundleDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationBundleUndeploying} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName of bundle
+ * @param applicationVersion of bundle
+ * @param bundle object un-deploying
+ */
+ public ApplicationBundleUndeploying(String applicationSymbolicName, Version applicationVersion, Bundle bundle) {
+ super(applicationSymbolicName, applicationVersion, bundle);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeployed.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeployed.java
new file mode 100644
index 00000000..e73093d6
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeployed.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.deployer.core.event;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationDeployed} is an event which is broadcast when an application has been successfully deployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationDeployed extends ApplicationDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationDeployed} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName
+ * @param applicationVersion
+ */
+ public ApplicationDeployed(String applicationSymbolicName, Version applicationVersion) {
+ super(applicationSymbolicName, applicationVersion);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploying.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploying.java
new file mode 100644
index 00000000..3d022fd2
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploying.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.deployer.core.event;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationDeploying} is an event which is broadcast when an application is about to be deployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationDeploying extends ApplicationDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationDeploying} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName
+ * @param applicationVersion
+ */
+ public ApplicationDeploying(String applicationSymbolicName, Version applicationVersion) {
+ super(applicationSymbolicName, applicationVersion);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploymentEvent.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploymentEvent.java
new file mode 100644
index 00000000..5a2f49f8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploymentEvent.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.deployer.core.event;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+/**
+ * {@link ApplicationDeploymentEvent} is the base class for all events relating to deployment of applications and
+ * modules of applications.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public abstract class ApplicationDeploymentEvent {
+
+ private final String applicationSymbolicName;
+
+ private final Version applicationVersion;
+
+ /**
+ * Construct a new {@link ApplicationDeploymentEvent} with the given application name and version.
+ *
+ * @param applicationSymbolicName the symbolic name of the application
+ * @param applicationVersion the version of the application
+ */
+ protected ApplicationDeploymentEvent(String applicationSymbolicName, Version applicationVersion) {
+ this.applicationSymbolicName = applicationSymbolicName;
+ this.applicationVersion = applicationVersion;
+ }
+
+ /**
+ * Get the symbolic name of this event's application.
+ *
+ * @return the application symbolic name
+ */
+ public String getApplicationSymbolicName() {
+ return applicationSymbolicName;
+ }
+
+ /**
+ * Get the version of this event's application.
+ *
+ * @return the application version
+ */
+ public Version getApplicationVersion() {
+ return applicationVersion;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override public String toString() {
+ return this.getClass().getName() + " for application " + this.applicationSymbolicName + " version " + this.applicationVersion;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploymentEventNotifier.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploymentEventNotifier.java
new file mode 100644
index 00000000..18ad815f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationDeploymentEventNotifier.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * 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.deployer.core.event;
+
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link ApplicationDeploymentEventNotifier} is used by the deployer to notify all {@link DeploymentListener
+ * ApplicationDeploymentListeners} of an application deployment event.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+class ApplicationDeploymentEventNotifier implements DeploymentListener {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final Set<DeploymentListener> listeners;
+
+ /**
+ * Construct an {@link ApplicationDeploymentEventNotifier} for the given set of listeners. The given set will be
+ * modified externally to this class as listeners come and go.
+ *
+ * @param listeners the listeners to be notified
+ */
+ ApplicationDeploymentEventNotifier(Set<DeploymentListener> listeners) {
+ this.listeners = listeners;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onEvent(ApplicationDeploymentEvent event) {
+ Bundle bundle = null;
+ if (event instanceof ApplicationBundleDeploymentEvent) {
+ bundle = ((ApplicationBundleDeploymentEvent) event).getBundle();
+ }
+
+ if (bundle == null) {
+ logger.info("Delivering '{}' for application '{}' version '{}' to application deployment listeners", new Object[] {
+ event.getClass().getName(), event.getApplicationSymbolicName(), event.getApplicationVersion() });
+ } else {
+ logger.info("Delivering '{}' for bundle '{}' of application '{}' version '{}' to application deployment listeners", new Object[] {
+ event.getClass().getName(), bundle.getSymbolicName(), event.getApplicationSymbolicName(), event.getApplicationVersion() });
+ }
+
+ for (DeploymentListener listener : listeners) {
+ try {
+ listener.onEvent(event);
+ } catch (RuntimeException e) {
+ // Trace and ignore the exception.
+ logger.error("Application deployment listener '{}' threw exception", e, listener);
+ }
+ }
+
+ if (bundle == null) {
+ logger.info("Delivered '{}' for application '%s' version '{}' to application deployment listeners", new Object[] {
+ event.getClass().getName(), event.getApplicationSymbolicName(), event.getApplicationVersion() });
+ } else {
+ logger.info("Delivered '{}' for bundle '{}' of application '{}' version '{}' to application deployment listeners", new Object[] {
+ event.getClass().getName(), bundle.getSymbolicName(), event.getApplicationSymbolicName(), event.getApplicationVersion() });
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationUndeployed.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationUndeployed.java
new file mode 100644
index 00000000..c19a0623
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationUndeployed.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.deployer.core.event;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationUndeployed} is an event which is broadcast when an application has been undeployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationUndeployed extends ApplicationDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationUndeployed} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName
+ * @param applicationVersion
+ */
+ public ApplicationUndeployed(String applicationSymbolicName, Version applicationVersion) {
+ super(applicationSymbolicName, applicationVersion);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationUndeploying.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationUndeploying.java
new file mode 100644
index 00000000..d46ac26a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/ApplicationUndeploying.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.deployer.core.event;
+
+import org.eclipse.virgo.kernel.osgi.common.Version;
+
+
+/**
+ * {@link ApplicationUndeploying} is an event which is broadcast when an application is about to be undeployed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public class ApplicationUndeploying extends ApplicationDeploymentEvent {
+
+ /**
+ * Construct a {@link ApplicationUndeploying} event with the given application symbolic name and version.
+ *
+ * @param applicationSymbolicName
+ * @param applicationVersion
+ */
+ public ApplicationUndeploying(String applicationSymbolicName, Version applicationVersion) {
+ super(applicationSymbolicName, applicationVersion);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/DeploymentListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/DeploymentListener.java
new file mode 100644
index 00000000..77a96d33
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/event/DeploymentListener.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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.deployer.core.event;
+
+/**
+ * {@link DeploymentListener} is used to listen for events relating to application deployment.
+ * <p />
+ * Application wide events extend {@link ApplicationDeploymentEvent} and are delivered synchronously by the thread which
+ * initiated deployment.
+ * <p />
+ * Events for specific bundles of an application extend {@link ApplicationBundleDeploymentEvent} and may be delivered
+ * during deployment or undeployment of the application or refresh of a bundle of the application.
+ * <p />
+ * Users of this interface should simply publish an object that implements this interface to the OSGi service registry.
+ * <p />
+ *
+ * @see ApplicationDeploymentEvent
+ * @see ApplicationBundleDeploymentEvent
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface DeploymentListener {
+
+ /**
+ * Notify an {@link ApplicationDeploymentEvent}.
+ *
+ * @param event the event being notified
+ */
+ void onEvent(ApplicationDeploymentEvent event);
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ApplicationRecoverer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ApplicationRecoverer.java
new file mode 100644
index 00000000..ec0bea2b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ApplicationRecoverer.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.deployer.core.internal;
+
+import java.net.URI;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+
+
+/**
+ * {@link ApplicationRecoverer} is an interface to an {@link org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer ApplicationDeployer}
+ * intended only for use by the recovery agent.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface ApplicationRecoverer {
+
+ /**
+ * Redeploy the artifact that was originally deployed from the given URI.
+ *
+ * @param location URI of artifact
+ * @param options the {@link DeploymentOptions} of the artifact
+ * @throws DeploymentException
+ */
+ void recoverDeployment(URI location, DeploymentOptions options) throws DeploymentException;
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ArtefactNotFoundException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ArtefactNotFoundException.java
new file mode 100644
index 00000000..e6a9da4d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ArtefactNotFoundException.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.deployer.core.internal;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+
+
+/**
+ * Thrown when an artefact is not found, i.e. the artefact does not exist.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public final class ArtefactNotFoundException extends DeploymentException {
+
+ private static final long serialVersionUID = 7374184559342205863L;
+
+ /**
+ * @param message The exception's message
+ */
+ public ArtefactNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/BlockingSignal.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/BlockingSignal.java
new file mode 100644
index 00000000..ede0f097
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/BlockingSignal.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.virgo.kernel.core.Signal;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+
+
+/**
+ * {@link BlockingSignal} is a {@link Signal} that blocks until complete.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public final class BlockingSignal implements Signal {
+
+ private Object monitor = new Object();
+
+ private final CountDownLatch latch = new CountDownLatch(1);
+
+ private boolean complete = false;
+
+ private Throwable cause = null;
+
+ private final boolean block;
+
+ public BlockingSignal(boolean block) {
+ this.block = block;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void signalFailure(Throwable cause) {
+ synchronized (this.monitor) {
+ this.complete = true;
+ this.cause = cause;
+ latch.countDown();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void signalSuccessfulCompletion() {
+ synchronized (this.monitor) {
+ this.complete = true;
+ latch.countDown();
+ }
+ }
+
+ public boolean awaitCompletion(long timeInSeconds) throws DeploymentException {
+ if (this.block) {
+ try {
+ this.latch.await(timeInSeconds, TimeUnit.SECONDS);
+ } catch (InterruptedException i) {
+ throw new DeploymentException("latch await interrupted", i);
+ }
+ }
+ synchronized (this.monitor) {
+ if (!complete) {
+ return false;
+ } else {
+ if (this.cause == null) {
+ return true;
+ } else {
+ try {
+ throw this.cause;
+ } catch (DeploymentException de) {
+ throw de;
+ } catch (Throwable t) {
+ throw new DeploymentException(t.getMessage(), t.getCause());
+ }
+ }
+ }
+ }
+ }
+
+ public boolean checkComplete() throws DeploymentException {
+ if (this.block) {
+ try {
+ this.latch.await();
+ } catch (InterruptedException i) {
+ throw new DeploymentException("latch await interrupted", i);
+ }
+ }
+ synchronized (this.monitor) {
+ if (!complete) {
+ return false;
+ } else {
+ if (this.cause == null) {
+ return true;
+ } else {
+ try {
+ throw this.cause;
+ } catch (DeploymentException de) {
+ throw de;
+ } catch (Throwable t) {
+ throw new DeploymentException(t.getMessage(), t.getCause());
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/BundleDeploymentPropertiesTransformer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/BundleDeploymentPropertiesTransformer.java
new file mode 100644
index 00000000..a3644fcd
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/BundleDeploymentPropertiesTransformer.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.deployer.core.internal;
+
+import java.io.IOException;
+import java.util.Map;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.BundleInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.environment.InstallEnvironment;
+import org.eclipse.virgo.kernel.install.pipeline.stage.transform.Transformer;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.common.Tree.ExceptionThrowingTreeVisitor;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link Transformer} implementation that converts deployment properties on bundle artifacts into the corresponding
+ * manifest headers.
+ * <p/>
+ * Any deployment property with a key prefixed by <code>header:</code> will be treated a as bundle header.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class BundleDeploymentPropertiesTransformer implements Transformer {
+
+ private static final String HEADER_PREFIX = "header:";
+
+ public void transform(Tree<InstallArtifact> installTree, InstallEnvironment installEnvironment) throws DeploymentException {
+ installTree.visit(new Visitor());
+ }
+
+ private void doTransformBundleArtifact(BundleInstallArtifact value, Map<String, String> deploymentProperties) throws IOException {
+ BundleManifest manifest = value.getBundleManifest();
+
+ for(Map.Entry<String, String> entries : deploymentProperties.entrySet()) {
+ String headerName = convertToHeaderName(entries.getKey());
+ if(headerName != null) {
+ manifest.setHeader(headerName, entries.getValue());
+ }
+ }
+ }
+
+ /**
+ * Attempts to convert the supplied property key into a header name. If the key is not a valid header name (it
+ * doesn't have the header: prefix) then <code>null</code> is returned.
+ */
+ private String convertToHeaderName(String propertyKey) {
+ if(propertyKey.startsWith(HEADER_PREFIX)) {
+ return propertyKey.substring(HEADER_PREFIX.length());
+ } else {
+ return null;
+ }
+ }
+
+ private final class Visitor implements ExceptionThrowingTreeVisitor<InstallArtifact, DeploymentException> {
+
+ public boolean visit(Tree<InstallArtifact> tree) throws DeploymentException {
+ InstallArtifact value = tree.getValue();
+ if (value instanceof BundleInstallArtifact) {
+ Map<String, String> deploymentProperties = ((BundleInstallArtifact)value).getDeploymentProperties();
+ if (deploymentProperties != null && !deploymentProperties.isEmpty()) {
+ try {
+ doTransformBundleArtifact((BundleInstallArtifact) value, deploymentProperties);
+ } catch (Exception e) {
+ throw new DeploymentException("Unable to apply deployment for artifact '" + value.getName() + "'", e);
+ }
+ }
+ }
+ return true;
+ }
+
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleManifest.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleManifest.java
new file mode 100644
index 00000000..27d41f19
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleManifest.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.util.osgi.manifest.BundleActivationPolicy;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleSymbolicName;
+import org.eclipse.virgo.util.osgi.manifest.DynamicImportPackage;
+import org.eclipse.virgo.util.osgi.manifest.ExportPackage;
+import org.eclipse.virgo.util.osgi.manifest.FragmentHost;
+import org.eclipse.virgo.util.osgi.manifest.ImportBundle;
+import org.eclipse.virgo.util.osgi.manifest.ImportLibrary;
+import org.eclipse.virgo.util.osgi.manifest.ImportPackage;
+import org.eclipse.virgo.util.osgi.manifest.RequireBundle;
+
+/**
+ * {@link DescopingBundleManifest} is a wrapper of a {@link BundleManifest} that reverses the effects of scoping. In the
+ * first instance only the effects on the bundle symbolic are reversed.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class DescopingBundleManifest implements BundleManifest {
+
+ private final BundleManifest wrappedManifest;
+
+ DescopingBundleManifest(BundleManifest bundleManifest) {
+ this.wrappedManifest = bundleManifest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleActivationPolicy getBundleActivationPolicy() {
+ return this.wrappedManifest.getBundleActivationPolicy();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<String> getBundleClasspath() {
+ return this.wrappedManifest.getBundleClasspath();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getBundleDescription() {
+ return this.wrappedManifest.getBundleDescription();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getBundleManifestVersion() {
+ return this.wrappedManifest.getBundleManifestVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getBundleName() {
+ return this.wrappedManifest.getBundleName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleSymbolicName getBundleSymbolicName() {
+ return new DescopingBundleSymbolicName(this.wrappedManifest.getBundleSymbolicName(), this.wrappedManifest.getModuleScope());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public URL getBundleUpdateLocation() {
+ return this.wrappedManifest.getBundleUpdateLocation();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Version getBundleVersion() {
+ return this.wrappedManifest.getBundleVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DynamicImportPackage getDynamicImportPackage() {
+ return this.wrappedManifest.getDynamicImportPackage();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ExportPackage getExportPackage() {
+ return this.wrappedManifest.getExportPackage();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public FragmentHost getFragmentHost() {
+ return this.wrappedManifest.getFragmentHost();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getHeader(String name) {
+ return this.wrappedManifest.getHeader(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ImportBundle getImportBundle() {
+ return this.wrappedManifest.getImportBundle();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ImportLibrary getImportLibrary() {
+ return this.wrappedManifest.getImportLibrary();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ImportPackage getImportPackage() {
+ return this.wrappedManifest.getImportPackage();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getModuleScope() {
+ return this.wrappedManifest.getModuleScope();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getModuleType() {
+ return this.wrappedManifest.getModuleType();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public RequireBundle getRequireBundle() {
+ return this.wrappedManifest.getRequireBundle();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setBundleDescription(String bundleDescription) {
+ this.wrappedManifest.setBundleDescription(bundleDescription);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setBundleManifestVersion(int bundleManifestVersion) {
+ this.wrappedManifest.setBundleManifestVersion(bundleManifestVersion);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setBundleName(String bundleName) {
+ this.wrappedManifest.setBundleName(bundleName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setBundleUpdateLocation(URL bundleUpdateLocation) {
+ this.wrappedManifest.setBundleUpdateLocation(bundleUpdateLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setBundleVersion(Version bundleVersion) {
+ this.wrappedManifest.setBundleVersion(bundleVersion);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setHeader(String name, String value) {
+ this.wrappedManifest.setHeader(name, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setModuleScope(String moduleScope) {
+ this.wrappedManifest.setModuleScope(moduleScope);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setModuleType(String moduleType) {
+ this.wrappedManifest.setModuleType(moduleType);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Dictionary<String, String> toDictionary() {
+ return this.wrappedManifest.toDictionary();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void write(Writer writer) throws IOException {
+ this.wrappedManifest.write(writer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return this.wrappedManifest.toString();
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleSymbolicName.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleSymbolicName.java
new file mode 100644
index 00000000..1b5711af
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/DescopingBundleSymbolicName.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.deployer.core.internal;
+
+import java.util.Map;
+
+import org.eclipse.virgo.util.osgi.manifest.BundleSymbolicName;
+
+/**
+ * {@link DescopingBundleSymbolicName} is a wrapper of a {@link BundleSymbolicName} that reverses the effects of
+ * scoping.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class DescopingBundleSymbolicName implements BundleSymbolicName {
+
+ public static final String SCOPE_SEPARATOR = "-";
+
+ private final BundleSymbolicName wrappedBundleSymbolicName;
+
+ private final String moduleScope;
+
+ DescopingBundleSymbolicName(BundleSymbolicName bundleSymbolicName, String moduleScope) {
+ this.wrappedBundleSymbolicName = bundleSymbolicName;
+ this.moduleScope = moduleScope;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public FragmentAttachment getFragmentAttachment() {
+ return this.wrappedBundleSymbolicName.getFragmentAttachment();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getSymbolicName() {
+ return getUnscopedSymbolicName();
+ }
+
+ private String getUnscopedSymbolicName() {
+ String symbolicName = null;
+ if (this.wrappedBundleSymbolicName != null) {
+ symbolicName = this.wrappedBundleSymbolicName.getSymbolicName();
+ if (this.moduleScope != null) {
+ String scopeName = this.moduleScope + SCOPE_SEPARATOR;
+ if (symbolicName.startsWith(scopeName)) {
+ symbolicName = symbolicName.substring(scopeName.length());
+ }
+ }
+ }
+ return symbolicName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isSingleton() {
+ return this.wrappedBundleSymbolicName.isSingleton();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setFragmentAttachment(FragmentAttachment fragmentAttachment) {
+ this.wrappedBundleSymbolicName.setFragmentAttachment(fragmentAttachment);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setSingleton(boolean singleton) {
+ this.wrappedBundleSymbolicName.setSingleton(singleton);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setSymbolicName(String symbolicName) {
+ this.wrappedBundleSymbolicName.setSymbolicName(symbolicName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, String> getAttributes() {
+ return this.wrappedBundleSymbolicName.getAttributes();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, String> getDirectives() {
+ return this.wrappedBundleSymbolicName.getDirectives();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void resetFromParseString(String string) {
+ this.wrappedBundleSymbolicName.resetFromParseString(string);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toParseString() {
+ return this.wrappedBundleSymbolicName.toParseString();
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ImportExpandingTransformer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ImportExpandingTransformer.java
new file mode 100644
index 00000000..e7b6014f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ImportExpandingTransformer.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.virgo.kernel.osgi.framework.ImportExpander;
+import org.eclipse.virgo.kernel.osgi.framework.ImportMergeException;
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyDependenciesException;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.BundleInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact;
+import org.eclipse.virgo.kernel.install.environment.InstallEnvironment;
+import org.eclipse.virgo.kernel.install.pipeline.stage.transform.Transformer;
+import org.eclipse.virgo.kernel.install.pipeline.stage.transform.internal.BundleInstallArtifactGatheringTreeVisitor;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.common.Tree.ExceptionThrowingTreeVisitor;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link ImportExpandingTransformer} is a {@link Transformer} that expands Import-Library and Import-Bundle into package imports.
+ * Expansion of imports in bundles that are part of a scope is performed against all of the bundles at the same time.
+ * Expansion of bundles that are not part of a scope is performed on a bundle-by-bundle basis.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class ImportExpandingTransformer implements Transformer {
+
+ private final ImportExpander importExpander;
+
+ ImportExpandingTransformer(ImportExpander importExpander) {
+ this.importExpander = importExpander;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void transform(Tree<InstallArtifact> installTree, final InstallEnvironment installEnvironment) throws DeploymentException {
+ installTree.visit(new ImportExpandingTreeVisitor(installEnvironment));
+ }
+
+ private final class ImportExpandingTreeVisitor implements ExceptionThrowingTreeVisitor<InstallArtifact, DeploymentException> {
+
+ private final InstallEnvironment installEnvironment;
+
+ ImportExpandingTreeVisitor(InstallEnvironment installEnvironment) {
+ this.installEnvironment = installEnvironment;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean visit(Tree<InstallArtifact> tree) throws DeploymentException {
+ if (tree.getValue() instanceof PlanInstallArtifact) {
+ PlanInstallArtifact planInstallArtifact = (PlanInstallArtifact) tree.getValue();
+ if (planInstallArtifact.isScoped()) {
+ expandImportsOfBundlesInScopedPlan(tree, this.installEnvironment);
+ return false;
+ }
+ } else if (tree.getValue() instanceof BundleInstallArtifact) {
+ expandImports(Collections.singleton((BundleInstallArtifact) tree.getValue()), this.installEnvironment);
+ }
+ return true;
+ }
+ }
+
+ void expandImportsOfBundlesInScopedPlan(Tree<InstallArtifact> planTree, InstallEnvironment installEnvironment) throws DeploymentException {
+ BundleInstallArtifactGatheringTreeVisitor visitor = new BundleInstallArtifactGatheringTreeVisitor();
+ planTree.visit(visitor);
+ expandImports(visitor.getChildBundles(), installEnvironment);
+ }
+
+ void expandImports(Set<BundleInstallArtifact> bundleInstallArtifacts, InstallEnvironment installEnvironment) throws DeploymentException {
+
+ List<BundleManifest> bundleManifestList = new ArrayList<BundleManifest>(bundleInstallArtifacts.size());
+
+ for (BundleInstallArtifact bundleInstallArtifact : bundleInstallArtifacts) {
+ try {
+ BundleManifest bundleManifest = bundleInstallArtifact.getBundleManifest();
+ bundleManifestList.add(bundleManifest);
+ } catch (IOException e) {
+ installEnvironment.getInstallLog().log(this, "I/O error getting bundle manifest for %s", bundleInstallArtifact.toString());
+ throw new DeploymentException("I/O error getting bundle manifest for " + bundleInstallArtifact, e);
+ }
+ }
+
+ try {
+ this.importExpander.expandImports(bundleManifestList);
+ installEnvironment.getInstallLog().log(this, "Expanded imports of %s", bundleInstallArtifacts.toString());
+ } catch (ImportMergeException e) {
+ installEnvironment.getInstallLog().log(this, "Error in %s merging expanded imports for package %s from %s",
+ bundleInstallArtifacts.toString(), e.getConflictingPackageName(), e.getSources());
+ throw new DeploymentException("Error merging expanded imports for " + bundleInstallArtifacts, e);
+ } catch (UnableToSatisfyDependenciesException e) {
+ installEnvironment.getInstallLog().log(this, "Unsatisfied dependencies in %s: %s", bundleInstallArtifacts.toString(),
+ e.getFailureDescription());
+ throw new DeploymentException("Unsatisfied dependency detected when expanding imports for " + bundleInstallArtifacts, e);
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ManifestUpgrader.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ManifestUpgrader.java
new file mode 100644
index 00000000..9df165c0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/ManifestUpgrader.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.deployer.core.internal;
+
+import java.io.IOException;
+import java.net.URL;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.BundleInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.environment.InstallEnvironment;
+import org.eclipse.virgo.kernel.install.pipeline.stage.transform.Transformer;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.common.Tree.ExceptionThrowingTreeVisitor;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link ManifestUpgrader} upgrades OSGi R3 manifests to be R4.1 compliant as this the minimum required to enable
+ * scoping.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class ManifestUpgrader implements Transformer {
+
+ private static final int BUNDLE_MANIFEST_VERSION_FOR_OSGI_R4 = 2;
+
+ /**
+ * {@inheritDoc}
+ */
+ public void transform(Tree<InstallArtifact> installTree, final InstallEnvironment installEnvironment) throws DeploymentException {
+ installTree.visit(new ExceptionThrowingTreeVisitor<InstallArtifact, DeploymentException>() {
+
+ public boolean visit(Tree<InstallArtifact> tree) throws DeploymentException {
+ operate(tree.getValue(), installEnvironment);
+ return true;
+ }
+ });
+ }
+
+ void operate(InstallArtifact installArtifact, InstallEnvironment installEnvironment) throws DeploymentException {
+ if (installArtifact instanceof BundleInstallArtifact) {
+ BundleManifest bundleManifest = getBundleManifest(installArtifact, installEnvironment);
+ upgradeManifestIfNecessary(bundleManifest, installArtifact, installEnvironment);
+ validateManifest(bundleManifest, installArtifact, installEnvironment);
+ removeBundleUpdateLocation(installArtifact, installEnvironment, bundleManifest);
+ }
+ }
+
+ private BundleManifest getBundleManifest(InstallArtifact installArtifact, InstallEnvironment installEnvironment) throws DeploymentException {
+ try {
+ return ((BundleInstallArtifact) installArtifact).getBundleManifest();
+ } catch (IOException e) {
+ installEnvironment.getInstallLog().log(DeployerLogEvents.BUNDLE_MANIFEST_NOT_FOUND, e, installArtifact.getName(), installArtifact.getVersion());
+ throw new DeploymentException("I/O error accessing bundle manifest for " + installArtifact, e);
+ }
+ }
+
+ /**
+ * Upgrade the bundle manifest version to OSGi R4 if it is older. Do not issue a log message to avoid flooding the
+ * log.
+ *
+ * This upgrade is necessary to support application scoping.
+ *
+ * @param bundleManifest The manifest to upgrade
+ * @throws DeploymentException
+ */
+ private void upgradeManifestIfNecessary(BundleManifest bundleManifest, InstallArtifact installArtifact, InstallEnvironment installEnvironment) {
+ if (bundleManifest.getBundleManifestVersion() < BUNDLE_MANIFEST_VERSION_FOR_OSGI_R4) {
+ installEnvironment.getInstallLog().log(this, "OSGi R3 or earlier manifest detected:\n'%s'\nfor %s - upgrading manifest to OSGi R4.",
+ bundleManifest.toString(), installArtifact.toString());
+
+ bundleManifest.setBundleManifestVersion(BUNDLE_MANIFEST_VERSION_FOR_OSGI_R4);
+
+ if (bundleManifest.getBundleSymbolicName().getSymbolicName() == null) {
+ bundleManifest.getBundleSymbolicName().setSymbolicName(installArtifact.getName());
+ }
+ }
+ }
+
+ private void validateManifest(BundleManifest bundleManifest, InstallArtifact installArtifact, InstallEnvironment installEnvironment)
+ throws DeploymentException {
+ if (bundleManifest.getBundleSymbolicName().getSymbolicName() == null) {
+ installEnvironment.getInstallLog().log(DeployerLogEvents.MISSING_BUNDLE_SYMBOLIC_NAME, installArtifact.getName(),
+ installArtifact.getVersion(), bundleManifest);
+ throw new DeploymentException("OSGi R4 manifest with no bundle symbolic name detected:\n" + bundleManifest + "'\nfor module '" + this
+ + "'.");
+ }
+ }
+
+ /**
+ * Remove any bundle update location header to avoid it interfering with refresh.
+ */
+ private void removeBundleUpdateLocation(InstallArtifact installArtifact, InstallEnvironment installEnvironment, BundleManifest bundleManifest) {
+ URL bundleUpdateLocation = bundleManifest.getBundleUpdateLocation();
+
+ if (bundleUpdateLocation != null) {
+ installEnvironment.getInstallLog().log(DeployerLogEvents.DISCARDING_BUNDLE_UPDATE_LOCATION, bundleUpdateLocation,
+ installArtifact.getName(), installArtifact.getVersion().toString());
+ bundleManifest.setBundleUpdateLocation(null);
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/PipelinedApplicationDeployer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/PipelinedApplicationDeployer.java
new file mode 100644
index 00000000..abf85dfa
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/PipelinedApplicationDeployer.java
@@ -0,0 +1,532 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+import java.io.File;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+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.core.KernelException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer;
+import org.eclipse.virgo.kernel.deployer.core.DeployUriNormaliser;
+import org.eclipse.virgo.kernel.deployer.core.DeployerConfiguration;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
+import org.eclipse.virgo.kernel.deployer.core.internal.event.DeploymentListener;
+import org.eclipse.virgo.kernel.deployer.model.DuplicateDeploymentIdentityException;
+import org.eclipse.virgo.kernel.deployer.model.DuplicateFileNameException;
+import org.eclipse.virgo.kernel.deployer.model.DuplicateLocationException;
+import org.eclipse.virgo.kernel.deployer.model.RuntimeArtifactModel;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeInclosure;
+import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact;
+import org.eclipse.virgo.kernel.install.environment.InstallEnvironment;
+import org.eclipse.virgo.kernel.install.environment.InstallEnvironmentFactory;
+import org.eclipse.virgo.kernel.install.pipeline.Pipeline;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.io.PathReference;
+
+/**
+ * {@link PipelinedApplicationDeployer} is an implementation of {@link ApplicationDeployer} which creates a {@link Tree}
+ * of {@link InstallArtifact InstallArtifacts} and processes the tree by passing it through a {@link Pipeline} while
+ * operating on an {@link InstallEnvironment}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class PipelinedApplicationDeployer implements ApplicationDeployer, ApplicationRecoverer {
+
+ private static final String BUNDLE_TYPE = "bundle";
+
+ private final EventLogger eventLogger;
+
+ private final Object monitor = new Object();
+
+ private final InstallEnvironmentFactory installEnvironmentFactory;
+
+ private final InstallArtifactTreeInclosure installArtifactTreeInclosure;
+
+ private final RuntimeArtifactModel ram;
+
+ private final DeploymentListener deploymentListener;
+
+ private final Map<DeploymentIdentity, DeploymentOptions> deploymentOptionsMap = new HashMap<DeploymentIdentity, DeploymentOptions>();
+
+ private final Pipeline pipeline;
+
+ private final DeployUriNormaliser deployUriNormaliser;
+
+ private final int deployerConfiguredTimeoutInSeconds;
+
+ public PipelinedApplicationDeployer(Pipeline pipeline, InstallArtifactTreeInclosure installArtifactTreeInclosure,
+ InstallEnvironmentFactory installEnvironmentFactory, RuntimeArtifactModel ram, DeploymentListener deploymentListener,
+ EventLogger eventLogger, DeployUriNormaliser normaliser, DeployerConfiguration deployerConfiguration) {
+ this.eventLogger = eventLogger;
+ this.installArtifactTreeInclosure = installArtifactTreeInclosure;
+ this.installEnvironmentFactory = installEnvironmentFactory;
+ this.ram = ram;
+ this.deploymentListener = deploymentListener;
+ this.deployUriNormaliser = normaliser;
+
+ this.pipeline = pipeline;
+ this.deployerConfiguredTimeoutInSeconds = deployerConfiguration.getDeploymentTimeoutSeconds();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity deploy(URI location) throws DeploymentException {
+ synchronized (this.monitor) {
+ return deploy(location, new DeploymentOptions());
+ }
+ }
+
+ private URI normaliseDeploymentUri(URI uri) throws DeploymentException {
+ URI normalisedLocation = this.deployUriNormaliser.normalise(uri);
+
+ if (normalisedLocation == null) {
+ this.eventLogger.log(DeployerLogEvents.UNSUPPORTED_URI_SCHEME, uri, uri.getScheme());
+ throw new DeploymentException("PipelinedApplicationDeployer.deploy does not support '" + uri.getScheme() + "' scheme URIs");
+ }
+
+ return normalisedLocation;
+ }
+
+ public DeploymentIdentity install(URI location) throws DeploymentException {
+ return install(location, new DeploymentOptions());
+ }
+
+ public DeploymentIdentity install(URI uri, DeploymentOptions deploymentOptions) throws DeploymentException {
+ URI normalisedUri = normaliseDeploymentUri(uri);
+
+ DeploymentIdentity deploymentIdentity = doInstall(normalisedUri, deploymentOptions);
+ this.deploymentListener.deployed(normalisedUri, deploymentOptions);
+
+ return deploymentIdentity;
+ }
+
+ private DeploymentIdentity doInstall(URI normalisedUri, DeploymentOptions deploymentOptions) throws DeploymentException {
+ synchronized (this.monitor) {
+ InstallArtifact existingArtifact = this.ram.get(normalisedUri);
+
+ if (existingArtifact != null) {
+ DeploymentIdentity refreshedIdentity = updateAndRefreshExistingArtifact(normalisedUri, existingArtifact);
+ if (refreshedIdentity != null) {
+ return refreshedIdentity;
+ }
+ }
+
+ Tree<InstallArtifact> installTree = this.installArtifactTreeInclosure.createInstallTree(new File(normalisedUri));
+ DeploymentIdentity deploymentIdentity;
+
+ try {
+ deploymentIdentity = addTreeToModel(normalisedUri, installTree);
+ } catch (KernelException ke) {
+ throw new DeploymentException(ke.getMessage(), ke);
+ }
+
+ this.deploymentOptionsMap.put(deploymentIdentity, deploymentOptions);
+ try {
+ driveInstallPipeline(normalisedUri, installTree);
+ } catch (DeploymentException de) {
+ this.ram.delete(deploymentIdentity);
+ throw de;
+ }
+
+ return deploymentIdentity;
+ }
+ }
+
+ private DeploymentIdentity updateAndRefreshExistingArtifact(URI normalisedLocation, InstallArtifact existingArtifact) throws DeploymentException {
+ String oldType = existingArtifact.getType();
+ String oldName = existingArtifact.getName();
+ Version oldVersion = existingArtifact.getVersion();
+
+ DeploymentIdentity deploymentIdentity = updateAndRefresh(normalisedLocation, existingArtifact);
+ if (deploymentIdentity != null) {
+ return deploymentIdentity;
+ }
+
+ DeploymentIdentity oldDeploymentIdentity = new StandardDeploymentIdentity(oldType, oldName, oldVersion.toString());
+ undeployInternal(oldDeploymentIdentity, true);
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity deploy(URI location, DeploymentOptions deploymentOptions) throws DeploymentException {
+ URI normalisedLocation = normaliseDeploymentUri(location);
+
+ InstallArtifact installedArtifact;
+ DeploymentIdentity deploymentIdentity;
+
+ synchronized (this.monitor) {
+ deploymentIdentity = install(location, deploymentOptions);
+ installedArtifact = this.ram.get(normalisedLocation);
+ }
+
+ try {
+ start(installedArtifact, deploymentOptions.getSynchronous());
+ } catch (DeploymentException de) {
+ synchronized (this.monitor) {
+ stopArtifact(installedArtifact);
+ uninstallArtifact(installedArtifact);
+ }
+ throw de;
+ }
+
+ this.deploymentListener.deployed(normalisedLocation, deploymentOptions);
+
+ return deploymentIdentity;
+ }
+
+ private DeploymentIdentity updateAndRefresh(URI location, InstallArtifact installArtifact) throws DeploymentException {
+ DeploymentIdentity deploymentIdentity = null;
+ this.installArtifactTreeInclosure.updateStagingArea(new File(location), new ArtifactIdentity(installArtifact.getType(),
+ installArtifact.getName(), installArtifact.getVersion(), installArtifact.getScopeName()));
+ if (installArtifact.refresh()) {
+ this.deploymentListener.refreshed(location);
+
+ deploymentIdentity = new StandardDeploymentIdentity(installArtifact.getType(), installArtifact.getName(),
+ installArtifact.getVersion().toString());
+ }
+ return deploymentIdentity;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity deploy(String type, String name, Version version) throws DeploymentException {
+ throw new UnsupportedOperationException(
+ "PipelinedApplicationDeployer ApplicationDeployer does not support deployment by type, name, and version");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity deploy(String type, String name, Version version, DeploymentOptions options) throws DeploymentException {
+ throw new UnsupportedOperationException(
+ "PipelinedApplicationDeployer ApplicationDeployer does not support deployment by type, name, and version");
+ }
+
+ private DeploymentIdentity addTreeToModel(URI location, Tree<InstallArtifact> installTree) throws DuplicateFileNameException,
+ DuplicateLocationException, DuplicateDeploymentIdentityException, DeploymentException {
+ return this.ram.add(location, installTree.getValue());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void recoverDeployment(URI uri, DeploymentOptions options) throws DeploymentException {
+
+ Tree<InstallArtifact> installTree = this.installArtifactTreeInclosure.recoverInstallTree(new File(uri), options);
+
+ if (installTree == null) {
+ // Remove the URI from the recovery log.
+ this.deploymentListener.undeployed(uri);
+ } else {
+ driveInstallPipeline(uri, installTree);
+
+ start(installTree.getValue(), options.getSynchronous());
+
+ try {
+ addTreeToModel(uri, installTree);
+ } catch (KernelException e) {
+ throw new DeploymentException(e.getMessage(), e);
+ }
+ }
+ }
+
+ private void driveInstallPipeline(URI uri, Tree<InstallArtifact> installTree) throws DeploymentException {
+
+ InstallEnvironment installEnvironment = this.installEnvironmentFactory.createInstallEnvironment(installTree.getValue());
+
+ try {
+ this.pipeline.process(installTree, installEnvironment);
+ } catch (UnableToSatisfyBundleDependenciesException utsbde) {
+ logDependencySatisfactionException(uri, utsbde);
+ throw new DeploymentException("Dependency satisfaction failed", utsbde);
+ }
+ }
+
+ private void logDependencySatisfactionException(URI uri, UnableToSatisfyDependenciesException ex) {
+ this.eventLogger.log(DeployerLogEvents.UNABLE_TO_SATISFY_CONSTRAINTS, ex, uri, ex.getSymbolicName(), ex.getVersion(),
+ ex.getFailureDescription());
+ }
+
+ private void start(InstallArtifact installArtifact, boolean synchronous) throws DeploymentException {
+ BlockingSignal blockingSignal = new BlockingSignal(synchronous);
+ installArtifact.start(blockingSignal);
+ if (synchronous && this.deployerConfiguredTimeoutInSeconds > 0) {
+ boolean complete = blockingSignal.awaitCompletion(this.deployerConfiguredTimeoutInSeconds);
+ if (!complete) {
+ this.eventLogger.log(DeployerLogEvents.START_TIMED_OUT, installArtifact.getType(), installArtifact.getName(),
+ installArtifact.getVersion(), this.deployerConfiguredTimeoutInSeconds);
+ }
+ } else {
+ // Completion messages will have been issued if complete, so ignore return value.
+ blockingSignal.checkComplete();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity[] getDeploymentIdentities() {
+ synchronized (this.monitor) {
+ return this.ram.getDeploymentIdentities();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity getDeploymentIdentity(URI location) {
+ synchronized (this.monitor) {
+ InstallArtifact installArtifact = this.ram.get(location);
+ if (installArtifact != null) {
+ return getDeploymentIdentity(installArtifact);
+ }
+ }
+ return null;
+ }
+
+ private DeploymentIdentity getDeploymentIdentity(InstallArtifact installArtifact) {
+ return new StandardDeploymentIdentity(installArtifact.getType(), installArtifact.getName(), installArtifact.getVersion().toString());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isDeployed(URI location) {
+ URI normalisedLocation;
+ try {
+ normalisedLocation = this.deployUriNormaliser.normalise(location);
+ } catch (DeploymentException e) {
+ return false;
+ }
+
+ if (normalisedLocation == null) {
+ this.eventLogger.log(DeployerLogEvents.UNSUPPORTED_URI_SCHEME, location.toString(), location.getScheme());
+ return false;
+ }
+ synchronized (this.monitor) {
+ return this.ram.get(normalisedLocation) != null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity refresh(URI location, String symbolicName) throws DeploymentException {
+ URI normalisedLocation = this.deployUriNormaliser.normalise(location);
+
+ if (normalisedLocation == null) {
+ this.eventLogger.log(DeployerLogEvents.UNSUPPORTED_URI_SCHEME, location.toString(), location.getScheme());
+ throw new DeploymentException("PipelinedApplicationDeployer.refresh does not support '" + location.getScheme() + "' scheme URIs");
+ }
+
+ DeploymentIdentity deploymentIdentity;
+ synchronized (this.monitor) {
+ InstallArtifact installArtifact = this.ram.get(normalisedLocation);
+ if (installArtifact == null) {
+ this.eventLogger.log(DeployerLogEvents.REFRESH_REQUEST_URI_NOT_FOUND, location.toString());
+ throw new DeploymentException("Refresh not possible as no application is deployed from URI " + location);
+ } else {
+ DeploymentIdentity originalDeploymentIdentity = getDeploymentIdentity(installArtifact);
+ deploymentIdentity = originalDeploymentIdentity;
+ try {
+ this.installArtifactTreeInclosure.updateStagingArea(new File(normalisedLocation), new ArtifactIdentity(installArtifact.getType(),
+ installArtifact.getName(), installArtifact.getVersion(), installArtifact.getScopeName()));
+ // Attempt to refresh the artifact and escalate to redeploy if this fails.
+ if (refreshInternal(symbolicName, installArtifact)) {
+ this.deploymentListener.refreshed(normalisedLocation);
+ } else {
+ DeploymentOptions deploymentOptions = this.deploymentOptionsMap.get(deploymentIdentity);
+ if (deploymentOptions == null) {
+ deploymentOptions = DeploymentOptions.DEFAULT_DEPLOYMENT_OPTIONS;
+ }
+ deploymentIdentity = redeploy(originalDeploymentIdentity, normalisedLocation, deploymentOptions);
+ }
+ this.eventLogger.log(DeployerLogEvents.REFRESH_REQUEST_COMPLETED, symbolicName, originalDeploymentIdentity.getType(),
+ originalDeploymentIdentity.getSymbolicName(), originalDeploymentIdentity.getVersion());
+ } catch (RuntimeException e) {
+ this.eventLogger.log(DeployerLogEvents.REFRESH_REQUEST_FAILED, e, symbolicName, originalDeploymentIdentity.getType(),
+ originalDeploymentIdentity.getSymbolicName(), originalDeploymentIdentity.getVersion());
+ throw e;
+ } catch (Exception e) {
+ this.eventLogger.log(DeployerLogEvents.REFRESH_REQUEST_FAILED, e, symbolicName, originalDeploymentIdentity.getType(),
+ originalDeploymentIdentity.getSymbolicName(), originalDeploymentIdentity.getVersion());
+ throw new DeploymentException("refresh failed", e);
+ }
+ }
+ }
+ return deploymentIdentity;
+ }
+
+ private boolean refreshInternal(String symbolicName, InstallArtifact installArtifact) throws DeploymentException {
+ if (installArtifact instanceof PlanInstallArtifact) {
+ return ((PlanInstallArtifact) installArtifact).refresh(symbolicName);
+ } else {
+ return installArtifact.refresh();
+ }
+ }
+
+ private DeploymentIdentity redeploy(DeploymentIdentity toUndeploy, URI toDeploy, DeploymentOptions deploymentOptions) throws DeploymentException {
+ synchronized (this.monitor) {
+ undeployInternal(toUndeploy, true);
+ }
+ return deploy(toDeploy, deploymentOptions);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void refreshBundle(String bundleSymbolicName, String bundleVersion) throws DeploymentException {
+ DeploymentIdentity deploymentIdentity = new StandardDeploymentIdentity(BUNDLE_TYPE, bundleSymbolicName, bundleVersion);
+ InstallArtifact bundleInstallArtifact;
+ synchronized (this.monitor) {
+ bundleInstallArtifact = this.ram.get(deploymentIdentity);
+ }
+ if (bundleInstallArtifact == null) {
+ this.eventLogger.log(DeployerLogEvents.REFRESH_ARTEFACT_NOT_FOUND, BUNDLE_TYPE, bundleSymbolicName, bundleVersion);
+ throw new DeploymentException("Refresh not possible as no " + BUNDLE_TYPE + " with name " + bundleSymbolicName + " and version "
+ + bundleVersion + " is deployed");
+ }
+ bundleInstallArtifact.refresh();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void undeploy(String symbolicName, String version) throws DeploymentException {
+ // This method is deprecated and should be deleted when it is no longer used. Meanwhile, just try undeploying
+ // the possible types...
+ DeploymentException de = null;
+ try {
+ undeploy(BUNDLE_TYPE, symbolicName, version);
+ return;
+ } catch (DeploymentException e) {
+ de = e;
+ }
+
+ try {
+ undeploy("par", symbolicName, version);
+ return;
+ } catch (DeploymentException e) {
+ de = e;
+ }
+
+ try {
+ undeploy("plan", symbolicName, version);
+ return;
+ } catch (DeploymentException e) {
+ de = e;
+ }
+
+ try {
+ undeploy("properties", symbolicName, version);
+ return;
+ } catch (DeploymentException e) {
+ de = e;
+ }
+
+ throw de;
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void undeploy(String type, String symbolicName, String version) throws DeploymentException {
+ DeploymentIdentity deploymentIdentity = new StandardDeploymentIdentity(type, symbolicName, version);
+ synchronized (this.monitor) {
+ undeployInternal(deploymentIdentity, false);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void undeploy(DeploymentIdentity deploymentIdentity) throws DeploymentException {
+ synchronized (this.monitor) {
+ undeployInternal(deploymentIdentity, false);
+ }
+ }
+
+ /**
+ * All the undeploy work goes on in here -- it is assumed that any required monitors are already held by the caller.
+ *
+ * @param deploymentIdentity identity of artifact to undeploy
+ * @param redeploying flag to indicate if we are performing a re-deploy
+ * @throws DeploymentException
+ */
+ private void undeployInternal(DeploymentIdentity deploymentIdentity, boolean redeploying) throws DeploymentException {
+ DeploymentOptions options = this.deploymentOptionsMap.remove(deploymentIdentity);
+ URI location = doUndeploy(deploymentIdentity);
+ if (!redeploying) {
+ deleteArtifactIfNecessary(location, options);
+ }
+ }
+
+ private void deleteArtifactIfNecessary(URI location, DeploymentOptions options) {
+ if (options != null && options.getDeployerOwned()) {
+ new PathReference(location).delete(true);
+ }
+ }
+
+ private URI doUndeploy(DeploymentIdentity deploymentIdentity) throws DeploymentException {
+ synchronized (this.monitor) {
+ InstallArtifact installArtifact = this.ram.get(deploymentIdentity);
+ if (installArtifact == null) {
+ String type = deploymentIdentity.getType();
+ String symbolicName = deploymentIdentity.getSymbolicName();
+ String version = deploymentIdentity.getVersion();
+ this.eventLogger.log(DeployerLogEvents.UNDEPLOY_ARTEFACT_NOT_FOUND, type, symbolicName, version);
+ throw new DeploymentException("Undeploy not possible as no " + type + " with name " + symbolicName + " and version " + version
+ + " is deployed");
+ } else {
+ URI location = this.ram.getLocation(deploymentIdentity);
+
+ stopArtifact(installArtifact);
+ uninstallArtifact(installArtifact);
+
+ return location;
+ }
+ }
+ }
+
+ private void stopArtifact(InstallArtifact installArtifact) throws DeploymentException {
+
+ installArtifact.stop();
+
+ }
+
+ private void uninstallArtifact(InstallArtifact installArtifact) throws DeploymentException {
+ installArtifact.uninstall();
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/PlanResolver.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/PlanResolver.java
new file mode 100644
index 00000000..8e7d6d46
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/PlanResolver.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+import java.util.List;
+
+import org.osgi.framework.Version;
+
+
+import org.eclipse.virgo.kernel.artifact.ArtifactSpecification;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeInclosure;
+import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.internal.AbstractInstallArtifact;
+import org.eclipse.virgo.kernel.install.environment.InstallEnvironment;
+import org.eclipse.virgo.kernel.install.pipeline.stage.transform.Transformer;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.common.Tree.ExceptionThrowingTreeVisitor;
+
+/**
+ * {@link PlanResolver} adds the immediate child nodes to a plan node.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class PlanResolver implements Transformer {
+
+ private static final String SCOPE_SEPARATOR = "-";
+
+ private final InstallArtifactTreeInclosure installArtifactTreeInclosure;
+
+ public PlanResolver(InstallArtifactTreeInclosure installArtifactTreeInclosure) {
+ this.installArtifactTreeInclosure = installArtifactTreeInclosure;
+ }
+
+ /**
+ * {@InheritDoc}
+ */
+ public void transform(Tree<InstallArtifact> installTree, final InstallEnvironment installEnvironment) throws DeploymentException {
+ installTree.visit(new ExceptionThrowingTreeVisitor<InstallArtifact, DeploymentException>() {
+
+ public boolean visit(Tree<InstallArtifact> tree) throws DeploymentException {
+ PlanResolver.this.operate(tree.getValue());
+ return true;
+ }
+ });
+ }
+
+ private void operate(InstallArtifact installArtifact) throws DeploymentException {
+ if (installArtifact instanceof PlanInstallArtifact) {
+ PlanInstallArtifact planInstallArtifact = (PlanInstallArtifact) installArtifact;
+ if (planInstallArtifact.getTree().getChildren().isEmpty()) {
+ String scopeName = getArtifactScopeName(planInstallArtifact);
+ Tree<InstallArtifact> tree = planInstallArtifact.getTree();
+ List<ArtifactSpecification> artifactSpecifications = planInstallArtifact.getArtifactSpecifications();
+ for (ArtifactSpecification artifactSpecification : artifactSpecifications) {
+ Tree<InstallArtifact> childInstallArtifactTree = createInstallArtifactTree(artifactSpecification, scopeName);
+ TreeUtils.addChild(tree, childInstallArtifactTree);
+
+ // Put child into the INSTALLING state as Transformers (like this) are after the "begin install" pipeline stage.
+ InstallArtifact childInstallArtifact = childInstallArtifactTree.getValue();
+ ((AbstractInstallArtifact) childInstallArtifact).beginInstall();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the scope name of the given {@link InstallArtifact} or <code>null</code> if the given InstallArtifact
+ * does not belong to a scope.
+ *
+ * @param installArtifact the <code>InstallArtiface</code> whose scope name is required
+ * @return the scope name or <code>null</code> if the given InstallArtifact does not belong to a scope
+ */
+ private String getArtifactScopeName(InstallArtifact installArtifact) {
+ if (installArtifact instanceof PlanInstallArtifact) {
+ PlanInstallArtifact planInstallArtifact = (PlanInstallArtifact) installArtifact;
+ boolean scoped = planInstallArtifact.isScoped();
+ if (scoped) {
+ return planInstallArtifact.getName() + SCOPE_SEPARATOR + versionToShortString(planInstallArtifact.getVersion());
+ }
+ }
+ Tree<InstallArtifact> tree = installArtifact.getTree();
+ Tree<InstallArtifact> parent = tree.getParent();
+ if (parent != null) {
+ return getArtifactScopeName(parent.getValue());
+ }
+ return null;
+ }
+
+ private static String versionToShortString(Version version) {
+ String result = version.toString();
+ while (result.endsWith(".0")) {
+ result = result.substring(0, result.length() - 2);
+ }
+ return result;
+ }
+
+ private Tree<InstallArtifact> createInstallArtifactTree(ArtifactSpecification artifactSpecification, String scopeName) throws DeploymentException {
+ return this.installArtifactTreeInclosure.createInstallTree(artifactSpecification, scopeName);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/Plumber.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/Plumber.java
new file mode 100644
index 00000000..7205e8ab
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/Plumber.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.internal.AbstractInstallArtifact;
+import org.eclipse.virgo.kernel.install.environment.InstallEnvironment;
+import org.eclipse.virgo.kernel.install.pipeline.Pipeline;
+import org.eclipse.virgo.kernel.install.pipeline.PipelineFactory;
+import org.eclipse.virgo.kernel.install.pipeline.stage.resolve.internal.CommitStage;
+import org.eclipse.virgo.kernel.install.pipeline.stage.resolve.internal.QuasiInstallStage;
+import org.eclipse.virgo.kernel.install.pipeline.stage.resolve.internal.QuasiResolveStage;
+import org.eclipse.virgo.kernel.install.pipeline.stage.resolve.internal.ResolveStage;
+import org.eclipse.virgo.kernel.install.pipeline.stage.transform.internal.TransformationStage;
+import org.eclipse.virgo.kernel.install.pipeline.stage.visit.Visitor;
+import org.eclipse.virgo.kernel.install.pipeline.stage.visit.internal.VisitationStage;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiFrameworkFactory;
+
+/**
+ * {@link Plumber} plumbs together pipeline stages for use in the {@link PipelinedApplicationDeployer}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class Plumber {
+
+ private final PackageAdmin packageAdmin;
+
+ private final BundleContext bundleContext;
+
+ private final PipelineFactory pipelineFactory;
+
+ private final Pipeline pipeline;
+
+ private final Pipeline refreshSubpipeline;
+
+ private final QuasiFrameworkFactory quasiFrameworkFactory;
+
+ public Plumber(@NonNull PackageAdmin packageAdmin, @NonNull BundleContext bundleContext, @NonNull PipelineFactory pipelineFactory,
+ @NonNull QuasiFrameworkFactory quasiFrameworkFactory) {
+ this.packageAdmin = packageAdmin;
+ this.bundleContext = bundleContext;
+ this.pipelineFactory = pipelineFactory;
+ this.quasiFrameworkFactory = quasiFrameworkFactory;
+ this.refreshSubpipeline = this.pipelineFactory.create();
+ this.pipeline = this.pipelineFactory.create();
+ initialisePipelines();
+ }
+
+ public Pipeline getMainPipeline() {
+ return this.pipeline;
+ }
+
+ public Pipeline getRefreshSubpipeline() {
+ return this.refreshSubpipeline;
+ }
+
+ private void initialisePipelines() {
+ // new ManifestUpgrader(), new ImportExpander(this.bundleInstaller), new
+ // PlanResolver(this.installArtifactTreeInclosure));
+
+ TransformationStage transformationStage = new TransformationStage(this.bundleContext);
+
+ plumbRefreshPipeline(transformationStage);
+
+ plumbMainPipeline(transformationStage);
+ }
+
+ /**
+ * Build the main pipeline from normalization, install, and resolve stages using the given normalization and
+ * transformation stages.
+ */
+ private void plumbMainPipeline(TransformationStage transformationStage) {
+ plumbMainPipelineInstallStages(transformationStage);
+ plumbMainPipelineResolveStages();
+ }
+
+ private void plumbMainPipelineInstallStages(TransformationStage transformationStage) {
+ VisitationStage beginInstallStage = new VisitationStage(new Visitor() {
+
+ public void operate(InstallArtifact installArtifact, InstallEnvironment installEnvironment) throws DeploymentException {
+ ((AbstractInstallArtifact) installArtifact).beginInstall();
+
+ }
+ });
+
+ VisitationStage endInstallStage = new VisitationStage(new Visitor() {
+
+ public void operate(InstallArtifact installArtifact, InstallEnvironment installEnvironment) throws DeploymentException {
+ ((AbstractInstallArtifact) installArtifact).endInstall();
+
+ }
+ }, false);
+
+ VisitationStage failInstallStage = new VisitationStage(new Visitor() {
+
+ public void operate(InstallArtifact installArtifact, InstallEnvironment installEnvironment) throws DeploymentException {
+ ((AbstractInstallArtifact) installArtifact).failInstall();
+
+ }
+ }, false);
+
+ Pipeline installStages = this.pipelineFactory.createCompensatingPipeline(failInstallStage);
+
+ installStages.appendStage(transformationStage).appendStage(new QuasiInstallStage()).appendStage(new QuasiResolveStage()).appendStage(
+ new CommitStage());
+
+ this.pipeline.appendStage(beginInstallStage).appendStage(installStages).appendStage(endInstallStage);
+ }
+
+ private void plumbMainPipelineResolveStages() {
+ VisitationStage beginResolveStage = new VisitationStage(new Visitor() {
+
+ public void operate(InstallArtifact installArtifact, InstallEnvironment installEnvironment) throws DeploymentException {
+ ((AbstractInstallArtifact) installArtifact).beginResolve();
+
+ }
+ });
+
+ VisitationStage endResolveStage = new VisitationStage(new Visitor() {
+
+ public void operate(InstallArtifact installArtifact, InstallEnvironment installEnvironment) throws DeploymentException {
+ ((AbstractInstallArtifact) installArtifact).endResolve();
+
+ }
+ });
+
+ VisitationStage failResolveStage = new VisitationStage(new Visitor() {
+
+ public void operate(InstallArtifact installArtifact, InstallEnvironment installEnvironment) throws DeploymentException {
+ ((AbstractInstallArtifact) installArtifact).failResolve();
+
+ }
+ });
+
+ Pipeline resolveStages = this.pipelineFactory.createCompensatingPipeline(failResolveStage);
+
+ resolveStages.appendStage(new ResolveStage(this.packageAdmin, this.quasiFrameworkFactory));
+
+ this.pipeline.appendStage(beginResolveStage).appendStage(resolveStages).appendStage(endResolveStage);
+ }
+
+ private void plumbRefreshPipeline(TransformationStage transformationStage) {
+ this.refreshSubpipeline.appendStage(transformationStage);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/RegionAwarePackageAdminAccessor.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/RegionAwarePackageAdminAccessor.java
new file mode 100644
index 00000000..700b4f28
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/RegionAwarePackageAdminAccessor.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.deployer.core.internal;
+
+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.region.Region;
+
+final class RegionAwarePackageAdminAccessor {
+ public static PackageAdmin getPackageAdmin(Region region) {
+ BundleContext bundleContext = region.getBundleContext();
+ return OsgiFrameworkUtils.getService(bundleContext, PackageAdmin.class).getService();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/SignalJunction.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/SignalJunction.java
new file mode 100644
index 00000000..2dbda4fc
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/SignalJunction.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.virgo.kernel.core.Signal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * {@link SignalJunction} provides a collection of signals of a given size that join to drive a given signal. The given
+ * signal is driven for completion when any of the signals in the collection is driven for failure or all the signals in
+ * the collection are driven for successful completion. The given signal is driven at most once.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public final class SignalJunction {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SignalJunction.class);
+
+ private final Signal signal;
+
+ private final List<Signal> subSignals;
+
+ private final AtomicInteger incompleteCount;
+
+ private final AtomicBoolean failureSignalled = new AtomicBoolean(false);
+
+ /**
+ * Constructs a {@link SignalJunction} of the given size for the given signal.
+ *
+ * @param signal the signal to be controlled
+ * @param size the number of signals in the collection
+ */
+ public SignalJunction(Signal signal, int size) {
+ this.signal = signal;
+ List<Signal> s = new ArrayList<Signal>();
+ for (int i = 0; i < size; i++) {
+ s.add(new SubSignal());
+ }
+ this.subSignals = Collections.unmodifiableList(s);
+ this.incompleteCount = new AtomicInteger(size);
+ if (size <= 0) {
+ if (this.signal != null) {
+ this.signal.signalSuccessfulCompletion();
+ }
+ }
+ }
+
+ /**
+ * Gets the signals in the collection.
+ *
+ * @return an unmodifiable {@link Set} of signals in the collection
+ */
+ public List<Signal> getSignals() {
+ return this.subSignals;
+ }
+
+ private void subSignalFailed(Throwable cause) {
+ // Ensure the incomplete count is zero.
+ int i = SignalJunction.this.incompleteCount.get();
+
+ LOGGER.debug("SubSignal failed. {} has {} incomplete signals", this, i);
+
+ while (i > 0 && !SignalJunction.this.incompleteCount.compareAndSet(i, 0)) {
+ i = SignalJunction.this.incompleteCount.get();
+ }
+
+ // If this invocation took the count to zero, drive failure.
+ if (i > 0) {
+ if (SignalJunction.this.signal != null) {
+ LOGGER.debug("{} signalling failure", this);
+ SignalJunction.this.signal.signalFailure(cause);
+ this.failureSignalled.set(true);
+ }
+ }
+ }
+
+ public boolean failed() {
+ return this.failureSignalled.get();
+ }
+
+ private void subSignalSucceeded() {
+ // Decrement the incomplete count.
+ int incomplete = SignalJunction.this.incompleteCount.decrementAndGet();
+
+ LOGGER.debug("SubSignal succeeded. {} now has {} incomplete signals", this, incomplete);
+
+ if (incomplete == 0) {
+ // If this invocation took the count to zero, drive successful completion.
+ if (SignalJunction.this.signal != null) {
+ LOGGER.debug("{} has no incomplete signals. Signalling success", this);
+ SignalJunction.this.signal.signalSuccessfulCompletion();
+ }
+ }
+ }
+
+ /**
+ * {@link SubSignal} is a signal that reports completion to its {@link SignalJunction} once and only once.
+ *
+ */
+ private class SubSignal implements Signal {
+
+ private final AtomicBoolean complete = new AtomicBoolean(false);
+
+ public void signalFailure(Throwable cause) {
+ if (this.complete.compareAndSet(false, true)) {
+ LOGGER.debug("SubSignal {} signalling failure", this);
+ subSignalFailed(cause);
+ }
+ }
+
+ public void signalSuccessfulCompletion() {
+ if (this.complete.compareAndSet(false, true)) {
+ LOGGER.debug("SubSignal {} signalling success", this);
+ subSignalSucceeded();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StandardDeployerConfiguration.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StandardDeployerConfiguration.java
new file mode 100644
index 00000000..49f48ef8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StandardDeployerConfiguration.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+import java.io.File;
+
+import org.eclipse.virgo.kernel.deployer.core.DeployerConfiguration;
+import org.eclipse.virgo.util.io.PathReference;
+
+/**
+ * Standard implementation of {@link DeployerConfiguration}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class StandardDeployerConfiguration implements DeployerConfiguration {
+
+ private final int deploymentTimeoutSeconds;
+ private final PathReference deploymentPickupDirectory;
+
+ /**
+ * Construct a deployment configuration using the given <code>deploymentTimeout</code>,
+ * and <code>pickupDirectory</code>.
+ *
+ * @param deploymentTimeout The timeout period, in seconds
+ * @param pickupDirectory The deployer's pickup directory
+ */
+ StandardDeployerConfiguration(int deploymentTimeout, File pickupDirectory) {
+ this.deploymentTimeoutSeconds = deploymentTimeout;
+ this.deploymentPickupDirectory = new PathReference(pickupDirectory);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getDeploymentTimeoutSeconds() {
+ return this.deploymentTimeoutSeconds;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PathReference getDeploymentPickupDirectory() {
+ return this.deploymentPickupDirectory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return String.format("Timeout: '%s', Pickup directory: '%s'", this.deploymentTimeoutSeconds, this.deploymentPickupDirectory);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StandardDeploymentIdentity.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StandardDeploymentIdentity.java
new file mode 100644
index 00000000..c2045a15
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StandardDeploymentIdentity.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.deployer.core.internal;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
+import org.osgi.framework.Version;
+
+
+
+/**
+ * {@link StandardDeploymentIdentity} is the implementation of {@link DeploymentIdentity}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is immutable and therefore thread safe.
+ *
+ */
+public final class StandardDeploymentIdentity implements DeploymentIdentity {
+
+ private static final long serialVersionUID = 67849234293492373L;
+
+ private final String type;
+
+ private final String symbolicName;
+
+ private final String version;
+
+ /**
+ * Construct a {@link StandardDeploymentIdentity} with the given type, name, and version.
+ *
+ * @param type the type of the deployed artefact
+ * @param symbolicName the symbolic name of the deployed artefact
+ * @param version the version of the deployed artefact
+ */
+ public StandardDeploymentIdentity(String type, String symbolicName, String version) {
+ this.type = type;
+ this.symbolicName = symbolicName;
+
+ // Normalise the version to ensure accurate comparisons.
+ this.version = Version.parseVersion(version).toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return this.type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getSymbolicName() {
+ return this.symbolicName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getVersion() {
+ return this.version;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((symbolicName == null) ? 0 : symbolicName.hashCode());
+ result = prime * result + ((version == null) ? 0 : version.hashCode());
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj instanceof StandardDeploymentIdentity) {
+ final StandardDeploymentIdentity other = (StandardDeploymentIdentity) obj;
+ return (type ==null ? other.type ==null : type .equals(other.type ))
+ && (symbolicName==null ? other.symbolicName==null : symbolicName.equals(other.symbolicName))
+ && (version ==null ? other.version ==null : version .equals(other.version ))
+ ;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override public String toString() {
+ return "DeploymentIdentity(" + getType() + ", " + getSymbolicName() + ", " + getVersion() + ")";
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StateCleanupInstallArtifactLifecycleListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StateCleanupInstallArtifactLifecycleListener.java
new file mode 100644
index 00000000..b6ce8f48
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/StateCleanupInstallArtifactLifecycleListener.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.deployer.core.internal;
+
+import java.net.URI;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
+import org.eclipse.virgo.kernel.deployer.core.internal.event.DeploymentListener;
+import org.eclipse.virgo.kernel.deployer.model.RuntimeArtifactModel;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListener;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListenerSupport;
+
+
+/**
+ * Cleans up the state related to an {@link InstallArtifact} when it is uninstalled.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class StateCleanupInstallArtifactLifecycleListener extends InstallArtifactLifecycleListenerSupport implements InstallArtifactLifecycleListener {
+
+ private final DeploymentListener deploymentListener;
+
+ private final RuntimeArtifactModel runtimeArtifactModel;
+
+ StateCleanupInstallArtifactLifecycleListener(DeploymentListener deploymentListener, RuntimeArtifactModel runtimeArtifactModel) {
+ this.deploymentListener = deploymentListener;
+ this.runtimeArtifactModel = runtimeArtifactModel;
+ }
+
+ @Override
+ public void onUninstalled(InstallArtifact installArtifact) throws DeploymentException {
+ DeploymentIdentity deploymentIdentity = new StandardDeploymentIdentity(installArtifact.getType(), installArtifact.getName(),
+ installArtifact.getVersion().toString());
+
+ URI location = this.runtimeArtifactModel.getLocation(deploymentIdentity);
+
+ if (location != null) {
+ this.deploymentListener.undeployed(location);
+ this.runtimeArtifactModel.delete(deploymentIdentity);
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/TreeUtils.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/TreeUtils.java
new file mode 100644
index 00000000..0d86d223
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/TreeUtils.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.deployer.core.internal;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.internal.AbstractInstallArtifact;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.common.Tree.ExceptionThrowingTreeVisitor;
+
+/**
+ * Helper methods for manipulating {@link Tree Tree&lt;InstallArtifact&gt;} instances.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class TreeUtils {
+
+ /**
+ * Adds the given child tree as a subtree of the given parent tree and fixes up the subtree artifacts to refer to
+ * the copies.
+ *
+ * @param parent to get new child
+ * @param child to copy into parent
+ * @throws DeploymentException not really
+ */
+ public static void addChild(Tree<InstallArtifact> parent, Tree<InstallArtifact> child) throws DeploymentException {
+ Tree<InstallArtifact> childCopy = parent.addChild(child);
+ setTreeReferences(childCopy);
+ }
+
+ /**
+ * The given tree was deeply copied. This method sets the references in each install artifact to its
+ * corresponding copied tree.
+ */
+ private static void setTreeReferences(Tree<InstallArtifact> tree) throws DeploymentException {
+ tree.visit(new ExceptionThrowingTreeVisitor<InstallArtifact, DeploymentException>() {
+ public boolean visit(Tree<InstallArtifact> tree) throws DeploymentException {
+ InstallArtifact value = tree.getValue();
+ if (value instanceof AbstractInstallArtifact) {
+ ((AbstractInstallArtifact)value).setTree(tree);
+ }
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/event/DeploymentListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/event/DeploymentListener.java
new file mode 100644
index 00000000..38e4ca42
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/event/DeploymentListener.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.deployer.core.internal.event;
+
+import java.net.URI;
+
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+
+
+
+/**
+ * A <code>DeploymentListener</code> is notified of deployment events.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public interface DeploymentListener {
+
+ void refreshed(URI sourceLocation);
+
+ void deployed(URI sourceLocation, DeploymentOptions deploymentOptions);
+
+ void undeployed(URI sourceLocation);
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLog.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLog.java
new file mode 100644
index 00000000..ad8a14de
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLog.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * 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.deployer.core.internal.recovery;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.deployer.core.FatalDeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+import org.eclipse.virgo.util.io.PathReference;
+
+/**
+ * {@link DeployerRecoveryLog} maintains the deployer's recoverable state across restarts.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+// TODO Make package private
+public final class DeployerRecoveryLog {
+
+ private static final String REDEPLOY_FILE_NAME = "deployed";
+
+ private static final String REDEPLOY_COMPRESSION_FILE_NAME = "deployed.compress";
+
+ private static final int INITIAL_REDEPLOY_DATA_SIZE = 32 * 1024;
+
+ private static final int COMPRESSION_THRESHOLD = 10;
+
+ private static final int COMMAND_LENGTH = 1;
+
+ private static final String DEPLOY_UNOWNED_URI_COMMAND = "+";
+
+ private static final String DEPLOY_OWNED_URI_COMMAND = ">";
+
+ private static final String DEPLOY_NON_RECOVERABLE_URI_COMMAND = "!";
+
+ private static final String UNDEPLOY_URI_COMMAND = "-";
+
+ private static final String URI_SEPARATOR = ";";
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private static final DeploymentOptions UNOWNED = new DeploymentOptions(true, false, true);
+
+ private static final DeploymentOptions OWNED = new DeploymentOptions(true, true, true);
+
+ private static final DeploymentOptions NON_RECOVERABLE = new DeploymentOptions(false, false, true);
+
+ private final PathReference redeployDataset;
+
+ private final PathReference redeployCompressionDataset;
+
+ private final long redeployFileLastModified;
+
+ DeployerRecoveryLog(PathReference workArea) {
+ PathReference recoveryArea = workArea.newChild("recovery");
+ recoveryArea.createDirectory();
+
+ this.redeployDataset = recoveryArea.newChild(REDEPLOY_FILE_NAME);
+ this.redeployFileLastModified = this.redeployDataset.toFile().lastModified();
+ this.redeployCompressionDataset = recoveryArea.newChild(REDEPLOY_COMPRESSION_FILE_NAME);
+
+ // Recover from a crash during compression
+ if (!this.redeployDataset.exists() && this.redeployCompressionDataset.exists()) {
+ this.redeployCompressionDataset.copy(this.redeployDataset);
+ if (!this.redeployCompressionDataset.delete()) {
+ logger.warn("Could not delete '%s' in recovery after compression failure.", this.redeployCompressionDataset);
+ }
+ }
+ }
+
+ /**
+ * Get the URIs that need to be recovered along with their deployment options.
+ *
+ * @return a map of URI to deployment options
+ */
+ public Map<URI, DeploymentOptions> getRecoveryState() {
+ Map<URI, DeploymentOptions> redeploySet = new LinkedHashMap<URI, DeploymentOptions>(20);
+
+ String redeployData = readRedployData();
+
+ int recordCount = 0;
+ int undeployCount = 0;
+
+ for (String uriCommandString : redeployData.split(URI_SEPARATOR)) {
+ recordCount++;
+ // Skip zero length command strings as there will typically be one
+ // at the end of the dataset.
+ if (uriCommandString.length() >= COMMAND_LENGTH) {
+ String uriCommand = uriCommandString.substring(0, COMMAND_LENGTH);
+ String uriString = uriCommandString.substring(1);
+ try {
+ URI uri = new URI(uriString);
+ if (DEPLOY_UNOWNED_URI_COMMAND.equals(uriCommand)) {
+ redeploySet.put(uri, UNOWNED);
+ } else if (DEPLOY_OWNED_URI_COMMAND.equals(uriCommand)) {
+ redeploySet.put(uri, OWNED);
+ } else if (DEPLOY_NON_RECOVERABLE_URI_COMMAND.equals(uriCommand)) {
+ redeploySet.put(uri, NON_RECOVERABLE);
+ } else if (UNDEPLOY_URI_COMMAND.equals(uriCommand)) {
+ undeployCount++;
+ redeploySet.remove(uri);
+ } else {
+ logger.error("Invalid URI command string '%s' read from redeploy dataset", uriCommandString);
+ // skip and carry on
+ }
+ } catch (URISyntaxException e) {
+ logger.error("Invalid URI command string '%s' read from redeploy dataset", e, uriCommandString);
+ // skip and carry on
+ }
+ }
+ }
+
+ // If there is a significant amount of wasted space in the redeploy
+ // dataset, rewrite it.
+ if (COMPRESSION_THRESHOLD * undeployCount > recordCount) {
+ rewriteRedeploySet(redeploySet);
+ }
+
+ return redeploySet;
+ }
+
+ private String readRedployData() {
+ StringBuffer redeployData = new StringBuffer(INITIAL_REDEPLOY_DATA_SIZE);
+ Reader redeployDataReader;
+ try {
+ redeployDataReader = new BufferedReader(new FileReader(redeployDataset.toFile()));
+ try {
+ char[] chars = new char[INITIAL_REDEPLOY_DATA_SIZE];
+ int numRead;
+ while (-1 != (numRead = redeployDataReader.read(chars))) {
+ redeployData.append(String.valueOf(chars, 0, numRead));
+ }
+
+ } catch (IOException e) {
+ logger.error("Problem reading redeploy dataset", e);
+ } finally {
+ try {
+ redeployDataReader.close();
+ } catch (IOException e) {
+ logger.error("Problem closing redeploy dataset", e);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Ignore - this is acceptable if there are no deployed applications
+ }
+ return redeployData.toString();
+ }
+
+ /**
+ * Write the given set of URIs to the redeploy dataset. To avoid corruption if a crash occurs, write to a redeploy
+ * compression file and then switch this for the redeploy dataset.
+ *
+ * @param redeploySet the URIs to be written
+ */
+ private void rewriteRedeploySet(Map<URI, DeploymentOptions> redeploySet) {
+ this.redeployCompressionDataset.delete();
+ Writer redeployDataWriter;
+ try {
+ redeployDataWriter = new BufferedWriter(new FileWriter(this.redeployCompressionDataset.toFile()));
+ for (Entry<URI, DeploymentOptions> redeployEntry : redeploySet.entrySet()) {
+ recordUriCommand(redeployDataWriter, redeployEntry.getKey(), getCommandString(redeployEntry.getValue()));
+ }
+ redeployDataWriter.close();
+ } catch (IOException e) {
+ logger.warn("Problem while rewriting redeploy dataset", e);
+ // Return without replacing the redeploy dataset.
+ }
+ // Now switch the files
+ this.redeployDataset.delete();
+ this.redeployCompressionDataset.copy(this.redeployDataset);
+ this.redeployCompressionDataset.delete();
+ }
+
+ /**
+ * Add the given location and deployment options to the recovery state.
+ *
+ * @param location
+ * @param deploymentOptions
+ */
+ // TODO Make package private
+ public void add(URI location, DeploymentOptions deploymentOptions) {
+ recordUriCommand(location, getCommandString(deploymentOptions));
+ }
+
+ /**
+ * Return the command string for tagging the type of a log record based on the given deployment options.
+ *
+ * @param deploymentOptions
+ * @return the command string
+ */
+ private String getCommandString(DeploymentOptions deploymentOptions) {
+ return deploymentOptions.getRecoverable() ? (deploymentOptions.getDeployerOwned() ? DEPLOY_OWNED_URI_COMMAND : DEPLOY_UNOWNED_URI_COMMAND)
+ : DEPLOY_NON_RECOVERABLE_URI_COMMAND;
+ }
+
+ /**
+ * Remove the given location and associated deployment options from the recovery state.
+ *
+ * @param location
+ */
+ // TODO Make package private
+ public void remove(URI location) {
+ recordUriCommand(location, UNDEPLOY_URI_COMMAND);
+ }
+
+ private void recordUriCommand(URI uri, String command) {
+ Writer writer;
+ try {
+ writer = new FileWriter(this.redeployDataset.toFile(), true);
+ recordUriCommand(writer, uri, command);
+ writer.close();
+ } catch (IOException e) {
+ throw new FatalDeploymentException("Failed to record (un)deployment", e);
+ }
+
+ }
+
+ private static void recordUriCommand(Writer writer, URI uri, String command) throws IOException {
+ writer.write(command);
+ writer.write(uri.toString());
+ writer.write(URI_SEPARATOR);
+ }
+
+ /**
+ * Get the last modified time of the deployer's recovery file. Any applications in the pickup directory with a later
+ * last modified time will need to be redeployed.
+ *
+ * @return the last modified time of the deployer's recovery file
+ */
+ public long getRedeployFileLastModified() {
+ return redeployFileLastModified;
+ }
+
+ /**
+ * Update the last modified time of the deployer's recovery file.
+ * @return <code>true</code> iff the operation succeeded
+ */
+ // TODO Make package private
+ public boolean setRedeployFileLastModified() {
+ return this.redeployDataset.touch();
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLogDeploymentListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLogDeploymentListener.java
new file mode 100644
index 00000000..048cce7c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/DeployerRecoveryLogDeploymentListener.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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.deployer.core.internal.recovery;
+
+import java.net.URI;
+
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+import org.eclipse.virgo.kernel.deployer.core.internal.event.DeploymentListener;
+
+
+
+/**
+ * TODO Document DeployerRecoveryLogDeploymentListener
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * TODO Document concurrent semantics of DeployerRecoveryLogDeploymentListener
+ *
+ */
+final class DeployerRecoveryLogDeploymentListener implements DeploymentListener {
+
+ private final DeployerRecoveryLog recoveryLog;
+
+ DeployerRecoveryLogDeploymentListener(DeployerRecoveryLog recoveryLog) {
+ this.recoveryLog = recoveryLog;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void deployed(URI sourceLocation, DeploymentOptions deploymentOptions) {
+ this.recoveryLog.add(sourceLocation, deploymentOptions);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void refreshed(URI sourceLocation) {
+ this.recoveryLog.setRedeployFileLastModified();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void undeployed(URI sourceLocation) {
+ this.recoveryLog.remove(sourceLocation);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/RecoveryAgent.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/RecoveryAgent.java
new file mode 100644
index 00000000..70710b70
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/recovery/RecoveryAgent.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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.deployer.core.internal.recovery;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventHandler;
+
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.FatalDeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+import org.eclipse.virgo.kernel.deployer.core.internal.ApplicationRecoverer;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+
+/**
+ * A <code>RecoveryAgent</code> is an {@link EventHandler} that waits for <code>systemartifacts/DEPLOYED</code> and, if
+ * recovery is enabled, drives recovery on all redeploy entries in the recovery state.
+ * The recovery is run in another thread.
+ * When complete (or if not enabled), fires the <code>recovery/COMPLETED</code> event.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * thread-safe
+ *
+ */
+final class RecoveryAgent implements EventHandler {
+
+ private static final String TOPIC_SYSTEM_ARTIFACTS_DEPLOYED = "org/eclipse/virgo/kernel/userregion/systemartifacts/DEPLOYED";
+
+ private static final String TOPIC_RECOVERY_COMPLETED = "org/eclipse/virgo/kernel/deployer/recovery/COMPLETED";
+
+ private final ApplicationRecoverer recoverer;
+
+ private final EventLogger eventLogger;
+
+ private final EventAdmin eventAdmin;
+
+ private final Map<URI, DeploymentOptions> recoveryState;
+
+ public RecoveryAgent(ApplicationRecoverer recoverer, DeployerRecoveryLog recoveryLog, EventLogger eventLogger, EventAdmin eventAdmin) {
+ this.recoverer = recoverer;
+ this.recoveryState = recoveryLog.getRecoveryState();
+ this.eventLogger = eventLogger;
+ this.eventAdmin = eventAdmin;
+ }
+
+ void performRecovery() {
+ if (isRecoveryEnabled()) {
+ Thread recoveryThread = new Thread(new Runnable() {
+ public void run() {
+ try {
+ for (Entry<URI, DeploymentOptions> redeployEntry : recoveryState.entrySet()) {
+ URI uri = redeployEntry.getKey();
+ DeploymentOptions deploymentOptions = redeployEntry.getValue();
+ try {
+ recoverer.recoverDeployment(uri, deploymentOptions);
+ } catch (DeploymentException e) {
+ eventLogger.log(DeployerLogEvents.RECOVERY_FAILED, e, uri);
+ } catch (FatalDeploymentException e) {
+ eventLogger.log(DeployerLogEvents.RECOVERY_FAILED, e, uri);
+ }
+ }
+ } finally {
+ recoveryComplete();
+ }
+ }
+ }, "deployer-recovery");
+ recoveryThread.start();
+ } else {
+ recoveryComplete();
+ }
+ }
+
+ private void recoveryComplete() {
+ this.recoveryState.clear();
+ eventAdmin.postEvent(new Event(TOPIC_RECOVERY_COMPLETED, null));
+ }
+
+ private boolean isRecoveryEnabled() {
+ return !Boolean.valueOf(FrameworkUtil.getBundle(getClass()).getBundleContext().getProperty("org.eclipse.virgo.kernel.deployer.disableRecovery"));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleEvent(Event event) {
+ if (TOPIC_SYSTEM_ARTIFACTS_DEPLOYED.equals(event.getTopic())) {
+ performRecovery();
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/CompoundDeployUriNormaliser.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/CompoundDeployUriNormaliser.java
new file mode 100644
index 00000000..747e694e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/CompoundDeployUriNormaliser.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.deployer.core.internal.uri;
+
+import java.net.URI;
+
+import org.eclipse.virgo.kernel.deployer.core.DeployUriNormaliser;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+
+
+
+/**
+ * A {@link DeployUriNormaliser} implementation that calls a list of delegate
+ * normalisers until the {@link URI} is normalised.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * As thread-safe as the delegates.
+ *
+ */
+class CompoundDeployUriNormaliser implements DeployUriNormaliser {
+
+ private final DeployUriNormaliser[] normalisers;
+
+ public CompoundDeployUriNormaliser(@NonNull DeployUriNormaliser[] normalisers) {
+ this.normalisers = normalisers.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public URI normalise(URI uri) throws DeploymentException {
+ for (DeployUriNormaliser normaliser : normalisers) {
+ URI normalised = normaliser.normalise(uri);
+ if (normalised != null) {
+ return normalised;
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/FileDeployUriNormaliser.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/FileDeployUriNormaliser.java
new file mode 100644
index 00000000..3ec8cede
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/FileDeployUriNormaliser.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.deployer.core.internal.uri;
+
+import java.net.URI;
+
+import org.eclipse.virgo.kernel.deployer.core.DeployUriNormaliser;
+
+
+/**
+ * A {@link DeployUriNormaliser} implementation that normalises file: {@link URI URIs}.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+class FileDeployUriNormaliser implements DeployUriNormaliser {
+
+ private static final String SCHEME_FILE = "file";
+
+ /**
+ * {@inheritDoc}
+ */
+ public URI normalise(URI uri) {
+ if (SCHEME_FILE.equals(uri.getScheme())) {
+ return uri;
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/RepositoryDeployUriNormaliser.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/RepositoryDeployUriNormaliser.java
new file mode 100644
index 00000000..a060599f
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/core/internal/uri/RepositoryDeployUriNormaliser.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * 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.deployer.core.internal.uri;
+
+import java.net.URI;
+
+import org.osgi.framework.Version;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeployUriNormaliser;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.repository.ArtifactDescriptor;
+import org.eclipse.virgo.repository.Repository;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+/**
+ * A {@link DeployUriNormaliser} implementation that works with a {@link Repository}. Uris in the form
+ * repository://type/name/version are normalised by querying the <code>Repository</code> for a matching
+ * {@link ArtifactDescriptor} and, if one is found, returning its {@link ArtifactDescriptor#getUri() URI}.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class RepositoryDeployUriNormaliser implements DeployUriNormaliser {
+
+ private static final String SCHEME_REPOSITORY = "repository";
+
+ private static final int COMPONENTS_MINIMUM = 2;
+
+ private static final int COMPONENTS_MAXIMUM = 3;
+
+ private final Repository repository;
+
+ private final EventLogger eventLogger;
+
+ RepositoryDeployUriNormaliser(@NonNull Repository repository, EventLogger eventLogger) {
+ this.repository = repository;
+ this.eventLogger = eventLogger;
+ }
+
+ public URI normalise(URI uri) throws DeploymentException {
+ if (SCHEME_REPOSITORY.equals(uri.getScheme())) {
+ return normaliseRepositoryUri(uri);
+ }
+ return null;
+ }
+
+ private URI normaliseRepositoryUri(URI uri) throws DeploymentException {
+ String[] tnv = extractTypeNameAndVersion(uri);
+
+ URI normalisedUri = null;
+ if (tnv.length >= COMPONENTS_MINIMUM && tnv.length <= COMPONENTS_MAXIMUM) {
+ String versionString = null;
+ if (tnv.length == 3) {
+ versionString = tnv[2];
+ }
+ ArtifactDescriptor artifactDescriptor = lookupArtifactDescriptor(tnv[0], tnv[1], versionString, uri);
+ if (artifactDescriptor != null) {
+ normalisedUri = artifactDescriptor.getUri();
+ }
+ } else {
+ this.eventLogger.log(DeployerLogEvents.REPOSITORY_DEPLOYMENT_URI_MALFORMED, uri);
+ throw new DeploymentException("The URI '" + uri + "' is malformed");
+ }
+ return normalisedUri;
+ }
+
+ private String[] extractTypeNameAndVersion(URI uri) {
+ String tnv = uri.getSchemeSpecificPart();
+ String[] tnvComponents = tnv.split("/");
+ return tnvComponents;
+ }
+
+ private ArtifactDescriptor lookupArtifactDescriptor(String type, String name, String versionString, URI uri) throws DeploymentException {
+ try {
+ VersionRange versionRange;
+
+ if (versionString == null) {
+ versionRange = VersionRange.NATURAL_NUMBER_RANGE;
+ } else {
+ Version version = new Version(versionString);
+ versionRange = VersionRange.createExactRange(version);
+ }
+
+ ArtifactDescriptor artifactDescriptor = this.repository.get(type, name, versionRange);
+ if (artifactDescriptor == null) {
+ this.eventLogger.log(DeployerLogEvents.ARTIFACT_NOT_FOUND, type, name, versionRange, this.repository.getName());
+ throw new DeploymentException("The URI '" + uri + "' references a non-existent artifact");
+ }
+ return artifactDescriptor;
+ } catch (IllegalArgumentException iae) {
+ this.eventLogger.log(DeployerLogEvents.REPOSITORY_DEPLOYMENT_INVALID_VERSION, versionString, uri);
+ throw new DeploymentException("The version '" + versionString + "' is invalid");
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployer.java
new file mode 100644
index 00000000..6fcdf5d3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployer.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * 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.deployer.hot;
+
+import java.io.File;
+
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer;
+import org.eclipse.virgo.kernel.deployer.core.DeployerConfiguration;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.io.FileSystemChecker;
+import org.eclipse.virgo.util.io.PathReference;
+
+/**
+ * Handles hot deployment of application artefacts.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+public final class HotDeployer implements EventHandler {
+
+ private static final String EXCLUDE_PATTERN = "\\.DS_Store";
+
+ private static final String TOPIC_RECOVERY_COMPLETED = "org/eclipse/virgo/kernel/deployer/recovery/COMPLETED";
+
+ private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final Object lifecycleLock = new Object();
+
+ private final File pickupDir;
+
+ private final Thread thread;
+
+ /**
+ * Creates a new <code>HotDeployer</code>.
+ *
+ * @param deployerConfiguration the {@link DeployerConfiguration} parameters.
+ * @param deployer the {@link ApplicationDeployer} to deploy to.
+ * @param eventLogger where to log events
+ */
+ public HotDeployer(@NonNull DeployerConfiguration deployerConfiguration, @NonNull ApplicationDeployer deployer,
+ EventLogger eventLogger) {
+ this.pickupDir = createHotDeployDir(deployerConfiguration.getDeploymentPickupDirectory());
+ FileSystemChecker checker = createFileSystemChecker(deployer, eventLogger);
+ this.thread = new Thread(new WatchTask(checker, this.pickupDir), "fs-watcher");
+ }
+
+ private FileSystemChecker createFileSystemChecker(
+ ApplicationDeployer deployer, EventLogger eventLogger) {
+ FileSystemChecker checker = new FileSystemChecker(this.pickupDir, EXCLUDE_PATTERN);
+ checker.addListener(new HotDeploymentFileSystemListener(deployer, eventLogger));
+ return checker;
+ }
+
+ /**
+ * Creates the hot deployment directory.
+ *
+ * @param pickUpDirectoryPath the {@link PathReference} location of the pickup directory.
+ * @return the {@link File} of the hot deployment directory.
+ */
+ private File createHotDeployDir(@NonNull PathReference pickUpDirectoryPath) {
+ if (pickUpDirectoryPath.isFile()) {
+ logger.debug("Deleting stray file from hot deployment directory location '{}'.", pickUpDirectoryPath.getAbsolutePath());
+ pickUpDirectoryPath.delete();
+ }
+ if (!pickUpDirectoryPath.exists()) {
+ logger.info("Creating hot deployment directory at '{}'.", pickUpDirectoryPath.getAbsolutePath());
+ pickUpDirectoryPath.createDirectory();
+ } else {
+ logger.info("Using hot deployment directory at '{}'.", pickUpDirectoryPath.getAbsolutePath());
+ }
+ return pickUpDirectoryPath.toFile();
+ }
+
+ /**
+ * Start the <code>FileSystemWatcher</code>.
+ */
+ private void doStart() {
+ synchronized (this.lifecycleLock) {
+ if (this.thread != null) {
+ this.thread.start();
+ logger.info("Started hot deployer on '{}'.", this.pickupDir);
+ }
+ }
+ }
+
+ /**
+ * Stop the <code>FileSystemWatcher</code>,
+ */
+ public void stop() {
+ synchronized (this.lifecycleLock) {
+ if (this.thread != null) {
+ logger.info("Stopping hot deployer");
+ this.thread.interrupt();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return String.format("Hot Deployer [pickupDir = %s]", this.pickupDir.getAbsolutePath());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleEvent(Event event) {
+ if (TOPIC_RECOVERY_COMPLETED.equals(event.getTopic())) {
+ doStart();
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployerLogEvents.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployerLogEvents.java
new file mode 100644
index 00000000..032363f5
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeployerLogEvents.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.deployer.hot;
+
+import org.eclipse.virgo.kernel.serviceability.LogEventDelegate;
+import org.eclipse.virgo.medic.eventlog.Level;
+import org.eclipse.virgo.medic.eventlog.LogEvent;
+
+public enum HotDeployerLogEvents implements LogEvent {
+
+ HOT_DEPLOY_PROCESSING_FILE(1, Level.INFO), //
+ HOT_DEPLOY_FAILED(2, Level.ERROR), //
+ HOT_REDEPLOY_FAILED(3, Level.ERROR), //
+ HOT_UNDEPLOY_FAILED(4, Level.ERROR), //
+ HOT_DEPLOY_SKIPPED(5, Level.INFO);
+
+ private static final String PREFIX = "HD";
+
+ private final LogEventDelegate delegate;
+
+ private HotDeployerLogEvents(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.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeploymentFileSystemListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeploymentFileSystemListener.java
new file mode 100644
index 00000000..03c8299c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/HotDeploymentFileSystemListener.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.deployer.hot;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
+import org.eclipse.virgo.kernel.deployer.core.FatalDeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.medic.eventlog.LogEvent;
+import org.eclipse.virgo.util.io.FileSystemEvent;
+import org.eclipse.virgo.util.io.FileSystemListener;
+import org.eclipse.virgo.util.io.PathReference;
+
+/**
+ * {@link FileSystemListener} that monitors a pickup directory for file system events. When a file is created it is
+ * passed to the {@link ApplicationDeployer} for deployment. When a file is modified, it is re-deployed. When a file is
+ * deleted, the application is undeployed.
+ * <p />
+ * The <code>ApplicationDeployer</code> is responsible for recovering the files deployed via this route and is given
+ * ownership of these files so that undeploying one of them, e.g. by name and version, will delete the corresponding
+ * file in the pickup directory.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+final class HotDeploymentFileSystemListener implements FileSystemListener {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final EventLogger eventLogger;
+
+ private final ApplicationDeployer deployer;
+
+ /**
+ * Creates a new <code>HotDeploymentFileSystemListener</code>.
+ *
+ * @param deployer the {@link ApplicationDeployer} to deploy to.
+ * @param eventLogger where to log events
+ */
+ public HotDeploymentFileSystemListener(@NonNull ApplicationDeployer deployer, EventLogger eventLogger) {
+ this.deployer = deployer;
+ this.eventLogger = eventLogger;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Reacts to changes in the pickup directory and calls the {@link ApplicationDeployer} as appropriate.
+ */
+ public void onChange(String path, FileSystemEvent event) {
+ String fileName = new PathReference(path).getName();
+ this.eventLogger.log(HotDeployerLogEvents.HOT_DEPLOY_PROCESSING_FILE, event, fileName);
+ try {
+ if (event == FileSystemEvent.CREATED) {
+ logger.info("ApplicationDeploying path '{}'.", path);
+ deploy(path);
+ } else if (event == FileSystemEvent.MODIFIED) {
+ logger.info("Redeploying path '{}'.", path);
+ deploy(path);
+ } else if (event == FileSystemEvent.DELETED) {
+ logger.info("ApplicationUndeploying path '{}'.", path);
+ undeploy(path);
+ } else if (event == FileSystemEvent.INITIAL) {
+ logger.info("ApplicationConditionallyDeploying path '{}'.", path);
+ deployIfNotDeployed(path, fileName);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace(System.out);
+ determineFailureAndLogMessage(event, fileName, ex);
+ }
+ }
+
+ /**
+ * Determines the {@link LogEvent} that corresponds the {@link FileSystemEvent}.
+ */
+ private void determineFailureAndLogMessage(FileSystemEvent event, String fileName, Exception ex) {
+ switch (event) {
+ case CREATED: // fall through
+ case INITIAL:
+ this.eventLogger.log(HotDeployerLogEvents.HOT_DEPLOY_FAILED, ex, fileName);
+ break;
+ case MODIFIED:
+ this.eventLogger.log(HotDeployerLogEvents.HOT_REDEPLOY_FAILED, ex, fileName);
+ break;
+ case DELETED:
+ this.eventLogger.log(HotDeployerLogEvents.HOT_UNDEPLOY_FAILED, ex, fileName);
+ break;
+ }
+ }
+
+ /**
+ * Undeploys the application that corresponds to the supplied source artefact URI.
+ *
+ * @param sourceArtefact the source artefact URI string
+ * @throws DeploymentException
+ */
+ private void undeploy(String sourceArtefact) throws DeploymentException {
+ DeploymentIdentity deploymentIdentity = getDeploymentIdentity(sourceArtefact);
+ if (deploymentIdentity != null) {
+ this.deployer.undeploy(deploymentIdentity);
+ }
+ }
+
+ /**
+ * Get the {@link DeploymentIdentity} of the given artefact. Return <code>null</code> if the given artefact is not
+ * currently deployed.
+ *
+ * @param sourceArtefact the source artefact URI string
+ * @return the <code>DeploymentIdentity</code> of the given artefact or <code>null</code>
+ */
+ private DeploymentIdentity getDeploymentIdentity(String sourceArtefact) {
+ return this.deployer.getDeploymentIdentity(getDefinitiveUri(sourceArtefact));
+ }
+
+ /**
+ * Determine whether or not the given artefact is already deployed. Return <code>true</code> if the given artefact
+ * at its file's last modified time is already deployed.
+ *
+ * @param sourceArtefact the source artefact URI string
+ * @return <code>true</code> if and only if the given artefact at its file's last modified time is already deployed
+ */
+ private boolean isDeployed(String sourceArtefact) {
+ return this.deployer.isDeployed(getDefinitiveUri(sourceArtefact));
+ }
+
+ /**
+ * Converts a string URI to a URI with a predictable format, particularly in the case where the string URI ends in a
+ * file separator.
+ *
+ * @param sourceArtefact the URI string
+ * @return
+ */
+ private URI getDefinitiveUri(String sourceArtefact) {
+ URI baseUri = new File(sourceArtefact).toURI();
+ if (sourceArtefact.endsWith(File.separator) && !baseUri.toString().endsWith("/")) {
+ try {
+ baseUri = new URI(baseUri.toString() + "/");
+ } catch (URISyntaxException e) {
+ throw new FatalDeploymentException("Unexpected URI syntax problem.", e);
+ }
+ }
+ return baseUri;
+ }
+
+ /**
+ * Deploys the application at the supplied PathReference asynchronously.
+ *
+ * @param sourceArtefact the source artefact URI string
+ * @throws DeploymentException
+ */
+ private void deploy(String sourceArtefact) throws DeploymentException {
+ this.deployer.deploy(getDefinitiveUri(sourceArtefact), new DeploymentOptions(true, true, false));
+ }
+
+ /**
+ * Deploys the application at the supplied PathReference if it is not already deployed.
+ *
+ * @param sourceArtefact the source artefact URI string
+ * @param fileName the artefact file name
+ * @throws DeploymentException
+ */
+ private void deployIfNotDeployed(String sourceArtefact, String fileName) throws DeploymentException {
+ if (!isDeployed(sourceArtefact)) {
+ deploy(sourceArtefact);
+ } else {
+ this.eventLogger.log(HotDeployerLogEvents.HOT_DEPLOY_SKIPPED, fileName);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "Hot Deploy Listener";
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/WatchTask.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/WatchTask.java
new file mode 100644
index 00000000..9ded6049
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/hot/WatchTask.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.deployer.hot;
+
+import java.io.File;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.util.io.FileSystemChecker;
+import org.eclipse.virgo.util.io.FileSystemListener;
+
+/**
+ * Task that monitors a given directory and notifies configured {@link FileSystemListener FileSystemListeners}.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+final class WatchTask implements Runnable {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final int scanIntervalMillis = 1000;
+
+ private final FileSystemChecker checker;
+
+ private final File watchDir;
+
+ WatchTask(FileSystemChecker checker, File watchDir) {
+ this.checker = checker;
+ this.watchDir = watchDir;
+ }
+
+ /**
+ * Watches the configured directory for modifications.
+ */
+ public void run() {
+ while (!Thread.currentThread().isInterrupted()) {
+ try {
+ Thread.sleep(this.scanIntervalMillis);
+ } catch (InterruptedException e) {
+ break;
+ }
+
+ try {
+ this.checker.check();
+ } catch (Exception e) {
+ logger.error("Error watching directory '{}'", e, this.watchDir.getAbsolutePath());
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/management/StandardDeployer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/management/StandardDeployer.java
new file mode 100644
index 00000000..5924f6f2
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/management/StandardDeployer.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * 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.deployer.management;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.eclipse.virgo.kernel.deployer.ArtifactIdentity;
+import org.eclipse.virgo.kernel.deployer.Deployer;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+
+
+/**
+ * Standard implementation of the deployer's control.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe assuming that the ApplicationDeployer is thread-safe.
+ *
+ */
+public class StandardDeployer implements Deployer {
+
+ private final ApplicationDeployer applicationDeployer;
+
+ public StandardDeployer(ApplicationDeployer applicationDeployer) {
+ this.applicationDeployer = applicationDeployer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity deploy(String uri) throws DeploymentException {
+ return this.applicationDeployer.deploy(URI.create(uri));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity deploy(String uri, boolean recoverable) throws DeploymentException {
+ return this.applicationDeployer.deploy(URI.create(uri), new DeploymentOptions(recoverable, false, true));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void undeploy(String applicationSymbolicName, String version) throws DeploymentException {
+ this.applicationDeployer.undeploy(applicationSymbolicName, version);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void refresh(String uri, String symbolicName) throws DeploymentException {
+ this.applicationDeployer.refresh(URI.create(uri), symbolicName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void refreshBundle(String bundleSymbolicName, String bundleVersion) throws DeploymentException {
+ this.applicationDeployer.refreshBundle(bundleSymbolicName, bundleVersion);
+ }
+
+ public ArtifactIdentity install(String artifactUri) throws DeploymentException {
+ DeploymentIdentity deploymentIdentity = this.applicationDeployer.install(createURI(artifactUri), new DeploymentOptions());
+ return new ArtifactIdentity(deploymentIdentity.getType(), deploymentIdentity.getSymbolicName(), deploymentIdentity.getVersion());
+ }
+
+ public ArtifactIdentity install(String artifactUri, boolean recover) throws DeploymentException {
+ DeploymentIdentity deploymentIdentity = this.applicationDeployer.install(createURI(artifactUri), new DeploymentOptions(recover, false, true));
+ return new ArtifactIdentity(deploymentIdentity.getType(), deploymentIdentity.getSymbolicName(), deploymentIdentity.getVersion());
+ }
+
+ public ArtifactIdentity install(String type, String name, String version) throws DeploymentException {
+ throw new UnsupportedOperationException("Not yet implemented, use deploy instead of install and start");
+ }
+
+ public ArtifactIdentity install(String type, String name, String version, boolean recover) throws DeploymentException {
+ throw new UnsupportedOperationException("Not yet implemented, use deploy instead of install and start");
+ }
+
+ public void start(ArtifactIdentity artifactIdentity) throws DeploymentException, IllegalStateException {
+ throw new UnsupportedOperationException("Not yet implemented, use deploy instead of install and start");
+
+ }
+
+ public void start(String type, String name, String version) throws DeploymentException, IllegalStateException {
+ throw new UnsupportedOperationException("Not yet implemented, use deploy instead of install and start");
+ }
+
+ public void stop(ArtifactIdentity artifactIdentity) throws DeploymentException, IllegalStateException {
+ throw new UnsupportedOperationException("Not yet implemented, use undeploy instead of stop and uninstall");
+ }
+
+ public void stop(String type, String name, String version) throws DeploymentException, IllegalStateException {
+ throw new UnsupportedOperationException("Not yet implemented, use undeploy instead of stop and uninstall");
+ }
+
+ public void uninstall(ArtifactIdentity artifactIdentity) throws DeploymentException {
+ throw new UnsupportedOperationException("Not yet implemented, use undeploy instead of stop and uninstall");
+ }
+
+ public void uninstall(String type, String name, String version) throws DeploymentException {
+ throw new UnsupportedOperationException("Not yet implemented, use undeploy instead of stop and uninstall");
+ }
+
+ private URI createURI(String uriString) {
+ URI uri;
+ try {
+ uri = new URI(uriString);
+ } catch (URISyntaxException urise) {
+ throw new IllegalArgumentException(String.format("The location '%s' is not a valid URI", uriString));
+ }
+
+ if ("file".equals(uri.getScheme())) {
+ uri = new File(uri.getSchemeSpecificPart()).toURI();
+ }
+
+ return uri;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/management/StandardRecoveryMonitor.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/management/StandardRecoveryMonitor.java
new file mode 100644
index 00000000..c8ef16d0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/management/StandardRecoveryMonitor.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * 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.deployer.management;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.Notification;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+
+import org.eclipse.virgo.kernel.deployer.RecoveryMonitor;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+
+/**
+ * Monitors the process of the deployer recovery process.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+class StandardRecoveryMonitor implements NotificationBroadcaster, RecoveryMonitor, EventHandler {
+
+ private static final String TOPIC_RECOVERY_COMPLETED = "org/eclipse/virgo/kernel/deployer/recovery/COMPLETED";
+
+ private final NotificationBroadcasterSupport broadcasterSupport = new NotificationBroadcasterSupport();
+
+ private final Object monitor = new Object();
+
+ private boolean recoveryComplete = false;
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isRecoveryComplete() {
+ synchronized (this.monitor) {
+ return this.recoveryComplete;
+ }
+ }
+
+ private void signalRecoveryComplete() {
+ boolean sendNotification = false;
+
+ synchronized (this.monitor) {
+ if (!this.recoveryComplete) {
+ this.recoveryComplete = true;
+ sendNotification = true;
+ }
+ }
+
+ if (sendNotification) {
+ this.broadcasterSupport.sendNotification(new Notification(NOTIFICATION_TYPE, this, 1));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException {
+ this.broadcasterSupport.addNotificationListener(listener, filter, handback);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MBeanNotificationInfo[] getNotificationInfo() {
+ return new MBeanNotificationInfo[] { new MBeanNotificationInfo(new String[] { NOTIFICATION_TYPE }, Notification.class.getName(),
+ "Deployer Recovery Complete Notification.") };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
+ this.broadcasterSupport.removeNotificationListener(listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleEvent(Event event) {
+ if (TOPIC_RECOVERY_COMPLETED.equals(event.getTopic())) {
+ signalRecoveryComplete();
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateDeploymentIdentityException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateDeploymentIdentityException.java
new file mode 100644
index 00000000..450355a7
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateDeploymentIdentityException.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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.deployer.model;
+
+import org.eclipse.virgo.kernel.core.KernelException;
+
+/**
+ */
+public final class DuplicateDeploymentIdentityException extends KernelException {
+
+ private static final long serialVersionUID = -730509322497877970L;
+
+ public DuplicateDeploymentIdentityException(String message) {
+ super(message);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateFileNameException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateFileNameException.java
new file mode 100644
index 00000000..73c84185
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateFileNameException.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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.deployer.model;
+
+import org.eclipse.virgo.kernel.core.KernelException;
+
+/**
+ */
+public final class DuplicateFileNameException extends KernelException {
+
+ private static final long serialVersionUID = -3176735650891485758L;
+
+ public DuplicateFileNameException(String message) {
+ super(message);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateLocationException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateLocationException.java
new file mode 100644
index 00000000..925c5daf
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/DuplicateLocationException.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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.deployer.model;
+
+import org.eclipse.virgo.kernel.core.KernelException;
+
+/**
+ */
+public final class DuplicateLocationException extends KernelException {
+
+ private static final long serialVersionUID = -3566869644719767195L;
+
+ public DuplicateLocationException(String message) {
+ super(message);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/RuntimeArtifactModel.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/RuntimeArtifactModel.java
new file mode 100644
index 00000000..4d9255fe
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/RuntimeArtifactModel.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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.deployer.model;
+
+import java.net.URI;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+
+
+/**
+ * {@link RuntimeArtifactModel} tracks all the {@link InstallArtifact InstallArtifacts} in the kernel.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface RuntimeArtifactModel {
+
+ /**
+ * Adds the given {@link InstallArtifact} deployed from the given {@link URI} to this {@link RuntimeArtifactModel}.
+ *
+ * @param location the <code>URI</code> from which the artifact was deployed
+ * @param installArtifact the <code>InstallArtifact</code>
+ * @return the {@link DeploymentIdentity} of the <code>InstallArtifact</code>
+ * @throws DuplicateFileNameException
+ * @throws DuplicateLocationException
+ * @throws DuplicateDeploymentIdentityException
+ * @throws DeploymentException
+ */
+ DeploymentIdentity add(URI location, InstallArtifact installArtifact) throws DuplicateFileNameException, DuplicateLocationException,
+ DuplicateDeploymentIdentityException, DeploymentException;
+
+ /**
+ * Gets the {@link InstallArtifact} with the given {@link DeploymentIdentity} in this {@link RuntimeArtifactModel}.
+ * If there is no such <code>InstallArtifact</code>, returns <code>null</code>.
+ *
+ * @param deploymentIdentity the <code>DeploymentIdentity</code> of the <code>InstallArtifact</code> to get
+ * @return the <code>InstallArtifact</code> or <code>null</code> if there is no such <code>InstallArtifact</code>
+ */
+ InstallArtifact get(DeploymentIdentity deploymentIdentity);
+
+ /**
+ * Gets the {@link InstallArtifact} deployed from the given {@link URI} in this {@link RuntimeArtifactModel}. If
+ * there is no such <code>InstallArtifact</code>, returns <code>null</code>.
+ *
+ * @param location the <code>URI</code> of the <code>InstallArtifact</code> to get
+ * @return the <code>InstallArtifact</code> or <code>null</code> if there is no such <code>InstallArtifact</code>
+ */
+ InstallArtifact get(URI location);
+
+ /**
+ * Gets the {@link URI} from which the artifact with the given {@link DeploymentIdentity} was deployed.
+ *
+ * @param deploymentIdentity the <code>DeploymentIdentity</code> of the artifact
+ * @return the <code>URI</code> from which the artifact was deployed, or <code>null</code> if no such artifact was
+ * found
+ */
+ URI getLocation(DeploymentIdentity deploymentIdentity);
+
+ /**
+ * Gets an array of all the {@link DeploymentIdentity DeploymentIdentities} in this {@link RuntimeArtifactModel}.
+ *
+ * @return an array of <code>DeploymentIdentity</code>
+ */
+ DeploymentIdentity[] getDeploymentIdentities();
+
+ /**
+ * Deletes the {@link InstallArtifact} with the given {@link DeploymentIdentity} from this
+ * {@link RuntimeArtifactModel} and returns the <code>InstallArtifact</code>. If no such artifact is present, does
+ * not modify this <code>RuntimeArtifactModel</code> and returns <code>null</code>.
+ *
+ * @param deploymentIdentity the <code>DeploymentIdentity</code> of the artifact to be deleted
+ * @return the <code>InstallArtifact</code> which was deleted
+ * @throws DeploymentException
+ */
+ InstallArtifact delete(DeploymentIdentity deploymentIdentity) throws DeploymentException;
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/internal/StandardRuntimeArtifactModel.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/internal/StandardRuntimeArtifactModel.java
new file mode 100644
index 00000000..b7236995
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/deployer/model/internal/StandardRuntimeArtifactModel.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.deployer.model.internal;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.virgo.kernel.deployer.core.DeployUriNormaliser;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
+import org.eclipse.virgo.kernel.deployer.core.internal.StandardDeploymentIdentity;
+import org.eclipse.virgo.kernel.deployer.model.DuplicateDeploymentIdentityException;
+import org.eclipse.virgo.kernel.deployer.model.DuplicateFileNameException;
+import org.eclipse.virgo.kernel.deployer.model.DuplicateLocationException;
+import org.eclipse.virgo.kernel.deployer.model.RuntimeArtifactModel;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.serviceability.Assert;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+
+
+/**
+ * {@link StandardRuntimeArtifactModel} is the default {@link RuntimeArtifactModel} implementation.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class StandardRuntimeArtifactModel implements RuntimeArtifactModel {
+
+ private static final String CLASH_MESSAGE_FORMAT = "The artifact %s at URI '%s' cannot be stored in the runtime artifact model as it clashes with the artifact %s which is already present.";
+
+ private static final String URI_PATH_SEPARATOR = "/";
+
+ private final Object monitor = new Object();
+
+ private final Map<URI, InstallArtifact> artifactByUri = new HashMap<URI, InstallArtifact>();
+
+ private final Map<DeploymentIdentity, URI> uriByIdentity = new HashMap<DeploymentIdentity, URI>();
+
+ private final Map<String, URI> uriByFileName = new HashMap<String, URI>();
+
+ private final DeployUriNormaliser uriNormaliser;
+
+ StandardRuntimeArtifactModel(DeployUriNormaliser uriNormaliser) {
+ this.uriNormaliser = uriNormaliser;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity add(@NonNull URI location, @NonNull InstallArtifact installArtifact) throws DuplicateFileNameException,
+ DuplicateLocationException, DuplicateDeploymentIdentityException, DeploymentException {
+ synchronized (this.monitor) {
+
+ // Check the precondition and throw an exception if it is violated.
+ checkLocation(location, installArtifact);
+
+ String fileName = getFileName(location);
+ checkFileName(location, installArtifact, fileName);
+
+ DeploymentIdentity deploymentIdentity = getDeploymentIdentity(installArtifact);
+ checkDeploymentIdentity(location, installArtifact, deploymentIdentity);
+
+ // The precondition is true, so update the state. The invariants are preserved.
+ updateState(location, installArtifact, fileName, deploymentIdentity);
+
+ return deploymentIdentity;
+ }
+ }
+
+ private void checkLocation(URI location, InstallArtifact installArtifact) throws DuplicateLocationException {
+ if (this.artifactByUri.containsKey(location)) {
+ InstallArtifact clashingArtifact = this.artifactByUri.get(location);
+ throw new DuplicateLocationException(getClashMessage(location, installArtifact, clashingArtifact));
+ }
+ }
+
+ private void checkFileName(URI location, InstallArtifact installArtifact, String fileName) throws DuplicateFileNameException {
+ if (this.uriByFileName.containsKey(fileName)) {
+ InstallArtifact clashingArtifact = this.artifactByUri.get(this.uriByFileName.get(fileName));
+ throw new DuplicateFileNameException(getClashMessage(location, installArtifact, clashingArtifact));
+ }
+ }
+
+ private void checkDeploymentIdentity(URI location, InstallArtifact installArtifact, DeploymentIdentity deploymentIdentity)
+ throws DuplicateDeploymentIdentityException {
+ if (this.uriByIdentity.containsKey(deploymentIdentity)) {
+ InstallArtifact clashingArtifact = this.artifactByUri.get(this.uriByIdentity.get(deploymentIdentity));
+ throw new DuplicateDeploymentIdentityException(getClashMessage(location, installArtifact, clashingArtifact));
+ }
+ }
+
+ private String getClashMessage(URI location, InstallArtifact installArtifact, InstallArtifact clashingArtifact) {
+ return String.format(CLASH_MESSAGE_FORMAT, installArtifact, location, clashingArtifact);
+ }
+
+ private DeploymentIdentity getDeploymentIdentity(@NonNull InstallArtifact installArtifact) {
+ return new StandardDeploymentIdentity(installArtifact.getType(), installArtifact.getName(), installArtifact.getVersion().toString());
+ }
+
+ private String getFileName(@NonNull URI location) throws DeploymentException {
+ URI normalisedLocation = this.uriNormaliser.normalise(location);
+ String path = normalisedLocation.getPath();
+ if (path.endsWith(URI_PATH_SEPARATOR)) {
+ path = path.substring(0, path.length() - 1);
+ }
+ int separatorIndex = path.lastIndexOf(URI_PATH_SEPARATOR);
+ return separatorIndex != -1 ? path.substring(separatorIndex + 1) : path;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public InstallArtifact get(@NonNull DeploymentIdentity deploymentIdentity) {
+ synchronized (this.monitor) {
+ URI location = this.uriByIdentity.get(deploymentIdentity);
+ return location == null ? null : this.artifactByUri.get(location);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public InstallArtifact get(@NonNull URI location) {
+ synchronized (this.monitor) {
+ return this.artifactByUri.get(location);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public URI getLocation(DeploymentIdentity deploymentIdentity) {
+ synchronized (this.monitor) {
+ return this.uriByIdentity.get(deploymentIdentity);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DeploymentIdentity[] getDeploymentIdentities() {
+ synchronized (this.monitor) {
+ Set<DeploymentIdentity> deploymentIdentities = this.uriByIdentity.keySet();
+ return deploymentIdentities.toArray(new DeploymentIdentity[deploymentIdentities.size()]);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public InstallArtifact delete(DeploymentIdentity deploymentIdentity) throws DeploymentException {
+ synchronized (this.monitor) {
+ URI location = this.uriByIdentity.get(deploymentIdentity);
+
+ if (location == null) {
+ return null;
+ }
+
+ InstallArtifact installArtifact = this.artifactByUri.get(location);
+ Assert.notNull(installArtifact,
+ "Broken invariant: artifactByUri is missing an entry for URI '%s' but this URI is present in uriByIdentity for '%s'", location,
+ deploymentIdentity);
+ String fileName = getFileName(location);
+
+ Assert.isTrue(this.uriByFileName.containsKey(fileName),
+ "Broken invariant: uriByFileName is missing an entry for file name '%s' but URI '%s' is present in uriByIdentity for '%s'", fileName,
+ location, deploymentIdentity);
+ removeState(deploymentIdentity, location, fileName);
+ return installArtifact;
+ }
+ }
+
+ private void updateState(URI location, InstallArtifact installArtifact, String fileName, DeploymentIdentity deploymentIdentity) {
+ this.artifactByUri.put(location, installArtifact);
+ this.uriByIdentity.put(deploymentIdentity, location);
+ this.uriByFileName.put(fileName, location);
+ }
+
+ private void removeState(DeploymentIdentity deploymentIdentity, URI location, String fileName) {
+ this.artifactByUri.remove(location);
+ this.uriByIdentity.remove(deploymentIdentity);
+ this.uriByFileName.remove(fileName);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactIdentity.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactIdentity.java
new file mode 100644
index 00000000..dabab4a8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactIdentity.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * 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.install.artifact;
+
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.osgi.framework.Version;
+
+
+/**
+ * An {@link ArtifactIdentity} encapsulates the identity of an artifact.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public class ArtifactIdentity {
+
+ private final String type;
+
+ private final String name;
+
+ private final Version version;
+
+ private final String scopeName;
+
+ /**
+ * Creates a new <code>ArtifactIdentity</code> with the supplied <code>type</code>, <code>name</code>, <code>version</code>,
+ * and <code>scopeName</code>.
+ *
+ * @param type the artifact's type
+ * @param name the artifact's name
+ * @param version the artifact's version
+ * @param scopeName the name of the artifact's scope, or <code>null</code> if the artifact is not scoped.
+ */
+ public ArtifactIdentity(@NonNull String type, @NonNull String name, @NonNull Version version, String scopeName) {
+ this.type = type;
+ this.name = name;
+ this.version = version;
+ this.scopeName = scopeName;
+ }
+
+ /**
+ * Returns the type of the artifact
+ * @return the artifact's type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Returns the name of the artifact
+ * @return the artifact's name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the version of the artifact
+ * @return the artifact's version
+ */
+ public Version getVersion() {
+ return version;
+ }
+
+ /**
+ * Returns the name of the artifact's scope, or <code>null</code> if the artifact is not in a scope
+ * @return the artifact's scope name
+ */
+ public String getScopeName() {
+ return scopeName;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + name.hashCode();
+ result = prime * result + ((scopeName == null) ? 0 : scopeName.hashCode());
+ result = prime * result + type.hashCode();
+ result = prime * result + version.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+
+ if (obj == null)
+ return false;
+
+ if (getClass() != obj.getClass())
+ return false;
+
+ ArtifactIdentity other = (ArtifactIdentity) obj;
+
+ if (!name.equals(other.name))
+ return false;
+
+ if (scopeName == null) {
+ if (other.scopeName != null)
+ return false;
+ } else if (!scopeName.equals(other.scopeName))
+ return false;
+
+ if (!type.equals(other.type))
+ return false;
+
+ if (!version.equals(other.version))
+ return false;
+
+ return true;
+ }
+
+ public String toString() {
+ return String.format("%s '%s' version '%s' in scope '%s'", this.type, this.name, this.version, this.scopeName);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactIdentityDeterminer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactIdentityDeterminer.java
new file mode 100644
index 00000000..cf59d880
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactIdentityDeterminer.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.install.artifact;
+
+import java.io.File;
+
+/**
+ * {@link ArtifactIdentityDeterminer} is kernel extension point for determining the identity of an install artifact.
+ * It also provides some constants for well known Artifact types.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface ArtifactIdentityDeterminer {
+
+ public static final String BUNDLE_TYPE = "bundle";
+
+ public static final String PLAN_TYPE = "plan";
+
+ public static final String CONFIGURATION_TYPE = "configuration";
+
+ public static final String PAR_TYPE = "par";
+
+ /**
+ * Determines the identity of the given artifact.
+ *
+ * @param file The {@link File} that represents the artifact
+ * @param scopeName The name of the artifact's scope
+ * @return the identity of the artifact or <code>null</code> if the identity could not be determined
+ */
+ ArtifactIdentity determineIdentity(File file, String scopeName);
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactState.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactState.java
new file mode 100644
index 00000000..c933ce5e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ArtifactState.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * 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.install.artifact;
+
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact.State;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+
+
+/**
+ * {@link ArtifactState} encapsulates the state of an install artifact.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public final class ArtifactState {
+
+ private final Object monitor = new Object();
+
+ private State state;
+
+ /**
+ * Creates an {@link ArtifactState} in the INITIAL {@link State}.
+ */
+ public ArtifactState() {
+ this.state = State.INITIAL;
+ }
+
+ /**
+ * Gets the current {@link State}.
+ *
+ * @return the current <code>State</code>
+ */
+ public State getState() {
+ synchronized (this.monitor) {
+ return this.state;
+ }
+ }
+
+ /**
+ * Sets the state to the given value.
+ *
+ * @param newState
+ * @return <code>true</code> if and only if the state changed
+ */
+ private boolean setState(@NonNull State newState) {
+ synchronized (this.monitor) {
+ boolean changed = !this.state.equals(newState);
+ this.state = newState;
+ return changed;
+ }
+ }
+
+ /**
+ * Sets the current state to INITIAL.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setInitial() {
+ return setState(State.INITIAL);
+ }
+
+ /**
+ * Sets the current state to INSTALLING.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setInstalling() {
+ return setState(State.INSTALLING);
+ }
+
+ /**
+ * Sets the current state to INSTALLED.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setInstalled() {
+ return setState(State.INSTALLED);
+ }
+
+ /**
+ * Sets the current state to RESOLVING.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setResolving() {
+ return setState(State.RESOLVING);
+ }
+
+ /**
+ * Sets the current state to RESOLVED.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setResolved() {
+ return setState(State.RESOLVED);
+ }
+
+ /**
+ * Sets the current state to STARTING.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setStarting() {
+ return setState(State.STARTING);
+ }
+
+ /**
+ * Sets the current state to ACTIVE.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setActive() {
+ return setState(State.ACTIVE);
+ }
+
+ /**
+ * Sets the current state to STOPPING.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setStopping() {
+ return setState(State.STOPPING);
+ }
+
+ /**
+ * Sets the current state to UNINSTALLING.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setUninstalling() {
+ return setState(State.UNINSTALLING);
+ }
+
+ /**
+ * Sets the current state to UNINSTALLED.
+ * @return <code>true</code> if and only if the state changed
+ */
+ public boolean setUninstalled() {
+ return setState(State.UNINSTALLED);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/BundleInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/BundleInstallArtifact.java
new file mode 100644
index 00000000..eba57151
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/BundleInstallArtifact.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.install.artifact;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link BundleInstallArtifact} is an {@link InstallArtifact} for a bundle.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public interface BundleInstallArtifact extends InstallArtifact {
+
+ /**
+ * Returns the bundle manifest. This may differ from the bundle's original manifest file contents if transformations
+ * have been performed in the installation pipeline. Note that such transformations are performed in memory and the
+ * results are not written to a manifest file.
+ *
+ * @return the {@link BundleManifest} abstract syntax tree
+ * @throws IOException if an I/O error occurred while reading the bundle manifest
+ */
+ BundleManifest getBundleManifest() throws IOException;
+
+ /**
+ * Sets the {@link QuasiBundle} for this {@link BundleInstallArtifact}.
+ *
+ * @param quasiBundle the <code>QuasiBundle</code>
+ */
+ void setQuasiBundle(QuasiBundle quasiBundle);
+
+ /**
+ * Gets the {@link QuasiBundle} for this {@link BundleInstallArtifact}.
+ *
+ * @return the <code>QuasiBundle</code> or <code>null</code> if no <code>QuasiBundle</code> has been set
+ */
+ QuasiBundle getQuasiBundle();
+
+ /**
+ * Gets the OSGi {@link Bundle} for this {@link BundleInstallArtifact}.
+ *
+ * @return the <code>Bundle</code> or <code>null</code> if this <code>BundleInstallArtifact</code> has not been
+ * installed in the OSGi framework
+ */
+ Bundle getBundle();
+
+ /**
+ * Returns the properties that are used to customize the deployment of the <code>BundleInstallArtifact</code>.
+ *
+ * @return The deployment properties
+ */
+ Map<String, String> getDeploymentProperties();
+
+ /**
+ * Delete an entry within this bundle
+ *
+ * @param targetPath The bundle relative path to delete
+ */
+ void deleteEntry(String targetPath);
+
+ /**
+ * Update an entry within this bundle. If the target path does not already exist, creates a new entry at that
+ * location.
+ *
+ * @param inputPath The path to read update from
+ * @param targetPath The bundle relative path to write the update to
+ */
+ void updateEntry(URI inputPath, String targetPath);
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifact.java
new file mode 100644
index 00000000..efd31909
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifact.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * 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.install.artifact;
+
+import java.util.Set;
+
+import org.osgi.framework.Version;
+
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.core.Signal;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * An {@link InstallArtifact} is a single node in an install tree.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementation <strong>must</strong> be thread-safe.
+ *
+ */
+public interface InstallArtifact {
+
+ /**
+ * The possible states of an artifact.
+ */
+ public enum State {
+ INITIAL, INSTALLING, INSTALLED, RESOLVING, RESOLVED, STARTING, ACTIVE, STOPPING, UNINSTALLING, UNINSTALLED
+ }
+
+ /**
+ * Returns the type of the artifact.
+ *
+ * @return the artifact's type.
+ */
+ String getType();
+
+ /**
+ * Returns the name of the artifact.
+ *
+ * @return the artifact's name.
+ */
+ String getName();
+
+ /**
+ * Returns the version of the artifact.
+ *
+ * @return the artifact's version.
+ */
+ Version getVersion();
+
+ /**
+ * Returns the name of the scope in which this <code>InstallArtifact</code> resides, or <code>null</code> if it is
+ * not scoped.
+ * @return the artifact's scope name
+ */
+ String getScopeName();
+
+ /**
+ * Gets the {@link State} of this {@link InstallArtifact}.
+ *
+ * @return a non-<code>null</code> <code>State</code>
+ */
+ State getState();
+
+ /**
+ * Starts this {@link InstallArtifact}. Returns before any asynchronous processing has necessarily completed.
+ * <p/>
+ * Equivalent to calling {@link InstallArtifact#start(Signal) start(null)}.
+ *
+ * @throws DeploymentException
+ */
+ void start() throws DeploymentException;
+
+ /**
+ * Starts this {@link InstallArtifact} and drives the given {@link Signal} when the start, including any
+ * asynchronous processing, completes either successfully or unsuccessfully.
+ * <p/>
+ * If the start does not involve asynchronous processing, drives the given <code>Signal</code> before returning.
+ * <p/>
+ * Note that the given <code>Signal</code> may be driven before this method returns, after the method has returned,
+ * or possibly never if there is asynchronous processing which never completes. The caller must ensure that the
+ * given <code>Signal</code> is ready to be driven <i>before</i> calling this method.
+ * <p/>
+ * If the caller is not interested in being signalled, it should pass a <code>null</code> <code>Signal</code>.
+ * <p/>
+ * Note: this method behaves as specified above when called multiple times on the same <code>InstallArtifact</code>
+ * with any combination of the same or distinct or <code>null</code> <code>Signals</code>.
+ *
+ * @param signal a <code>Signal</code> that is ready to be driven or <code>null</code> if signalling is not required
+ * @throws DeploymentException
+ */
+ void start(Signal signal) throws DeploymentException;
+
+ /**
+ * Stops this {@link InstallArtifact}. If the <code>InstallArtifact</code> is already stopped, do nothing.
+ *
+ * @throws DeploymentException if the operation fails
+ */
+ void stop() throws DeploymentException;
+
+ /**
+ * Uninstalls this {@link InstallArtifact}. If the <code>InstallArtifact</code> is already uninstalled, do nothing.
+ *
+ * @throws DeploymentException if the operation fails
+ */
+ void uninstall() throws DeploymentException;
+
+ /**
+ * Returns the <code>ArtifactFS</code> for this artifact
+ *
+ * @return the <code>ArtifactFS</code>
+ */
+ ArtifactFS getArtifactFS();
+
+ /**
+ * Attempts to refresh this {@link InstallArtifact}. Returns <code>true</code> if and only if this has completed
+ * successfully.
+ *
+ * @return <code>true</code> if and only if the operation completed successfully
+ * @throws DeploymentException
+ */
+ boolean refresh() throws DeploymentException;
+
+ /**
+ * Associates the property with the given name and value with this {@link InstallArtifact}. Properties may be set by
+ * participants in the artifact's installation. Once the artifact has been installed, an MBean representing it is
+ * exported and these properties will be available via that MBean.
+ *
+ * @param name the property name
+ * @param value the property value
+ * @return the property's previous value or <code>null</code> if there was no such property
+ */
+ String setProperty(@NonNull String name, @NonNull String value);
+
+ /**
+ * Returns the property with the given name associated with this {@link InstallArtifact}. If there is no associated
+ * property with the given name, returns <code>null</code>.
+ *
+ * @param name the property name
+ * @return the property value or <code>null</code> if there is no property with the given name
+ */
+ String getProperty(@NonNull String name);
+
+ /**
+ * Returns the names of the properties that are associated with this {@link InstallArtifact}.
+ *
+ * @return a set of property names
+ */
+ Set<String> getPropertyNames();
+
+ /**
+ * Returns the local name of the repository from which the artifact was installed, or null if not from a repository.
+ *
+ * @return the local name of the repository whence the artifact came
+ */
+ String getRepositoryName();
+
+ /**
+ * Returns the install tree rooted in this {@link InstallArtifact}.
+ *
+ * @return this artifact's tree
+ */
+ Tree<InstallArtifact> getTree();
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactLifecycleListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactLifecycleListener.java
new file mode 100644
index 00000000..129a466a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactLifecycleListener.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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.install.artifact;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+
+/**
+ * An <code>InstallArtifactLifecycleListener</code> is notified of {@link InstallArtifact} lifecycle events.
+ * <p />
+ * An <code>InstallArtifactLifecycleListener</code> implementation should be stateless and is made known to the deployer
+ * by publishing it as an OSGi service.
+ * <p />
+ * An <code>InstallArtifactLifecycleListener</code> is not notified of events it has missed. For example, if an
+ * <code>InstallArtifactLifecycleListener</code> is published while an <code>InstallArtifact</code> is being started, it
+ * may miss the <code>onStarting</code> notification and any <code>onStarted</code> notification.
+ * <p />
+ * If an <code>InstallArtifactLifecycleListener</code> has missed some events, it is still notified of other events. For
+ * example, if an <code>InstallArtifactLifecycleListener</code> is published while an <code>InstallArtifact</code> is
+ * being started, it may be notified of an <code>onStarted</code> event for the <code>InstallArtifact</code> even if it
+ * has missed the <code>onStarting</code> event for the <code>InstallArtifact</code>. Similarly, an
+ * <code>InstallArtifactLifecycleListener</code> may be notified of an <code>onStopping</code> event for an
+ * <code>InstallArtifact</code> event if it missed the <code>onStarting</code> event and any <code>onStarted</code>
+ * event for the <code>InstallArtifact</code>.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations <strong>must</strong> be thread-safe. Notifications of events for a given
+ * <code>InstallArtifact</code> may be delivered on the same thread or on distinct threads.
+ *
+ */
+public interface InstallArtifactLifecycleListener {
+
+ /**
+ * Notification that the given {@link InstallArtifact} is installing.
+ * <p/>
+ *
+ * Throwing a {@link DeploymentException} will result in no further listeners being notified that the
+ * <code>installArtifact</code> is being installed. Install failure will then be notified and the installation
+ * aborted.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that is installing
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onInstalling(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} failed to install.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that failed to install
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onInstallFailed(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} installed successfully.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that installed successfully
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onInstalled(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} is resolving.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that is resolving
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onResolving(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} failed to resolve.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that failed to resolve
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onResolveFailed(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} resolved successfully.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that resolved successfully
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onResolved(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} is starting.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that is starting
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onStarting(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} failed to start.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that failed to start
+ * @param cause the exception indicating the cause of the failure or <code>null</code> if there was no exception
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onStartFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} has started.
+ *
+ * <p/>
+ *
+ * Throwing a {@link DeploymentException} will result in no further listeners being notified that the
+ * <code>installArtifact</code> has started. The <code>installArtifact</code> will then be stopped.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that has started
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onStarted(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} is stopping.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that is stopping
+ */
+ void onStopping(InstallArtifact installArtifact);
+
+ /**
+ * Notification that the given {@link InstallArtifact} failed to stop.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that failed to stop
+ * @param cause the exception indicating the cause of the failure or <code>null</code> if there was no exception
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onStopFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} has stopped.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that has stopped
+ */
+ void onStopped(InstallArtifact installArtifact);
+
+ /**
+ * Notification that the given {@link InstallArtifact} has become unresolved.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that has become unresolved
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onUnresolved(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} is uninstalling.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that is unintalling
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onUninstalling(InstallArtifact installArtifact) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} failed to uninstall.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that failed to uninstall
+ * @param cause the exception indicating the cause of the failure or <code>null</code> if there was no exception
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onUninstallFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException;
+
+ /**
+ * Notification that the given {@link InstallArtifact} has uninstalled.
+ *
+ * @param installArtifact the <code>InstallArtifact</code> that has uninstalled
+ * @throws DeploymentException if the listener failed to handle the event
+ */
+ void onUninstalled(InstallArtifact installArtifact) throws DeploymentException;
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactLifecycleListenerSupport.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactLifecycleListenerSupport.java
new file mode 100644
index 00000000..52b1593d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactLifecycleListenerSupport.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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.install.artifact;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+
+/**
+ * {@link InstallArtifactLifecycleListenerSupport} is an abstract implementation of
+ * {@link InstallArtifactLifecycleListener} which ignores all events.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public abstract class InstallArtifactLifecycleListenerSupport implements InstallArtifactLifecycleListener {
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onInstallFailed(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onInstalled(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onInstalling(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onResolveFailed(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onResolved(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onResolving(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStartFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStarted(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStarting(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStopFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStopped(InstallArtifact installArtifact) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStopping(InstallArtifact installArtifact) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onUninstallFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onUninstalled(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onUninstalling(InstallArtifact installArtifact) throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onUnresolved(InstallArtifact installArtifact) throws DeploymentException {
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactTreeFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactTreeFactory.java
new file mode 100644
index 00000000..81caaba0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactTreeFactory.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.install.artifact;
+
+import java.util.Map;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStorage;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * {@link InstallArtifactTreeFactory} is used to create trees of {@link InstallArtifact InstallArtifacts}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this interface must be thread safe.
+ *
+ */
+public interface InstallArtifactTreeFactory {
+
+ /**
+ * Constructs an install tree from the {@link ArtifactStorage}. If this factory cannot handle the given artifact type, it
+ * returns <code>null</code>.
+ * @param artifactIdentity
+ * @param artifactStorage
+ * @param deploymentProperties the deployment properties for the artifact. Can be <code>null</code>.
+ * @param repositoryName The name of the repository from which that artifact originates, or <code>null</code> if the artifact is not from a repository.
+ * @return an install tree or <code>null</code> if the factory cannot handle the given artifact type
+ * @throws DeploymentException if the tree cannot be constructed
+ */
+ Tree<InstallArtifact> constructInstallArtifactTree(ArtifactIdentity artifactIdentity, ArtifactStorage artifactStorage, Map<String, String> deploymentProperties,
+ String repositoryName) throws DeploymentException;
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactTreeInclosure.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactTreeInclosure.java
new file mode 100644
index 00000000..4eb1314d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/InstallArtifactTreeInclosure.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.install.artifact;
+
+import java.io.File;
+
+
+import org.eclipse.virgo.kernel.artifact.ArtifactSpecification;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * {@link InstallArtifactTreeInclosure} is used to create, and store persistently, various types of
+ * {@link InstallArtifact}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public interface InstallArtifactTreeInclosure {
+
+ /**
+ * Create an install tree consisting of the single artifact matching the supplied {@link ArtifactSpecification}.
+ *
+ * @param artifactSpecification the <code>ArtifactSpecification</code>.
+ * @return an install tree
+ * @throws DeploymentException if the tree cannot be created
+ */
+ Tree<InstallArtifact> createInstallTree(ArtifactSpecification artifactSpecification) throws DeploymentException;
+
+ /**
+ * Create an install tree consisting of the single artifact matching the supplied {@link ArtifactSpecification}. if
+ * the given scope name is non-<code>null</code>, the installation is scoped. If the given scope name is
+ * <code>null</code>, the behaviour of this method is equivalent to that of the <code>createInstallTree</code>
+ * method with no scope name parameter.
+ *
+ * @param artifactSpecification the <code>ArtifactSpecification</code>.
+ * @param scopeName the scope name of the artifact or <code>null</code> if it does not belong to a scope
+ * @return an install tree
+ * @throws DeploymentException if the tree cannot be created
+ */
+ Tree<InstallArtifact> createInstallTree(ArtifactSpecification artifactSpecification, String scopeName) throws DeploymentException;
+
+ /**
+ * Create an install tree consisting of the single artifact available at the given file URI.
+ *
+ * @param location the artifact's location
+ * @return an install tree
+ * @throws DeploymentException if the tree cannot be created
+ */
+ Tree<InstallArtifact> createInstallTree(File location) throws DeploymentException;
+
+ /**
+ * Optionally recover an install tree from the staging area using the given file URI to identify the artifact. The
+ * source URI need no longer be valid unless the artifact is owned by the deployer. Non-recoverable artifacts are
+ * "recovered" by deleting them.
+ *
+ * @param location the source location of the artifact
+ * @param options the {@link DeploymentOptions} of the artifact
+ * @return an install tree or <code>null</code>
+ */
+ Tree<InstallArtifact> recoverInstallTree(File location, DeploymentOptions options);
+
+ /**
+ * Update the copy of the given artefact in the deploy area.
+ *
+ * @param sourceLocation the location of the artefact to be updated
+ * @param identity the identity of the artifact to be updated
+ * @throws DeploymentException
+ */
+ void updateStagingArea(File sourceLocation, ArtifactIdentity identity) throws DeploymentException;
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/PlanInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/PlanInstallArtifact.java
new file mode 100644
index 00000000..0cd67a80
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/PlanInstallArtifact.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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.install.artifact;
+
+import java.util.List;
+
+import org.eclipse.virgo.kernel.artifact.ArtifactSpecification;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+
+
+/**
+ * {@link PlanInstallArtifact} is an {@link InstallArtifact} for a plan.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public interface PlanInstallArtifact extends InstallArtifact {
+
+ /**
+ * Returns whether or not this plan is scoped.
+ *
+ * @return <code>true</code> if and only if this plan is scoped
+ */
+ boolean isScoped();
+
+ /**
+ * Returns whether or not this plan is atomic.
+ *
+ * @return <code>true</code> if and only if this plan is atomic
+ */
+ boolean isAtomic();
+
+ /**
+ * Returns {@link ArtifactSpecification}s for the artifacts of this plan.
+ *
+ * @return a list of <code>ArtifactSpecification</code>s
+ */
+ public List<ArtifactSpecification> getArtifactSpecifications();
+
+ /**
+ * Refresh the child of this plan with the given symbolic name.
+ *
+ * @param symbolicName the symbolic name of the child to refresh
+ * @return <code>true</code> if and only if the child was successfully refreshed
+ * @throws DeploymentException if an error occurred during refresh
+ */
+ boolean refresh(String symbolicName) throws DeploymentException;
+
+ /**
+ * If this plan is scoped, run its install tree through the refresh subpipeline. If this plan is unscoped, recurse
+ * to any parents this plan has. Note that a plan may be a descendent of at most one scoped plan since scopes do not
+ * overlap.
+ *
+ * @return true if successfully refreshed, false otherwise
+ */
+ boolean refreshScope();
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ScopeServiceRepository.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ScopeServiceRepository.java
new file mode 100644
index 00000000..d43b2f0d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/ScopeServiceRepository.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * 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.install.artifact;
+
+import java.util.Dictionary;
+import java.util.Set;
+
+import org.osgi.framework.InvalidSyntaxException;
+
+public interface ScopeServiceRepository {
+
+ void recordService(String scopeName, String[] types, Dictionary<Object, Object> properties);
+
+ boolean scopeHasMatchingService(String scopeName, String type, String filter) throws InvalidSyntaxException;
+
+ void clearScope(String scopeName);
+
+ Set<String> knownScopes();
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/AbstractInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/AbstractInstallArtifact.java
new file mode 100644
index 00000000..e087a164
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/AbstractInstallArtifact.java
@@ -0,0 +1,456 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.framework.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.core.Signal;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactState;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * {@link AbstractInstallArtifact} is a base class for implementations of {@link InstallArtifact}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public abstract class AbstractInstallArtifact implements InstallArtifact {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final Object monitor = new Object();
+
+ private final ArtifactIdentity identity;
+
+ protected final ArtifactStorage artifactStorage;
+
+ private final Map<String, String> properties = new ConcurrentHashMap<String, String>();
+
+ private final Map<String, String> deploymentProperties = new ConcurrentHashMap<String, String>();
+
+ private final ArtifactStateMonitor artifactStateMonitor;
+
+ private final String repositoryName;
+
+ protected final EventLogger eventLogger;
+
+ private Tree<InstallArtifact> tree;
+
+ private volatile boolean isRefreshing;
+
+ /**
+ * Construct an {@link AbstractInstallArtifact} from the given type, name, version, {@link ArtifactFS}, and
+ * {@link ArtifactState}, none of which may be null.
+ *
+ * @param type a non-<code>null</code> artifact type
+ * @param name a non-<code>null</code> artifact name
+ * @param version a non-<code>null</code> artifact {@link Version}
+ * @param artifactFS a non-<code>null</code> <code>ArtifactFS</code>
+ * @param repositoryName the name of the source repository, or <code>null</code> if the artifact is not from a
+ * repository
+ */
+ protected AbstractInstallArtifact(@NonNull ArtifactIdentity identity, @NonNull ArtifactStorage artifactStorage,
+ @NonNull ArtifactStateMonitor artifactStateMonitor, String repositoryName, EventLogger eventLogger) {
+ this.identity = identity;
+ this.artifactStorage = artifactStorage;
+ this.artifactStateMonitor = artifactStateMonitor;
+ this.repositoryName = repositoryName;
+ this.eventLogger = eventLogger;
+ this.isRefreshing = false;
+ }
+
+ final ArtifactIdentity getIdentity() {
+ return this.identity;
+ }
+
+ public final boolean isRefreshing() {
+ return this.isRefreshing;
+ }
+
+ public void beginInstall() throws DeploymentException {
+ try {
+ this.artifactStateMonitor.onInstalling(this);
+ } catch (DeploymentException de) {
+ failInstall();
+ throw de;
+ }
+
+ }
+
+ public void failInstall() throws DeploymentException {
+ this.artifactStateMonitor.onInstallFailed(this);
+ }
+
+ public void endInstall() throws DeploymentException {
+ this.artifactStateMonitor.onInstalled(this);
+ }
+
+ public void beginResolve() throws DeploymentException {
+ pushThreadContext();
+ try {
+ this.artifactStateMonitor.onResolving(this);
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ public void failResolve() throws DeploymentException {
+ pushThreadContext();
+ try {
+ this.artifactStateMonitor.onResolveFailed(this);
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ public void endResolve() throws DeploymentException {
+ pushThreadContext();
+ try {
+ this.artifactStateMonitor.onResolved(this);
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String getType() {
+ return this.identity.getType();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String getName() {
+ return this.identity.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final Version getVersion() {
+ return this.identity.getVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String getScopeName() {
+ return this.identity.getScopeName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public State getState() {
+ return this.artifactStateMonitor.getState();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start() throws DeploymentException {
+ start(null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start(Signal signal) throws DeploymentException {
+ pushThreadContext();
+ try {
+ boolean stateChanged = this.artifactStateMonitor.onStarting(this);
+ if (stateChanged || signal != null) {
+ driveDoStart(signal);
+ }
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ protected final void driveDoStart(Signal signal) throws DeploymentException {
+ Signal stateMonitorSignal = createStateMonitorSignal(signal);
+ doStart(stateMonitorSignal);
+ }
+
+ protected Signal createStateMonitorSignal(Signal signal) {
+ return new StateMonitorSignal(signal);
+ }
+
+ private final class StateMonitorSignal implements Signal {
+
+ private final Signal signal;
+
+ public StateMonitorSignal(Signal signal) {
+ this.signal = signal;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void signalSuccessfulCompletion() {
+ try {
+ asyncStartSucceeded();
+ AbstractInstallArtifact.signalSuccessfulCompletion(this.signal);
+ } catch (DeploymentException de) {
+ signalFailure(de);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void signalFailure(Throwable cause) {
+ asyncStartFailed(cause);
+ handleFailure(cause);
+ }
+
+ private void handleFailure(Throwable cause) {
+ try {
+ stop();
+ } catch (DeploymentException de) {
+ AbstractInstallArtifact.this.logger.error("Stop failed", de);
+ }
+ AbstractInstallArtifact.signalFailure(this.signal, cause);
+ }
+
+ }
+
+ protected static void signalSuccessfulCompletion(Signal signal) {
+ if (signal != null) {
+ signal.signalSuccessfulCompletion();
+ }
+ }
+
+ protected static void signalFailure(Signal signal, Throwable e) {
+ if (signal != null) {
+ signal.signalFailure(e);
+ }
+ }
+
+ /**
+ * Perform the actual start of this {@link InstallArtifact} and drive the given {@link Signal} on successful or
+ * unsuccessful completion.
+ *
+ * @param signal the <code>Signal</code> to be driven
+ * @throws DeploymentException if the start fails synchronously
+ */
+ protected abstract void doStart(Signal signal) throws DeploymentException;
+
+ private final void asyncStartSucceeded() throws DeploymentException {
+ pushThreadContext();
+ try {
+ this.artifactStateMonitor.onStarted(this);
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ private final void asyncStartFailed(Throwable cause) {
+ pushThreadContext();
+ try {
+ this.artifactStateMonitor.onStartFailed(this, cause);
+ } catch (DeploymentException e) {
+ logger.error(String.format("listener for %s threw DeploymentException", this), e);
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop() throws DeploymentException {
+ if (this.getState().equals(State.ACTIVE)) {
+ pushThreadContext();
+ try {
+ this.artifactStateMonitor.onStopping(this);
+ try {
+ doStop();
+ this.artifactStateMonitor.onStopped(this);
+ } catch (DeploymentException e) {
+ this.artifactStateMonitor.onStopFailed(this, e);
+ }
+ } finally {
+ popThreadContext();
+ }
+ }
+ }
+
+ /**
+ * @see stop
+ */
+ protected abstract void doStop() throws DeploymentException;
+
+ /**
+ * {@inheritDoc}
+ */
+ public void uninstall() throws DeploymentException {
+ if (getState().equals(State.STARTING) || getState().equals(State.ACTIVE) || getState().equals(State.RESOLVED)
+ || getState().equals(State.INSTALLED)) {
+ pushThreadContext();
+ try {
+ if (getState().equals(State.ACTIVE) || getState().equals(State.STARTING)) {
+ stop();
+ }
+ this.artifactStateMonitor.onUninstalling(this);
+ try {
+ doUninstall();
+ this.artifactStateMonitor.onUninstalled(this);
+ } catch (DeploymentException e) {
+ this.artifactStateMonitor.onUninstallFailed(this, e);
+ }
+ } finally {
+ this.artifactStorage.delete();
+ popThreadContext();
+ }
+ }
+ }
+
+ /**
+ * @see uninstall
+ */
+ protected abstract void doUninstall() throws DeploymentException;
+
+ /**
+ * {@inheritDoc}
+ */
+ public final ArtifactFS getArtifactFS() {
+ return this.artifactStorage.getArtifactFS();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return this.identity.toString();
+ }
+
+ /**
+ * Push the thread context including any application trace name and thread context class loader. The caller is
+ * responsible for calling <code>popThreadContext</code>.
+ */
+ public void pushThreadContext() {
+ // There is no default thread context. Subclasses must override to provide one.
+ }
+
+ /**
+ * Pop a previously pushed thread context.
+ */
+ public void popThreadContext() {
+ // There is no default thread context. Subclasses must override to provide one.
+ }
+
+ protected final ArtifactStateMonitor getStateMonitor() {
+ return this.artifactStateMonitor;
+ }
+
+ /**
+ * @return false
+ */
+ public boolean refresh() throws DeploymentException {
+ try {
+ this.isRefreshing = true;
+ this.eventLogger.log(DeployerLogEvents.REFRESHING, getType(), getName(), getVersion());
+ this.artifactStorage.synchronize();
+
+ boolean refreshed = doRefresh();
+
+ if (refreshed) {
+ this.eventLogger.log(DeployerLogEvents.REFRESHED, getType(), getName(), getVersion());
+ } else {
+ this.artifactStorage.rollBack();
+ this.eventLogger.log(DeployerLogEvents.REFRESH_FAILED, getType(), getName(), getVersion());
+ }
+
+ return refreshed;
+ } catch (DeploymentException de) {
+ this.eventLogger.log(DeployerLogEvents.REFRESH_FAILED, de, getType(), getName(), getVersion());
+ throw de;
+ } finally {
+ this.isRefreshing = false;
+ }
+ }
+
+ protected abstract boolean doRefresh() throws DeploymentException;
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String getProperty(@NonNull String name) {
+ return this.properties.get(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final Set<String> getPropertyNames() {
+ HashSet<String> propertyNames = new HashSet<String>(this.properties.keySet());
+ return Collections.unmodifiableSet(propertyNames);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String setProperty(String name, String value) {
+ return this.properties.put(name, value);
+ }
+
+ public Map<String, String> getDeploymentProperties() {
+ return this.deploymentProperties;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final String getRepositoryName() {
+ return this.repositoryName;
+ }
+
+ /**
+ * @param tree
+ * @throws DeploymentException
+ */
+ public void setTree(Tree<InstallArtifact> tree) throws DeploymentException {
+ synchronized (this.monitor) {
+ this.tree = tree;
+ }
+ }
+
+ public final Tree<InstallArtifact> getTree() {
+ synchronized (this.monitor) {
+ return this.tree;
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStateMonitor.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStateMonitor.java
new file mode 100644
index 00000000..446fad91
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStateMonitor.java
@@ -0,0 +1,324 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactState;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListener;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact.State;
+import org.osgi.framework.BundleContext;
+
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkUtils;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiServiceHolder;
+
+/**
+ * {@link ArtifactStateMonitor} logs {@link InstallArtifact} state changes and notifies
+ * {@link InstallArtifactLifecycleListener InstallArtifactLifecycleListeners}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class ArtifactStateMonitor {
+
+ private final BundleContext bundleContext;
+
+ private final ArtifactState artifactState;
+
+ private ArtifactStateMonitor(BundleContext bundleContext, ArtifactState artifactState) {
+ this.bundleContext = bundleContext;
+ this.artifactState = artifactState;
+ }
+
+ public ArtifactStateMonitor(BundleContext bundleContext) {
+ this(bundleContext, new ArtifactState());
+ }
+
+ /**
+ * Returns the current state of the install artifact according to the events which have occurred.
+ *
+ * @return the {@link State} of this {@link ArtifactStateMonitor}
+ */
+ public State getState() {
+ return this.artifactState.getState();
+ }
+
+ public void setState(State state) {
+ switch (state) {
+ case ACTIVE:
+ this.artifactState.setActive();
+ break;
+ case INITIAL:
+ this.artifactState.setInitial();
+ break;
+ case INSTALLED:
+ this.artifactState.setInstalled();
+ break;
+ case INSTALLING:
+ this.artifactState.setInstalling();
+ break;
+ case RESOLVED:
+ this.artifactState.setResolved();
+ break;
+ case RESOLVING:
+ this.artifactState.setResolving();
+ break;
+ case STARTING:
+ this.artifactState.setStarting();
+ break;
+ case STOPPING:
+ this.artifactState.setStopping();
+ break;
+ case UNINSTALLED:
+ this.artifactState.setUninstalled();
+ break;
+ case UNINSTALLING:
+ this.artifactState.setUninstalling();
+ break;
+ }
+ }
+
+ public void onInstalling(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setInstalling()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onInstalling(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onInstallFailed(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setInitial()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onInstallFailed(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onInstalled(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setInstalled()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onInstalled(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onResolving(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setResolving()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onResolving(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onResolveFailed(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setInstalled()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onResolveFailed(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onResolved(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setResolved()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onResolved(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public boolean onStarting(InstallArtifact installArtifact) throws DeploymentException {
+ boolean stateChanged = this.artifactState.setStarting();
+ if (stateChanged) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onStarting(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ return stateChanged;
+ }
+
+ public void onStartFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onStartFailed(installArtifact, cause);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+
+ public void onStarted(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setActive()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onStarted(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onStopping(InstallArtifact installArtifact) {
+ if (this.artifactState.setStopping()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onStopping(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onStopFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException {
+ if (this.artifactState.setActive()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onStopFailed(installArtifact, cause);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onStopped(InstallArtifact installArtifact) {
+ if (this.artifactState.setResolved()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onStopped(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onUnresolved(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setInstalled()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onUnresolved(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onUninstalling(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setUninstalling()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onUninstalling(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onUninstallFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException {
+ if (this.artifactState.setResolved()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onUninstallFailed(installArtifact, cause);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ public void onUninstalled(InstallArtifact installArtifact) throws DeploymentException {
+ if (this.artifactState.setUninstalled()) {
+ List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders = getListenerHolders();
+ try {
+ for (InstallArtifactLifecycleListener listener : getListeners(listenerHolders)) {
+ listener.onUninstalled(installArtifact);
+ }
+ } finally {
+ ungetListeners(listenerHolders);
+ }
+ }
+ }
+
+ private List<OsgiServiceHolder<InstallArtifactLifecycleListener>> getListenerHolders() {
+ return OsgiFrameworkUtils.getServices(this.bundleContext, InstallArtifactLifecycleListener.class);
+ }
+
+ private List<InstallArtifactLifecycleListener> getListeners(List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders) {
+ List<InstallArtifactLifecycleListener> listeners;
+ listeners = new ArrayList<InstallArtifactLifecycleListener>(listenerHolders.size());
+
+ for (OsgiServiceHolder<InstallArtifactLifecycleListener> listenerHolder : listenerHolders) {
+ listeners.add(listenerHolder.getService());
+ }
+ return listeners;
+ }
+
+ private void ungetListeners(List<OsgiServiceHolder<InstallArtifactLifecycleListener>> listenerHolders) {
+ for (OsgiServiceHolder<InstallArtifactLifecycleListener> listenerHolder : listenerHolders) {
+ this.bundleContext.ungetService(listenerHolder.getServiceReference());
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStorage.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStorage.java
new file mode 100644
index 00000000..89cc6185
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStorage.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.net.URI;
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+
+
+public interface ArtifactStorage {
+
+ void synchronize();
+
+ void synchronize(URI sourceUri);
+
+ void rollBack();
+
+ void delete();
+
+ ArtifactFS getArtifactFS();
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStorageFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStorageFactory.java
new file mode 100644
index 00000000..d68e2ed9
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ArtifactStorageFactory.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.File;
+
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+
+
+/**
+ * An {@link ArtifactStorageFactory} can be used to create {@link ArtifactStorage} instances.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations <strong>must</strong> be thread-safe.
+ *
+ */
+public interface ArtifactStorageFactory {
+
+ /**
+ * Creates a new <code>ArtifactStorage</code> for the artifact in the given <code>File</code>. The artifact has the
+ * supplied <code>artifactIdentity</code>.
+ *
+ * @param artifact The artifact's <code>File</code>.
+ * @param artifactIdentity The identity of the artifact for which storage is being created
+ * @return <code>ArtifactStorage</code> for the artifact.
+ */
+ ArtifactStorage create(File artifact, ArtifactIdentity artifactIdentity);
+
+ /**
+ * Creates a new empty <code>ArtifactStorage</code>. Once created, the artifact must have the supplied
+ * <code>artifactIdentity</code>.
+ *
+ * @param artifactIdentity The identity of the artifact for which storage is being created
+ * @param directoryName The name of the directory for the artifact
+ * @param scopeName The scope in which the artifact resides
+ * @return <code>ArtifactStorage</code> for the artifact.
+ */
+ ArtifactStorage createDirectoryStorage(ArtifactIdentity artifactIdentity, String directoryName);
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/AtomicInstallArtifactLifecycleListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/AtomicInstallArtifactLifecycleListener.java
new file mode 100644
index 00000000..52b08bd3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/AtomicInstallArtifactLifecycleListener.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListenerSupport;
+import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * <code>AtomicInstallArtifactLifecycleListener</code> is an InstallArtifactLifecycleListener which initiates state
+ * changes on an atomic ancestor of an artifact when the artifact changes state.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe
+ *
+ */
+public final class AtomicInstallArtifactLifecycleListener extends InstallArtifactLifecycleListenerSupport {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @Override
+ public void onStarting(InstallArtifact installArtifact) throws DeploymentException {
+ logger.debug("Processing atomic starting event for {}", installArtifact);
+
+ InstallArtifact atomicParent = getAtomicParent(installArtifact);
+ if (atomicParent != null) {
+ if (aChildIsRefreshing(atomicParent)) {
+ logger.info("Atomic starting event not propagated from {} as a child of {} is refreshing.", installArtifact, atomicParent);
+ } else {
+ logger.info("Propagating atomic starting event from {} to {}", installArtifact, atomicParent);
+ atomicParent.start();
+ }
+ } else {
+ logger.info("No atomic parent of {} to propagate starting event to", installArtifact);
+ }
+ }
+
+ @Override
+ public void onStartFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException {
+ logger.debug("Processing atomic start failed (stop) event for {}", installArtifact);
+
+ InstallArtifact atomicParent = getAtomicParent(installArtifact);
+
+ if (atomicParent != null) {
+ if (aChildIsRefreshing(atomicParent)) {
+ logger.info("Atomic start failed event not propagated from {} as a child of {} is refreshing.", installArtifact, atomicParent);
+ } else {
+ logger.info("Propagating atomic start failed (stop) event from {} to {}", installArtifact, atomicParent);
+ atomicParent.stop();
+ }
+ } else {
+ logger.info("No atomic parent of {} to propagate start failed (stop) event to", installArtifact);
+ }
+ }
+
+ @Override
+ public void onStopped(InstallArtifact installArtifact) {
+ logger.debug("Processing atomic stopped event for {}", installArtifact);
+
+ InstallArtifact atomicParent = getAtomicParent(installArtifact);
+
+ if (atomicParent != null) {
+ if (aChildIsRefreshing(atomicParent)) {
+ logger.info("Atomic stopped event not propagated from {} as a child of {} is refreshing.", installArtifact, atomicParent);
+ } else {
+ logger.info("Propagating atomic stopped event from {} to {}", installArtifact, atomicParent);
+ try {
+ atomicParent.stop();
+ } catch (DeploymentException e) {
+ logger.warn("Unable to propagate stopped event to the atomic root due to an exception", e);
+ }
+ }
+ } else {
+ logger.info("No atomic parent of {} to propagate stopped event to", installArtifact);
+ }
+ }
+
+ @Override
+ public void onUninstalled(InstallArtifact installArtifact) throws DeploymentException {
+ logger.debug("Processing atomic uninstalled event for {}", installArtifact);
+
+ InstallArtifact atomicParent = getAtomicParent(installArtifact);
+
+ if (atomicParent != null) {
+ if (aChildIsRefreshing(atomicParent)) {
+ logger.info("Atomic uninstalled event not propagated from {} as a child of {} is refreshing.", installArtifact, atomicParent);
+ } else {
+ logger.info("Propagating atomic uninstalled event from {} to {}", installArtifact, atomicParent);
+ atomicParent.uninstall();
+ }
+ } else {
+ logger.info("No atomic parent of {} to propagate uninstalled event to", installArtifact);
+ }
+ }
+
+ private InstallArtifact getAtomicParent(InstallArtifact installArtifact) {
+ InstallArtifact parent = getParentInstallArtifact(installArtifact);
+ return isAtomicInstallArtifact(parent) ? parent : null;
+ }
+
+ /**
+ * Return true if this is an {@link InstallArtifact} and contains others (in the {@link Tree}) and is an atomic
+ * container.
+ *
+ * @param installArtifact
+ * @return true iff this is an artifact that has the {@link PlanInstallArtifact#isAtomic} attribute equal to true
+ */
+ private static final boolean isAtomicInstallArtifact(InstallArtifact installArtifact) {
+ if (installArtifact instanceof PlanInstallArtifact) {
+ return ((PlanInstallArtifact) installArtifact).isAtomic();
+ }
+ return false;
+ }
+
+ /**
+ * Get the parent {@link InstallArtifact} in the {@link Tree} associated with the {@link InstallArtifact}, if there
+ * is one.
+ *
+ * @param installArtifact to find the parent of
+ * @return the parent artifact in the tree, or null if there isn't one
+ */
+ private static final InstallArtifact getParentInstallArtifact(InstallArtifact installArtifact) {
+ Tree<InstallArtifact> iaTree = installArtifact.getTree();
+ if (iaTree != null) {
+ iaTree = iaTree.getParent();
+ if (iaTree != null) {
+ return iaTree.getValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Determines if <em>any</em> child of this {@link InstallArtifact} has {@link AbstractInstallArtifact#isRefreshing() isRefreshing()} which returns true.
+ *
+ * @param atomicParent whose children are checked
+ * @return true if any child is refreshing, otherwise false.
+ */
+ private static boolean aChildIsRefreshing(InstallArtifact atomicParent) {
+ for (InstallArtifact child : childrenOf(atomicParent)) {
+ if (child instanceof AbstractInstallArtifact) {
+ AbstractInstallArtifact aChild = (AbstractInstallArtifact) child;
+ if (aChild.isRefreshing()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param parent
+ * @return an array of the children of the parent (which can be zero length)
+ */
+ private static InstallArtifact[] childrenOf(InstallArtifact parent) {
+ List<InstallArtifact> children = new ArrayList<InstallArtifact>();
+ if (parent!=null) {
+ Tree<InstallArtifact> tree = parent.getTree();
+ if (tree!=null) {
+ for(Tree<InstallArtifact> childBranch : tree.getChildren()) {
+ InstallArtifact child = childBranch.getValue();
+ if (child!=null) {
+ children.add(child);
+ }
+ }
+ }
+ }
+ return children.toArray(new InstallArtifact[children.size()]);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigInstallArtifact.java
new file mode 100644
index 00000000..e8eb8f7a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigInstallArtifact.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+
+import org.eclipse.virgo.kernel.core.Signal;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+
+/**
+ * {@link ConfigInstallArtifact} is an {@link InstallArtifact} for a configuration properties file.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class ConfigInstallArtifact extends AbstractInstallArtifact {
+
+ private final StartEngine startEngine;
+
+ private final RefreshEngine refreshEngine;
+
+ private final StopEngine stopEngine;
+
+ ConfigInstallArtifact(@NonNull ArtifactIdentity identity, @NonNull ArtifactStorage artifactStorage, @NonNull StartEngine startEngine,
+ @NonNull RefreshEngine refreshEngine, @NonNull StopEngine stopEngine, @NonNull ArtifactStateMonitor artifactStateMonitor,
+ String repositoryName, EventLogger eventLogger) throws DeploymentException {
+ super(identity, artifactStorage, artifactStateMonitor, repositoryName, eventLogger);
+
+ this.startEngine = startEngine;
+ this.refreshEngine = refreshEngine;
+ this.stopEngine = stopEngine;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void doStop() throws DeploymentException {
+ this.stopEngine.stop(getIdentity(), getArtifactFS());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean doRefresh() throws DeploymentException {
+ this.refreshEngine.refresh(getIdentity(), getArtifactFS());
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void doUninstall() throws DeploymentException {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected final void doStart(Signal signal) throws DeploymentException {
+ try {
+ this.startEngine.start(getIdentity(), getArtifactFS());
+ signalSuccessfulCompletion(signal);
+ } catch (DeploymentException e) {
+ signalFailure(signal, e);
+ throw e;
+ } catch (RuntimeException e) {
+ signalFailure(signal, e);
+ throw e;
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigInstallArtifactTreeFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigInstallArtifactTreeFactory.java
new file mode 100644
index 00000000..076663a8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigInstallArtifactTreeFactory.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeFactory;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.ThreadSafeArrayListTree;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * {@link ConfigInstallArtifactTreeFactory} is an {@link InstallArtifactTreeFactory} for configuration properties file
+ * {@link InstallArtifact InstallArtifacts}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class ConfigInstallArtifactTreeFactory implements InstallArtifactTreeFactory {
+
+ private static final String PROPERTIES_TYPE = ArtifactIdentityDeterminer.CONFIGURATION_TYPE;
+
+ private final BundleContext bundleContext;
+
+ private final ConfigLifecycleEngine lifecycleEngine;
+
+ private final EventLogger eventLogger;
+
+ ConfigInstallArtifactTreeFactory(BundleContext bundleContext, ConfigurationAdmin configurationAdmin, EventLogger eventLogger) {
+ this.bundleContext = bundleContext;
+ this.lifecycleEngine = new ConfigLifecycleEngine(configurationAdmin);
+ this.eventLogger = eventLogger;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Tree<InstallArtifact> constructInstallArtifactTree(ArtifactIdentity artifactIdentity, ArtifactStorage artifactStorage,
+ Map<String, String> deploymentProperties, String repositoryName) throws DeploymentException {
+ if (PROPERTIES_TYPE.equalsIgnoreCase(artifactIdentity.getType())) {
+ ArtifactStateMonitor artifactStateMonitor = new ArtifactStateMonitor(this.bundleContext);
+ InstallArtifact configInstallArtifact = new ConfigInstallArtifact(artifactIdentity, artifactStorage, this.lifecycleEngine,
+ this.lifecycleEngine, this.lifecycleEngine, artifactStateMonitor, repositoryName, eventLogger);
+ return constructInstallTree(configInstallArtifact);
+ } else {
+ return null;
+ }
+ }
+
+ private Tree<InstallArtifact> constructInstallTree(InstallArtifact rootArtifact) {
+ return new ThreadSafeArrayListTree<InstallArtifact>(rootArtifact);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigLifecycleEngine.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigLifecycleEngine.java
new file mode 100644
index 00000000..6a0d45a3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigLifecycleEngine.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.util.io.IOUtils;
+
+public final class ConfigLifecycleEngine implements StartEngine, RefreshEngine, StopEngine {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final ConfigurationAdmin configurationAdmin;
+
+ public ConfigLifecycleEngine(ConfigurationAdmin configurationAdmin) {
+ this.configurationAdmin = configurationAdmin;
+ }
+
+ public void start(ArtifactIdentity artifactIdentity, ArtifactFS artifactFS) throws StartException {
+ try {
+ updateConfiguration(artifactIdentity, artifactFS);
+ } catch (IOException e) {
+ String message = String.format("Unable to start configuration '%s' with '%s'", artifactIdentity.getName(), artifactFS);
+ logger.error(message);
+ throw new StartException(message, e);
+ }
+ }
+
+ public void refresh(ArtifactIdentity artifactIdentity, ArtifactFS artifactFS) throws RefreshException {
+ try {
+ updateConfiguration(artifactIdentity, artifactFS);
+ } catch (IOException e) {
+ String message = String.format("Unable to refresh configuration '%s' with '%s'", artifactIdentity.getName(), artifactFS);
+ logger.error(message);
+ throw new RefreshException(message, e);
+ }
+ }
+
+ private void updateConfiguration(ArtifactIdentity artifactIdentity, ArtifactFS artifactFS) throws IOException {
+ InputStream inputStream = null;
+ try {
+ inputStream = artifactFS.getEntry("").getInputStream();
+
+ Configuration configuration = getConfiguration(artifactIdentity);
+ configuration.update(getProperties(inputStream));
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ }
+
+ private Properties getProperties(InputStream inputSteam) throws IOException {
+ Properties p = new Properties();
+ p.load(inputSteam);
+ return p;
+ }
+
+ public void stop(ArtifactIdentity artifactIdentity, ArtifactFS artifactFS) throws StopException {
+ try {
+ Configuration configuration = getConfiguration(artifactIdentity);
+ configuration.delete();
+ } catch (IOException e) {
+ String message = String.format("Unable to stop configuration '%s'", artifactIdentity.getName());
+ logger.error(message);
+ throw new StopException(message, e);
+ }
+ }
+
+ private Configuration getConfiguration(ArtifactIdentity artifactIdentity) throws IOException {
+ return this.configurationAdmin.getConfiguration(artifactIdentity.getName());
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/DelegatingServiceRegistryBackedArtifactIdentityDeterminer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/DelegatingServiceRegistryBackedArtifactIdentityDeterminer.java
new file mode 100644
index 00000000..da77941e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/DelegatingServiceRegistryBackedArtifactIdentityDeterminer.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.File;
+
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+/**
+ * An {@link ArtifactIdentityDeterminer} that delegates to the <code>ArtifactTypeDeterminer</code>s available in the
+ * OSGi service registry.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+public class DelegatingServiceRegistryBackedArtifactIdentityDeterminer implements ArtifactIdentityDeterminer {
+
+ private final ServiceTracker serviceTracker;
+
+ /**
+ * @param bundleContext
+ */
+ public DelegatingServiceRegistryBackedArtifactIdentityDeterminer(BundleContext bundleContext) {
+ this.serviceTracker = new ServiceTracker(bundleContext, ArtifactIdentityDeterminer.class.getName(), null);
+ }
+
+ public void init() {
+ this.serviceTracker.open();
+ }
+
+ public void destroy() {
+ this.serviceTracker.close();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ArtifactIdentity determineIdentity(File file, String scopeName) {
+ Object[] services = this.serviceTracker.getServices();
+
+ if (services != null) {
+ for (Object service : services) {
+ if (service != null) {
+ ArtifactIdentity identity = ((ArtifactIdentityDeterminer)service).determineIdentity(file, scopeName);
+ if (identity != null) {
+ return identity;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/InstallArtifactRefreshHandler.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/InstallArtifactRefreshHandler.java
new file mode 100644
index 00000000..f6cafa78
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/InstallArtifactRefreshHandler.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+
+
+/**
+ * A <code>InstallArtifactRefreshHandler</code> is used to handle the refresh of an
+ * {@link InstallArtifact}.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations <strong>must</strong> be thread-safe.
+ *
+ */
+public interface InstallArtifactRefreshHandler {
+
+ /**
+ * Handles refresh of the supplied {@link InstallArtifact}.
+ *
+ * @param installArtifact The <code>InstallArtifact</code> to refresh
+ * @return <code>true</code> if the refresh was successful, otherwise <code>false</code>.
+ */
+ boolean refresh(InstallArtifact installArtifact);
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/LoggingInstallArtifactLifecycleListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/LoggingInstallArtifactLifecycleListener.java
new file mode 100644
index 00000000..b2f24bd7
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/LoggingInstallArtifactLifecycleListener.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.install.artifact.internal;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListener;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+
+/**
+ * {@link LoggingInstallArtifactLifecycleListener} listens for lifecycle events for {@link InstallArtifact
+ * InstallArtifacts} and logs messages.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class LoggingInstallArtifactLifecycleListener implements InstallArtifactLifecycleListener {
+
+ private final EventLogger eventLogger;
+
+ public LoggingInstallArtifactLifecycleListener(EventLogger eventLogger) {
+ this.eventLogger = eventLogger;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onInstallFailed(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.INSTALL_FAILED, installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onInstalled(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.INSTALLED, installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onInstalling(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.INSTALLING, installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onResolveFailed(InstallArtifact installArtifact) {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onResolved(InstallArtifact installArtifact) {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onResolving(InstallArtifact installArtifact) {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStartFailed(InstallArtifact installArtifact, Throwable cause) {
+ logEvent(DeployerLogEvents.START_FAILED, installArtifact, cause);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStarted(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.STARTED, installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStarting(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.STARTING, installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStopFailed(InstallArtifact installArtifact, Throwable cause) {
+ logEvent(DeployerLogEvents.STOP_FAILED, installArtifact, cause);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStopped(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.STOPPED, installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onStopping(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.STOPPING, installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onUnresolved(InstallArtifact installArtifact) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onUninstallFailed(InstallArtifact installArtifact, Throwable cause) {
+ logEvent(DeployerLogEvents.UNINSTALL_FAILED, installArtifact, cause);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onUninstalled(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.UNINSTALLED, installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onUninstalling(InstallArtifact installArtifact) {
+ logEvent(DeployerLogEvents.UNINSTALLING, installArtifact);
+ }
+
+ private void logEvent(DeployerLogEvents event, InstallArtifact installArtifact) {
+ this.eventLogger.log(event, installArtifact.getType(), installArtifact.getName(), installArtifact.getVersion());
+ }
+
+ private void logEvent(DeployerLogEvents event, InstallArtifact installArtifact, Throwable cause) {
+ if (cause == null) {
+ logEvent(event, installArtifact);
+ } else {
+ this.eventLogger.log(event, cause, installArtifact.getType(), installArtifact.getName(), installArtifact.getVersion());
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifact.java
new file mode 100644
index 00000000..d85b6fd6
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifact.java
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.virgo.kernel.artifact.ArtifactSpecification;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.internal.TreeUtils;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeFactory;
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.ArtifactIdentityScoper;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.ScopeNameFactory;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.kernel.shim.scope.ScopeFactory;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.math.OrderedPair;
+
+/**
+ * {@link ParPlanInstallArtifact} is an {@link InstallArtifact} for a PAR file.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class ParPlanInstallArtifact extends StandardPlanInstallArtifact {
+
+ private static final ArrayList<ArtifactSpecification> EMPTY_ARTIFACT_SPECIFICATION_LIST = new ArrayList<ArtifactSpecification>();
+
+ private static final String META_INF_PATH = "//META-INF";
+
+ private final Object monitor = new Object();
+
+ private final InstallArtifactTreeFactory bundleInstallArtifactTreeFactory;
+
+ private final InstallArtifactTreeFactory configInstallArtifactTreeFactory;
+
+ private final ArtifactStorageFactory artifactStorageFactory;
+
+ private final ArtifactIdentityDeterminer artifactIdentityDeterminer;
+
+ private final List<Tree<InstallArtifact>> childInstallArtifacts;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ParPlanInstallArtifact.class);
+
+ public ParPlanInstallArtifact(@NonNull ArtifactIdentity identity, @NonNull ArtifactStorage artifactStorage,
+ @NonNull ArtifactStateMonitor artifactStateMonitor, @NonNull ScopeServiceRepository scopeServiceRepository,
+ @NonNull ScopeFactory scopeFactory, @NonNull EventLogger eventLogger, @NonNull InstallArtifactTreeFactory bundleInstallArtifactTreeFactory,
+ @NonNull InstallArtifactRefreshHandler refreshHandler, String repositoryName,
+ @NonNull InstallArtifactTreeFactory configInstallArtifactTreeFactory, @NonNull ArtifactStorageFactory artifactStorageFactory,
+ @NonNull ArtifactIdentityDeterminer artifactIdentityDeterminer) throws DeploymentException {
+ super(identity, true, true, artifactStorage, artifactStateMonitor, scopeServiceRepository, scopeFactory, eventLogger, refreshHandler,
+ repositoryName, EMPTY_ARTIFACT_SPECIFICATION_LIST);
+
+ this.artifactStorageFactory = artifactStorageFactory;
+ this.configInstallArtifactTreeFactory = configInstallArtifactTreeFactory;
+
+ this.bundleInstallArtifactTreeFactory = bundleInstallArtifactTreeFactory;
+ this.artifactIdentityDeterminer = artifactIdentityDeterminer;
+
+ List<OrderedPair<ArtifactIdentity, ArtifactFSEntry>> childArtifacts = findChildArtifacts(artifactStorage.getArtifactFS());
+ this.childInstallArtifacts = createChildInstallArtifacts(childArtifacts);
+ }
+
+ private List<OrderedPair<ArtifactIdentity, ArtifactFSEntry>> findChildArtifacts(ArtifactFS artifactFS) throws DeploymentException {
+
+ List<OrderedPair<ArtifactIdentity, ArtifactFSEntry>> childArtifacts = new ArrayList<OrderedPair<ArtifactIdentity, ArtifactFSEntry>>();
+
+ ArtifactFSEntry entry = artifactFS.getEntry("/");
+ ArtifactFSEntry[] children = entry.getChildren();
+ if (children.length == 0) {
+ throw new DeploymentException("Failed to find child artifacts in par " + artifactFS);
+ }
+
+ String scopeName = ScopeNameFactory.createScopeName(this.getName(), this.getVersion());
+
+ for (ArtifactFSEntry child : children) {
+ String name = child.getPath();
+ if (!META_INF_PATH.equals(name)) {
+ ArtifactIdentity artifactIdentity = this.artifactIdentityDeterminer.determineIdentity(child.getArtifactFS().getFile(), scopeName);
+ if (artifactIdentity != null) {
+ ArtifactIdentity scopedIdentity = ArtifactIdentityScoper.scopeArtifactIdentity(artifactIdentity);
+ childArtifacts.add(new OrderedPair<ArtifactIdentity, ArtifactFSEntry>(scopedIdentity, child));
+ } else {
+ LOGGER.warn("Skipping entry " + name + " as it is not of a recognized type");
+ }
+ }
+ }
+
+ return childArtifacts;
+ }
+
+ List<Tree<InstallArtifact>> createChildInstallArtifacts(List<OrderedPair<ArtifactIdentity, ArtifactFSEntry>> childArtifacts)
+ throws DeploymentException {
+
+ List<Tree<InstallArtifact>> childInstallArtifacts = new ArrayList<Tree<InstallArtifact>>();
+
+ for (OrderedPair<ArtifactIdentity, ArtifactFSEntry> childArtifact : childArtifacts) {
+
+ Tree<InstallArtifact> subTree = null;
+
+ ArtifactIdentity identity = childArtifact.getFirst();
+ ArtifactFSEntry artifactFs = childArtifact.getSecond();
+
+ if (ArtifactIdentityDeterminer.BUNDLE_TYPE.equals(identity.getType())) {
+ subTree = this.bundleInstallArtifactTreeFactory.constructInstallArtifactTree(identity, createArtifactStorage(artifactFs, identity),
+ null, null);
+ } else if (ArtifactIdentityDeterminer.CONFIGURATION_TYPE.equals(identity.getType())) {
+ subTree = this.configInstallArtifactTreeFactory.constructInstallArtifactTree(identity, createArtifactStorage(artifactFs, identity),
+ null, null);
+ }
+
+ if (subTree == null) {
+ LOGGER.warn("Skipping " + identity + " as " + identity.getType() + " artifacts are not supported within a PAR");
+ } else {
+ childInstallArtifacts.add(subTree);
+ }
+ }
+
+ return childInstallArtifacts;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void beginInstall() throws DeploymentException {
+ super.beginInstall();
+
+ List<Tree<InstallArtifact>> children;
+ synchronized (this.monitor) {
+ children = new ArrayList<Tree<InstallArtifact>>(this.childInstallArtifacts);
+ }
+
+ for (Tree<InstallArtifact> child : children) {
+ ((AbstractInstallArtifact) child.getValue()).beginInstall();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTree(Tree<InstallArtifact> tree) throws DeploymentException {
+ synchronized (this.monitor) {
+ super.setTree(tree);
+ List<Tree<InstallArtifact>> children = tree.getChildren();
+ for (Tree<InstallArtifact> child : this.childInstallArtifacts) {
+ // Add any children that are not already present.
+ if (!isChildPresent(children, child)) {
+ TreeUtils.addChild(tree, child);
+ }
+ }
+ }
+ }
+
+ private static boolean isChildPresent(List<Tree<InstallArtifact>> children, Tree<InstallArtifact> newChild) {
+ InstallArtifact newChildValue = newChild.getValue();
+ for (Tree<InstallArtifact> child : children) {
+ InstallArtifact childValue = child.getValue();
+ if (equalIdentities(childValue, newChildValue)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean equalIdentities(InstallArtifact ia1, InstallArtifact ia2) {
+ ArtifactIdentity id1 = ((AbstractInstallArtifact) ia1).getIdentity();
+ ArtifactIdentity id2 = ((AbstractInstallArtifact) ia2).getIdentity();
+ return id1.equals(id2);
+ }
+
+ private ArtifactStorage createArtifactStorage(ArtifactFSEntry artifactFSEntry, ArtifactIdentity artifactIdentity) {
+ ArtifactStorage innerStorage = this.artifactStorageFactory.create(artifactFSEntry.getArtifactFS().getFile(), artifactIdentity);
+ ArtifactStorage outerStorage = this.artifactStorage;
+ return new DelegatingArtifactStorage(innerStorage, outerStorage);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean refresh(String symbolicName) throws DeploymentException {
+ InstallArtifact childToRefresh = findChild(symbolicName);
+
+ if (childToRefresh == null) {
+ this.eventLogger.log(DeployerLogEvents.REFRESH_REQUEST_FAILED, symbolicName, getType(), getName(), getVersion());
+ throw new DeploymentException("Refresh failed: child '" + symbolicName + "' not found in " + getType() + "(" + getName() + ", "
+ + getVersion() + ")");
+ }
+ return childToRefresh.refresh();
+ }
+
+ private InstallArtifact findChild(String symbolicName) {
+ InstallArtifact childToRefresh = null;
+ List<Tree<InstallArtifact>> children = getTree().getChildren();
+ for (Tree<InstallArtifact> child : children) {
+ InstallArtifact childInstallArtifact = child.getValue();
+ String childName = childInstallArtifact.getName();
+ if (childName.equals(symbolicName) || childName.equals(ScopeNameFactory.createScopeName(getName(), getVersion()) + "-" + symbolicName)) {
+ childToRefresh = childInstallArtifact;
+ break;
+ }
+ }
+ return childToRefresh;
+ }
+
+ private static final class DelegatingArtifactStorage implements ArtifactStorage {
+
+ private final ArtifactStorage delegate;
+
+ private final ArtifactStorage sourceStorage;
+
+ private DelegatingArtifactStorage(ArtifactStorage delegate, ArtifactStorage sourceStorage) {
+ this.delegate = delegate;
+ this.sourceStorage = sourceStorage;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void delete() {
+ this.delegate.delete();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ArtifactFS getArtifactFS() {
+ return this.delegate.getArtifactFS();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void synchronize() {
+ this.sourceStorage.synchronize();
+ this.delegate.synchronize();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void synchronize(URI sourceUri) {
+ this.delegate.synchronize(sourceUri);
+ }
+
+ public void rollBack() {
+ this.delegate.rollBack();
+ this.sourceStorage.rollBack();
+ }
+
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifactFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifactFactory.java
new file mode 100644
index 00000000..30bb6e76
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifactFactory.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.install.artifact.internal;
+
+import org.osgi.framework.BundleContext;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeFactory;
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.kernel.install.artifact.internal.bundle.BundleInstallArtifactTreeFactory;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.kernel.shim.scope.ScopeFactory;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+
+/**
+ * A factory for creating {@link ParPlanInstallArtifact} instances.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class ParPlanInstallArtifactFactory {
+
+ private final EventLogger eventLogger;
+
+ private final BundleContext bundleContext;
+
+ private final BundleInstallArtifactTreeFactory bundleInstallArtifactTreeFactory;
+
+ private final ScopeServiceRepository scopeServiceRepository;
+
+ private final ScopeFactory scopeFactory;
+
+ private final InstallArtifactRefreshHandler refreshHandler;
+
+ private final InstallArtifactTreeFactory configInstallArtifactTreeFactory;
+
+ private final ArtifactStorageFactory artifactStorageFactory;
+
+ private final ArtifactIdentityDeterminer artifactIdentityDeterminer;
+
+ ParPlanInstallArtifactFactory(EventLogger eventLogger, BundleContext bundleContext,
+ BundleInstallArtifactTreeFactory bundleInstallArtifactTreeFactory, ScopeServiceRepository scopeServiceRepository, ScopeFactory scopeFactory,
+ InstallArtifactRefreshHandler refreshHandler, ConfigInstallArtifactTreeFactory configInstallArtifactTreeFactory,
+ ArtifactStorageFactory artifactStorageFactory, ArtifactIdentityDeterminer artifactIdentityDeterminer) {
+ this.eventLogger = eventLogger;
+ this.bundleContext = bundleContext;
+ this.bundleInstallArtifactTreeFactory = bundleInstallArtifactTreeFactory;
+ this.scopeServiceRepository = scopeServiceRepository;
+ this.scopeFactory = scopeFactory;
+ this.refreshHandler = refreshHandler;
+ this.configInstallArtifactTreeFactory = configInstallArtifactTreeFactory;
+ this.artifactStorageFactory = artifactStorageFactory;
+ this.artifactIdentityDeterminer = artifactIdentityDeterminer;
+ }
+
+ ParPlanInstallArtifact createParPlanInstallArtifact(@NonNull ArtifactIdentity artifactIdentity, @NonNull ArtifactStorage artifactStorage, String repositoryName) throws DeploymentException {
+ ArtifactStateMonitor artifactStateMonitor = new ArtifactStateMonitor(this.bundleContext);
+ return new ParPlanInstallArtifact(artifactIdentity, artifactStorage, artifactStateMonitor, scopeServiceRepository, scopeFactory, eventLogger,
+ bundleInstallArtifactTreeFactory, refreshHandler, repositoryName, this.configInstallArtifactTreeFactory,
+ this.artifactStorageFactory, this.artifactIdentityDeterminer);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanInstallArtifactTreeFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanInstallArtifactTreeFactory.java
new file mode 100644
index 00000000..50e00949
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanInstallArtifactTreeFactory.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.install.artifact.internal;
+
+import static org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer.PAR_TYPE;
+import static org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer.PLAN_TYPE;
+
+import java.io.InputStream;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+
+
+import org.eclipse.virgo.kernel.artifact.plan.PlanDescriptor;
+import org.eclipse.virgo.kernel.artifact.plan.PlanReader;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeFactory;
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.kernel.install.artifact.internal.bundle.BundleInstallArtifactTreeFactory;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.kernel.shim.scope.ScopeFactory;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.ThreadSafeArrayListTree;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.io.IOUtils;
+
+/**
+ * {@link PlanInstallArtifactTreeFactory} is an {@link InstallArtifactTreeFactory} for plan {@link InstallArtifact
+ * InstallArtifacts}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class PlanInstallArtifactTreeFactory implements InstallArtifactTreeFactory {
+
+ private final BundleContext bundleContext;
+
+ private final ScopeServiceRepository scopeServiceRepository;
+
+ private final ScopeFactory scopeFactory;
+
+ private final EventLogger eventLogger;
+
+ private final InstallArtifactRefreshHandler refreshHandler;
+
+ private final ParPlanInstallArtifactFactory parFactory;
+
+ public PlanInstallArtifactTreeFactory(@NonNull BundleContext bundleContext, @NonNull ScopeServiceRepository scopeServiceRepository,
+ @NonNull ScopeFactory scopeFactory, @NonNull EventLogger eventLogger,
+ @NonNull BundleInstallArtifactTreeFactory bundleInstallArtifactTreeFactory, @NonNull InstallArtifactRefreshHandler refreshHandler,
+ @NonNull ConfigInstallArtifactTreeFactory configInstallArtifactTreeFactory, @NonNull ArtifactStorageFactory artifactStorageFactory,
+ @NonNull ArtifactIdentityDeterminer artifactIdentityDeterminer) {
+ this.bundleContext = bundleContext;
+ this.scopeServiceRepository = scopeServiceRepository;
+ this.scopeFactory = scopeFactory;
+ this.eventLogger = eventLogger;
+ this.refreshHandler = refreshHandler;
+
+ this.parFactory = new ParPlanInstallArtifactFactory(eventLogger, bundleContext, bundleInstallArtifactTreeFactory, scopeServiceRepository,
+ scopeFactory, refreshHandler, configInstallArtifactTreeFactory, artifactStorageFactory, artifactIdentityDeterminer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Tree<InstallArtifact> constructInstallArtifactTree(ArtifactIdentity identity, ArtifactStorage artifactStorage,
+ Map<String, String> deploymentProperties, String repositoryName) throws DeploymentException {
+ String type = identity.getType();
+ if (PLAN_TYPE.equalsIgnoreCase(type)) {
+ return createPlanTree(identity, artifactStorage, getPlanDescriptor(artifactStorage), repositoryName);
+ } else if (PAR_TYPE.equalsIgnoreCase(type)) {
+ return createParTree(identity, artifactStorage, repositoryName);
+ } else {
+ return null;
+ }
+ }
+
+ private Tree<InstallArtifact> createParTree(ArtifactIdentity artifactIdentity, ArtifactStorage artifactStorage, String repositoryName)
+ throws DeploymentException {
+
+ ParPlanInstallArtifact parArtifact = this.parFactory.createParPlanInstallArtifact(artifactIdentity, artifactStorage, repositoryName);
+ Tree<InstallArtifact> tree = constructInstallTree(parArtifact);
+ parArtifact.setTree(tree);
+ return tree;
+ }
+
+ private PlanDescriptor getPlanDescriptor(ArtifactStorage artifactStorage) throws DeploymentException {
+ InputStream in = null;
+ try {
+ in = artifactStorage.getArtifactFS().getEntry("").getInputStream();
+ PlanDescriptor planDescriptor = new PlanReader().read(in);
+ return planDescriptor;
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ private Tree<InstallArtifact> createPlanTree(ArtifactIdentity artifactIdentity, ArtifactStorage artifactStorage, PlanDescriptor planDescriptor,
+ String repositoryName) throws DeploymentException {
+
+ StandardPlanInstallArtifact planInstallArtifact;
+
+ planInstallArtifact = new StandardPlanInstallArtifact(artifactIdentity, planDescriptor.getAtomic(), planDescriptor.getScoped(),
+ artifactStorage, new ArtifactStateMonitor(this.bundleContext), this.scopeServiceRepository, this.scopeFactory,
+ this.eventLogger, this.refreshHandler, repositoryName, planDescriptor.getArtifactSpecifications());
+
+ Tree<InstallArtifact> tree = constructInstallTree(planInstallArtifact);
+ planInstallArtifact.setTree(tree);
+ return tree;
+ }
+
+ private Tree<InstallArtifact> constructInstallTree(InstallArtifact rootArtifact) {
+ return new ThreadSafeArrayListTree<InstallArtifact>(rootArtifact);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanMemberCollector.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanMemberCollector.java
new file mode 100644
index 00000000..ac26b0cc
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanMemberCollector.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.install.artifact.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.common.Tree.TreeVisitor;
+
+/**
+ * A simple helper class that can be used to collect all of the members of a plan. Collection is performed by visiting
+ * the entire tree beneath the plan.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class PlanMemberCollector {
+
+ /**
+ * Collects all of the members of the given <code>plan</code>, including any nested plans and their members. Note that the
+ * supplied <code>plan</code> will not be included in the returned <code>List</code>.
+ *
+ * @param plan the plan for which the members are to be collected
+ * @return all the members of the plan, not including the plan itself
+ */
+ List<InstallArtifact> collectPlanMembers(PlanInstallArtifact plan) {
+ ArtifactCollectingTreeVisitor visitor = new ArtifactCollectingTreeVisitor(plan);
+ plan.getTree().visit(visitor);
+ return visitor.getMembers();
+ }
+
+ private static final class ArtifactCollectingTreeVisitor implements TreeVisitor<InstallArtifact> {
+
+ private final InstallArtifact root;
+
+ private final List<InstallArtifact> members = new ArrayList<InstallArtifact>();
+
+ private final Object monitor = new Object();
+
+ /**
+ * @param root
+ */
+ public ArtifactCollectingTreeVisitor(InstallArtifact root) {
+ this.root = root;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean visit(Tree<InstallArtifact> tree) {
+ InstallArtifact artifact = tree.getValue();
+
+ if (!root.equals(artifact)) {
+ synchronized (this.monitor) {
+ this.members.add(artifact);
+ }
+ }
+
+ return true;
+ }
+
+ List<InstallArtifact> getMembers() {
+ synchronized (this.monitor) {
+ return new ArrayList<InstallArtifact>(this.members);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanScoper.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanScoper.java
new file mode 100644
index 00000000..99842cfa
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanScoper.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.osgi.framework.Version;
+
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.FatalDeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.BundleInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.Scoper;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.Scoper.DuplicateBundleSymbolicNameException;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.Scoper.DuplicateExportException;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.Scoper.UnsupportedBundleManifestVersionException;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link ApplicationScoper} provides scoping of bundle manifests and Spring services for {@link MultiBundleApplication}
+ * s.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is not thread safe.
+ *
+ */
+final class PlanScoper {
+
+ private static final String SCOPE_SEPARATOR = "-";
+
+ private List<InstallArtifact> scopeMembers;
+
+ private final EventLogger eventLogger;
+
+ private final Scoper scoper;
+
+ private final ServiceScoper serviceScoper;
+
+ private final String scopeName;
+
+ private final Version scopeVersion;
+
+ /**
+ * Create a new {@link PlanScoper}.
+ *
+ * @param scopeMembers the items to be scoped
+ * @param name the scope name
+ * @param version the scope version
+ * @param scopeServiceRepository the {@link StandardScopeServiceRepository}
+ * @param eventLogger an {@link EventLogger}
+ * @throws DeploymentException
+ */
+ public PlanScoper(List<InstallArtifact> scopeMembers, String name, Version version, ScopeServiceRepository scopeServiceRepository,
+ EventLogger eventLogger) throws DeploymentException {
+ this.scopeMembers = scopeMembers;
+ this.scopeName = createScopeName(name, version);
+ this.scopeVersion = version;
+ this.scoper = new Scoper(getBundleManifests(), this.scopeName);
+ this.serviceScoper = new ServiceScoper(this.scopeName, scopeServiceRepository, eventLogger);
+ this.eventLogger = eventLogger;
+ }
+
+ /**
+ * Get the application scope name.
+ *
+ * @return the application scope name
+ */
+ String getScopeName() {
+ return this.scopeName;
+ }
+
+ private String createScopeName(String name, Version version) {
+ String scopeName = name + SCOPE_SEPARATOR + versionToShortString(version);
+ return scopeName;
+ }
+
+ private List<BundleManifest> getBundleManifests() throws DeploymentException {
+ List<BundleManifest> bundleManifests = new ArrayList<BundleManifest>();
+ for (InstallArtifact scopeMember : this.scopeMembers) {
+ if (scopeMember instanceof BundleInstallArtifact) {
+ BundleInstallArtifact bundleInstallArtifact = (BundleInstallArtifact) scopeMember;
+ try {
+ bundleManifests.add(bundleInstallArtifact.getBundleManifest());
+ } catch (IOException e) {
+ throw new DeploymentException("Cannot access bundle manifest for scoping", e);
+ }
+ }
+ }
+ return bundleManifests;
+ }
+
+ /**
+ * Scope the application.
+ *
+ * @throws DeploymentException
+ */
+ void scope() throws DeploymentException {
+ try {
+ // Transform the modules' bundle manifests to scope the OSGi
+ // application.
+ this.scoper.scope();
+ } catch (UnsupportedBundleManifestVersionException ubmve) {
+ // This represents a failure to upgrade the manifest.
+ throw new FatalDeploymentException("Cannot scope a bundle which does not specify a bundle manifest version of at least "
+ + ubmve.getLowestSupportedVersion());
+ } catch (DuplicateBundleSymbolicNameException dbsne) {
+ this.eventLogger.log(DeployerLogEvents.DUPLICATE_BSN_IN_SCOPE, dbsne, this.scopeName, this.scopeVersion, dbsne.getBundleSymbolicName());
+ throw new DeploymentException("More than one bundle in scope '" + this.scopeName + "' version '" + this.scopeVersion
+ + "' has bundle symbolic name '" + dbsne.getBundleSymbolicName() + "'");
+ } catch (DuplicateExportException dee) {
+ String packageName = dee.getPackageName();
+ String exporters = dee.getExporters();
+ this.eventLogger.log(DeployerLogEvents.DUPLICATE_PACKAGE_DURING_SCOPING, dee, this.scopeName, this.scopeVersion, packageName, exporters);
+ throw new DeploymentException("Package '" + packageName + "' exported by more than one bundle [" + exporters + "] in scope '"
+ + this.scopeName + "' version '" + this.scopeVersion + "'");
+ }
+ this.serviceScoper.scope(getBundleArtifacts());
+ }
+
+ private Set<ArtifactFS> getBundleArtifacts() {
+ Set<ArtifactFS> bundleArtifacts = new HashSet<ArtifactFS>();
+ for (InstallArtifact scopeMember : this.scopeMembers) {
+ if (scopeMember instanceof BundleInstallArtifact) {
+ ArtifactFS artifactFS = scopeMember.getArtifactFS();
+ bundleArtifacts.add(artifactFS);
+ }
+ }
+ return bundleArtifacts;
+ }
+
+ private static String versionToShortString(Version version) {
+ String result = version.toString();
+ while (result.endsWith(".0")) {
+ result = result.substring(0, result.length() - 2);
+ }
+ return result;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/RefreshEngine.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/RefreshEngine.java
new file mode 100644
index 00000000..152fd6ce
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/RefreshEngine.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+
+
+public interface RefreshEngine {
+
+ void refresh(ArtifactIdentity artifactIdentity, ArtifactFS artifactFS) throws RefreshException;
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/RefreshException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/RefreshException.java
new file mode 100644
index 00000000..95e30463
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/RefreshException.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+
+public class RefreshException extends DeploymentException {
+
+ private static final long serialVersionUID = -7828141025340833008L;
+
+ public RefreshException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public RefreshException(String message) {
+ super(message);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ServiceScoper.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ServiceScoper.java
new file mode 100644
index 00000000..1865bb54
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ServiceScoper.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.jar.JarFile;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.xml.XmlValidationModeDetector;
+
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.io.IOUtils;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+
+/**
+ * Generates the service model in the {@link StandardScopeServiceRepository} for a bundle in a given scope.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Not thread safe.
+ *
+ */
+final class ServiceScoper {
+
+ private static final String SPRING_CONFIG_DIR = "META-INF/spring/";
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final XmlValidationModeDetector xmlValidationModeDetector = new XmlValidationModeDetector();
+
+ private final String scopeName;
+
+ private final ScopeServiceRepository repository;
+
+ private final EventLogger eventLogger;
+
+ /**
+ * Creates a new <code>ServiceScoper</code> for the supplied scope name.
+ *
+ * @param scopeName supplied
+ * @param scopeServiceRepository the {@link StandardScopeServiceRepository}.
+ * @param eventLogger logger for events
+ */
+ public ServiceScoper(String scopeName, ScopeServiceRepository scopeServiceRepository, EventLogger eventLogger) {
+ this.scopeName = scopeName;
+ this.repository = scopeServiceRepository;
+ this.eventLogger = eventLogger;
+ }
+
+ /**
+ * Scopes the application's services.
+ *
+ * @param modules set of stored artifacts to search for configurations
+ * @throws DeploymentException if configuration files or manifests are not well-formed
+ */
+ public void scope(Set<ArtifactFS> modules) throws DeploymentException {
+ Map<ArtifactFS, List<ArtifactFSEntry>> configFiles = new HashMap<ArtifactFS, List<ArtifactFSEntry>>();
+ for (ArtifactFS moduleData : modules) {
+ configFiles.put(moduleData, findConfigFiles(moduleData));
+ }
+ doScope(configFiles);
+ }
+
+ private List<ArtifactFSEntry> findConfigFiles(ArtifactFS bundleData) throws DeploymentException {
+ List<ArtifactFSEntry> configFiles = new ArrayList<ArtifactFSEntry>();
+
+ ArtifactFSEntry entry = bundleData.getEntry(SPRING_CONFIG_DIR);
+ if (entry.exists()) {
+ try {
+ configFiles.addAll(findConfigFiles(bundleData, entry));
+ } catch (IOException e) {
+ throw new DeploymentException("Unable to read Spring config files.", e);
+ }
+ }
+
+ return configFiles;
+ }
+
+ private List<ArtifactFSEntry> findConfigFiles(ArtifactFS bundleData, ArtifactFSEntry entry) throws IOException {
+ ArtifactFSEntry[] children = entry.getChildren();
+ List<ArtifactFSEntry> configFiles = new ArrayList<ArtifactFSEntry>();
+ for (ArtifactFSEntry e : children) {
+ if (e.isDirectory()) {
+ configFiles.addAll(findConfigFiles(bundleData, e));
+ } else if (e.getPath().endsWith(".xml")) {
+ try {
+ InputStream is = e.getInputStream();
+ int validationMode;
+ try {
+ validationMode = xmlValidationModeDetector.detectValidationMode(is);
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+ if (validationMode != XmlValidationModeDetector.VALIDATION_DTD) {
+ configFiles.add(e);
+ } else {
+ logger.debug("Skipping entry '{}' as it uses a DTD.", e);
+ }
+ } catch (IOException ioe) {
+ logger.debug("Unexpected error detecting validation mode of entry '{}'", ioe, e);
+ configFiles.add(e);
+ }
+ }
+ }
+ return configFiles;
+ }
+
+ /**
+ * Re-scope the service exports and service references of the given {@link ArtifactFS}.
+ *
+ * @param bundleData the {@link ArtifactFS} to be re-scoped.
+ * @throws DeploymentException
+ */
+ public void rescope(ArtifactFS bundleData) throws DeploymentException {
+ Map<ArtifactFS, List<ArtifactFSEntry>> configFiles = new HashMap<ArtifactFS, List<ArtifactFSEntry>>();
+ configFiles.put(bundleData, findConfigFiles(bundleData));
+ doScope(configFiles);
+ }
+
+ /**
+ * Updates the {@link StandardScopeServiceRepository} with the service information from the given config files.
+ *
+ * @param scopeName the name of the scope.
+ * @param configFiles the config files to scope.
+ * @throws DeploymentException
+ */
+ private void doScope(Map<ArtifactFS, List<ArtifactFSEntry>> configFiles) throws DeploymentException {
+ SpringConfigServiceModelScanner scanner = new SpringConfigServiceModelScanner(this.scopeName, this.repository, this.eventLogger);
+ Map<ArtifactFS, BundleManifest> manifests = loadBundleManifests(configFiles.keySet());
+ for (Entry<ArtifactFS, List<ArtifactFSEntry>> entry : configFiles.entrySet()) {
+ BundleManifest bundleManifest = manifests.get(entry.getKey());
+ for (ArtifactFSEntry configFile : entry.getValue()) {
+ InputStream is = configFile.getInputStream();
+ try {
+ scanner.scanConfigFile(bundleManifest.getBundleSymbolicName().getSymbolicName(), bundleManifest.getBundleVersion(),
+ configFile.getPath(), is);
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+ }
+ }
+ }
+
+ private Map<ArtifactFS, BundleManifest> loadBundleManifests(Collection<ArtifactFS> modules) throws DeploymentException {
+ Map<ArtifactFS, BundleManifest> result = new HashMap<ArtifactFS, BundleManifest>();
+ for (ArtifactFS module : modules) {
+ if (!result.containsKey(module)) {
+ BundleManifest manifest = loadManifest(module);
+ result.put(module, manifest);
+ }
+ }
+ return result;
+ }
+
+ private BundleManifest loadManifest(ArtifactFS compositeArtifactFS) throws DeploymentException {
+ ArtifactFSEntry entry = compositeArtifactFS.getEntry(JarFile.MANIFEST_NAME);
+ Reader reader = null;
+ try {
+ reader = new InputStreamReader(entry.getInputStream());
+ return BundleManifestFactory.createBundleManifest(reader);
+ } catch (IOException ex) {
+ throw new DeploymentException("Error reading MANIFEST.MF from '" + compositeArtifactFS + "'", ex);
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/SpringConfigServiceModelScanner.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/SpringConfigServiceModelScanner.java
new file mode 100644
index 00000000..acd71afe
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/SpringConfigServiceModelScanner.java
@@ -0,0 +1,222 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.osgi.framework.Version;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.FatalDeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+
+/**
+ * Utility class for parsing Spring config files and populating a {@link StandardScopeServiceRepository}.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe.
+ *
+ */
+final class SpringConfigServiceModelScanner {
+
+ private static final String ATTRIBUTE_REF = "ref";
+
+ private static final String ATTRIBUTE_INTERFACE = "interface";
+
+ private static final String ELEMENT_VALUE = "value";
+
+ private static final String ELEMENT_INTERFACES = "interfaces";
+
+ private static final String ATTRIBUTE_VALUE = ELEMENT_VALUE;
+
+ private static final String ATTRIBUTE_KEY = "key";
+
+ private static final String ELEMENT_ENTRY = "entry";
+
+ private static final String ELEMENT_SERVICE_PROPERTIES = "service-properties";
+
+ private static final String SPRING_DM_NAMESPACE = "http://www.springframework.org/schema/osgi";
+
+ private static final String SPRING_BEANS_NAMESPACE = "http://www.springframework.org/schema/beans";
+
+ private static final String ELEMENT_SERVICE = "service";
+
+ private static final String BEAN_NAME_PROPERTY = "org.springframework.osgi.bean.name";
+
+ private final EventLogger eventLogger;
+
+ private final ScopeServiceRepository repository;
+
+ private final DocumentBuilder documentBuilder;
+
+ private final String scopeName;
+
+ public SpringConfigServiceModelScanner(String scopeName, ScopeServiceRepository repository, EventLogger eventLogger) {
+ this.scopeName = scopeName;
+ this.repository = repository;
+ this.eventLogger = eventLogger;
+ this.documentBuilder = createDocumentBuilder();
+ }
+
+ public void scanConfigFile(String bundleSymbolicName, Version bundleVersion, String configFileName, InputStream stream) throws DeploymentException {
+ Document doc = parseConfigFile(bundleSymbolicName, bundleVersion, configFileName, stream);
+ doScopeServices(doc.getDocumentElement().getChildNodes());
+ }
+
+ private void doScopeServices(NodeList childNodes) {
+ for (int x = 0; x < childNodes.getLength(); x++) {
+ Node node = childNodes.item(x);
+ if (isServiceElement(node)) {
+ parseServiceElement((Element) node);
+ }
+ doScopeServices(node.getChildNodes());
+ }
+ }
+
+ private void parseServiceElement(Element elem) {
+ String[] types = extractInterfaces(elem);
+ Properties properties = extractServiceProperties(elem);
+ this.repository.recordService(this.scopeName, types, properties);
+ }
+
+ /**
+ * Extracts all the interfaces from the supplied <code>reference</code> or <code>service</code> {@link Element}.
+ *
+ * @param e the <code>Element</code> to parse.
+ * @return the interfaces.
+ */
+ private String[] extractInterfaces(Element e) {
+ Set<String> exportedInterfaces = new HashSet<String>();
+ String iface = StringUtils.trimWhitespace(e.getAttribute(ATTRIBUTE_INTERFACE));
+ if (StringUtils.hasText(iface)) {
+ exportedInterfaces.add(iface);
+ } else {
+ NodeList children = e.getChildNodes();
+ for (int y = 0; y < children.getLength(); y++) {
+ Node child = children.item(y);
+ if (child instanceof Element) {
+ if (isInterfacesElement(child)) {
+ Element elem = (Element) child;
+ NodeList intChildren = elem.getChildNodes();
+ for (int i = 0; i < intChildren.getLength(); i++) {
+ Node intChild = intChildren.item(i);
+ if (isValueElement(intChild)) {
+ exportedInterfaces.add(StringUtils.trimWhitespace(intChild.getTextContent()));
+ }
+ }
+ }
+ }
+ }
+ }
+ return exportedInterfaces.toArray(new String[exportedInterfaces.size()]);
+ }
+
+ private Properties extractServiceProperties(Element elem) {
+ NodeList servicePropertiesElems = elem.getElementsByTagNameNS(SPRING_DM_NAMESPACE, ELEMENT_SERVICE_PROPERTIES);
+ Properties p = null;
+ if (servicePropertiesElems.getLength() > 0) {
+ p = new Properties();
+ Node item = servicePropertiesElems.item(0);
+ readServiceProperties((Element) item, p);
+ }
+ p = addStandardServiceProperties(elem, p);
+ return p;
+ }
+
+ private Properties addStandardServiceProperties(Element elem, Properties p) {
+ // The only standard service property in the Spring DM reference manual is "bean name".
+ String beanName = StringUtils.trimWhitespace(elem.getAttribute(ATTRIBUTE_REF));
+ if (StringUtils.hasText(beanName)) {
+ if (p == null) {
+ p = new Properties();
+ }
+ p.setProperty(BEAN_NAME_PROPERTY, beanName);
+ }
+ return p;
+ }
+
+ private void readServiceProperties(Element servicePropertiesElement, Properties serviceProperties) {
+ NodeList childNodes = servicePropertiesElement.getChildNodes();
+ for (int y = 0; y < childNodes.getLength(); y++) {
+ Node child = childNodes.item(y);
+ if (isEntryElement(child)) {
+ Element entry = (Element) child;
+ serviceProperties.setProperty(entry.getAttribute(ATTRIBUTE_KEY), entry.getAttribute(ATTRIBUTE_VALUE));
+ }
+ }
+ }
+
+ private Document parseConfigFile(String bundleSymbolicName, Version bundleVersion, String configFileName, InputStream stream) throws DeploymentException {
+ try {
+ return this.documentBuilder.parse(new InputSource(stream));
+ } catch (SAXException ex) {
+ this.eventLogger.log(DeployerLogEvents.CONFIG_FILE_ERROR, ex, configFileName, bundleSymbolicName, bundleVersion);
+ throw new DeploymentException("Error parsing configuration file '" + configFileName + "'.", ex);
+ } catch (IOException ex) {
+ throw new FatalDeploymentException("Error accessing configuration file '" + configFileName + "'.", ex);
+ }
+ }
+
+ /**
+ * Creates a {@link DocumentBuilder}.
+ *
+ * @return the <code>DocumentBuilder</code>.
+ */
+ private DocumentBuilder createDocumentBuilder() {
+ try {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ return dbf.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new FatalDeploymentException("Unable to create DocumentBuilder - JAXP parser configuration error.", e);
+ }
+ }
+
+ private boolean isValueElement(Node node) {
+ return SPRING_BEANS_NAMESPACE.equals(node.getNamespaceURI()) && ELEMENT_VALUE.equals(node.getLocalName())
+ && node.getNodeType() == Node.ELEMENT_NODE;
+ }
+
+ private boolean isInterfacesElement(Node node) {
+ return SPRING_DM_NAMESPACE.equals(node.getNamespaceURI()) && ELEMENT_INTERFACES.equals(node.getLocalName())
+ && node.getNodeType() == Node.ELEMENT_NODE;
+ }
+
+ private boolean isServiceElement(Node node) {
+ return SPRING_DM_NAMESPACE.equals(node.getNamespaceURI()) && ELEMENT_SERVICE.equals(node.getLocalName())
+ && node.getNodeType() == Node.ELEMENT_NODE;
+ }
+
+ private boolean isEntryElement(Node node) {
+ return SPRING_BEANS_NAMESPACE.equals(node.getNamespaceURI()) && ELEMENT_ENTRY.equals(node.getLocalName())
+ && node.getNodeType() == Node.ELEMENT_NODE;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactIdentityDeterminer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactIdentityDeterminer.java
new file mode 100644
index 00000000..2603feeb
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactIdentityDeterminer.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.File;
+import java.util.Set;
+
+import org.osgi.framework.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.repository.ArtifactBridge;
+import org.eclipse.virgo.repository.ArtifactDescriptor;
+import org.eclipse.virgo.repository.ArtifactGenerationException;
+
+/**
+ * {@link StandardArtifactIdentityDeterminer} is a {@link ArtifactIdentityDeterminer} that can determine basic kernel artifact
+ * identities.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class StandardArtifactIdentityDeterminer implements ArtifactIdentityDeterminer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StandardArtifactIdentityDeterminer.class);
+
+ private static final String JAR_EXTENSION = ".jar";
+
+ private static final String PLAN_EXTENSION = ".plan";
+
+ private static final String PROPERTIES_EXTENSION = ".properties";
+
+ private static final String PAR_EXTENSION = ".par";
+
+ private final Set<ArtifactBridge> bridges;
+
+ public StandardArtifactIdentityDeterminer(Set<ArtifactBridge> bridges) {
+ this.bridges = bridges;
+ }
+
+ private ArtifactIdentity determineIdentityFromFile(File file, String scopeName) {
+ String type = null;
+ String name = null;
+
+ String filename = file.getName();
+
+ name = trimExtension(file);
+
+ if (filename.endsWith(JAR_EXTENSION)) {
+ type = BUNDLE_TYPE;
+ } else if (filename.endsWith(PLAN_EXTENSION)) {
+ type = PLAN_TYPE;
+ } else if (filename.endsWith(PROPERTIES_EXTENSION)) {
+ type = CONFIGURATION_TYPE;
+ } else if (filename.endsWith(PAR_EXTENSION)) {
+ type = PAR_TYPE;
+ }
+
+
+ if (type != null && name != null) {
+ return new ArtifactIdentity(type, name, Version.emptyVersion, scopeName);
+ } else {
+ return null;
+ }
+ }
+
+ private String trimExtension(File file) {
+ String filename = file.getName();
+
+ int lastIndexOf = filename.lastIndexOf('.');
+
+ if (lastIndexOf > 0) {
+ return filename.substring(0, lastIndexOf);
+ } else {
+ return filename;
+ }
+ }
+
+ public ArtifactIdentity determineIdentity(File file, String scopeName) {
+ ArtifactDescriptor artifactDescriptor = null;
+ for (ArtifactBridge artifactBridge : this.bridges) {
+ try {
+ artifactDescriptor = artifactBridge.generateArtifactDescriptor(file);
+ } catch (ArtifactGenerationException e) {
+ LOGGER.warn(String.format("Error occurred while determining the type of an Artifact '%s' with the bridge '%s'.", file,
+ artifactBridge.getClass().getSimpleName()), e);
+ }
+ if (artifactDescriptor != null) {
+ break;
+ }
+ }
+ if (artifactDescriptor == null) {
+ return this.determineIdentityFromFile(file, scopeName);
+ } else {
+ String type = artifactDescriptor.getType();
+ String name = artifactDescriptor.getName();
+ Version version = artifactDescriptor.getVersion();
+
+ name = name == null ? trimExtension(file) : name;
+ version = version == null ? Version.emptyVersion : version;
+
+ return new ArtifactIdentity(type, name, version, scopeName);
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorage.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorage.java
new file mode 100644
index 00000000..ab809af6
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorage.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.install.artifact.internal;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSFactory;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.io.JarUtils;
+import org.eclipse.virgo.util.io.PathReference;
+
+final class StandardArtifactStorage implements ArtifactStorage {
+
+ private static final List<String> JAR_EXTENSIONS = Arrays.asList("jar", "war", "par", "zip");
+
+ private final PathReference sourcePathReference;
+
+ private final PathReference baseStagingPathReference;
+
+ private final PathReference pastStagingPathReference;
+
+ private volatile ArtifactFS artifactFS;
+
+ private final EventLogger eventLogger;
+
+ private final Object monitor = new Object();
+
+ public StandardArtifactStorage(PathReference sourcePathReference, PathReference baseStagingPathReference, ArtifactFSFactory artifactFSFactory,
+ EventLogger eventLogger) {
+ this.sourcePathReference = sourcePathReference;
+
+ this.baseStagingPathReference = baseStagingPathReference;
+ this.pastStagingPathReference = new PathReference(String.format("%s-past", this.baseStagingPathReference.getAbsolutePath()));
+
+ this.eventLogger = eventLogger;
+
+ synchronize();
+ this.artifactFS = artifactFSFactory.create(this.baseStagingPathReference.toFile());
+ }
+
+ public void synchronize() {
+ synchronize(this.sourcePathReference);
+ }
+
+ public ArtifactFS getArtifactFS() {
+ return this.artifactFS;
+ }
+
+ public void synchronize(URI sourceUri) {
+ synchronize(new PathReference(sourceUri));
+ }
+
+ public void rollBack() {
+ synchronized (this.monitor) {
+ unstashContent();
+ }
+ }
+
+ public void delete() {
+ this.baseStagingPathReference.delete(true);
+ }
+
+ private void synchronize(PathReference normalizedSourcePathReference) {
+ synchronized (this.monitor) {
+ stashContent();
+ if (normalizedSourcePathReference != null && !normalizedSourcePathReference.isDirectory()
+ && looksLikeAJar(normalizedSourcePathReference.getName())) {
+ try {
+ JarUtils.unpackTo(normalizedSourcePathReference, this.baseStagingPathReference);
+ } catch (IOException e) {
+ this.eventLogger.log(DeployerLogEvents.JAR_UNPACK_ERROR, e, normalizedSourcePathReference);
+ throw new RuntimeException(String.format("Exception unpacking '%s'", normalizedSourcePathReference), e);
+ }
+ } else if (normalizedSourcePathReference != null) {
+ this.baseStagingPathReference.getParent().createDirectory();
+ normalizedSourcePathReference.copy(this.baseStagingPathReference, true);
+ } else {
+ this.baseStagingPathReference.createDirectory();
+ }
+ }
+ }
+
+ private boolean looksLikeAJar(String name) {
+ String fileName = name.toLowerCase(Locale.ENGLISH);
+
+ int dotLocation = fileName.lastIndexOf('.');
+ if (dotLocation == -1) {
+ return false;
+ }
+ return JAR_EXTENSIONS.contains(fileName.substring(dotLocation + 1));
+ }
+
+ private void stashContent() {
+ if (this.baseStagingPathReference.exists()) {
+ this.pastStagingPathReference.delete(true);
+ this.baseStagingPathReference.copy(this.pastStagingPathReference, true);
+ this.baseStagingPathReference.delete(true);
+ }
+ }
+
+ private void unstashContent() {
+ if (this.pastStagingPathReference.exists()) {
+ this.baseStagingPathReference.delete(true);
+ this.pastStagingPathReference.copy(this.baseStagingPathReference, true);
+ this.pastStagingPathReference.delete(true);
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorageFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorageFactory.java
new file mode 100644
index 00000000..9808a9b8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorageFactory.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.File;
+
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSFactory;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.io.PathReference;
+
+/**
+ * Standard implementation of {@link ArtifactStorage} that creates storage locations using a nested directory structure
+ * of the form scope/type/name/version.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe
+ *
+ */
+public final class StandardArtifactStorageFactory implements ArtifactStorageFactory {
+
+ private static final String DEPLOYER_STAGING_DIRECTORY = "staging";
+
+ private final PathReference workDirectory;
+
+ private final ArtifactFSFactory artifactFSFactory;
+
+ private final EventLogger eventLogger;
+
+ public StandardArtifactStorageFactory(PathReference workDirectory, ArtifactFSFactory artifactFSFactory, EventLogger eventLogger) {
+ this.workDirectory = workDirectory;
+ this.artifactFSFactory = artifactFSFactory;
+ this.eventLogger = eventLogger;
+ }
+
+ public ArtifactStorage create(File file, ArtifactIdentity artifactIdentity) {
+ PathReference sourcePathReference = new PathReference(file);
+ PathReference stagingPathReference = createStagingPathReference(artifactIdentity, file.getName());
+
+ return new StandardArtifactStorage(sourcePathReference, stagingPathReference, this.artifactFSFactory, this.eventLogger);
+ }
+
+ public ArtifactStorage createDirectoryStorage(ArtifactIdentity artifactIdentity, String directoryName) {
+ PathReference stagingPathReference = createStagingPathReference(artifactIdentity, directoryName);
+ stagingPathReference.createDirectory();
+
+ return new StandardArtifactStorage(null, stagingPathReference, this.artifactFSFactory, this.eventLogger);
+ }
+
+ private PathReference createStagingPathReference(ArtifactIdentity artifactIdentity, String name) {
+ return this.workDirectory.newChild(DEPLOYER_STAGING_DIRECTORY).newChild(normalizeScopeName(artifactIdentity.getScopeName())).newChild(
+ artifactIdentity.getType()).newChild(artifactIdentity.getName()).newChild(artifactIdentity.getVersion().toString()).newChild(name);
+ }
+
+ private String normalizeScopeName(String scopeName) {
+ return scopeName == null ? "global" : scopeName;
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactRefreshHandler.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactRefreshHandler.java
new file mode 100644
index 00000000..d53def9e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactRefreshHandler.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyBundleDependenciesException;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.environment.InstallEnvironmentFactory;
+import org.eclipse.virgo.kernel.install.pipeline.Pipeline;
+import org.eclipse.virgo.util.common.Tree;
+
+
+/**
+ * A helper class for handling refresh of an {@link InstallArtifact}. When an <code>InstallArtifact</code>
+ * is refreshed, its tree is passed through the refresh {@link Pipeline}.
+
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe
+ *
+ */
+final class StandardInstallArtifactRefreshHandler implements InstallArtifactRefreshHandler {
+
+ private final InstallEnvironmentFactory installEnvironmentFactory;
+
+ private final Pipeline refreshPipeline;
+
+ StandardInstallArtifactRefreshHandler(InstallEnvironmentFactory installEnvironmentFactory, Pipeline refreshPipeline) {
+ this.installEnvironmentFactory = installEnvironmentFactory;
+ this.refreshPipeline = refreshPipeline;
+ }
+
+ public boolean refresh(InstallArtifact installArtifact) {
+ Tree<InstallArtifact> tree = installArtifact.getTree();
+
+ boolean refreshed = true;
+ try {
+ this.refreshPipeline.process(tree, this.installEnvironmentFactory.createInstallEnvironment(installArtifact));
+ } catch (UnableToSatisfyBundleDependenciesException _) {
+ refreshed = false;
+ } catch (DeploymentException _) {
+ refreshed = false;
+ }
+ return refreshed;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactTreeInclosure.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactTreeInclosure.java
new file mode 100644
index 00000000..1c10d94a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardInstallArtifactTreeInclosure.java
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.io.File;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+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.artifact.ArtifactSpecification;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.ApplicationDeployer.DeploymentOptions;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeFactory;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeInclosure;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.ArtifactIdentityScoper;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.repository.Repository;
+import org.eclipse.virgo.repository.RepositoryAwareArtifactDescriptor;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+/**
+ * {@link StandardInstallArtifactTreeInclosure} is a default implementation of {@link InstallArtifactTreeInclosure} that
+ * can create with bundles, configuration files, and plans.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public final class StandardInstallArtifactTreeInclosure implements InstallArtifactTreeInclosure {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final EventLogger eventLogger;
+
+ private final BundleContext bundleContext;
+
+ private final Repository repository;
+
+ private final ArtifactStorageFactory artifactStorageFactory;
+
+ private final ArtifactIdentityDeterminer artifactIdentityDeterminer;
+
+ public StandardInstallArtifactTreeInclosure(@NonNull ArtifactStorageFactory artifactStorageFactory, @NonNull BundleContext bundleContext,
+ @NonNull Repository repository, @NonNull EventLogger eventLogger, @NonNull ArtifactIdentityDeterminer artifactIdentityDeterminer) {
+ this.repository = repository;
+ this.artifactStorageFactory = artifactStorageFactory;
+ this.eventLogger = eventLogger;
+ this.bundleContext = bundleContext;
+ this.artifactIdentityDeterminer = artifactIdentityDeterminer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Tree<InstallArtifact> createInstallTree(ArtifactSpecification specification) throws DeploymentException {
+ return createInstallTree(specification, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Tree<InstallArtifact> createInstallTree(ArtifactSpecification specification, String scopeName) throws DeploymentException {
+ String type = specification.getType();
+ String name = specification.getName();
+ VersionRange versionRange = specification.getVersionRange();
+ RepositoryAwareArtifactDescriptor artifactDescriptor = this.repository.get(type, name, versionRange);
+ if (artifactDescriptor == null) {
+ this.eventLogger.log(DeployerLogEvents.ARTIFACT_NOT_FOUND, type, name, versionRange, this.repository.getName());
+ throw new DeploymentException(type + " '" + name + "' version '" + versionRange + "' not found");
+ }
+
+ URI artifactURI = artifactDescriptor.getUri();
+
+ ArtifactIdentity identity = new ArtifactIdentity(type, name, artifactDescriptor.getVersion(), scopeName);
+ identity = ArtifactIdentityScoper.scopeArtifactIdentity(identity);
+
+ ArtifactStorage artifactStorage = this.artifactStorageFactory.create(new File(artifactURI), identity);
+
+ Tree<InstallArtifact> installArtifactTree = constructInstallArtifactTree(identity, specification.getProperties(), artifactStorage,
+ artifactDescriptor.getRepositoryName());
+ return installArtifactTree;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Tree<InstallArtifact> createInstallTree(File sourceFile) throws DeploymentException {
+
+ if (!sourceFile.exists()) {
+ throw new DeploymentException(sourceFile + " does not exist");
+ }
+
+ ArtifactIdentity artifactIdentity = determineIdentity(sourceFile);
+
+ ArtifactStorage artifactStorage = null;
+ try {
+ artifactStorage = this.artifactStorageFactory.create(sourceFile, artifactIdentity);
+
+ Tree<InstallArtifact> installArtifactTree = constructInstallArtifactTree(artifactIdentity, null, artifactStorage, null);
+ return installArtifactTree;
+ } catch (DeploymentException e) {
+ artifactStorage.delete();
+ throw e;
+ } catch (Exception e) {
+ throw new DeploymentException(e.getMessage(), e);
+ }
+ }
+
+ private ArtifactIdentity determineIdentity(File file) throws DeploymentException {
+ ArtifactIdentity artifactIdentity = this.artifactIdentityDeterminer.determineIdentity(file, null);
+
+ if (artifactIdentity == null) {
+ this.eventLogger.log(DeployerLogEvents.INDETERMINATE_ARTIFACT_TYPE, file);
+ throw new DeploymentException("Cannot determine the artifact identity of the file '" + file + "'");
+ }
+
+ return artifactIdentity;
+ }
+
+ private Tree<InstallArtifact> constructInstallArtifactTree(ArtifactIdentity identity, Map<String, String> deploymentProperties,
+ ArtifactStorage artifactStorage, String repositoryName) throws DeploymentException {
+ Tree<InstallArtifact> tree = null;
+ List<OsgiServiceHolder<InstallArtifactTreeFactory>> iatfHolders = OsgiFrameworkUtils.getServices(this.bundleContext,
+ InstallArtifactTreeFactory.class);
+
+ for (OsgiServiceHolder<InstallArtifactTreeFactory> iatfHolder : iatfHolders) {
+
+ InstallArtifactTreeFactory iatf = iatfHolder.getService();
+ try {
+ if (iatf != null) {
+ tree = iatf.constructInstallArtifactTree(identity, artifactStorage, deploymentProperties, repositoryName);
+ if (tree != null) {
+ break;
+ }
+ }
+ } finally {
+ this.bundleContext.ungetService(iatfHolder.getServiceReference());
+ }
+ }
+
+ if (tree == null) {
+ this.eventLogger.log(DeployerLogEvents.MISSING_ARTIFACT_FACTORY, identity.getType(), identity.getName(), identity.getVersion());
+ throw new DeploymentException("Cannot create InstallArtifact for '" + identity + "'");
+ }
+ return tree;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Tree<InstallArtifact> recoverInstallTree(File sourceFile, DeploymentOptions deploymentOptions) {
+ ArtifactStorage artifactStorage = null;
+ if (deploymentOptions.getRecoverable() && (!deploymentOptions.getDeployerOwned() || sourceFile.exists())) {
+ try {
+ ArtifactIdentity artifactIdentity = determineIdentity(sourceFile);
+ artifactStorage = this.artifactStorageFactory.create(sourceFile, artifactIdentity);
+ Tree<InstallArtifact> installArtifactTree = constructInstallArtifactTree(artifactIdentity, null, artifactStorage, null);
+
+ return installArtifactTree;
+ } catch (RuntimeException e) {
+ if (artifactStorage != null) {
+ artifactStorage.delete();
+ }
+ this.logger.error(String.format("An error occurred during recovery of artefact '%s'", sourceFile), e);
+ throw e;
+ } catch (DeploymentException e) {
+ artifactStorage.delete();
+ this.logger.warn(String.format("An error occurred during recovery of artefact '%s'", sourceFile), e);
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void updateStagingArea(File sourceFile, ArtifactIdentity identity) throws DeploymentException {
+ this.artifactStorageFactory.create(sourceFile, identity).synchronize();
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardPlanInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardPlanInstallArtifact.java
new file mode 100644
index 00000000..81debd94
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardPlanInstallArtifact.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.virgo.kernel.artifact.ArtifactSpecification;
+import org.eclipse.virgo.kernel.core.Signal;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.internal.SignalJunction;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.ArtifactIdentityScoper;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.kernel.shim.scope.Scope;
+import org.eclipse.virgo.kernel.shim.scope.ScopeFactory;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * {@link StandardPlanInstallArtifact} is the standard implementation of {@link PlanInstallArtifact}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public class StandardPlanInstallArtifact extends AbstractInstallArtifact implements PlanInstallArtifact {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StandardPlanInstallArtifact.class);
+
+ private final Object monitor = new Object();
+
+ private final ScopeServiceRepository scopeServiceRepository;
+
+ private final ScopeFactory scopeFactory;
+
+ private final InstallArtifactRefreshHandler refreshHandler;
+
+ private final boolean atomic;
+
+ private final boolean scoped;
+
+ private final List<ArtifactSpecification> artifactSpecifications;
+
+ protected final EventLogger eventLogger;
+
+ private Scope applicationScope;
+
+ protected StandardPlanInstallArtifact(@NonNull ArtifactIdentity artifactIdentity, boolean atomic, boolean scoped, @NonNull ArtifactStorage artifactStorage,
+ @NonNull ArtifactStateMonitor artifactStateMonitor, @NonNull ScopeServiceRepository scopeServiceRepository,
+ @NonNull ScopeFactory scopeFactory, @NonNull EventLogger eventLogger, @NonNull InstallArtifactRefreshHandler refreshHandler,
+ String repositoryName, List<ArtifactSpecification> artifactSpecifications) throws DeploymentException {
+ super(artifactIdentity, artifactStorage, artifactStateMonitor, repositoryName, eventLogger);
+
+ policeNestedScopes(artifactIdentity, scoped, eventLogger);
+
+ this.scopeServiceRepository = scopeServiceRepository;
+ this.scopeFactory = scopeFactory;
+ this.eventLogger = eventLogger;
+ this.refreshHandler = refreshHandler;
+ this.atomic = atomic;
+ this.scoped = scoped;
+ this.artifactSpecifications = artifactSpecifications;
+ }
+
+ private void policeNestedScopes(ArtifactIdentity artifactIdentity, boolean scoped, EventLogger eventLogger) throws DeploymentException {
+ if (artifactIdentity.getScopeName() != null && scoped) {
+ eventLogger.log(DeployerLogEvents.NESTED_SCOPES_NOT_SUPPORTED, artifactIdentity.getType(), ArtifactIdentityScoper.getUnscopedName(artifactIdentity), artifactIdentity.getVersion(), artifactIdentity.getScopeName());
+ throw new DeploymentException("Nested scope detected", true);
+ }
+ }
+
+ protected final List<Tree<InstallArtifact>> getChildrenSnapshot() {
+ List<Tree<InstallArtifact>> children = new ArrayList<Tree<InstallArtifact>>();
+ synchronized (this.monitor) {
+ children.addAll(getTree().getChildren());
+ }
+ return children;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected final void doStart(Signal signal) throws DeploymentException {
+ List<Tree<InstallArtifact>> children = getChildrenSnapshot();
+ int numChildren = children.size();
+
+ // The SignalJunction constructor will drive the signal if numChildren == 0.
+ SignalJunction signalJunction = new SignalJunction(signal, numChildren);
+
+ LOGGER.debug("Created {} that will notify {} to track start of {}", new Object[] {signalJunction, signal, this});
+
+ List<Signal> subSignals = signalJunction.getSignals();
+
+ for (int childIndex = 0; childIndex < numChildren && !signalJunction.failed(); childIndex++) {
+ InstallArtifact childArtifact = children.get(childIndex).getValue();
+ Signal subSignal = subSignals.get(childIndex);
+
+ LOGGER.debug("Starting {} with signal {} from {}", new Object[] {childArtifact, subSignal, signalJunction});
+
+ childArtifact.start(subSignal);
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected final void doStop() throws DeploymentException {
+ DeploymentException firstFailure = null;
+ for (Tree<InstallArtifact> child : getChildrenSnapshot()) {
+ try {
+ child.getValue().stop();
+ } catch (DeploymentException e) {
+ firstFailure = e;
+ }
+ }
+ if (firstFailure != null) {
+ throw firstFailure;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected final void doUninstall() throws DeploymentException {
+ deScope(); // TODO this was placed here in copying from the old deployer, but it is insufficient. Need to
+ // consider stop/start/stop etc. for package and service scoping.
+
+ DeploymentException firstFailure = null;
+ for (Tree<InstallArtifact> child : getChildrenSnapshot()) {
+ try {
+ child.getValue().uninstall();
+ } catch (DeploymentException e) {
+ firstFailure = e;
+ }
+ }
+ if (firstFailure != null) {
+ throw firstFailure;
+ }
+ }
+
+ public void scope() throws DeploymentException {
+ if (isScoped()) {
+ List<InstallArtifact> scopeMembers = new PlanMemberCollector().collectPlanMembers(this);
+ PlanScoper planScoper = new PlanScoper(scopeMembers, getName(), getVersion(), this.scopeServiceRepository, this.eventLogger);
+ String scopeName = planScoper.getScopeName();
+
+ synchronized (this.monitor) {
+ this.applicationScope = this.scopeFactory.getApplicationScope(scopeName);
+ // TODO Do we really need to hold this lock while we're driving the planScoper?
+ planScoper.scope();
+ }
+ }
+ }
+
+ private void deScope() {
+ synchronized (this.monitor) {
+ if (this.applicationScope != null) {
+ this.scopeFactory.destroyApplicationScope(this.applicationScope);
+ this.scopeServiceRepository.clearScope(this.applicationScope.getScopeName());
+ this.applicationScope = null;
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean refresh(String symbolicName) throws DeploymentException {
+ return false;
+ }
+
+ protected boolean doRefresh() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean refreshScope() {
+ return this.refreshHandler.refresh(this);
+ }
+
+ @Override
+ public boolean refresh() throws DeploymentException {
+ this.eventLogger.log(DeployerLogEvents.INSTALL_ARTIFACT_REFRESH_NOT_SUPPORTED, getType(), getName(), getVersion(), getType());
+ return false;
+ }
+
+ public final boolean isAtomic() {
+ return this.atomic;
+ }
+
+ public final boolean isScoped() {
+ return this.scoped;
+ }
+
+ public final List<ArtifactSpecification> getArtifactSpecifications() {
+ return this.artifactSpecifications;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardScopeServiceRepository.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardScopeServiceRepository.java
new file mode 100644
index 00000000..b96c49b3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardScopeServiceRepository.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.install.artifact.internal;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
+import org.eclipse.virgo.util.math.Sets;
+
+/**
+ * Tracks the service model for a given named scope. The service model of a named scope defines the set of services that
+ * are statically known to be published by bundles in that named scope. Typically, this reflects the metadata derived
+ * from Spring DM configuration files.
+ *
+ * <p/>
+ *
+ * The service model information is used to determine which service lookups are automatically application scoped.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Threadsafe
+ *
+ */
+final class StandardScopeServiceRepository implements ScopeServiceRepository {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final Map<String, List<Service>> scopeServices = new HashMap<String, List<Service>>();
+
+ private final Object monitor = new Object();
+
+ /**
+ * {@inheritDoc}
+ */
+ public void recordService(String scopeName, String[] types, Dictionary<Object, Object> properties) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Adding service to scope '{}' with service types '{}' and properties '{}'", new Object[] { scopeName,
+ StringUtils.arrayToCommaDelimitedString(types), dictionaryToCommaSeparatedString(properties) });
+ }
+
+ synchronized (this.monitor) {
+ if (properties == null) {
+ properties = new Hashtable<Object, Object>();
+ }
+ setStandardProperties(types, properties);
+ List<Service> servicesForScope = this.scopeServices.get(scopeName);
+ if (servicesForScope == null) {
+ servicesForScope = new ArrayList<Service>();
+ this.scopeServices.put(scopeName, servicesForScope);
+ }
+ servicesForScope.add(new Service(types, properties));
+ }
+ }
+
+ private static String dictionaryToCommaSeparatedString(Dictionary<Object, Object> properties) {
+ StringBuffer propsString = new StringBuffer();
+ if (properties != null) {
+ Enumeration<Object> keys = properties.keys();
+ for (int i = 0; keys.hasMoreElements(); i++) {
+ if (i > 0) {
+ propsString.append(", ");
+ }
+ String key = keys.nextElement().toString();
+ propsString.append(key + "=" + properties.get(key).toString());
+ }
+ }
+ return propsString.toString();
+ }
+
+ private void setStandardProperties(String[] types, Dictionary<Object, Object> properties) {
+ if (properties.get(Constants.OBJECTCLASS) == null) {
+ properties.put(Constants.OBJECTCLASS, types);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean scopeHasMatchingService(String scopeName, String type, String filter) throws InvalidSyntaxException {
+ synchronized (this.monitor) {
+ List<Service> servicesForScope = this.scopeServices.get(scopeName);
+ boolean matches = false;
+ if (servicesForScope != null) {
+ Filter f = (filter == null ? null : FrameworkUtil.createFilter(filter));
+ for (Service service : servicesForScope) {
+ if (service.matches(type, f)) {
+ matches = true;
+ break;
+ }
+ }
+ }
+ return matches;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void clearScope(String scopeName) {
+ synchronized (this.monitor) {
+ this.scopeServices.remove(scopeName);
+
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<String> knownScopes() {
+ Set<String> scopes = new HashSet<String>();
+ synchronized (this.monitor) {
+ scopes.addAll(this.scopeServices.keySet());
+ }
+ return scopes;
+ }
+
+ private static final class Service {
+
+ private final Set<String> types;
+
+ private final Dictionary<Object, Object> properties;
+
+ public Service(String[] types, Dictionary<Object, Object> properties) {
+ this.types = Sets.asSet(types);
+ this.properties = properties;
+ }
+
+ public boolean matches(String type, Filter filter) {
+ if (type == null || this.types.contains(type)) {
+ return filter == null || filter.match(this.properties);
+ } else {
+ return false;
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StartEngine.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StartEngine.java
new file mode 100644
index 00000000..5037eb82
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StartEngine.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+
+
+interface StartEngine {
+
+ void start(ArtifactIdentity artifactIdentity, ArtifactFS artifactFS) throws StartException;
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StartException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StartException.java
new file mode 100644
index 00000000..d60edc01
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StartException.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+
+public class StartException extends DeploymentException {
+
+ private static final long serialVersionUID = -6772961656579791059L;
+
+ public StartException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public StartException(String message) {
+ super(message);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StopEngine.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StopEngine.java
new file mode 100644
index 00000000..cb7488a5
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StopEngine.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+
+
+public interface StopEngine {
+
+ void stop(ArtifactIdentity artifactIdentity, ArtifactFS artifactFS) throws StopException;
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StopException.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StopException.java
new file mode 100644
index 00000000..bb4235f8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StopException.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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.install.artifact.internal;
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+
+public class StopException extends DeploymentException {
+
+ private static final long serialVersionUID = -1125558729622608583L;
+
+ public StopException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public StopException(String message) {
+ super(message);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/TreeRestrictingInstallArtifactLifecycleListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/TreeRestrictingInstallArtifactLifecycleListener.java
new file mode 100644
index 00000000..d4d8bf3e
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/TreeRestrictingInstallArtifactLifecycleListener.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.install.artifact.internal;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListenerSupport;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * {@link TreeRestrictingInstallArtifactLifecycleListener} fails any install which would turn the forest of
+ * {@link InstallArtifact} trees into a more general directed acyclic graph.
+ * <p/>
+ * Before this restriction was put in place, various failures were possible. For example, a shared node could be
+ * uninstalled prematurely.
+ * <p/>
+ * In order to remove this restriction, this class must be deleted, the forest must be changed to a DAG, reference and
+ * possibly other lifecycle counts must be implemented for shared nodes in the DAG, and all algorithms which traverse
+ * from a node to its parent must be generalised to cope with multiple parents.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread safe.
+ *
+ */
+final class TreeRestrictingInstallArtifactLifecycleListener extends InstallArtifactLifecycleListenerSupport {
+
+ private final ConcurrentHashMap<ArtifactIdentity, InstallArtifact> artifactMap = new ConcurrentHashMap<ArtifactIdentity, InstallArtifact>();
+
+ private final EventLogger eventLogger;
+
+ public TreeRestrictingInstallArtifactLifecycleListener(EventLogger eventLogger) {
+ this.eventLogger = eventLogger;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onInstalling(InstallArtifact installArtifact) throws DeploymentException {
+ ArtifactIdentity artifactIdentity = getArtifactIdentity(installArtifact);
+ InstallArtifact oldInstallArtifact = this.artifactMap.putIfAbsent(artifactIdentity, installArtifact);
+
+ if (oldInstallArtifact != null) {
+ InstallArtifact oldRootInstallArtifact = getRoot(oldInstallArtifact);
+
+ this.eventLogger.log(DeployerLogEvents.INSTALL_ARTIFACT_DAG_NOT_SUPPORTED, artifactIdentity.getType(), artifactIdentity.getName(),
+ artifactIdentity.getVersion(), oldRootInstallArtifact.getType(), oldRootInstallArtifact.getName(),
+ oldRootInstallArtifact.getVersion());
+
+ throw new DeploymentException("InstallArtifact " + artifactIdentity + " was installed when "
+ + getArtifactIdentity(oldRootInstallArtifact) + " was installed", true);
+ }
+ }
+
+ private ArtifactIdentity getArtifactIdentity(InstallArtifact installArtifact) {
+ return new ArtifactIdentity(installArtifact.getType(), installArtifact.getName(), installArtifact.getVersion(), installArtifact.getScopeName());
+ }
+
+ private InstallArtifact getRoot(InstallArtifact installArtifact) {
+ InstallArtifact root = installArtifact;
+ Tree<InstallArtifact> rootParent = root.getTree();
+ while (rootParent != null) {
+ root = rootParent.getValue();
+ rootParent = rootParent.getParent();
+ }
+ return root;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onInstallFailed(InstallArtifact installArtifact) throws DeploymentException {
+ remove(installArtifact);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onUninstalled(InstallArtifact installArtifact) throws DeploymentException {
+ remove(installArtifact);
+ }
+
+ private void remove(InstallArtifact installArtifact) {
+ ArtifactIdentity artifactIdentity = getArtifactIdentity(installArtifact);
+ this.artifactMap.remove(artifactIdentity);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriver.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriver.java
new file mode 100644
index 00000000..8a669a19
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriver.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.install.artifact.internal.bundle;
+
+import org.osgi.framework.Bundle;
+
+
+import org.eclipse.virgo.kernel.core.Signal;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactState;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link BundleDriver} monitors the state of a bundle and updates an associated {@link ArtifactState}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations of this class must be thread safe.
+ *
+ */
+public interface BundleDriver {
+
+ /**
+ * Set the bundle associated with this {@link BundleDriver}. If a bundle has already been set, do nothing.
+ *
+ * @param bundle the {@link Bundle} to be associated
+ */
+ void setBundle(Bundle bundle);
+
+ /**
+ * Starts the bundle associated with this {@link BundleDriver} and drives the given {@link Signal} when the start, including any
+ * asynchronous processing, completes either successfully or unsuccessfully.
+ * <p/>
+ * If the start does not involve asynchronous processing, drives the given <code>Signal</code> before returning.
+ * <p/>
+ * Note that the given <code>Signal</code> may be driven before this method returns, after the method has returned,
+ * or possibly never if there is asynchronous processing which never completes. The caller must ensure that the
+ * given <code>Signal</code> is ready to be driven <i>before</i> calling this method.
+ * <p/>
+ * If the caller is not interested in being signalled, it should pass a <code>null</code> <code>Signal</code>.
+ * <p/>
+ * Note: this method behaves as specified above when called multiple times on the same <code>InstallArtifact</code>
+ * with any combination of the same or distinct or <code>null</code> <code>Signals</code>.
+ *
+ * @param signal a <code>Signal</code> that is ready to be driven or <code>null</code> if no signalling is required
+ * @throws DeploymentException
+ */
+ void start(Signal signal) throws DeploymentException;
+
+ /**
+ * Updates the bundle associated with this {@link BundleDriver} using the given {@link BundleManifest} which
+ * could, for example, result from transforming the updated artifact.
+ *
+ * @param manifest the <code>BundleManifest</code>
+ * @return <code>true</code> if and only if the bundle was successfully updated
+ * @throws DeploymentException if the update failed
+ */
+ boolean update(BundleManifest manifest) throws DeploymentException;
+
+ /**
+ * Performs a refresh packages operation specifying the bundle associated with this {@link BundleDriver} and
+ * waits, for a period of time, for the operation to complete before returning.
+ *
+ * @throws DeploymentException if the refresh failed
+ */
+ void refreshBundle() throws DeploymentException;
+
+ /**
+ * Stops the bundle associated with this {@link BundleDriver}. If the bundle is already stopped, does nothing.
+ *
+ * @throws DeploymentException if stopping the bundle fails
+ */
+ void stop() throws DeploymentException;
+
+ /**
+ * Uninstalls the bundle associated with this {@link BundleDriver}.
+ *
+ * @throws DeploymentException if uninstalling the bundle fails
+ */
+ void uninstall() throws DeploymentException;
+
+ /**
+ * Push the thread context including any application trace name and thread context class loader. The caller is
+ * responsible for calling <code>popThreadContext</code>.
+ */
+ void pushThreadContext();
+
+ /**
+ * Pop a previously pushed thread context.
+ */
+ void popThreadContext();
+
+ void trackStart(Signal signal);
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverBundleListener.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverBundleListener.java
new file mode 100644
index 00000000..a2f599fa
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverBundleListener.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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.install.artifact.internal.bundle;
+
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStateMonitor;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.util.math.ConcurrentHashSet;
+
+/**
+ * {@link BundleDriverBundleListener} listens for bundle events and notifies the bundle's {@link ArtifactStateMonitor}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class BundleDriverBundleListener implements SynchronousBundleListener {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final StandardBundleInstallArtifact installArtifact;
+
+ private final Bundle bundle;
+
+ private final ArtifactStateMonitor artifactStateMonitor;
+
+ private final Set<Bundle> solicitedStartBundleSet = new ConcurrentHashSet<Bundle>();
+
+ public BundleDriverBundleListener(@NonNull StandardBundleInstallArtifact installArtifact, @NonNull Bundle bundle,
+ @NonNull ArtifactStateMonitor artifactStateMonitor) {
+ this.installArtifact = installArtifact;
+ this.bundle = bundle;
+ this.artifactStateMonitor = artifactStateMonitor;
+ }
+
+ void addSolicitedStart(Bundle bundle) {
+ this.solicitedStartBundleSet.add(bundle);
+ }
+
+ void removeSolicitedStart(Bundle bundle) {
+ this.solicitedStartBundleSet.remove(bundle);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void bundleChanged(BundleEvent event) {
+ if (event.getBundle() == this.bundle) {
+ try {
+ switch (event.getType()) {
+ case BundleEvent.RESOLVED:
+ artifactStateMonitor.onResolved(this.installArtifact);
+ break;
+ case BundleEvent.LAZY_ACTIVATION:
+ case BundleEvent.STARTING:
+ artifactStateMonitor.onStarting(this.installArtifact);
+ break;
+ case BundleEvent.STARTED:
+ if (!this.solicitedStartBundleSet.contains(this.bundle)) {
+ /*
+ * Track an unsolicited start of the install artifact so that its state transitions are performed
+ * correctly. Solicited starts are tracked by the solicitor.
+ */
+ this.installArtifact.trackStart();
+ }
+ break;
+ case BundleEvent.STOPPING:
+ artifactStateMonitor.onStopping(this.installArtifact);
+ break;
+ case BundleEvent.STOPPED:
+ artifactStateMonitor.onStopped(this.installArtifact);
+ break;
+ case BundleEvent.UNRESOLVED:
+ artifactStateMonitor.onUnresolved(this.installArtifact);
+ break;
+ case BundleEvent.UNINSTALLED:
+ artifactStateMonitor.onUninstalled(this.installArtifact);
+ break;
+ default:
+ break;
+ }
+ } catch (DeploymentException e) {
+ logger.error(String.format("listener for bundle %s threw DeploymentException", this.bundle), e);
+ throw new RuntimeException("percolated listener exception", e);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverFactory.java
new file mode 100644
index 00000000..9a994d8b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverFactory.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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.install.artifact.internal.bundle;
+
+import org.eclipse.virgo.kernel.core.BundleStarter;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStateMonitor;
+import org.eclipse.virgo.kernel.shim.serviceability.TracingService;
+import org.osgi.framework.BundleContext;
+
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFramework;
+import org.eclipse.virgo.kernel.osgi.framework.PackageAdminUtil;
+
+
+/**
+ * A factory for creating {@link BundleDriver} instances.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe
+ *
+ */
+final class BundleDriverFactory {
+
+ private final OsgiFramework osgiFramework;
+
+ private final BundleContext regionBundleContext;
+
+ private final BundleStarter bundleStarter;
+
+ private final TracingService tracingService;
+
+ private final PackageAdminUtil packageAdminUtil;
+
+ public BundleDriverFactory(OsgiFramework osgiFramework, BundleContext regionBundleContext, BundleStarter bundleStarter,
+ TracingService tracingService, PackageAdminUtil packageAdminUtil) {
+ this.osgiFramework = osgiFramework;
+ this.regionBundleContext = regionBundleContext;
+ this.bundleStarter = bundleStarter;
+ this.tracingService = tracingService;
+ this.packageAdminUtil = packageAdminUtil;
+ }
+
+ StandardBundleDriver createBundleDriver(ArtifactIdentity identity, ArtifactStateMonitor artifactStateMonitor) {
+ return new StandardBundleDriver(this.osgiFramework, this.regionBundleContext, this.bundleStarter, this.tracingService, this.packageAdminUtil, identity.getScopeName(), artifactStateMonitor);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverManifestTransformer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverManifestTransformer.java
new file mode 100644
index 00000000..71c7139a
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleDriverManifestTransformer.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.install.artifact.internal.bundle;
+
+import org.eclipse.virgo.kernel.osgi.framework.ManifestTransformer;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link BundleDriverManifestTransformer} is a {@link ManifesTransformer} that transforms a bundle manifest by replacing it with an in-memory version.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class BundleDriverManifestTransformer implements ManifestTransformer {
+
+ private final BundleManifest bundleManifest;
+
+ public BundleDriverManifestTransformer(BundleManifest bundleManifest) {
+ this.bundleManifest = bundleManifest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleManifest transform(BundleManifest bundleManifest) {
+ return this.bundleManifest;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactFactory.java
new file mode 100644
index 00000000..26adadd8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactFactory.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * 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.install.artifact.internal.bundle;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.jar.JarFile;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.kernel.install.artifact.BundleInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStateMonitor;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStorage;
+import org.eclipse.virgo.kernel.install.artifact.internal.InstallArtifactRefreshHandler;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.io.IOUtils;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+
+/**
+ * A factory for creating {@link BundleInstallArtifact} instances.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class BundleInstallArtifactFactory {
+
+ private static final Version DEFAULT_BUNDLE_VERSION = Version.emptyVersion;
+
+ private final BundleContext kernelBundleContext;
+
+ private final InstallArtifactRefreshHandler refreshHandler;
+
+ private final BundleDriverFactory bundleDriverFactory;
+
+ private final EventLogger eventLogger;
+
+ private final ArtifactIdentityDeterminer identityDeterminer;
+
+ BundleInstallArtifactFactory(BundleContext kernelBundleContext, InstallArtifactRefreshHandler refreshHandler,
+ BundleDriverFactory bundleDriverFactory, EventLogger eventLogger, ArtifactIdentityDeterminer identityDeterminer) {
+ this.kernelBundleContext = kernelBundleContext;
+ this.refreshHandler = refreshHandler;
+ this.bundleDriverFactory = bundleDriverFactory;
+ this.eventLogger = eventLogger;
+ this.identityDeterminer = identityDeterminer;
+ }
+
+ BundleInstallArtifact createBundleInstallArtifact(ArtifactIdentity identity, ArtifactStorage artifactStorage, String repositoryName) throws DeploymentException {
+
+ ArtifactStateMonitor artifactStateMonitor = new ArtifactStateMonitor(this.kernelBundleContext);
+
+ StandardBundleDriver bundleDriver = this.bundleDriverFactory.createBundleDriver(identity, artifactStateMonitor);
+
+ BundleManifest bundleManifest = retrieveArtifactFSManifest(artifactStorage.getArtifactFS());
+
+ StandardBundleInstallArtifact bundleInstallArtifact = new StandardBundleInstallArtifact(identity, bundleManifest, artifactStorage, bundleDriver, artifactStateMonitor, this.refreshHandler, repositoryName, this.eventLogger, this.identityDeterminer);
+
+ bundleDriver.setInstallArtifact(bundleInstallArtifact);
+
+ return bundleInstallArtifact;
+ }
+
+ private BundleManifest retrieveArtifactFSManifest(ArtifactFS artifactFS) throws DeploymentException {
+ ArtifactFSEntry manifestEntry = artifactFS.getEntry(JarFile.MANIFEST_NAME);
+ if (manifestEntry != null && manifestEntry.exists()) {
+ try {
+ Reader manifestReader = new InputStreamReader(manifestEntry.getInputStream());
+ try {
+ return BundleManifestFactory.createBundleManifest(manifestReader);
+ } finally {
+ IOUtils.closeQuietly(manifestReader);
+ }
+ } catch (IOException ioe) {
+ throw new DeploymentException("Failed to read manifest for bundle from " + artifactFS, ioe);
+ }
+ } else {
+ return BundleManifestFactory.createBundleManifest();
+ }
+ }
+
+ static Version getVersionFromManifest(BundleManifest bundleManifest) {
+ Version version = bundleManifest.getBundleVersion();
+ return (version == null ? DEFAULT_BUNDLE_VERSION : version);
+ }
+
+ private static String getNameFromManifest(BundleManifest bundleManifest) {
+ return bundleManifest.getBundleSymbolicName().getSymbolicName();
+ }
+
+ static String determineName(BundleManifest bundleManifest, ArtifactFS artifactFS) {
+ String name = getNameFromManifest(bundleManifest);
+ if (name == null) {
+ name = artifactFS.getFile().getName();
+ }
+ return name;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactTreeFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactTreeFactory.java
new file mode 100644
index 00000000..805930a7
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactTreeFactory.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.install.artifact.internal.bundle;
+
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFramework;
+import org.eclipse.virgo.kernel.osgi.framework.PackageAdminUtil;
+
+import org.eclipse.virgo.kernel.core.BundleStarter;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.kernel.install.artifact.BundleInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifactTreeFactory;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStorage;
+import org.eclipse.virgo.kernel.install.artifact.internal.InstallArtifactRefreshHandler;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.kernel.shim.serviceability.TracingService;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.ThreadSafeArrayListTree;
+import org.eclipse.virgo.util.common.Tree;
+
+/**
+ * {@link BundleInstallArtifactTreeFactory} is an {@link InstallArtifactTreeFactory} for {@link BundleInstallArtifact
+ * BundleInstallArtifacts}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+public final class BundleInstallArtifactTreeFactory implements InstallArtifactTreeFactory {
+
+ private final BundleInstallArtifactFactory bundleArtifactFactory;
+
+ public BundleInstallArtifactTreeFactory(@NonNull OsgiFramework osgiFramework, @NonNull BundleContext kernelBundleContext,
+ @NonNull InstallArtifactRefreshHandler refreshHandler, @NonNull BundleStarter bundleStarter, @NonNull TracingService tracingService,
+ @NonNull PackageAdminUtil packageAdminUtil, @NonNull BundleContext regionBundleContext, EventLogger eventLogger, ArtifactIdentityDeterminer identityDeterminer) {
+
+ BundleDriverFactory bundleDriverFactory = new BundleDriverFactory(osgiFramework, regionBundleContext, bundleStarter, tracingService,
+ packageAdminUtil);
+
+ this.bundleArtifactFactory = new BundleInstallArtifactFactory(kernelBundleContext, refreshHandler, bundleDriverFactory, eventLogger, identityDeterminer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Tree<InstallArtifact> constructInstallArtifactTree(ArtifactIdentity identity, ArtifactStorage artifactStorage, Map<String, String> deploymentProperties,
+ String repositoryName) throws DeploymentException {
+ if (ArtifactIdentityDeterminer.BUNDLE_TYPE.equalsIgnoreCase(identity.getType())) {
+ BundleInstallArtifact bundleInstallArtifact = this.bundleArtifactFactory.createBundleInstallArtifact(identity, artifactStorage, repositoryName);
+
+ if (deploymentProperties != null) {
+ bundleInstallArtifact.getDeploymentProperties().putAll(deploymentProperties);
+ }
+
+ Tree<InstallArtifact> tree = constructInstallTree(bundleInstallArtifact);
+ ((StandardBundleInstallArtifact) bundleInstallArtifact).setTree(tree);
+ return tree;
+ } else {
+ return null;
+ }
+ }
+
+ private Tree<InstallArtifact> constructInstallTree(InstallArtifact rootArtifact) {
+ return new ThreadSafeArrayListTree<InstallArtifact>(rootArtifact);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleThreadContextManager.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleThreadContextManager.java
new file mode 100644
index 00000000..21c794d4
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleThreadContextManager.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * 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.install.artifact.internal.bundle;
+
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.kernel.shim.serviceability.TracingService;
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.osgi.framework.BundleClassLoaderUnavailableException;
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFramework;
+
+/**
+ * {@link BundleThreadContextManager} is a utility used by {@link StandardBundleDriver} to manage the thread context
+ * relating to a bundle.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * TODO Document concurrent semantics of BundleThreadContextManager
+ *
+ */
+final class BundleThreadContextManager {
+
+ private static final String WRAPPED_NULL = "WRAPPED_NULL";
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final Object monitor = new Object();
+
+ private final OsgiFramework osgi;
+
+ private final Bundle contextBundle;
+
+ private final String scopeName;
+
+ private final TracingService tracingService;
+
+ private final ThreadLocal<BlockingDeque<ClassLoader>> contextClassLoaderStack = new ThreadLocal<BlockingDeque<ClassLoader>>() {
+
+ @Override
+ protected BlockingDeque<ClassLoader> initialValue() {
+ return new LinkedBlockingDeque<ClassLoader>();
+ }
+ };
+
+ private final ThreadLocal<BlockingDeque<String>> applicationTraceNameStack = new ThreadLocal<BlockingDeque<String>>() {
+
+ @Override
+ protected BlockingDeque<String> initialValue() {
+ return new LinkedBlockingDeque<String>();
+ }
+ };
+
+ public BundleThreadContextManager(@NonNull OsgiFramework osgi, @NonNull Bundle contextBundle, String scopeName,
+ @NonNull TracingService tracingService) {
+ this.osgi = osgi;
+ this.contextBundle = contextBundle;
+ this.scopeName = scopeName;
+ this.tracingService = tracingService;
+ }
+
+ public void pushThreadContext() {
+ synchronized (this.monitor) {
+ pushThreadContextClassLoader();
+ this.applicationTraceNameStack.get().push(wrapNull(this.tracingService.getCurrentApplicationName()));
+ this.tracingService.setCurrentApplicationName(this.scopeName);
+ }
+ }
+
+ private String wrapNull(String string) {
+ return string != null ? string : WRAPPED_NULL;
+ }
+
+ private String unwrapNull(String string) {
+ return WRAPPED_NULL.equals(string) ? null : string;
+ }
+
+ public void popThreadContext() {
+ synchronized (this.monitor) {
+ popThreadContextClassLoader();
+ this.tracingService.setCurrentApplicationName(unwrapNull(this.applicationTraceNameStack.get().pop()));
+ }
+ }
+
+ private void pushThreadContextClassLoader() {
+ Thread currentThread = Thread.currentThread();
+ ClassLoader oldContextClassLoader = currentThread.getContextClassLoader();
+ this.contextClassLoaderStack.get().push(oldContextClassLoader);
+
+ int state = this.contextBundle.getState();
+
+ if (state != Bundle.INSTALLED && state != Bundle.UNINSTALLED) {
+ ClassLoader newContextClassLoader = null;
+ try {
+ newContextClassLoader = this.osgi.getBundleClassLoader(this.contextBundle);
+ } catch (BundleClassLoaderUnavailableException _) {
+ this.logger.info("Bundle class loader not available, it may not be resolved");
+ }
+ if (newContextClassLoader != null) {
+ currentThread.setContextClassLoader(newContextClassLoader);
+ this.logger.info("Thread context class loader '{}' pushed and set to '{}'", oldContextClassLoader, newContextClassLoader);
+ } else {
+ this.logger.info("Thread context class loader not found for bundle '{}'", this.contextBundle.getSymbolicName());
+ }
+ }
+ }
+
+ private void popThreadContextClassLoader() {
+ Thread currentThread = Thread.currentThread();
+ ClassLoader oldContextClassLoader = currentThread.getContextClassLoader();
+ ClassLoader newContextClassLoader = this.contextClassLoaderStack.get().pop();
+ currentThread.setContextClassLoader(newContextClassLoader);
+ this.logger.info("Thread context class loader '{}' popped and set to '{}'", oldContextClassLoader, newContextClassLoader);
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleDriver.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleDriver.java
new file mode 100644
index 00000000..2de961df
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleDriver.java
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * 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.install.artifact.internal.bundle;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+
+import org.eclipse.virgo.kernel.osgi.framework.OsgiFramework;
+import org.eclipse.virgo.kernel.osgi.framework.PackageAdminUtil;
+
+import org.eclipse.virgo.kernel.core.BundleStarter;
+import org.eclipse.virgo.kernel.core.BundleUtils;
+import org.eclipse.virgo.kernel.core.KernelException;
+import org.eclipse.virgo.kernel.core.Signal;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactState;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStateMonitor;
+import org.eclipse.virgo.kernel.serviceability.Assert;
+import org.eclipse.virgo.kernel.shim.serviceability.TracingService;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * {@link StandardBundleDriver} monitors the state of a bundle and keeps the associated {@link ArtifactState} up to
+ * date.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class StandardBundleDriver implements BundleDriver {
+
+ private final Object monitor = new Object();
+
+ private final BundleStarter bundleStarter;
+
+ private final TracingService tracingService;
+
+ private final PackageAdminUtil packageAdminUtil;
+
+ private final BundleContext bundleContext;
+
+ private final OsgiFramework osgi;
+
+ private final ArtifactStateMonitor artifactStateMonitor;
+
+ private volatile BundleThreadContextManager threadContextManager;
+
+ private volatile StandardBundleInstallArtifact installArtifact;
+
+ private volatile BundleDriverBundleListener bundleListener;
+
+ private Bundle bundle;
+
+ private final String applicationTraceName;
+
+ /**
+ * Creates a {@link StandardBundleDriver} for the given {@link Bundle} and {@link ArtifactState}.
+ * @param osgiFramework framework
+ * @param bundleContext context
+ * @param bundleStarter to start bundles
+ * @param tracingService to trace bundle operations
+ * @param packageAdminUtil utilities for package administration
+ */
+ StandardBundleDriver(OsgiFramework osgiFramework, BundleContext bundleContext, BundleStarter bundleStarter, TracingService tracingService, PackageAdminUtil packageAdminUtil, String scopeName, ArtifactStateMonitor artifactStateMonitor) {
+ this.osgi = osgiFramework;
+ this.bundleContext = bundleContext;
+ this.tracingService = tracingService;
+ this.packageAdminUtil = packageAdminUtil;
+ this.bundleStarter = bundleStarter;
+ this.applicationTraceName = scopeName;
+ this.artifactStateMonitor = artifactStateMonitor;
+ }
+
+ public void setInstallArtifact(StandardBundleInstallArtifact installArtifact) {
+ this.installArtifact = installArtifact;
+ }
+
+ public void setBundle(Bundle bundle) {
+ BundleListener bundleListener = null;
+
+ synchronized (this.monitor) {
+ if (this.bundle == null) {
+ this.bundle = bundle;
+ if (this.bundle != null) {
+ this.bundleListener = new BundleDriverBundleListener(this.installArtifact, this.bundle, this.artifactStateMonitor);
+ bundleListener = this.bundleListener;
+ }
+ }
+ }
+
+ if (bundleListener != null) {
+ this.bundleContext.addBundleListener(bundleListener);
+ }
+ }
+
+ public void syncStart() throws KernelException {
+ pushThreadContext();
+ try {
+ this.bundleStarter.start(obtainLocalBundle(), null);
+ } catch (BundleException be) {
+ throw new KernelException("BundleException", be);
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void pushThreadContext() {
+ ensureThreadContextManager();
+ this.threadContextManager.pushThreadContext();
+ }
+
+ public void popThreadContext() {
+ this.threadContextManager.popThreadContext();
+ }
+
+ private void ensureThreadContextManager() {
+ synchronized (this.monitor) {
+ if (this.threadContextManager == null) {
+ this.threadContextManager = new BundleThreadContextManager(this.osgi, this.bundle, this.applicationTraceName, this.tracingService);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start(Signal signal) throws DeploymentException {
+ Bundle bundle = obtainLocalBundle();
+
+ if (!BundleUtils.isFragmentBundle(bundle)) {
+ pushThreadContext();
+ try {
+ startBundle(bundle, signal);
+ } catch (DeploymentException e) {
+ signalFailure(signal, e);
+ throw e;
+ } catch (RuntimeException e) {
+ signalFailure(signal, e);
+ throw e;
+ } finally {
+ popThreadContext();
+ }
+ } else {
+ signalSuccessfulCompletion(signal);
+ }
+
+ }
+
+ private void startBundle(Bundle bundle, Signal signal) throws DeploymentException {
+ this.bundleListener.addSolicitedStart(bundle);
+ try {
+ this.bundleStarter.start(bundle, signal);
+ } catch (BundleException e) {
+ throw new DeploymentException("BundleException", e);
+ } finally {
+ this.bundleListener.removeSolicitedStart(bundle);
+ }
+ }
+
+ protected static void signalFailure(Signal signal, Throwable e) {
+ if (signal != null) {
+ signal.signalFailure(e);
+ }
+ }
+
+ private static void signalSuccessfulCompletion(Signal signal) {
+ if (signal != null) {
+ signal.signalSuccessfulCompletion();
+ }
+ }
+
+ public void syncStart(int options) throws KernelException {
+ Bundle bundle = obtainLocalBundle();
+
+ if (!BundleUtils.isFragmentBundle(bundle)) {
+ pushThreadContext();
+ try {
+ this.bundleStarter.start(obtainLocalBundle(), options, null);
+ } catch (BundleException be) {
+ throw new KernelException("BundleException", be);
+ } finally {
+ popThreadContext();
+ }
+ }
+ }
+
+ public boolean asyncStart(Signal signal) {
+ Bundle bundle = obtainLocalBundle();
+
+ if (!BundleUtils.isFragmentBundle(bundle)) {
+ pushThreadContext();
+ try {
+ this.bundleStarter.start(obtainLocalBundle(), signal);
+ } catch (BundleException be) {
+ // ignore - the bundle starter will have already notified the signal
+ } finally {
+ popThreadContext();
+ }
+ } else
+ signalSuccessfulCompletion(signal);
+ return true;
+ }
+
+ private Bundle obtainLocalBundle() {
+ synchronized (this.monitor) {
+ if (this.bundle == null) {
+ throw new IllegalStateException("bundle not set");
+ }
+ return this.bundle;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean update(BundleManifest bundleManifest) throws DeploymentException {
+ updateBundle(bundleManifest);
+ refreshBundle();
+ return true;
+ }
+
+ private void updateBundle(BundleManifest bundleManifest) throws DeploymentException {
+ if (!isFragment(bundleManifest)) {
+ Bundle bundle = obtainLocalBundle();
+ Assert.isTrue(bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.RESOLVED,
+ "A bundle cannot be updated unless is in INSTALLED or RESOLVED state");
+ try {
+ this.osgi.update(bundle, new BundleDriverManifestTransformer(bundleManifest));
+ } catch (BundleException e) {
+ throw new DeploymentException("Failed to update bundle '" + bundle + "'.", e);
+ }
+ }
+ }
+
+ private static boolean isFragment(BundleManifest bundleManifest) {
+ return bundleManifest.getFragmentHost().getBundleSymbolicName() != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void refreshBundle() throws DeploymentException {
+ Bundle bundle = obtainLocalBundle();
+ this.packageAdminUtil.synchronouslyRefreshPackages(new Bundle[] { bundle });
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop() throws DeploymentException {
+ pushThreadContext();
+ try {
+ obtainLocalBundle().stop();
+ } catch (BundleException e) {
+ throw new DeploymentException("stop failed", e);
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void uninstall() throws DeploymentException {
+ Bundle bundle = obtainLocalBundle();
+
+ pushThreadContext();
+ try {
+ bundle.uninstall();
+ } catch (BundleException e) {
+ throw new DeploymentException("uninstall failed", e);
+ } finally {
+ popThreadContext();
+ }
+
+ BundleListener localBundleListener = this.bundleListener;
+ this.bundleListener = null;
+
+ if (localBundleListener != null) {
+ this.bundleContext.removeBundleListener(localBundleListener);
+ }
+
+ this.packageAdminUtil.synchronouslyRefreshPackages(new Bundle[] {bundle});
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void trackStart(Signal signal) {
+ this.bundleStarter.trackStart(obtainLocalBundle(), signal);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifact.java
new file mode 100644
index 00000000..f4d45c63
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifact.java
@@ -0,0 +1,519 @@
+/*******************************************************************************
+ * 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.install.artifact.internal.bundle;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.kernel.osgi.quasi.QuasiBundle;
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
+import org.eclipse.virgo.kernel.core.Signal;
+import org.eclipse.virgo.kernel.deployer.core.DeployerLogEvents;
+import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
+import org.eclipse.virgo.kernel.deployer.core.internal.BlockingSignal;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+import org.eclipse.virgo.kernel.install.artifact.BundleInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.internal.AbstractInstallArtifact;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStateMonitor;
+import org.eclipse.virgo.kernel.install.artifact.internal.ArtifactStorage;
+import org.eclipse.virgo.kernel.install.artifact.internal.InstallArtifactRefreshHandler;
+import org.eclipse.virgo.kernel.install.artifact.internal.scoping.ArtifactIdentityScoper;
+import org.eclipse.virgo.kernel.serviceability.NonNull;
+import org.eclipse.virgo.medic.eventlog.EventLogger;
+import org.eclipse.virgo.util.common.Tree;
+import org.eclipse.virgo.util.io.FileCopyUtils;
+import org.eclipse.virgo.util.io.IOUtils;
+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;
+
+/**
+ * {@link StandardBundleInstallArtifact} is the default implementation of {@link BundleInstallArtifact}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is thread safe.
+ *
+ */
+final class StandardBundleInstallArtifact extends AbstractInstallArtifact implements BundleInstallArtifact {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private static final String MANIFEST_ENTRY_NAME = "/META-INF/MANIFEST.MF";
+
+ private static final long REFRESH_RESTART_WAIT_PERIOD = 60;
+
+ private final Object monitor = new Object();
+
+ private final ArtifactStorage artifactStorage;
+
+ private final BundleDriver bundleDriver;
+
+ private final InstallArtifactRefreshHandler refreshHandler;
+
+ private final ArtifactIdentityDeterminer identityDeterminer;
+
+ private BundleManifest bundleManifest;
+
+ private QuasiBundle quasiBundle;
+
+ /**
+ * Construct a {@link StandardBundleInstallArtifact} with the given type and {@link ArtifactStorage}, none of which
+ * may be <code>null</code>.
+ *
+ * @param artifactIdentifier
+ * @param bundleManifest
+ * @param artifactStorage the bundle artifact storage
+ * @param bundleDriver a {@link BundleDriver} for manipulating the bundle
+ * @param artifactStateMonitor
+ * @param refreshHandler
+ * @param repositoryName
+ * @param eventLogger
+ * @param identityDeterminer
+ */
+ public StandardBundleInstallArtifact(@NonNull ArtifactIdentity artifactIdentifier, @NonNull BundleManifest bundleManifest,
+ @NonNull ArtifactStorage artifactStorage, @NonNull BundleDriver bundleDriver, @NonNull ArtifactStateMonitor artifactStateMonitor,
+ @NonNull InstallArtifactRefreshHandler refreshHandler, String repositoryName, EventLogger eventLogger, ArtifactIdentityDeterminer identityDeterminer) {
+ super(artifactIdentifier, artifactStorage, artifactStateMonitor, repositoryName, eventLogger);
+
+ this.artifactStorage = artifactStorage;
+ this.bundleManifest = bundleManifest;
+
+ this.bundleDriver = bundleDriver;
+ this.refreshHandler = refreshHandler;
+
+ this.identityDeterminer = identityDeterminer;
+
+ synchronizeBundleSymbolicNameWithIdentity();
+ }
+
+ private void synchronizeBundleSymbolicNameWithIdentity() {
+ BundleManifest bundleManifest = this.bundleManifest;
+ BundleSymbolicName bundleSymbolicName = bundleManifest.getBundleSymbolicName();
+ if (!getName().equals(bundleSymbolicName.getSymbolicName())) {
+ bundleSymbolicName.setSymbolicName(getName());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleManifest getBundleManifest() throws IOException {
+ synchronized (this.monitor) {
+ if (this.bundleManifest == null) {
+ this.bundleManifest = getManifestFromArtifactFS();
+ }
+ return this.bundleManifest;
+ }
+ }
+
+ private BundleManifest getManifestFromArtifactFS() throws IOException {
+ ArtifactFSEntry manifestEntry = this.artifactStorage.getArtifactFS().getEntry(MANIFEST_ENTRY_NAME);
+ if (manifestEntry != null && manifestEntry.exists()) {
+ Reader manifestReader = new InputStreamReader(manifestEntry.getInputStream());
+ try {
+ return BundleManifestFactory.createBundleManifest(manifestReader);
+ } finally {
+ manifestReader.close();
+ }
+ } else {
+ return BundleManifestFactory.createBundleManifest();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public QuasiBundle getQuasiBundle() {
+ synchronized (this.monitor) {
+ return this.quasiBundle;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setQuasiBundle(QuasiBundle quasiBundle) {
+ synchronized (this.monitor) {
+ this.quasiBundle = quasiBundle;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Bundle getBundle() {
+ synchronized (this.monitor) {
+ return this.quasiBundle == null ? null : this.quasiBundle.getBundle();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public State getState() {
+ // Before returning the state, ensure any bundle is set into the bundle state monitor.
+ monitorBundle();
+ return super.getState();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void endInstall() throws DeploymentException {
+ monitorBundle();
+ super.endInstall();
+ }
+
+ private void monitorBundle() {
+ synchronized (this.monitor) {
+ Bundle bundle = getBundle();
+ if (bundle != null) {
+ this.bundleDriver.setBundle(bundle);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void pushThreadContext() {
+ this.bundleDriver.pushThreadContext();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void popThreadContext() {
+ this.bundleDriver.popThreadContext();
+ }
+
+ /**
+ * Track the start of the bundle.
+ */
+ void trackStart() {
+ Signal signal = createStateMonitorSignal(null);
+ this.bundleDriver.trackStart(signal);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void start(Signal signal) throws DeploymentException {
+ /*
+ * Do not call super.start(signal) as it is essential that the starting event is driven under the bundle
+ * lifecycle event so the listeners see a suitable bundle state.
+ */
+ pushThreadContext();
+ try {
+ driveDoStart(signal);
+ } finally {
+ popThreadContext();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void doStart(Signal signal) throws DeploymentException {
+ this.bundleDriver.start(signal);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stop() throws DeploymentException {
+ if (this.getBundle().getState() == Bundle.ACTIVE) {
+ /*
+ * Do not call super.stop() as it is essential that stopping and stopped events are driven under the bundle
+ * lifecycle events so the listeners see a suitable bundle state, however we must ensure that we ignore
+ * requests if the bundle is already stopping to prevent stop being performed more than once.
+ */
+ pushThreadContext();
+ try {
+ doStop();
+ } catch (DeploymentException e) {
+ getStateMonitor().onStopFailed(this, e);
+ } finally {
+ popThreadContext();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void doStop() throws DeploymentException {
+ this.bundleDriver.stop();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void uninstall() throws DeploymentException {
+ super.uninstall();
+ }
+
+ @Override
+ protected void doUninstall() throws DeploymentException {
+ this.bundleDriver.uninstall();
+ }
+
+ private boolean isScoped() {
+ return this.getScopeName() != null;
+ }
+
+ private boolean stopBundleIfNecessary() throws DeploymentException {
+ int bundleState = this.getBundle().getState();
+ boolean stopBundle = bundleState == Bundle.STARTING || bundleState == Bundle.ACTIVE;
+
+ if (stopBundle) {
+ stop();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean completeUpdateAndRefresh(boolean startRequired) {
+ try {
+ boolean refreshed = this.bundleDriver.update(bundleManifest);
+ if (refreshed) {
+ if (startRequired) {
+ BlockingSignal blockingSignal = new BlockingSignal(true);
+ start(blockingSignal);
+ try {
+ refreshed = blockingSignal.checkComplete();
+ } catch (DeploymentException e) {
+ refreshed = false;
+ }
+ }
+ }
+ return refreshed;
+ } catch (Exception _) {
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean doRefresh() throws DeploymentException {
+ BundleManifest currentBundleManifest;
+
+ synchronized (this.monitor) {
+ currentBundleManifest = this.bundleManifest;
+ }
+
+ BundleManifest newBundleManifest;
+
+ try {
+ newBundleManifest = getManifestFromArtifactFS();
+ } catch (IOException ioe) {
+ throw new DeploymentException("Failed to read new bundle manifest during refresh", ioe);
+ }
+
+ ArtifactIdentity newIdentity = this.identityDeterminer.determineIdentity(this.artifactStorage.getArtifactFS().getFile(), getScopeName());
+
+ if (newIdentity == null) {
+ throw new DeploymentException("Failed to determine new identity during refresh");
+ }
+
+ newIdentity = ArtifactIdentityScoper.scopeArtifactIdentity(newIdentity);
+ if (!isNameAndVersionUnchanged(newIdentity)) {
+ return false;
+ }
+
+ /*
+ * To avoid this module's bundle from being stopped and started by each of update and refresh packages, stop it
+ * if necessary and restart it later if we had to stop it.
+ */
+ boolean bundleStopped = stopBundleIfNecessary();
+
+ synchronized (this.monitor) {
+ this.bundleManifest = newBundleManifest;
+ }
+
+ synchronizeBundleSymbolicNameWithIdentity();
+
+ if (!refreshScope()) {
+ synchronized (this.monitor) {
+ this.bundleManifest = currentBundleManifest;
+ }
+ startIfNecessary(bundleStopped);
+ return false;
+ }
+
+ if (isScoped() && !isExportPackageUnchanged(currentBundleManifest, newBundleManifest)) {
+ this.eventLogger.log(DeployerLogEvents.CANNOT_REFRESH_BUNDLE_AS_SCOPED_AND_EXPORTS_CHANGED, getName(), getVersion());
+ synchronized (this.monitor) {
+ this.bundleManifest = currentBundleManifest;
+ }
+ startIfNecessary(bundleStopped);
+ return false;
+ }
+
+ boolean refreshSuccessful = completeUpdateAndRefresh(bundleStopped);
+
+ if (!refreshSuccessful) {
+ synchronized (this.monitor) {
+ this.bundleManifest = currentBundleManifest;
+ }
+ startIfNecessary(bundleStopped);
+ }
+ return refreshSuccessful;
+ }
+
+ private void startIfNecessary(boolean bundleStopped) throws DeploymentException {
+ if (bundleStopped) {
+ BlockingSignal signal = new BlockingSignal(true);
+ start(signal);
+ signal.awaitCompletion(REFRESH_RESTART_WAIT_PERIOD);
+ }
+ }
+
+ private boolean isExportPackageUnchanged(BundleManifest currentBundleManifest, BundleManifest newBundleManifest) {
+ Set<ExportedPackage> previousExportedPackageSet = getExportedPackageSet(currentBundleManifest.getExportPackage().getExportedPackages());
+ Set<ExportedPackage> newExportedPackageSet = getExportedPackageSet(newBundleManifest.getExportPackage().getExportedPackages());
+ return newExportedPackageSet.equals(previousExportedPackageSet);
+ }
+
+ private boolean isNameAndVersionUnchanged(ArtifactIdentity newIdentity) {
+ if (newIdentity.getName().equals(getName())
+ && newIdentity.getVersion().equals(getVersion())) {
+ return true;
+ }
+
+ this.eventLogger.log(DeployerLogEvents.CANNOT_REFRESH_BUNDLE_IDENTITY_CHANGED, getName(), getVersion(), newIdentity.getName(),
+ newIdentity.getVersion());
+ return false;
+ }
+
+ // If this bundle belongs to a plan, run the subtree of any scoped plan containing the bundle through the refresh
+ // subpipeline.
+ private boolean refreshScope() {
+ boolean refreshed;
+
+ PlanInstallArtifact scopedAncestor = getScopedAncestor();
+
+ if (scopedAncestor != null) {
+ refreshed = scopedAncestor.refreshScope();
+ } else {
+ refreshed = this.refreshHandler.refresh(this);
+ }
+ return refreshed;
+ }
+
+ private PlanInstallArtifact getScopedAncestor() {
+ Tree<InstallArtifact> ancestor = getTree().getParent();
+
+ while (ancestor != null) {
+ InstallArtifact ancestorArtifact = ancestor.getValue();
+ PlanInstallArtifact planAncestor = (PlanInstallArtifact)ancestorArtifact;
+ if (planAncestor.isScoped()) {
+ return planAncestor;
+ } else {
+ ancestor = ancestor.getParent();
+ }
+ }
+
+ return null;
+ }
+
+ private Set<ExportedPackage> getExportedPackageSet(List<ExportedPackage> exportedPackages) {
+ Set<ExportedPackage> packageExports = new HashSet<ExportedPackage>();
+ for (ExportedPackage exportPackage : exportedPackages) {
+ packageExports.add(exportPackage);
+ }
+ return packageExports;
+ }
+
+ public void deleteEntry(String targetPath) {
+ deleteEntry(getQuasiBundle().getBundleFile(), targetPath);
+ getArtifactFS().getEntry(targetPath).delete();
+ }
+
+ private void deleteEntry(File root, String path) {
+ if (root.isDirectory()) {
+ File f = new File(root, path);
+ if (f.exists() && !f.delete()) {
+ logger.warn("Unable to delete resource at {}", path);
+ }
+ } else {
+ logger.warn("Unable to delete resource in non-directory location at {}", root.getAbsolutePath());
+ }
+ }
+
+ public void updateEntry(URI inputPath, String targetPath) {
+ updateEntry(getQuasiBundle().getBundleFile(), inputPath, targetPath);
+ updateEntry(getArtifactFS().getEntry(targetPath), inputPath);
+ }
+
+ private void updateEntry(ArtifactFSEntry entry, URI inputPath) {
+ doUpdate(inputPath, entry.getOutputStream(), entry.getArtifactFS().getFile().getAbsolutePath() + File.separatorChar + entry.getPath());
+ }
+
+ private void updateEntry(File root, URI inputPath, String targetPath) {
+ try {
+ if (root.isDirectory()) {
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(new File(root, targetPath));
+ doUpdate(inputPath, out, targetPath);
+ } finally {
+ IOUtils.closeQuietly(out);
+ }
+ } else {
+ logger.warn("Unable to update resource in non-directory location at {}", root.getAbsolutePath());
+ }
+ } catch (Exception e) {
+ logger.warn("Unable to update resource at {} with resource at {}", targetPath, inputPath.toASCIIString());
+ }
+ }
+
+ private void doUpdate(URI input, OutputStream output, String targetPath) {
+ try {
+ InputStream in = input.toURL().openStream();
+ FileCopyUtils.copy(in, output);
+ } catch (Exception e) {
+ logger.warn("Unable to update resource at {} with resource at {}", targetPath, input.toASCIIString());
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/ArtifactIdentityScoper.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/ArtifactIdentityScoper.java
new file mode 100644
index 00000000..84d33b3c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/ArtifactIdentityScoper.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.install.artifact.internal.scoping;
+
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
+import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
+
+
+/**
+ * <code>ArifactIdentityScoper</code> is used to scope {@link ArtifactIdentity} instances.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public class ArtifactIdentityScoper {
+
+ private static final String SCOPE_SEPARATOR = "-";
+
+ /**
+ * Scopes the supplied <code>ArtifactIdentity</code>.
+ *
+ * @param artifactIdentity The <code>ArtifactIdentity</code> to scope
+ * @return The scoped <code>ArtifactIdentity</code>
+ */
+ public static ArtifactIdentity scopeArtifactIdentity(ArtifactIdentity artifactIdentity) {
+
+ String scopeName = artifactIdentity.getScopeName();
+
+ if (scopeName != null && !ArtifactIdentityDeterminer.CONFIGURATION_TYPE.equals(artifactIdentity.getType())) {
+ String scopedName = scopeName + SCOPE_SEPARATOR + artifactIdentity.getName();
+ return new ArtifactIdentity(artifactIdentity.getType(), scopedName, artifactIdentity.getVersion(), scopeName);
+ } else {
+ return artifactIdentity;
+ }
+ }
+
+ /**
+ * Returns the unscoped name of the supplied <code>identity</code>.
+ * @param identity The <code>ArtifactIdentity</code> for which the unscoped name is required
+ * @return The unscoped name
+ */
+ public static String getUnscopedName(ArtifactIdentity identity) {
+ String scopeName = identity.getScopeName();
+ String name = identity.getName();
+
+ if (scopeName != null && name.length() > (scopeName.length() + SCOPE_SEPARATOR.length())) {
+ return name.substring(scopeName.length() + 1);
+ }
+
+ return name;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/ScopeNameFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/ScopeNameFactory.java
new file mode 100644
index 00000000..65b476b3
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/ScopeNameFactory.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.install.artifact.internal.scoping;
+
+import org.osgi.framework.Version;
+
+
+/**
+ * A factory for creating scope names.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public class ScopeNameFactory {
+
+ private static final String SCOPE_SEPARATOR = "-";
+
+ public static String createScopeName(String name, Version version) {
+ String scopeName = name + SCOPE_SEPARATOR + versionToShortString(version);
+ return scopeName;
+ }
+
+ private static String versionToShortString(Version version) {
+ String result = version.toString();
+ while (result.endsWith(".0")) {
+ result = result.substring(0, result.length() - 2);
+ }
+ return result;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/Scoper.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/Scoper.java
new file mode 100644
index 00000000..bb80f008
--- /dev/null
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/scoping/Scoper.java
@@ -0,0 +1,403 @@
+/*******************************************************************************
+ * 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.install.artifact.internal.scoping;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleSymbolicName;
+import org.eclipse.virgo.util.osgi.manifest.DynamicallyImportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.ExportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.FragmentHost;
+import org.eclipse.virgo.util.osgi.manifest.ImportedBundle;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.RequiredBundle;
+
+/**
+ * {@link Scoper} is a utility class for scoping a set of bundles.
+ * <p/>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * This class is not thread safe.
+ *
+ */
+public class Scoper {
+
+ private static final String BUNDLE_SYMBOLIC_NAME_ATTRIBUTE_NAME = "bundle-symbolic-name";
+
+ public static final String SCOPE_SEPARATOR = "-";
+
+ private static final int BUNDLE_MANIFEST_VERSION_FOR_OSGI_R4 = 2;
+
+ private static final String SCOPING_ATTRIBUTE_NAME = "module_scope";
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final List<BundleManifest> bundleManifests;
+
+ private final String scopeName;
+
+ private final String scopePrefix;
+
+ // Map of unscoped package name to package version for all the packages exported by the bundles.
+ private final Map<String, Version> exportedPackages = new HashMap<String, Version>();
+
+ // Map of unscoped bundle symbolic name to bundle version for all the bundles.
+ private final Map<String, Version> bundles = new HashMap<String, Version>();
+
+ /**
+ * @param bundleManifests the manifests of the bundles to be scoped
+ * @param scopeName the name of the scope to be created
+ */
+ public Scoper(List<BundleManifest> bundleManifests, String scopeName) {
+ this.bundleManifests = bundleManifests;
+ this.scopeName = scopeName;
+ this.scopePrefix = scopeName + "-";
+ }
+
+ /**
+ * Scope the bundles by transforming their metadata. This involves adding a mandatory matching attribute to exports
+ * and any corresponding imports and adding a prefix to each bundle's bundle symbolic name and any corresponding
+ * require bundle statements and matching attributes. The value of the mandatory matching attribute and the prefix
+ * are uniquely determined by the application name and version.
+ *
+ * Scoping also checks for ambiguities which could lead to unexpected wirings and throws an exception if these
+ * checks fail. Specifically, each package exported by the bundles must be exported only once and no two bundles may
+ * have the same bundle symbolic name.
+ * @throws UnsupportedBundleManifestVersionException
+ * @throws DuplicateExportException
+ * @throws DuplicateBundleSymbolicNameException
+ */
+ public void scope() throws UnsupportedBundleManifestVersionException, DuplicateExportException, DuplicateBundleSymbolicNameException {
+ scopeReferents();
+ scopeRefe