[296576] Add some synchronization to RuntimeClasspathProvider handling to avoid user reported NPE.
diff --git a/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/RuntimeClasspathProviderDelegate.java b/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/RuntimeClasspathProviderDelegate.java
index f4f9509..4b19407 100644
--- a/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/RuntimeClasspathProviderDelegate.java
+++ b/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/RuntimeClasspathProviderDelegate.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2003, 2009 IBM Corporation and others.
+ * Copyright (c) 2003, 2010 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
@@ -54,13 +54,13 @@
 		IClasspathAttribute[] attributes;
 	}
 
-	private List<SourceAttachmentUpdate> sourceAttachments;
+	private volatile List<SourceAttachmentUpdate> sourceAttachments;
 
 	private String extensionId;
 
-	private Map<String, IPath> runtimePathMap = new HashMap<String, IPath>();
+	private Map<String, IPath> runtimePathMap = Collections.synchronizedMap(new HashMap<String, IPath>());
 
-	private Map<String, Integer> previousClasspath = new HashMap<String, Integer>();
+	private Map<String, Integer> previousClasspath = Collections.synchronizedMap(new HashMap<String, Integer>());
 
 	public RuntimeClasspathProviderDelegate() {
 		// default constructor
@@ -137,17 +137,23 @@
 		if (entries == null)
 			entries = new IClasspathEntry[0];
 		
-		if (sourceAttachments == null)
-			load();
-		
-		int size = entries.length;
-		int size2 = sourceAttachments.size();
-		for (int i = 0; i < size; i++) {
-			for (int j = 0; j < size2; j++) {
-				SourceAttachmentUpdate sau = sourceAttachments.get(j);
-				if (sau.runtimeId.equals(runtime.getId()) && sau.entry.equals(entries[i].getPath())) {
-					IClasspathAttribute[] consolidatedClasspathAttributes = consolidateClasspathAttributes(sau.attributes, entries[i].getExtraAttributes());
-					entries[i] = JavaCore.newLibraryEntry(entries[i].getPath(), sau.sourceAttachmentPath, sau.sourceAttachmentRootPath, entries[i].getAccessRules(), consolidatedClasspathAttributes, false);
+		synchronized (this) {
+			if (sourceAttachments == null)
+				load();
+		}
+		List<SourceAttachmentUpdate> srcAttachments = sourceAttachments;
+
+		if (srcAttachments != null) {
+			int size = entries.length;
+			int size2 = srcAttachments.size();
+			for (int i = 0; i < size; i++) {
+				for (int j = 0; j < size2; j++) {
+					SourceAttachmentUpdate sau = srcAttachments.get(j);
+					if (sau.runtimeId.equals(runtime.getId()) && sau.entry.equals(entries[i].getPath())) {
+						IClasspathAttribute[] consolidatedClasspathAttributes = consolidateClasspathAttributes(sau.attributes, entries[i].getExtraAttributes());
+						entries[i] = JavaCore.newLibraryEntry(entries[i].getPath(), sau.sourceAttachmentPath, sau.sourceAttachmentRootPath, entries[i].getAccessRules(), consolidatedClasspathAttributes, false);
+						break;
+					}
 				}
 			}
 		}
@@ -249,19 +255,20 @@
 			return;
 		
 		// find the source attachments
-		sourceAttachments = new ArrayList<SourceAttachmentUpdate>();
+		List<SourceAttachmentUpdate> srcAttachments = new ArrayList<SourceAttachmentUpdate>();
 		
 		for (IClasspathEntry entry : entries) {
-			if (entry.getSourceAttachmentPath() != null || entry.getExtraAttributes() != null) {
+			if (entry.getSourceAttachmentPath() != null || (entry.getExtraAttributes() != null && entry.getExtraAttributes().length > 0)) {
 				SourceAttachmentUpdate sau = new SourceAttachmentUpdate();
 				sau.runtimeId = runtime.getId();
 				sau.entry = entry.getPath();
 				sau.sourceAttachmentPath = entry.getSourceAttachmentPath();
 				sau.sourceAttachmentRootPath = entry.getSourceAttachmentRootPath();
 				sau.attributes = entry.getExtraAttributes();
-				sourceAttachments.add(sau);
+				srcAttachments.add(sau);
 			}
 		}
+		sourceAttachments = srcAttachments;
 		save();
 	}
 
@@ -269,7 +276,7 @@
 	 * Load source attachment info.
 	 */
 	private void load() {
-		sourceAttachments = new ArrayList<SourceAttachmentUpdate>();
+		List<SourceAttachmentUpdate> srcAttachments = new ArrayList<SourceAttachmentUpdate>();
 		
 		String id = extensionId;
 		String filename = JavaServerPlugin.getInstance().getStateLocation().append(id + ".xml").toOSString();
@@ -303,7 +310,7 @@
 							sau.attributes[j] = JavaCore.newClasspathAttribute(name, value);
 						}
 					}
-					sourceAttachments.add(sau);
+					srcAttachments.add(sau);
 				} catch (Exception e) {
 					Trace.trace(Trace.WARNING, "Could not load source attachment: " + e);
 				}
@@ -311,20 +318,22 @@
 		} catch (Exception e) {
 			Trace.trace(Trace.WARNING, "Could not load source path info", e);
 		}
+		sourceAttachments = srcAttachments;
 	}
 
 	/**
 	 * Save source attachment info.
 	 */
-	private void save() {
-		if (sourceAttachments == null)
+	private synchronized void save() {
+		List<SourceAttachmentUpdate> srcAttachments = sourceAttachments;
+		if (srcAttachments == null)
 			return;
 		String id = extensionId;
 		String filename = JavaServerPlugin.getInstance().getStateLocation().append(id + ".xml").toOSString();
 		try {
 			XMLMemento memento = XMLMemento.createWriteRoot("classpath");
 
-			Iterator iterator = sourceAttachments.iterator();
+			Iterator iterator = srcAttachments.iterator();
 			while (iterator.hasNext()) {
 				SourceAttachmentUpdate sau = (SourceAttachmentUpdate) iterator.next();
 				IMemento child = memento.createChild("source-attachment");
diff --git a/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/internal/RuntimeClasspathProviderWrapper.java b/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/internal/RuntimeClasspathProviderWrapper.java
index 279a7f1..5256ffb 100644
--- a/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/internal/RuntimeClasspathProviderWrapper.java
+++ b/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/internal/RuntimeClasspathProviderWrapper.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * Copyright (c) 2005, 2010 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
@@ -25,7 +25,7 @@
  */
 public class RuntimeClasspathProviderWrapper {
 	private IConfigurationElement element;
-	private RuntimeClasspathProviderDelegate delegate;
+	private volatile RuntimeClasspathProviderDelegate delegate;
 
 	/**
 	 * Create a new runtime target handler.
@@ -127,8 +127,15 @@
 	public RuntimeClasspathProviderDelegate getDelegate() {
 		if (delegate == null) {
 			try {
-				delegate = (RuntimeClasspathProviderDelegate) element.createExecutableExtension("class");
-				delegate.initialize(getId());
+				// Create delegate unsynchronized to avoid possible deadlocks
+				RuntimeClasspathProviderDelegate tempDelegate = (RuntimeClasspathProviderDelegate) element.createExecutableExtension("class");
+				tempDelegate.initialize(getId());
+				// If delegate is not already set, use this delegate
+				synchronized (this) {
+					if (delegate == null) {
+						delegate = tempDelegate;
+					}
+				}
 			} catch (Throwable t) {
 				ServerPlugin.logExtensionFailure(toString(), t);
 			}