| /******************************************************************************* |
| * Copyright (c) 2012, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.container; |
| |
| import java.util.Collections; |
| import java.util.EnumSet; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicReference; |
| import org.eclipse.osgi.container.ModuleContainer.ContainerStartLevel; |
| import org.eclipse.osgi.container.ModuleContainerAdaptor.ContainerEvent; |
| import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent; |
| import org.eclipse.osgi.internal.messages.Msg; |
| import org.eclipse.osgi.report.resolution.ResolutionReport; |
| import org.osgi.framework.AdminPermission; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.launch.Framework; |
| import org.osgi.service.resolver.ResolutionException; |
| |
| /** |
| * A special kind of module that represents the system module for the container. Additional |
| * methods are available on the system module for operations that effect the whole container. |
| * For example, initializing the container, restarting and waiting for the container to stop. |
| * @since 3.10 |
| */ |
| public abstract class SystemModule extends Module { |
| private volatile AtomicReference<ContainerEvent> forStop = new AtomicReference<>(); |
| |
| public SystemModule(ModuleContainer container) { |
| super(new Long(0), Constants.SYSTEM_BUNDLE_LOCATION, container, EnumSet.of(Settings.AUTO_START, Settings.USE_ACTIVATION_POLICY), Integer.valueOf(0)); |
| } |
| |
| /** |
| * Initializes the module container |
| * @throws BundleException if an exeption occurred while initializing |
| */ |
| public final void init() throws BundleException { |
| getRevisions().getContainer().checkAdminPermission(getBundle(), AdminPermission.EXECUTE); |
| |
| boolean lockedStarted = false; |
| // Indicate we are in the middle of a start. |
| // This must be incremented before we acquire the STARTED lock the first time. |
| inStart.incrementAndGet(); |
| try { |
| lockStateChange(ModuleEvent.STARTED); |
| lockedStarted = true; |
| getContainer().getAdaptor().initBegin(); |
| checkValid(); |
| if (ACTIVE_SET.contains(getState())) |
| return; |
| getRevisions().getContainer().open(); |
| if (getState().equals(State.INSTALLED)) { |
| // must unlock to avoid out of order locks when multiple unresolved |
| // bundles are started at the same time from different threads |
| unlockStateChange(ModuleEvent.STARTED); |
| lockedStarted = false; |
| ResolutionReport report; |
| try { |
| report = getRevisions().getContainer().resolve(Collections.singletonList((Module) this), true); |
| } finally { |
| lockStateChange(ModuleEvent.STARTED); |
| lockedStarted = true; |
| } |
| // need to check valid again in case someone uninstalled the bundle |
| checkValid(); |
| ResolutionException e = report.getResolutionException(); |
| if (e != null) { |
| if (e.getCause() instanceof BundleException) { |
| throw (BundleException) e.getCause(); |
| } |
| } |
| if (ACTIVE_SET.contains(getState())) |
| return; |
| if (getState().equals(State.INSTALLED)) { |
| String reportMessage = report.getResolutionReportMessage(getCurrentRevision()); |
| throw new BundleException(Msg.Module_ResolveError + reportMessage, BundleException.RESOLVE_ERROR); |
| } |
| } |
| |
| setState(State.STARTING); |
| AtomicReference<ContainerEvent> existingForStop = forStop; |
| if (existingForStop.get() != null) { |
| // There was a previous launch, reset the reference forStop |
| forStop = new AtomicReference<>(); |
| } |
| publishEvent(ModuleEvent.STARTING); |
| try { |
| initWorker(); |
| } catch (Throwable t) { |
| setState(State.STOPPING); |
| publishEvent(ModuleEvent.STOPPING); |
| setState(State.RESOLVED); |
| publishEvent(ModuleEvent.STOPPED); |
| getRevisions().getContainer().close(); |
| if (t instanceof BundleException) { |
| throw (BundleException) t; |
| } |
| throw new BundleException("Error initializing container.", BundleException.ACTIVATOR_ERROR, t); //$NON-NLS-1$ |
| } |
| } finally { |
| getContainer().getAdaptor().initEnd(); |
| if (lockedStarted) { |
| unlockStateChange(ModuleEvent.STARTED); |
| } |
| inStart.decrementAndGet(); |
| } |
| } |
| |
| /** |
| * Waits until the module container has stopped. |
| * @param timeout The amount of time to wait. |
| * @return The container event indicated why the framework stopped |
| * or if there was a time out waiting for stop. |
| * @see Framework#waitForStop(long) |
| * @throws InterruptedException if the thread was interrupted while waiting |
| */ |
| public ContainerEvent waitForStop(long timeout) throws InterruptedException { |
| final boolean waitForever = timeout == 0; |
| final long start = System.currentTimeMillis(); |
| long timeLeft = timeout; |
| AtomicReference<ContainerEvent> stopEvent = null; |
| State currentState = null; |
| boolean stateLocked = false; |
| try { |
| if (timeout == 0) { |
| stateChangeLock.lockInterruptibly(); |
| stateLocked = true; |
| } else { |
| stateLocked = stateChangeLock.tryLock(timeLeft, TimeUnit.MILLISECONDS); |
| } |
| if (stateLocked) { |
| stopEvent = forStop; |
| currentState = getState(); |
| } |
| } finally { |
| if (stateLocked) { |
| stateChangeLock.unlock(); |
| } |
| } |
| |
| if (stopEvent == null || currentState == null) { |
| // Could not lock system module stateChangeLock; timeout |
| return ContainerEvent.STOPPED_TIMEOUT; |
| } |
| if (!ACTIVE_SET.contains(currentState)) { |
| // check if a past event is waiting for us |
| ContainerEvent result = stopEvent.get(); |
| if (result != null) { |
| return result; |
| } |
| // framework must not have even been started yet |
| return ContainerEvent.STOPPED; |
| } |
| synchronized (stopEvent) { |
| do { |
| ContainerEvent result = stopEvent.get(); |
| if (result != null) { |
| return result; |
| } |
| timeLeft = waitForever ? 0 : start + timeout - System.currentTimeMillis(); |
| if (waitForever || timeLeft > 0) { |
| stopEvent.wait(timeLeft); |
| } else { |
| return ContainerEvent.STOPPED_TIMEOUT; |
| } |
| } while (true); |
| } |
| } |
| |
| /** |
| * @throws BundleException |
| */ |
| protected void initWorker() throws BundleException { |
| // Do nothing |
| } |
| |
| @Override |
| public void start(StartOptions... options) throws BundleException { |
| // make sure to init if needed |
| init(); |
| // Always transient |
| super.start(StartOptions.TRANSIENT, StartOptions.USE_ACTIVATION_POLICY); |
| getRevisions().getContainer().adaptor.publishContainerEvent(ContainerEvent.STARTED, this, null); |
| } |
| |
| @Override |
| public void stop(StopOptions... options) throws BundleException { |
| ContainerEvent containerEvent = ContainerEvent.STOPPED_TIMEOUT; |
| // Need to lock the state change lock with no state to prevent |
| // other threads from starting the framework while we are shutting down |
| try { |
| if (stateChangeLock.tryLock(10, TimeUnit.SECONDS)) { |
| try { |
| try { |
| // Always transient |
| super.stop(StopOptions.TRANSIENT); |
| } catch (BundleException e) { |
| getRevisions().getContainer().adaptor.publishContainerEvent(ContainerEvent.ERROR, this, e); |
| // must continue on |
| } |
| if (holdsTransitionEventLock(ModuleEvent.UPDATED)) { |
| containerEvent = ContainerEvent.STOPPED_UPDATE; |
| } else if (holdsTransitionEventLock(ModuleEvent.UNRESOLVED)) { |
| containerEvent = ContainerEvent.STOPPED_REFRESH; |
| } else { |
| containerEvent = ContainerEvent.STOPPED; |
| } |
| getRevisions().getContainer().adaptor.publishContainerEvent(containerEvent, this, null); |
| getRevisions().getContainer().close(); |
| } finally { |
| AtomicReference<ContainerEvent> eventReference = forStop; |
| eventReference.compareAndSet(null, containerEvent); |
| stateChangeLock.unlock(); |
| synchronized (eventReference) { |
| eventReference.notifyAll(); |
| } |
| } |
| } else { |
| throw new BundleException(Msg.SystemModule_LockError); |
| } |
| } catch (InterruptedException e) { |
| getRevisions().getContainer().adaptor.publishContainerEvent(ContainerEvent.ERROR, this, e); |
| throw new BundleException(Msg.Module_LockError + toString(), BundleException.STATECHANGE_ERROR, e); |
| } |
| |
| } |
| |
| /** |
| * Restarts the module container. |
| * @see Framework#update() |
| * @throws BundleException |
| */ |
| public void update() throws BundleException { |
| getContainer().checkAdminPermission(getBundle(), AdminPermission.LIFECYCLE); |
| State previousState; |
| lockStateChange(ModuleEvent.UPDATED); |
| try { |
| previousState = getState(); |
| stop(); |
| } finally { |
| unlockStateChange(ModuleEvent.UPDATED); |
| } |
| // would publish an updated event here but the listener services are down |
| switch (previousState) { |
| case STARTING : |
| init(); |
| break; |
| case ACTIVE : |
| start(); |
| default : |
| break; |
| } |
| } |
| |
| @Override |
| protected void startWorker() throws BundleException { |
| super.startWorker(); |
| ((ContainerStartLevel) getRevisions().getContainer().getFrameworkStartLevel()).doContainerStartLevel(this, ContainerStartLevel.USE_BEGINNING_START_LEVEL); |
| } |
| |
| @Override |
| protected void stopWorker() throws BundleException { |
| super.stopWorker(); |
| ((ContainerStartLevel) getRevisions().getContainer().getFrameworkStartLevel()).doContainerStartLevel(this, 0); |
| } |
| } |