summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Ross2013-05-15 16:39:14 (EDT)
committer Thomas Watson2013-05-21 15:44:17 (EDT)
commitec635d407bcd928ff84d2d0a2e22cf3066ab3fd2 (patch)
tree05d0bff8934853142b2a3ed876784c1914a94427
parent8f791908bd8cedd1ddfd69fc33c6109763169e6d (diff)
downloadrt.equinox.framework-ec635d407bcd928ff84d2d0a2e22cf3066ab3fd2.zip
rt.equinox.framework-ec635d407bcd928ff84d2d0a2e22cf3066ab3fd2.tar.gz
rt.equinox.framework-ec635d407bcd928ff84d2d0a2e22cf3066ab3fd2.tar.bz2
Bug 405918 - [unity] save persistent data after period of inactivity
Add the eclipse.stateSaveDelayInterval property, along with its default value, to EquinoxConfiguration. The configuration is responsible for filling in the default value and type validation. Zero means the state is saved immediately with each update. A negative value means the state is never saved as part of an update. A positive value results in a scheduled task being created that executes every value milliseconds and saves the current state. Add a ScheduledExecutorService to EquinoxContainer with a min pool size of zero and a max size of one. The service is created on init() and shutdown on close(). Add the method updatedDatabase() to ModuleContainerAdaptor. This method is called whenever a database update occurs. The default behavior is to do nothing. The EquinoxContainerAdaptor overrides the method and saves the database state based on the value of the eclipse.stateSaveDelayInterval property. Add the StorageSaver class. It encapsulates the scheduling of the task, registering of the shutdown hook, and determining which type of save is required. This same functionality was removed from EquinoxContainerAdaptor. EquinoxContainer initializes the StorageSaver on init() and disposes of it on close(). EquinoxContainerAdaptor receives the updatedDatabase() call from ModuleDatabase and requests a save by getting the StorageSaver from the container. The osgi.framework.activeThreadType property is obsolete. The framework active thread task is no longer necessary because the ExecutorService maintained by the EquinoxContainer will always have one and only one active, non-daemon thread until system shutdown.
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainerAdaptor.java9
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java3
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java21
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java56
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainerAdaptor.java8
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/StorageSaver.java96
6 files changed, 161 insertions, 32 deletions
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainerAdaptor.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainerAdaptor.java
index 20684ff..b00f6ad 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainerAdaptor.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainerAdaptor.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 IBM Corporation and others.
+ * Copyright (c) 2012, 2013 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
@@ -202,4 +202,11 @@ public abstract class ModuleContainerAdaptor {
public void refreshedSystemModule() {
// do nothing by default
}
+
+ /**
+ * This is called whenever the module database has been updated.
+ */
+ public void updatedDatabase() {
+ // do nothing by default
+ }
}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java
index 1d01e77..19d6444 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 IBM Corporation and others.
+ * Copyright (c) 2012, 2013 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
@@ -792,6 +792,7 @@ public class ModuleDatabase {
revisionsTimeStamp.incrementAndGet();
}
allTimeStamp.incrementAndGet();
+ adaptor.updatedDatabase();
}
private void setSystemLastModified(long currentTime) {
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java
index 2b06eb9..00663d1 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java
@@ -162,8 +162,6 @@ public class EquinoxConfiguration implements EnvironmentInfo {
public static final String CONTEXTCLASSLOADER_PARENT_FWK = "fwk"; //$NON-NLS-1$
public static final String PROP_FRAMEWORK_LIBRARY_EXTENSIONS = "osgi.framework.library.extensions"; //$NON-NLS-1$
- public static final String PROP_FRAMEWORK_THREAD = "osgi.framework.activeThreadType"; //$NON-NLS-1$
- public static final String FRAMEWORK_THREAD_NORMAL = "normal"; //$NON-NLS-1$
public static final String PROP_COPY_NATIVES = "osgi.classloader.copy.natives"; //$NON-NLS-1$
public static final String PROP_DEFINE_PACKAGES = "osgi.classloader.define.packages"; //$NON-NLS-1$
public static final String PROP_BUNDLE_SETTCCL = "eclipse.bundle.setTCCL"; //$NON-NLS-1$
@@ -183,6 +181,9 @@ public class EquinoxConfiguration implements EnvironmentInfo {
public static final String PROP_CHECK_CONFIGURATION = "osgi.checkConfiguration"; //$NON-NLS-1$
private final boolean inCheckConfigurationMode;
+ public static final String DEFAULT_STATE_SAVE_DELAY_INTERVAL = "30000"; //$NON-NLS-1$
+ public static final String PROP_STATE_SAVE_DELAY_INTERVAL = "eclipse.stateSaveDelayInterval"; //$NON-NLS-1$
+
private final static Collection<String> populateInitConfig = Arrays.asList(PROP_OSGI_ARCH, PROP_OSGI_OS, PROP_OSGI_WS, PROP_OSGI_NL, FRAMEWORK_OS_NAME, FRAMEWORK_OS_VERSION, FRAMEWORK_PROCESSOR, FRAMEWORK_LANGUAGE);
EquinoxConfiguration(Map<String, ?> initialConfiguration, HookRegistry hookRegistry) {
@@ -582,6 +583,20 @@ public class EquinoxConfiguration implements EnvironmentInfo {
}
}
+ private static void initializeStateSaveDelayIntervalProperty(Properties configuration) {
+ if (!configuration.containsKey(PROP_STATE_SAVE_DELAY_INTERVAL))
+ // Property not specified. Use the default.
+ configuration.setProperty(PROP_STATE_SAVE_DELAY_INTERVAL, DEFAULT_STATE_SAVE_DELAY_INTERVAL);
+ try {
+ // Verify type compatibility.
+ Long.parseLong(configuration.getProperty(PROP_STATE_SAVE_DELAY_INTERVAL));
+ } catch (NumberFormatException e) {
+ // TODO Consider logging here.
+ // The specified value is not type compatible. Use the default.
+ configuration.setProperty(PROP_STATE_SAVE_DELAY_INTERVAL, DEFAULT_STATE_SAVE_DELAY_INTERVAL);
+ }
+ }
+
private static void initializeProperties(Properties configuration, AliasMapper aliasMapper) {
// initialize some framework properties that must always be set
if (configuration.get(PROP_FRAMEWORK) == null || configuration.get(EquinoxLocations.PROP_INSTALL_AREA) == null) {
@@ -748,7 +763,7 @@ public class EquinoxConfiguration implements EnvironmentInfo {
archValue = name;
configuration.put(PROP_OSGI_ARCH, archValue);
}
-
+ initializeStateSaveDelayIntervalProperty(configuration);
}
private static String getFrameworkPath(String path, boolean parent) {
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java
index 2d2d8cb..cc700c8 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012 IBM Corporation and others.
+ * Copyright (c) 2012, 2013 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
@@ -16,6 +16,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.util.*;
+import java.util.concurrent.*;
import org.eclipse.osgi.framework.eventmgr.EventManager;
import org.eclipse.osgi.framework.eventmgr.ListenerQueue;
import org.eclipse.osgi.framework.internal.core.Msg;
@@ -58,17 +59,18 @@ public class EquinoxContainer {
private EquinoxEventPublisher eventPublisher;
private ServiceRegistry serviceRegistry;
private ContextFinder contextFinder;
- private boolean initialized = false;
private ServiceTracker<SignedContentFactory, SignedContentFactory> signedContentFactory;
+ private ScheduledExecutorService executor;
+ private StorageSaver storageSaver;
+
public EquinoxContainer(Map<String, ?> configuration) {
this.equinoxConfig = new EquinoxConfiguration(configuration, new HookRegistry(this));
this.equinoxLocations = new EquinoxLocations(this.equinoxConfig);
loadConfig(equinoxConfig, equinoxLocations);
this.logServices = new EquinoxLogServices(this.equinoxConfig, this.equinoxLocations.getConfigurationLocation());
this.equinoxConfig.getHookRegistry().initialize();
-
try {
this.storage = Storage.createStorage(this);
} catch (IOException e) {
@@ -207,28 +209,8 @@ public class EquinoxContainer {
eventPublisher = new EquinoxEventPublisher(this);
serviceRegistry = new ServiceRegistry(this);
initializeContextFinder();
- initialized = true;
- startActiveThread();
- }
- }
-
- private void startActiveThread() {
- if (EquinoxConfiguration.FRAMEWORK_THREAD_NORMAL.equals(equinoxConfig.getConfiguration(EquinoxConfiguration.PROP_FRAMEWORK_THREAD, EquinoxConfiguration.FRAMEWORK_THREAD_NORMAL))) {
- Thread fwkThread = new Thread(new Runnable() {
- @Override
- public void run() {
- synchronized (EquinoxContainer.this.monitor) {
- while (EquinoxContainer.this.initialized)
- try {
- EquinoxContainer.this.monitor.wait(1000);
- } catch (InterruptedException e) {
- // do nothing
- }
- }
- }
- }, "Framework Active Thread: " + toString()); //$NON-NLS-1$
- fwkThread.setDaemon(false);
- fwkThread.start();
+ executor = Executors.newScheduledThreadPool(1);
+ storageSaver = new StorageSaver(this);
}
}
@@ -238,9 +220,11 @@ public class EquinoxContainer {
eventManager = null;
eventPublisher = null;
serviceRegistry = null;
+ storageSaver.close();
storage.close();
- initialized = false;
- this.monitor.notifyAll();
+ // Must be done last since it will result in termination of the
+ // framework active thread.
+ executor.shutdown();
}
}
@@ -278,6 +262,18 @@ public class EquinoxContainer {
}
}
+ // TODO Providing access to the ExecutorService has potential issues. For
+ // example, something could shut it down. Make these methods package
+ // private? Isolate the executor and add utility methods for adding tasks
+ // instead?
+ public ExecutorService getExecutor() {
+ return executor;
+ }
+
+ public ScheduledExecutorService getScheduledExecutor() {
+ return executor;
+ }
+
public ServiceRegistry getServiceRegistry() {
synchronized (this.monitor) {
return serviceRegistry;
@@ -330,4 +326,10 @@ public class EquinoxContainer {
String UUID = equinoxConfig == null ? null : equinoxConfig.getConfiguration(Constants.FRAMEWORK_UUID);
return "Equinox Container: " + UUID; //$NON-NLS-1$
}
+
+ StorageSaver getStorageSaver() {
+ synchronized (this.monitor) {
+ return storageSaver;
+ }
+ }
}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainerAdaptor.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainerAdaptor.java
index 9004269..56e2450 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainerAdaptor.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainerAdaptor.java
@@ -216,4 +216,12 @@ public class EquinoxContainerAdaptor extends ModuleContainerAdaptor {
public String toString() {
return container.toString();
}
+
+ @Override
+ public void updatedDatabase() {
+ StorageSaver saver = container.getStorageSaver();
+ if (saver == null)
+ return;
+ saver.save();
+ }
} \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/StorageSaver.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/StorageSaver.java
new file mode 100644
index 0000000..efd21c8
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/StorageSaver.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.framework;
+
+import java.io.IOException;
+import java.util.concurrent.*;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+
+public final class StorageSaver {
+ private static class StorageSaverTask implements Runnable {
+ private final EquinoxContainer container;
+
+ public StorageSaverTask(EquinoxContainer container) {
+ this.container = container;
+ }
+
+ @Override
+ public void run() {
+ try {
+ container.getStorage().save();
+ } catch (IOException e) {
+ container.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "Error saving on update", e);
+ }
+ }
+ }
+
+ private final EquinoxContainer container;
+ private final long delay;
+ private final ScheduledFuture<?> future;
+ private final Thread hook;
+ private final StorageSaverTask task;
+
+ public StorageSaver(EquinoxContainer container) {
+ this.container = container;
+ task = new StorageSaverTask(container);
+ delay = computeDelay();
+ future = scheduleTask();
+ hook = registerShutdownHook();
+ }
+
+ public void close() {
+ unregisterShutdownHook();
+ unscheduleTask();
+ }
+
+ public void save() {
+ if (delay != 0)
+ // Periodic saves are enabled or saves are disabled altogether.
+ return;
+ // Immediately save on request.
+ task.run();
+ }
+
+ private Thread registerShutdownHook() {
+ Thread thread = new Thread(task, "Equinox Shutdown Hook");
+ Runtime.getRuntime().addShutdownHook(thread);
+ return thread;
+ }
+
+ private long computeDelay() {
+ EquinoxConfiguration configuration = container.getConfiguration();
+ // Default provided by the configuration, if necessary.
+ String delayProp = configuration.getConfiguration(EquinoxConfiguration.PROP_STATE_SAVE_DELAY_INTERVAL);
+ // Type compatibility verified by the configuration.
+ return Long.parseLong(delayProp);
+ }
+
+ private ScheduledFuture<?> scheduleTask() {
+ // Negative delay disables saves. Zero delay results in immediate saves.
+ if (delay <= 0)
+ return null;
+ ScheduledExecutorService executor = container.getScheduledExecutor();
+ return executor.scheduleWithFixedDelay(task, delay, delay, TimeUnit.MILLISECONDS);
+ }
+
+ private void unregisterShutdownHook() {
+ try {
+ Runtime.getRuntime().removeShutdownHook(hook);
+ } catch (IllegalStateException e) {
+ // Ignore.
+ }
+ }
+
+ private void unscheduleTask() {
+ if (future != null)
+ future.cancel(false);
+ }
+}