/******************************************************************************* * Copyright (c) 2003, 2006 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 *******************************************************************************/ package org.eclipse.osgi.framework.adaptor.core; 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. *
* Clients may extend this class. *
* @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 BundleInstaller installer; 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(getResolver(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(getResolver(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 thennull
is returned.
* @return the State used by the system or null
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 null
is returned.
* @return the State used by the system or null
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 {
// no installer have been provided - commit not supported
if (installer == null)
throw new IllegalArgumentException("PlatformAdmin.commit() not supported"); //$NON-NLS-1$
if (!(state instanceof UserState))
throw new IllegalArgumentException("Wrong state implementation"); //$NON-NLS-1$
if (state.getTimeStamp() != systemState.getTimeStamp())
throw new BundleException(StateMsg.COMMIT_INVALID_TIMESTAMP);
StateDelta delta = state.compare(systemState);
BundleDelta[] changes = delta.getChanges();
for (int i = 0; i < changes.length; i++)
if ((changes[i].getType() & BundleDelta.ADDED) > 0)
installer.installBundle(changes[i].getBundle());
else if ((changes[i].getType() & BundleDelta.REMOVED) > 0)
installer.uninstallBundle(changes[i].getBundle());
else if ((changes[i].getType() & BundleDelta.UPDATED) > 0)
installer.updateBundle(changes[i].getBundle());
else {
// bug in StateDelta#getChanges
}
}
/**
* @see PlatformAdmin#getResolver()
*/
public Resolver getResolver() {
return getResolver(false);
}
private Resolver getResolver(boolean checkPermissions) {
return new org.eclipse.osgi.internal.module.ResolverImpl(context, checkPermissions);
}
/**
* @see PlatformAdmin#getStateHelper()
*/
public StateHelper getStateHelper() {
return StateHelperImpl.getInstance();
}
/**
* Returns the bundle installer.
* @return the bundle installer
*/
public BundleInstaller getInstaller() {
return installer;
}
/**
* Sets the bundle installer. The bundle installer will be used when a state is commited
* using the commit(State) method.
* @param installer the bundle installer
*/
public void setInstaller(BundleInstaller installer) {
this.installer = installer;
}
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 && timeStamp == systemState.getTimeStamp())
systemState.unloadLazyData(expireTime);
}
}
}