blob: 5f491053e878cd60d80f01fafefb2010569ebb8c [file] [log] [blame]
/*******************************************************************************
* 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.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(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 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()
*/
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();
}
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.dynamicCacheChanged())
systemState.unloadLazyData(expireTime);
}
}
}