Bug 383338 - Null Pointer Exception at JVM shutdown
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java
index d3aeaf6..617db7f 100644
--- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/Framework.java
@@ -56,26 +56,26 @@
 	private static String JAVASE = "JavaSE-"; //$NON-NLS-1$
 	private static String PROFILE_EXT = ".profile"; //$NON-NLS-1$
 	/** FrameworkAdaptor specific functions. */
-	protected FrameworkAdaptor adaptor;
+	protected volatile FrameworkAdaptor adaptor;
 	/** Framework properties object.  A reference to the 
 	 * System.getProperies() object.  The properties from
 	 * the adaptor will be merged into these properties.
 	 */
 	protected Properties properties;
 	/** Has the framework been started */
-	protected boolean active;
+	protected volatile boolean active;
 	/** Event indicating the reason for shutdown*/
 	private FrameworkEvent[] shutdownEvent;
 	/** The bundles installed in the framework */
 	protected BundleRepository bundles;
 	/** Package Admin object. This object manages the exported packages. */
-	protected PackageAdminImpl packageAdmin;
+	protected volatile PackageAdminImpl packageAdmin;
 	/** PermissionAdmin and ConditionalPermissionAdmin impl. This object manages the bundle permissions. */
 	protected SecurityAdmin securityAdmin;
 	/** Startlevel object. This object manages the framework and bundle startlevels */
 	protected StartLevelManager startLevelManager;
 	/** The ServiceRegistry */
-	private ServiceRegistry serviceRegistry;
+	private volatile ServiceRegistry serviceRegistry;
 	private final int BSN_VERSION;
 	private static final int BSN_VERSION_SINGLE = 1;
 	private static final int BSN_VERSION_MULTIPLE = 2;
@@ -104,7 +104,7 @@
 	static final String findHookName = FindHook.class.getName();
 	static final String collisionHookName = CollisionHook.class.getName();
 	/** EventManager for event delivery. */
-	protected EventManager eventManager;
+	protected volatile EventManager eventManager;
 	/* Reservation object for install synchronization */
 	private Map<String, Thread> installLock;
 	/** System Bundle object */
@@ -121,7 +121,7 @@
 	 * The AliasMapper used to alias OS Names.
 	 */
 	protected static AliasMapper aliasMapper = new AliasMapper();
-	SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
+	static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
 	// cache of AdminPermissions keyed by Bundle ID
 	private final Map<Long, Map<String, AdminPermission>> adminPermissions = new HashMap<Long, Map<String, AdminPermission>>();
 
@@ -616,7 +616,6 @@
 			eventManager.close();
 			eventManager = null;
 		}
-		secureAction = null;
 		packageAdmin = null;
 		adaptor = null;
 		uninstallURLStreamHandlerFactory();
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorage.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorage.java
index cd68456..982b816 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorage.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorage.java
@@ -912,7 +912,7 @@
 		this.context = fwContext;
 		// System property can be set to enable state saver or not.
 		if (Boolean.valueOf(FrameworkProperties.getProperty(BaseStorage.PROP_ENABLE_STATE_SAVER, "true")).booleanValue()) //$NON-NLS-1$
-			stateSaver = new StateSaver();
+			stateSaver = new StateSaver(adaptor.getState());
 
 	}
 
@@ -1249,7 +1249,13 @@
 		private Thread runningThread = null;
 		private Thread shutdownHook = null;
 
-		StateSaver() {
+		// Bug 383338. Pass the BaseAdaptor state as a constructor argument and
+		// reuse rather than call adaptor.getState(), and risk an NPE from
+		// Framework due to a null ServiceRegistry, each time a lock is desired.
+		private final State lock;
+
+		StateSaver(State lock) {
+			this.lock = lock;
 			String prop = FrameworkProperties.getProperty("eclipse.stateSaveDelayInterval"); //$NON-NLS-1$
 			long delayValue = 30000; // 30 seconds.
 			long maxDelayValue = 1800000; // 30 minutes.
@@ -1272,8 +1278,7 @@
 		}
 
 		public void run() {
-			State systemState = adaptor.getState();
-			synchronized (systemState) {
+			synchronized (lock) {
 				long firstSaveTime = lastSaveTime;
 				long curSaveTime = 0;
 				long delayTime;
@@ -1289,7 +1294,7 @@
 						// wait for other save requests 
 						try {
 							if (!shutdown)
-								systemState.wait(delayTime);
+								lock.wait(delayTime);
 						} catch (InterruptedException ie) {
 							// force break from do/while loops
 							curSaveTime = lastSaveTime;
@@ -1312,12 +1317,13 @@
 		}
 
 		void shutdown() {
-			State systemState = adaptor.getState();
-			Thread joinWith = null;
-			synchronized (systemState) {
+			Thread joinWith;
+			synchronized (lock) {
+				if (shutdown)
+					return; // Don't shutdown more than once. Bug 383338.
 				shutdown = true;
 				joinWith = runningThread;
-				systemState.notifyAll(); // To wakeup sleeping thread.
+				lock.notifyAll(); // To wakeup sleeping thread.
 			}
 			try {
 				if (joinWith != null) {
@@ -1337,8 +1343,7 @@
 		}
 
 		void requestSave() {
-			final State systemState = adaptor.getState();
-			synchronized (systemState) {
+			synchronized (lock) {
 				if (shutdown)
 					return; // do not start another thread if we have already shutdown
 				if (delay_interval == 0) {