| /******************************************************************************* |
| * Copyright (c) 2003, 2008 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| * Rob Harrop - SpringSource Inc. (bug 247522) |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.baseadaptor; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import org.eclipse.osgi.framework.internal.core.FrameworkProperties; |
| import org.eclipse.osgi.internal.resolver.*; |
| import org.eclipse.osgi.service.resolver.*; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| |
| /** |
| * The StateManager manages the system state for the framework. It also provides the implementation |
| * to the PlatformAdmin service. |
| * <p> |
| * Clients may extend this class. |
| * </p> |
| * @since 3.1 |
| */ |
| public class StateManager implements PlatformAdmin, Runnable { |
| /** |
| * General debug flag |
| */ |
| public static boolean DEBUG = false; |
| /** |
| * Reader debug flag |
| */ |
| public static boolean DEBUG_READER = false; |
| /** |
| * PlatformAdmin debug flag |
| */ |
| public static boolean DEBUG_PLATFORM_ADMIN = false; |
| /** |
| * PlatformAdmin resolver debug flag |
| */ |
| public static boolean DEBUG_PLATFORM_ADMIN_RESOLVER = false; |
| /** |
| * Monitor PlatformAdmin debug flag |
| */ |
| public static boolean MONITOR_PLATFORM_ADMIN = false; |
| /** |
| * System property used to disable lazy state loading |
| */ |
| public static String PROP_NO_LAZY_LOADING = "osgi.noLazyStateLoading"; //$NON-NLS-1$ |
| /** |
| * System property used to specify to amount time before lazy data can be flushed from memory |
| */ |
| public static String PROP_LAZY_UNLOADING_TIME = "osgi.lazyStateUnloadingTime"; //$NON-NLS-1$ |
| private long expireTime = 300000; // default to five minutes |
| private long readStartupTime; |
| private StateImpl systemState; |
| private StateObjectFactoryImpl factory; |
| private long lastTimeStamp; |
| private boolean cachedState = false; |
| private File stateFile; |
| private File lazyFile; |
| private long expectedTimeStamp; |
| private BundleContext context; |
| private Thread dataManagerThread; |
| |
| /** |
| * Constructs a StateManager using the specified files and context |
| * @param stateFile a file with the data required to persist in memory |
| * @param lazyFile a file with the data that may be lazy loaded and can be flushed from memory |
| * @param context the bundle context of the system bundle |
| */ |
| public StateManager(File stateFile, File lazyFile, BundleContext context) { |
| // a negative timestamp means no timestamp checking |
| this(stateFile, lazyFile, context, -1); |
| } |
| |
| /** |
| * Constructs a StateManager using the specified files and context |
| * @param stateFile a file with the data required to persist in memory |
| * @param lazyFile a file with the data that may be lazy loaded and can be flushed from memory |
| * @param context the bundle context of the system bundle |
| * @param expectedTimeStamp the expected timestamp of the persisted system state. A negative |
| * value indicates that no timestamp checking is done |
| */ |
| public StateManager(File stateFile, File lazyFile, BundleContext context, long expectedTimeStamp) { |
| this.stateFile = stateFile; |
| this.lazyFile = lazyFile; |
| this.context = context; |
| this.expectedTimeStamp = expectedTimeStamp; |
| factory = new StateObjectFactoryImpl(); |
| } |
| |
| /** |
| * Shutsdown the state manager. If the timestamp of the system state has changed |
| * @param stateFile |
| * @param lazyFile |
| * @throws IOException |
| */ |
| public void shutdown(File stateFile, File lazyFile) throws IOException { |
| BundleDescription[] removalPendings = systemState.getRemovalPendings(); |
| if (removalPendings.length > 0) |
| systemState.resolve(removalPendings); |
| writeState(systemState, stateFile, lazyFile); |
| stopDataManager(); |
| } |
| |
| /** |
| * Update the given target files with the state data in memory. |
| * @param stateFile |
| * @param lazyFile |
| * @throws IOException |
| */ |
| public void update(File stateFile, File lazyFile) throws IOException { |
| BundleDescription[] removalPendings = systemState.getRemovalPendings(); |
| StateImpl state = systemState; |
| if (removalPendings.length > 0) { |
| state = (StateImpl) state.getFactory().createState(systemState); |
| state.setResolver(createResolver(System.getSecurityManager() != null)); |
| state.setPlatformProperties(FrameworkProperties.getProperties()); |
| state.resolve(false); |
| } |
| writeState(state, stateFile, lazyFile); |
| lastTimeStamp = state.getTimeStamp(); |
| // TODO consider updating the state files for lazy loading |
| } |
| |
| private void readSystemState(File stateFile, File lazyFile, long expectedTimeStamp) { |
| if (stateFile == null || !stateFile.isFile()) |
| return; |
| if (DEBUG_READER) |
| readStartupTime = System.currentTimeMillis(); |
| try { |
| boolean lazyLoad = !Boolean.valueOf(FrameworkProperties.getProperty(PROP_NO_LAZY_LOADING)).booleanValue(); |
| systemState = factory.readSystemState(stateFile, lazyFile, lazyLoad, expectedTimeStamp); |
| // problems in the cache (corrupted/stale), don't create a state object |
| if (systemState == null || !initializeSystemState()) { |
| systemState = null; |
| return; |
| } |
| cachedState = true; |
| try { |
| expireTime = Long.parseLong(FrameworkProperties.getProperty(PROP_LAZY_UNLOADING_TIME, Long.toString(expireTime))); |
| } catch (NumberFormatException nfe) { |
| // default to not expire |
| expireTime = 0; |
| } |
| if (lazyLoad && expireTime > 0) |
| startDataManager(); |
| } catch (IOException ioe) { |
| // TODO: how do we log this? |
| ioe.printStackTrace(); |
| } finally { |
| if (DEBUG_READER) |
| System.out.println("Time to read state: " + (System.currentTimeMillis() - readStartupTime)); //$NON-NLS-1$ |
| } |
| } |
| |
| private synchronized void startDataManager() { |
| if (dataManagerThread != null) |
| return; |
| dataManagerThread = new Thread(this, "State Data Manager"); //$NON-NLS-1$ |
| dataManagerThread.setDaemon(true); |
| dataManagerThread.start(); |
| } |
| |
| /** |
| * Stops the active data manager thread which is used to unload unused |
| * state objects from memory. |
| */ |
| public synchronized void stopDataManager() { |
| if (dataManagerThread == null) |
| return; |
| dataManagerThread.interrupt(); |
| dataManagerThread = null; |
| } |
| |
| private void writeState(StateImpl state, File stateFile, File lazyFile) throws IOException { |
| if (state == null) |
| return; |
| if (cachedState && !saveNeeded()) |
| return; |
| state.fullyLoad(); // make sure we are fully loaded before saving |
| factory.writeState(state, stateFile, lazyFile); |
| } |
| |
| private boolean initializeSystemState() { |
| systemState.setResolver(createResolver(System.getSecurityManager() != null)); |
| lastTimeStamp = systemState.getTimeStamp(); |
| return !systemState.setPlatformProperties(FrameworkProperties.getProperties()); |
| } |
| |
| /** |
| * Creates a new State used by the system. If the system State already |
| * exists then a new system State is not created. |
| * @return the State used by the system. |
| */ |
| public synchronized State createSystemState() { |
| if (systemState == null) { |
| systemState = factory.createSystemState(); |
| initializeSystemState(); |
| } |
| return systemState; |
| } |
| |
| /** |
| * Reads the State used by the system. If the system State already |
| * exists then the system State is not read from a cache. If the State could |
| * not be read from a cache then <code>null</code> is returned. |
| * @return the State used by the system or <code>null</code> if the State |
| * could not be read from a cache. |
| */ |
| public synchronized State readSystemState() { |
| if (systemState == null) |
| readSystemState(stateFile, lazyFile, expectedTimeStamp); |
| return systemState; |
| } |
| |
| /** |
| * Returns the State used by the system. If the system State does |
| * not exist then <code>null</code> is returned. |
| * @return the State used by the system or <code>null</code> if one |
| * does not exist. |
| */ |
| public State getSystemState() { |
| return systemState; |
| } |
| |
| /** |
| * Returns the cached time stamp of the system State. This value is the |
| * original time stamp of the system state when it was created or read from |
| * a cache. |
| * @see State#getTimeStamp() |
| * @return the cached time stamp of the system State |
| */ |
| public long getCachedTimeStamp() { |
| return lastTimeStamp; |
| } |
| |
| public boolean saveNeeded() { |
| return systemState.getTimeStamp() != lastTimeStamp || systemState.dynamicCacheChanged(); |
| } |
| |
| /** |
| * @see PlatformAdmin#getState(boolean) |
| */ |
| public State getState(boolean mutable) { |
| return mutable ? factory.createState(systemState) : new ReadOnlyState(systemState); |
| } |
| |
| /** |
| * @see PlatformAdmin#getState() |
| */ |
| public State getState() { |
| return getState(true); |
| } |
| |
| /** |
| * @see PlatformAdmin#getFactory() |
| */ |
| public StateObjectFactory getFactory() { |
| return factory; |
| } |
| |
| /** |
| * @see PlatformAdmin#commit(State) |
| */ |
| public synchronized void commit(State state) throws BundleException { |
| throw new IllegalArgumentException("PlatformAdmin.commit() not supported"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * @see PlatformAdmin#getResolver() |
| * @deprecated |
| */ |
| public Resolver getResolver() { |
| return createResolver(false); |
| } |
| |
| /** |
| * @see PlatformAdmin#createResolver() |
| */ |
| public Resolver createResolver() { |
| return createResolver(false); |
| } |
| |
| private Resolver createResolver(boolean checkPermissions) { |
| return new org.eclipse.osgi.internal.module.ResolverImpl(context, checkPermissions); |
| } |
| |
| /** |
| * @see PlatformAdmin#getStateHelper() |
| */ |
| public StateHelper getStateHelper() { |
| return StateHelperImpl.getInstance(); |
| } |
| |
| public void run() { |
| long timeStamp = lastTimeStamp; // cache the original timestamp incase of updates |
| while (true) { |
| try { |
| Thread.sleep(expireTime); |
| } catch (InterruptedException e) { |
| return; |
| } |
| if (systemState != null) |
| synchronized (systemState) { |
| if (timeStamp == systemState.getTimeStamp() && !systemState.dynamicCacheChanged()) |
| systemState.unloadLazyData(); |
| } |
| } |
| } |
| |
| public void addDisabledInfo(DisabledInfo disabledInfo) { |
| if (systemState == null) |
| throw new IllegalStateException(); // should never happen |
| systemState.addDisabledInfo(disabledInfo); |
| } |
| |
| public void removeDisabledInfo(DisabledInfo disabledInfo) { |
| if (systemState == null) |
| throw new IllegalStateException(); // should never happen |
| systemState.removeDisabledInfo(disabledInfo); |
| } |
| } |