/******************************************************************************* * Copyright (c) 2008, 2011 VMware Inc. and others * 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 * EclipseSource - Bug 358442 Change InstallArtifact graph from a tree to a DAG *******************************************************************************/ package org.eclipse.virgo.kernel.install.artifact.internal.bundle; import java.io.File; import org.eclipse.virgo.nano.core.AbortableSignal; import org.eclipse.virgo.nano.core.BundleStarter; import org.eclipse.virgo.nano.core.BundleUtils; import org.eclipse.virgo.nano.core.KernelException; import org.eclipse.virgo.nano.core.Signal; import org.eclipse.virgo.nano.deployer.api.core.DeploymentException; import org.eclipse.virgo.kernel.install.artifact.ArtifactState; 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.ArtifactStateMonitor; import org.eclipse.virgo.kernel.osgi.framework.OsgiFramework; import org.eclipse.virgo.kernel.osgi.framework.PackageAdminUtil; import org.eclipse.virgo.nano.serviceability.Assert; import org.eclipse.virgo.nano.shim.serviceability.TracingService; import org.eclipse.virgo.util.common.GraphNode; import org.eclipse.virgo.util.osgi.manifest.BundleManifest; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.BundleListener; /** * {@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 static final String SYNTHETIC_CONTEXT_SUFFIX = "-synthetic.context"; 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, getThreadContextBundle(), this.applicationTraceName, this.tracingService); } } } private Bundle getThreadContextBundle() { if (this.installArtifact != null) { PlanInstallArtifact scopedAncestor = this.installArtifact.getScopedAncestor(); if (scopedAncestor != null) { String syntheticContextBundleSymbolicName = this.installArtifact.getScopeName() + SYNTHETIC_CONTEXT_SUFFIX; for (GraphNode<InstallArtifact> scopedPlanChild : scopedAncestor.getGraph().getChildren()) { InstallArtifact scopedPlanChildArtifact = scopedPlanChild.getValue(); if (scopedPlanChildArtifact instanceof BundleInstallArtifact && syntheticContextBundleSymbolicName.equals(scopedPlanChildArtifact.getName())) { BundleInstallArtifact syntheticContextBundleArtifact = (BundleInstallArtifact) scopedPlanChildArtifact; return syntheticContextBundleArtifact.getBundle(); } } } } return this.bundle; } /** * {@inheritDoc} */ public void start(AbortableSignal 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, AbortableSignal 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(); } } } 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, File location) throws DeploymentException { updateBundle(bundleManifest, location); refreshBundle(); return true; } private void updateBundle(BundleManifest bundleManifest, File location) 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), location); } 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(AbortableSignal signal) { this.bundleStarter.trackStart(obtainLocalBundle(), signal); } }