Bug 578243 - Deadlock between ApiBaseline.resolvePackage() and
BundleComponent.getHost()
Removed unneeded synchronization in ApiBaseline / Component and related
classes.
Change-Id: Id119d3d00c6faa5c3e08dd82f58adc6deb573b06
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
Reviewed-on: https://git.eclipse.org/r/c/pde/eclipse.pde.ui/+/189712
Tested-by: PDE Bot <pde-bot@eclipse.org>
diff --git a/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/search/tests/TestRequestor.java b/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/search/tests/TestRequestor.java
index c915e68..4491c61 100644
--- a/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/search/tests/TestRequestor.java
+++ b/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/search/tests/TestRequestor.java
@@ -133,7 +133,7 @@
}
}
catch(Exception e) {
- this.test.reportFailure(e.getMessage());
+ throw new IllegalStateException(e);
}
}
return this.scope;
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java
index 3d537a6..cb6d374 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java
@@ -24,13 +24,16 @@
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -110,9 +113,9 @@
/**
* Cache of baseline names to the location with their infos in it
*/
- private HashMap<String, String> handlecache = null;
+ private volatile Map<String, String> handlecache;
- private HashSet<String> hasinfos = null;
+ private volatile Set<String> hasinfos;
/**
* The current default {@link IApiBaseline}
@@ -147,6 +150,7 @@
ApiPlugin.getDefault().addSaveParticipant(this);
savelocation = ApiPlugin.getDefault().getStateLocation().append(".api_profiles").addTrailingSeparator(); //$NON-NLS-1$
}
+ hasinfos = Collections.emptySet();
}
/**
@@ -224,7 +228,7 @@
*/
public void loadBaselineInfos(IApiBaseline baseline) throws CoreException {
initializeStateCache();
- if (hasinfos.contains(baseline.getName())) {
+ if (isBaselineLoaded(baseline)) {
return;
}
String filename = handlecache.get(baseline.getName());
@@ -241,6 +245,10 @@
}
}
+ public boolean isBaselineLoaded(IApiBaseline baseline) {
+ return hasinfos.contains(baseline.getName());
+ }
+
/**
* Initializes the baseline cache lazily. Only performs work if the current
* cache has not been created yet
@@ -252,7 +260,7 @@
long time = System.currentTimeMillis();
if (baselinecache == null) {
handlecache = new HashMap<>(8);
- hasinfos = new HashSet<>(8);
+ hasinfos = ConcurrentHashMap.newKeySet(8);
baselinecache = new LinkedHashMap<>(8);
if (!ApiPlugin.isRunningInFramework()) {
return;
@@ -302,14 +310,14 @@
} else {
node.remove(DEFAULT_BASELINE);
}
- if (baselinecache != null && hasinfos != null) {
+ if (baselinecache != null && !hasinfos.isEmpty()) {
File dir = new File(savelocation.toOSString());
Files.createDirectories(dir.toPath());
IApiBaseline baseline = null;
for (Entry<String, IApiBaseline> entry : baselinecache.entrySet()) {
String id = entry.getKey();
baseline = entry.getValue();
- if (!hasinfos.contains(baseline.getName())) {
+ if (!isBaselineLoaded(baseline)) {
continue;
}
File file = savelocation.append(id + BASELINE_FILE_EXTENSION).toFile();
@@ -557,7 +565,7 @@
if (handlecache != null) {
handlecache.clear();
}
- if (hasinfos != null) {
+ if (!hasinfos.isEmpty()) {
hasinfos.clear();
}
StubApiComponent.disposeAllCaches();
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/AbstractApiTypeContainer.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/AbstractApiTypeContainer.java
index 30f4931..9f00247 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/AbstractApiTypeContainer.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/AbstractApiTypeContainer.java
@@ -16,7 +16,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
@@ -39,7 +38,7 @@
/**
* Collection of {@link IApiTypeContainer}s
*/
- private List<IApiTypeContainer> fApiTypeContainers = null;
+ private volatile List<IApiTypeContainer> fApiTypeContainers;
/**
* Constructor
@@ -62,27 +61,34 @@
}
@Override
- public synchronized void close() throws CoreException {
+ public void close() throws CoreException {
if (fApiTypeContainers == null) {
return;
}
- // clean component cache elements
- ApiModelCache.getCache().removeElementInfo(this);
-
MultiStatus multi = null;
IStatus single = null;
- IApiTypeContainer[] containers = getApiTypeContainers();
- for (IApiTypeContainer container : containers) {
- try {
- container.close();
- } catch (CoreException e) {
- if (single == null) {
- single = e.getStatus();
- } else {
- if (multi == null) {
- multi = new MultiStatus(ApiPlugin.PLUGIN_ID, single.getCode(), single.getMessage(), single.getException());
+ synchronized (this) {
+ if (fApiTypeContainers == null) {
+ return;
+ }
+
+ // clean component cache elements
+ ApiModelCache.getCache().removeElementInfo(this);
+
+ IApiTypeContainer[] containers = getApiTypeContainers();
+ for (IApiTypeContainer container : containers) {
+ try {
+ container.close();
+ } catch (CoreException e) {
+ if (single == null) {
+ single = e.getStatus();
+ } else {
+ if (multi == null) {
+ multi = new MultiStatus(ApiPlugin.PLUGIN_ID, single.getCode(), single.getMessage(),
+ single.getException());
+ }
+ multi.add(e.getStatus());
}
- multi.add(e.getStatus());
}
}
}
@@ -171,11 +177,17 @@
*
* @return the {@link IApiTypeContainer}s
*/
- protected synchronized IApiTypeContainer[] getApiTypeContainers() throws CoreException {
- if (fApiTypeContainers == null) {
- fApiTypeContainers = createApiTypeContainers();
+ protected IApiTypeContainer[] getApiTypeContainers() throws CoreException {
+ List<IApiTypeContainer> typeContainers = fApiTypeContainers;
+ if (typeContainers == null) {
+ synchronized (this) {
+ if (typeContainers == null) {
+ typeContainers = createApiTypeContainers();
+ fApiTypeContainers = typeContainers;
+ }
+ }
}
- return fApiTypeContainers.toArray(new IApiTypeContainer[fApiTypeContainers.size()]);
+ return typeContainers.toArray(new IApiTypeContainer[typeContainers.size()]);
}
/**
@@ -185,16 +197,11 @@
* @param id the given id
* @return the {@link IApiTypeContainer}s
*/
- protected synchronized IApiTypeContainer[] getApiTypeContainers(String id) throws CoreException {
- if (fApiTypeContainers == null) {
- fApiTypeContainers = createApiTypeContainers();
- }
+ protected IApiTypeContainer[] getApiTypeContainers(String id) throws CoreException {
+ IApiTypeContainer[] typeContainers = getApiTypeContainers();
List<IApiTypeContainer> containers = new ArrayList<>();
- String origin = null;
- IApiTypeContainer container = null;
- for (Iterator<IApiTypeContainer> iterator = fApiTypeContainers.iterator(); iterator.hasNext();) {
- container = iterator.next();
- origin = ((IApiComponent) container.getAncestor(IApiElement.COMPONENT)).getSymbolicName();
+ for (IApiTypeContainer container : typeContainers) {
+ String origin = ((IApiComponent) container.getAncestor(IApiElement.COMPONENT)).getSymbolicName();
if (origin != null && origin.equals(id)) {
containers.add(container);
}
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ApiBaseline.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ApiBaseline.java
index a160194..fd9dfc3 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ApiBaseline.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ApiBaseline.java
@@ -35,6 +35,7 @@
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
@@ -88,7 +89,7 @@
/**
* OSGi bundle state
*/
- private State fState;
+ private volatile State fState;
/**
* Execution environment identifier
@@ -127,30 +128,33 @@
* <p>
* Map of <code>PackageName -> Map(componentName -> IApiComponent[])</code>
* </p>
- * For each package the cache contains a map of API components that provide
- * that package, by source component name (including the <code>null</code>
- * component name).
+ * For each package the cache contains a map of API components that provide that
+ * package, by source component name (including the <code>null</code> component
+ * name). This map can be updated on the fly on changes in the workspave.
*/
- private HashMap<String, HashMap<IApiComponent, IApiComponent[]>> fComponentsProvidingPackageCache = null;
+ private final Map<String, Map<IApiComponent, IApiComponent[]>> fComponentsProvidingPackageCache;
/**
* Maps component id's to components.
* <p>
* Map of <code>componentId -> {@link IApiComponent}</code>
* </p>
+ * This map is not supposed to be modified except on creation / disposal.
*/
private volatile Map<String, IApiComponent> fComponentsById;
/**
* Maps component id's to all components sorted from higher to lower version.
+ * This map is not supposed to be modified except on creation / disposal.
*/
- private HashMap<String, Set<IApiComponent>> fAllComponentsById = null;
+ private volatile Map<String, Set<IApiComponent>> fAllComponentsById;
/**
* Maps project name's to components.
* <p>
* Map of <code>project name -> {@link IApiComponent}</code>
* </p>
+ * This map is not supposed to be modified except on creation / disposal.
*/
- private HashMap<String, IApiComponent> fComponentsByProjectNames = null;
+ private volatile Map<String, IApiComponent> fComponentsByProjectNames;
/**
* Cache of system package names
*/
@@ -160,8 +164,9 @@
* The VM install this baseline is bound to for system libraries or
* <code>null</code>. Only used in the IDE when OSGi is running.
*/
- private IVMInstall fVMBinding = null;
+ private IVMInstall fVMBinding;
+ private volatile boolean disposed;
/**
* Constructs a new API baseline with the given name.
@@ -170,6 +175,7 @@
*/
public ApiBaseline(String name) {
super(null, IApiElement.BASELINE, name);
+ fComponentsProvidingPackageCache = new ConcurrentHashMap<>(8);
fAutoResolve = true;
fEEStatus = Status.error(CoreMessages.ApiBaseline_0);
}
@@ -361,13 +367,10 @@
/**
- * Clears the package -> components cache and sets it to <code>null</code>
+ * Clears the package -> components cache
*/
- private synchronized void clearComponentsCache() {
- if (fComponentsProvidingPackageCache != null) {
- fComponentsProvidingPackageCache.clear();
- fComponentsProvidingPackageCache = null;
- }
+ private void clearComponentsCache() {
+ fComponentsProvidingPackageCache.clear();
}
/**
@@ -545,30 +548,26 @@
@Override
public IApiComponent[] getApiComponents() {
loadBaselineInfos();
- if (fComponentsById == null) {
+ Map<String, IApiComponent> componentsById = fComponentsById;
+ if (disposed || componentsById == null) {
return EMPTY_COMPONENTS;
}
- Collection<IApiComponent> values = fComponentsById.values();
+ Collection<IApiComponent> values = componentsById.values();
return values.toArray(new IApiComponent[values.size()]);
}
@Override
- public synchronized IApiComponent[] resolvePackage(IApiComponent sourceComponent, String packageName) throws CoreException {
- HashMap<IApiComponent, IApiComponent[]> componentsForPackage = null;
- if (fComponentsProvidingPackageCache != null) {
- componentsForPackage = fComponentsProvidingPackageCache.get(packageName);
- } else {
- fComponentsProvidingPackageCache = new HashMap<>(8);
+ public IApiComponent[] resolvePackage(IApiComponent sourceComponent, String packageName) throws CoreException {
+ if (disposed) {
+ IStatus error = Status.error("Trying to use disposed baseline " + getName()); //$NON-NLS-1$
+ throw new CoreException(error);
}
+ Map<IApiComponent, IApiComponent[]> componentsForPackage = fComponentsProvidingPackageCache
+ .computeIfAbsent(packageName, x -> new ConcurrentHashMap<>(8));
IApiComponent[] cachedComponents = null;
- if (componentsForPackage != null) {
- cachedComponents = componentsForPackage.get(sourceComponent);
- if (cachedComponents != null && cachedComponents.length > 0) {
- return cachedComponents;
- }
- } else {
- componentsForPackage = new HashMap<>(8);
- fComponentsProvidingPackageCache.put(packageName, componentsForPackage);
+ cachedComponents = componentsForPackage.get(sourceComponent);
+ if (cachedComponents != null && cachedComponents.length > 0) {
+ return cachedComponents;
}
// check resolvePackage0 before the system packages to avoid wrong
@@ -613,7 +612,8 @@
* @param componentsList
* @throws CoreException
*/
- private void resolvePackage0(IApiComponent component, String packageName, List<IApiComponent> componentsList) throws CoreException {
+ private void resolvePackage0(IApiComponent component, String packageName, List<IApiComponent> componentsList)
+ throws CoreException {
if (component instanceof BundleComponent) {
BundleDescription bundle = ((BundleComponent) component).getBundleDescription();
if (bundle != null) {
@@ -704,8 +704,13 @@
* @noreference This method is not intended to be referenced by clients.
*/
public State getState() {
+ if (disposed) {
+ return fState;
+ }
if (fState == null) {
- fState = StateObjectFactory.defaultFactory.createState(true);
+ synchronized (this) {
+ fState = StateObjectFactory.defaultFactory.createState(true);
+ }
}
return fState;
}
@@ -713,22 +718,25 @@
@Override
public IApiComponent getApiComponent(String id) {
loadBaselineInfos();
- if (fComponentsById == null) {
+ Map<String, IApiComponent> componentsById = fComponentsById;
+ if (disposed || componentsById == null) {
return null;
}
- return fComponentsById.get(id);
+ return componentsById.get(id);
}
@Override
public Set<IApiComponent> getAllApiComponents(String id) {
loadBaselineInfos();
- if (fAllComponentsById == null) {
+ Map<String, Set<IApiComponent>> componentsById = fAllComponentsById;
+ if (disposed || componentsById == null) {
return Collections.emptySet();
}
- if (fAllComponentsById.get(id) == null) {
+ Set<IApiComponent> set = componentsById.get(id);
+ if (set == null) {
return Collections.emptySet();
}
- return fAllComponentsById.get(id);
+ return set;
}
@Override
@@ -740,14 +748,20 @@
* Loads the information from the *.profile file the first time the baseline
* is accessed
*/
- private synchronized void loadBaselineInfos() {
- if (fComponentsById != null) {
+ private void loadBaselineInfos() {
+ if (disposed) {
return;
}
- try {
- ApiBaselineManager.getManager().loadBaselineInfos(this);
- } catch (CoreException ce) {
- ApiPlugin.log(ce);
+ ApiBaselineManager manager = ApiBaselineManager.getManager();
+ if (fComponentsById != null && manager.isBaselineLoaded(this)) {
+ return;
+ }
+ synchronized (this) {
+ try {
+ manager.loadBaselineInfos(this);
+ } catch (CoreException ce) {
+ ApiPlugin.log(ce);
+ }
}
}
@@ -810,6 +824,9 @@
* performs the actual dispose of mappings and cached elements
*/
protected void doDispose() {
+ if (disposed) {
+ return;
+ }
if (ApiPlugin.isRunningInFramework()) {
JavaRuntime.removeVMInstallChangedListener(this);
}
@@ -840,6 +857,7 @@
}
fSystemLibraryComponentList = new ArrayList<>();
}
+ disposed = true;
}
/**
@@ -912,10 +930,8 @@
* @nooverride This method is not intended to be re-implemented or extended
* by clients.
*/
- public synchronized void clearPackage(String packageName) {
- if (fComponentsProvidingPackageCache != null) {
- fComponentsProvidingPackageCache.remove(packageName);
- }
+ public void clearPackage(String packageName) {
+ fComponentsProvidingPackageCache.remove(packageName);
}
@Override
@@ -996,9 +1012,10 @@
@Override
public IApiComponent getApiComponent(IProject project) {
loadBaselineInfos();
- if (fComponentsByProjectNames == null) {
+ Map<String, IApiComponent> componentsByProjectNames = fComponentsByProjectNames;
+ if (disposed || componentsByProjectNames == null) {
return null;
}
- return fComponentsByProjectNames.get(project.getName());
+ return componentsByProjectNames.get(project.getName());
}
}
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/BundleComponent.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/BundleComponent.java
index 74511b8..6fa57ba 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/BundleComponent.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/BundleComponent.java
@@ -99,7 +99,7 @@
/**
* Dictionary parsed from MANIFEST.MF
*/
- private Map<String, String> fManifest;
+ private volatile Map<String, String> fManifest;
/**
* Manifest headers that are maintained after {@link BundleDescription}
@@ -114,7 +114,7 @@
/**
* Whether there is an underlying .api_description file
*/
- private boolean fHasApiDescription = false;
+ private volatile boolean fHasApiDescription;
/**
* Root location of component in the file system
@@ -124,33 +124,35 @@
/**
* Underlying bundle description (OSGi model of a bundle)
*/
- private BundleDescription fBundleDescription;
+ private volatile BundleDescription fBundleDescription;
/**
* Symbolic name of this bundle
*/
- private String fSymbolicName = null;
+ private String fSymbolicName;
/**
* Bundle version
*/
- private Version fVersion = null;
+ private volatile Version fVersion;
/**
* Cached value for the lowest EEs
*/
- private String[] lowestEEs;
+ private volatile String[] lowestEEs;
/**
* Flag to know if this component is a binary bundle in the workspace i.e.
* an imported binary bundle
*/
- private boolean fWorkspaceBinary = false;
+ private boolean fWorkspaceBinary;
/**
* The id of this component
*/
- private long fBundleId = 0L;
+ private long fBundleId;
+
+ private volatile boolean disposed;
/**
* Constructs a new API component from the specified location in the file
@@ -178,12 +180,16 @@
@Override
public void dispose() {
+ if (isDisposed()) {
+ return;
+ }
try {
super.dispose();
} finally {
synchronized (this) {
fManifest = null;
fBundleDescription = null;
+ disposed = true;
}
}
}
@@ -195,28 +201,39 @@
* @return manifest dictionary or <code>null</code>
* @exception CoreException if something goes terribly wrong
*/
- protected synchronized Map<String, String> getManifest() throws CoreException {
- if (fManifest == null) {
- File bundleLocation = new File(fLocation);
- try {
- fManifest = ManifestUtils.loadManifest(bundleLocation);
- } catch (CoreException e) {
- if (e.getStatus().getCode() == ManifestUtils.STATUS_CODE_NOT_A_BUNDLE_MANIFEST) {
- // If we load a component with a manifest file that isn't a
- // bundle, ignore it
- return null;
- } else {
- throw e;
- }
+ protected Map<String, String> getManifest() throws CoreException {
+ if (fManifest != null) {
+ return fManifest;
+ }
+ Map<String, String> manifest = loadManifest(new File(fLocation), isWorkspaceBinary());
+ synchronized (this) {
+ if (fManifest == null) {
+ fManifest = manifest;
}
- if (isWorkspaceBinary()) {
+ return fManifest;
+ }
+ }
+
+ private static Map<String, String> loadManifest(File bundleLocation, boolean isWorkspaceBinary)
+ throws CoreException {
+ try {
+ Map<String, String> manifest = ManifestUtils.loadManifest(bundleLocation);
+ if (isWorkspaceBinary) {
// must account for bundles in development mode - look for class
// files in output
// folders rather than jars
- TargetWeaver.weaveManifest(fManifest, bundleLocation);
+ TargetWeaver.weaveManifest(manifest, bundleLocation);
+ }
+ return manifest;
+ } catch (CoreException e) {
+ if (e.getStatus().getCode() == ManifestUtils.STATUS_CODE_NOT_A_BUNDLE_MANIFEST) {
+ // If we load a component with a manifest file that isn't a
+ // bundle, ignore it
+ return null;
+ } else {
+ throw e;
}
}
- return fManifest;
}
/**
@@ -266,29 +283,32 @@
*
* @throws CoreException on failure
*/
- protected synchronized void init() {
- if (fBundleDescription != null) {
+ protected void init() {
+ if (isDisposed() || fBundleDescription != null) {
return;
}
- try {
- Map<String, String> manifest = getManifest();
- if (manifest == null) {
- ApiPlugin.log(Status.error("Unable to find a manifest for the component from: " + fLocation, //$NON-NLS-1$
- null));
- return;
+ synchronized (this) {
+ try {
+ Map<String, String> manifest = getManifest();
+ if (manifest == null) {
+ ApiPlugin.log(Status.error("Unable to find a manifest for the component from: " + fLocation, //$NON-NLS-1$
+ null));
+ return;
+ }
+ BundleDescription bundleDescription = getBundleDescription(manifest, fLocation, fBundleId);
+ fSymbolicName = bundleDescription.getSymbolicName();
+ fVersion = bundleDescription.getVersion();
+ setName(manifest.get(Constants.BUNDLE_NAME));
+ fBundleDescription = bundleDescription;
+ } catch (BundleException e) {
+ ApiPlugin.log(Status.error("Unable to create API component from specified location: " + fLocation, //$NON-NLS-1$
+ e));
+ } catch (CoreException ce) {
+ ApiPlugin.log(ce);
}
- fBundleDescription = getBundleDescription(manifest, fLocation, fBundleId);
- fSymbolicName = fBundleDescription.getSymbolicName();
- fVersion = fBundleDescription.getVersion();
- setName(manifest.get(Constants.BUNDLE_NAME));
- } catch (BundleException e) {
- ApiPlugin.log(Status.error("Unable to create API component from specified location: " + fLocation, //$NON-NLS-1$
- e));
- } catch (CoreException ce) {
- ApiPlugin.log(ce);
+ // compact manifest after initialization - only keep used headers
+ doManifestCompaction();
}
- // compact manifest after initialization - only keep used headers
- doManifestCompaction();
}
/**
@@ -342,7 +362,7 @@
* @return the bundle for the given manifest, <code>null</code> otherwise
* @throws BundleException
*/
- protected BundleDescription lookupBundle(State state, Map<String, String> manifest) throws BundleException {
+ protected static BundleDescription lookupBundle(State state, Map<String, String> manifest) throws BundleException {
Version version = null;
try {
// just in case the version is not a number
@@ -542,7 +562,7 @@
* @see org.eclipse.pde.api.tools.internal.AbstractApiTypeContainer#createApiTypeContainers()
*/
@Override
- protected synchronized List<IApiTypeContainer> createApiTypeContainers() throws CoreException {
+ protected List<IApiTypeContainer> createApiTypeContainers() throws CoreException {
List<IApiTypeContainer> containers = new ArrayList<>(5);
List<IApiComponent> all = new ArrayList<>();
// build the classpath from bundle and all fragments
@@ -629,7 +649,7 @@
* @return classpath entries as bundle relative paths
* @throws BundleException
*/
- protected String[] getClasspathEntries(Map<String, String> manifest) throws BundleException {
+ protected static String[] getClasspathEntries(Map<String, String> manifest) throws BundleException {
ManifestElement[] classpath = ManifestElement.parseHeader(Constants.BUNDLE_CLASSPATH, manifest.get(Constants.BUNDLE_CLASSPATH));
String elements[] = null;
if (classpath == null) {
@@ -734,7 +754,7 @@
* fails to write the file(s)
* @throws IOException, CoreException
*/
- void extractDirectory(ZipFile zip, String pathprefix, File parent) throws IOException, CoreException {
+ static void extractDirectory(ZipFile zip, String pathprefix, File parent) throws IOException, CoreException {
Enumeration<? extends ZipEntry> entries = zip.entries();
String prefix = (pathprefix == null ? Util.EMPTY_STRING : pathprefix);
ZipEntry entry = null;
@@ -768,7 +788,7 @@
* otherwise
* @throws IOException
*/
- File extractEntry(ZipFile zip, ZipEntry entry, File parent) throws IOException {
+ static File extractEntry(ZipFile zip, ZipEntry entry, File parent) throws IOException {
InputStream inputStream = null;
File file;
FileOutputStream outputStream = null;
@@ -806,7 +826,7 @@
return file;
}
- public void closingZipFileAndStream(InputStream stream, ZipFile jarFile) {
+ public static void closingZipFileAndStream(InputStream stream, ZipFile jarFile) {
try {
if (stream != null) {
stream.close();
@@ -831,7 +851,7 @@
* @param bundleLocation the root location of the bundle
* @return the file contents or <code>null</code> if not present
*/
- protected String readFileContents(String xmlFileName, File bundleLocation) {
+ protected static String readFileContents(String xmlFileName, File bundleLocation) {
ZipFile jarFile = null;
InputStream stream = null;
try {
@@ -868,7 +888,7 @@
* @return API description XML as a string or <code>null</code> if none
* @throws IOException if unable to parse
*/
- protected String loadApiDescription(File bundleLocation) throws IOException {
+ protected static String loadApiDescription(File bundleLocation) throws IOException {
ZipFile jarFile = null;
InputStream stream = null;
String contents = null;
@@ -908,7 +928,7 @@
* @return URL to the file
* @throws MalformedURLException
*/
- protected URL getFileInBundle(File bundleLocation, String filePath) throws MalformedURLException {
+ protected static URL getFileInBundle(File bundleLocation, String filePath) throws MalformedURLException {
String extension = new Path(bundleLocation.getName()).getFileExtension();
StringBuilder urlSt = new StringBuilder();
if (extension != null && extension.equals("jar") && bundleLocation.isFile()) { //$NON-NLS-1$
@@ -926,11 +946,8 @@
}
@Override
- public synchronized String[] getExecutionEnvironments() throws CoreException {
- if (fBundleDescription == null) {
- baselineDisposed(getBaseline());
- }
- return fBundleDescription.getExecutionEnvironments();
+ public String[] getExecutionEnvironments() throws CoreException {
+ return getBundleDescription().getExecutionEnvironments();
}
@Override
@@ -940,11 +957,8 @@
}
@Override
- public synchronized IRequiredComponentDescription[] getRequiredComponents() throws CoreException {
- if (fBundleDescription == null) {
- baselineDisposed(getBaseline());
- }
- BundleSpecification[] requiredBundles = fBundleDescription.getRequiredBundles();
+ public IRequiredComponentDescription[] getRequiredComponents() throws CoreException {
+ BundleSpecification[] requiredBundles = getBundleDescription().getRequiredBundles();
IRequiredComponentDescription[] req = new IRequiredComponentDescription[requiredBundles.length];
for (int i = 0; i < requiredBundles.length; i++) {
BundleSpecification bundle = requiredBundles[i];
@@ -954,7 +968,7 @@
}
@Override
- public synchronized String getVersion() {
+ public String getVersion() {
init();
// remove the qualifier
StringBuilder buffer = new StringBuilder();
@@ -971,14 +985,16 @@
/**
* Returns this component's bundle description.
*
- * @return bundle description
+ * @return bundle description, never null
+ * @throws CoreException if this component or the baseline is already disposed
*/
- public synchronized BundleDescription getBundleDescription() throws CoreException {
+ public BundleDescription getBundleDescription() throws CoreException {
init();
- if (fBundleDescription == null) {
+ BundleDescription description = fBundleDescription;
+ if (isDisposed() || description == null) {
baselineDisposed(getBaseline());
}
- return fBundleDescription;
+ return description;
}
@Override
@@ -995,16 +1011,20 @@
buffer.append("[dev bundle: ").append(fWorkspaceBinary).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
return buffer.toString();
} catch (CoreException ce) {
- // ignore
+ return super.toString();
}
} else {
StringBuilder buffer = new StringBuilder();
- buffer.append("Un-initialized Bundle Component"); //$NON-NLS-1$
+ if (isDisposed()) {
+ buffer.append("Disposed "); //$NON-NLS-1$
+ } else {
+ buffer.append("Un-initialized "); //$NON-NLS-1$
+ }
+ buffer.append("Bundle Component"); //$NON-NLS-1$
buffer.append("[location: ").append(fLocation).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
buffer.append("[dev bundle: ").append(fWorkspaceBinary).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
return buffer.toString();
}
- return super.toString();
}
@Override
@@ -1018,11 +1038,15 @@
}
@Override
- public synchronized boolean isSourceComponent() throws CoreException {
+ public boolean isSourceComponent() throws CoreException {
Map<String, String> manifest = getManifest();
if (manifest == null) {
baselineDisposed(getBaseline());
}
+ return isSourceComponent(manifest, new File(getLocation()));
+ }
+
+ private static boolean isSourceComponent(Map<String, String> manifest, File location) {
ManifestElement[] sourceBundle = null;
try {
sourceBundle = ManifestElement.parseHeader(IApiCoreConstants.ECLIPSE_SOURCE_BUNDLE, manifest.get(IApiCoreConstants.ECLIPSE_SOURCE_BUNDLE));
@@ -1034,7 +1058,7 @@
return true;
}
// check for the old format
- String pluginXMLContents = readFileContents(IApiCoreConstants.PLUGIN_XML_NAME, new File(getLocation()));
+ String pluginXMLContents = readFileContents(IApiCoreConstants.PLUGIN_XML_NAME, location);
if (pluginXMLContents != null) {
if (containsSourceExtensionPoint(pluginXMLContents)) {
return true;
@@ -1042,7 +1066,7 @@
}
// check if it contains a fragment.xml with the appropriate extension
// point
- pluginXMLContents = readFileContents(IApiCoreConstants.FRAGMENT_XML_NAME, new File(getLocation()));
+ pluginXMLContents = readFileContents(IApiCoreConstants.FRAGMENT_XML_NAME, location);
if (pluginXMLContents != null) {
if (containsSourceExtensionPoint(pluginXMLContents)) {
return true;
@@ -1058,7 +1082,7 @@
* @param pluginXMLContents the given file contents
* @return true if it contains a source extension point, false otherwise
*/
- private boolean containsSourceExtensionPoint(String pluginXMLContents) {
+ private static boolean containsSourceExtensionPoint(String pluginXMLContents) {
SAXParserFactory factory = null;
try {
factory = SAXParserFactory.newInstance();
@@ -1089,21 +1113,13 @@
}
@Override
- public synchronized boolean isFragment() throws CoreException {
- init();
- if (fBundleDescription == null) {
- baselineDisposed(getBaseline());
- }
- return fBundleDescription.getHost() != null;
+ public boolean isFragment() throws CoreException {
+ return getBundleDescription().getHost() != null;
}
@Override
- public synchronized IApiComponent getHost() throws CoreException {
- init();
- if (fBundleDescription == null) {
- baselineDisposed(getBaseline());
- }
- HostSpecification host = fBundleDescription.getHost();
+ public IApiComponent getHost() throws CoreException {
+ HostSpecification host = getBundleDescription().getHost();
if (host != null) {
return getBaseline().getApiComponent(host.getName());
}
@@ -1111,7 +1127,7 @@
}
@Override
- public synchronized boolean hasFragments() throws CoreException {
+ public boolean hasFragments() throws CoreException {
return getBundleDescription().getFragments().length != 0;
}
@@ -1141,8 +1157,17 @@
if (lowestEEs != null) {
return lowestEEs;
}
- String[] temp = null;
String[] executionEnvironments = getExecutionEnvironments();
+ String[] ees = computeLowestEEs(executionEnvironments);
+ synchronized (this) {
+ lowestEEs = ees;
+ return lowestEEs;
+ }
+ }
+
+ private static String[] computeLowestEEs(String[] executionEnvironments) {
+ String[] temp = null;
+
int length = executionEnvironments.length;
switch (length) {
case 0:
@@ -1224,19 +1249,15 @@
}
}
}
- lowestEEs = temp;
return temp;
}
@Override
- public synchronized ResolverError[] getErrors() throws CoreException {
- init();
+ public ResolverError[] getErrors() throws CoreException {
ApiBaseline baseline = (ApiBaseline) getBaseline();
- if (fBundleDescription == null) {
- baselineDisposed(baseline);
- }
if (baseline != null) {
- ResolverError[] resolverErrors = baseline.getState().getResolverErrors(fBundleDescription);
+ BundleDescription bundleDescription = getBundleDescription();
+ ResolverError[] resolverErrors = baseline.getState().getResolverErrors(bundleDescription);
if (resolverErrors.length == 0) {
return null;
}
@@ -1250,6 +1271,11 @@
* @throws CoreException with the baseline disposed information
*/
protected void baselineDisposed(IApiBaseline baseline) throws CoreException {
- throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, ApiPlugin.REPORT_BASELINE_IS_DISPOSED, NLS.bind(Messages.BundleApiComponent_baseline_disposed, baseline.getName()), null));
+ throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, ApiPlugin.REPORT_BASELINE_IS_DISPOSED,
+ NLS.bind(Messages.BundleApiComponent_baseline_disposed, getName(), baseline.getName()), null));
+ }
+
+ protected boolean isDisposed() {
+ return disposed;
}
}
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/Component.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/Component.java
index a66bbab..a9ad50c 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/Component.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/Component.java
@@ -37,17 +37,17 @@
/**
* API description
*/
- private IApiDescription fApiDescription = null;
+ private volatile IApiDescription fApiDescription;
/**
* API Filter store
*/
- private IApiFilterStore fFilterStore = null;
+ private volatile IApiFilterStore fFilterStore;
/**
* References in API use scan reports
*/
- private IReferenceCollection fReferences;
+ private volatile IReferenceCollection fReferences;
/**
* Constructs an API component in the given {@link IApiBaseline}.
@@ -95,8 +95,11 @@
}
@Override
- public synchronized IApiDescription getApiDescription() throws CoreException {
- if (fApiDescription == null) {
+ public IApiDescription getApiDescription() throws CoreException {
+ if (fApiDescription != null) {
+ return fApiDescription;
+ }
+ synchronized (this) {
fApiDescription = createApiDescription();
}
return fApiDescription;
@@ -107,7 +110,7 @@
*
* @return whether this component has created an API description
*/
- protected synchronized boolean isApiDescriptionInitialized() {
+ protected boolean isApiDescriptionInitialized() {
return fApiDescription != null;
}
@@ -116,17 +119,17 @@
*
* @return true if a store has been created, false other wise
*/
- protected synchronized boolean hasApiFilterStore() {
+ protected boolean hasApiFilterStore() {
return fFilterStore != null;
}
@Override
- public synchronized IApiTypeContainer[] getApiTypeContainers() throws CoreException {
+ public IApiTypeContainer[] getApiTypeContainers() throws CoreException {
return super.getApiTypeContainers();
}
@Override
- public synchronized IApiTypeContainer[] getApiTypeContainers(String id) throws CoreException {
+ public IApiTypeContainer[] getApiTypeContainers(String id) throws CoreException {
if (this.hasFragments()) {
return super.getApiTypeContainers(id);
} else {
@@ -144,7 +147,11 @@
@Override
public IApiFilterStore getFilterStore() throws CoreException {
if (fFilterStore == null) {
- fFilterStore = createApiFilterStore();
+ synchronized (this) {
+ if (fFilterStore == null) {
+ fFilterStore = createApiFilterStore();
+ }
+ }
}
return fFilterStore;
}
@@ -170,7 +177,11 @@
@Override
public IReferenceCollection getExternalDependencies() {
if (fReferences == null) {
- fReferences = new UseScanReferences();
+ synchronized (this) {
+ if (fReferences == null) {
+ fReferences = new UseScanReferences();
+ }
+ }
}
return fReferences;
}
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/CompositeApiTypeContainer.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/CompositeApiTypeContainer.java
index cdcbfd7..5596f19 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/CompositeApiTypeContainer.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/CompositeApiTypeContainer.java
@@ -45,6 +45,12 @@
}
@Override
+ public void close() throws CoreException {
+ fContainers.clear();
+ super.close();
+ }
+
+ @Override
public String toString() {
StringBuilder buff = new StringBuilder();
buff.append("Composite Class File Container:\n"); //$NON-NLS-1$
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/Messages.properties b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/Messages.properties
index 0364a20..2d54b7d 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/Messages.properties
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/Messages.properties
@@ -16,7 +16,7 @@
ApiType_2=Unsupported operation - API component required for resolution
ApiType_3=Unable to resolve member type {0} for {1}
ApiScope_0=Unable to visit this element type: {0}
-BundleApiComponent_baseline_disposed=Baseline ''{0}'' is disposed
+BundleApiComponent_baseline_disposed=Component ''{0}'' in the baseline ''{1}'' is disposed
BundleComponent_failed_to_lookup_fragment=Failed to look up resolved fragment: {0}
configuring_baseline=Configuring baseline
resolving_target_definition=resolving target definition...
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ProjectComponent.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ProjectComponent.java
index 7239c25..7fe2a2c 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ProjectComponent.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/ProjectComponent.java
@@ -15,9 +15,9 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
@@ -39,7 +39,6 @@
import org.eclipse.pde.api.tools.internal.provisional.IApiDescription;
import org.eclipse.pde.api.tools.internal.provisional.IApiFilterStore;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
-import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeContainer;
import org.eclipse.pde.api.tools.internal.util.Util;
import org.eclipse.pde.core.build.IBuild;
@@ -77,22 +76,22 @@
/**
* Associated Java project
*/
- private IJavaProject fProject = null;
+ private IJavaProject fProject;
/**
* Associated IPluginModelBase object
*/
- private IPluginModelBase fModel = null;
+ private IPluginModelBase fModel;
/**
* A cache of bundle class path entries to class file containers.
*/
- private Map<String, IApiTypeContainer> fPathToOutputContainers = null;
+ private volatile Map<String, IApiTypeContainer> fPathToOutputContainers;
/**
* A cache of output location paths to corresponding class file containers.
*/
- private Map<IPath, IApiTypeContainer> fOutputLocationToContainer = null;
+ private volatile Map<IPath, IApiTypeContainer> fOutputLocationToContainer;
/**
* Constructs an API component for the given Java project in the specified
@@ -160,6 +159,9 @@
@Override
public void dispose() {
+ if (isDisposed()) {
+ return;
+ }
try {
if (hasApiFilterStore()) {
getFilterStore().dispose();
@@ -204,114 +206,131 @@
}
@Override
- protected synchronized List<IApiTypeContainer> createApiTypeContainers() throws CoreException {
+ protected List<IApiTypeContainer> createApiTypeContainers() throws CoreException {
+ if (isDisposed()) {
+ baselineDisposed(getBaseline());
+ }
// first populate build.properties cache so we can create class file
// containers
// from bundle classpath entries
- fPathToOutputContainers = new HashMap<>(4);
- fOutputLocationToContainer = new HashMap<>(4);
+ fPathToOutputContainers = new ConcurrentHashMap<>(4);
+ fOutputLocationToContainer = new ConcurrentHashMap<>(4);
if (fProject.exists() && fProject.getProject().isOpen()) {
IPluginModelBase model = PluginRegistry.findModel(fProject.getProject());
if (model != null) {
- IBuildModel buildModel = PluginRegistry.createBuildModel(model);
- if (buildModel != null) {
- IBuild build = buildModel.getBuild();
- IBuildEntry entry = build.getEntry(ENTRY_CUSTOM);
- if (entry != null) {
- String[] tokens = entry.getTokens();
- if (tokens.length == 1 && tokens[0].equals("true")) { //$NON-NLS-1$
- // hack : add the current output location for each
- // classpath entries
- IClasspathEntry[] classpathEntries = fProject.getRawClasspath();
- List<IApiTypeContainer> containers = new ArrayList<>();
- for (IClasspathEntry classpathEntrie : classpathEntries) {
- IClasspathEntry classpathEntry = classpathEntrie;
- switch (classpathEntry.getEntryKind()) {
- case IClasspathEntry.CPE_SOURCE:
- String containerPath = classpathEntry.getPath().removeFirstSegments(1).toString();
- IApiTypeContainer container = getApiTypeContainer(containerPath, this);
- if (container != null && !containers.contains(container)) {
- containers.add(container);
- }
- break;
- case IClasspathEntry.CPE_VARIABLE:
- classpathEntry = JavaCore.getResolvedClasspathEntry(classpathEntry);
- //$FALL-THROUGH$
- case IClasspathEntry.CPE_LIBRARY:
- IPath path = classpathEntry.getPath();
- if (Util.isArchive(path.lastSegment())) {
- IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
- if (resource != null) {
- // jar inside the workspace
- containers.add(new ArchiveApiTypeContainer(this, resource.getLocation().toOSString()));
- } else {
- // external jar
- containers.add(new ArchiveApiTypeContainer(this, path.toOSString()));
- }
- }
- break;
- default:
- break;
- }
- }
- if (!containers.isEmpty()) {
- IApiTypeContainer cfc = null;
- if (containers.size() == 1) {
- cfc = containers.get(0);
- } else {
- cfc = new CompositeApiTypeContainer(this, containers);
- }
- fPathToOutputContainers.put(".", cfc); //$NON-NLS-1$
- }
- }
- } else {
- IBuildEntry[] entries = build.getBuildEntries();
- int length = entries.length;
- for (int i = 0; i < length; i++) {
- IBuildEntry buildEntry = entries[i];
- String name = buildEntry.getName();
- if (name.startsWith(IBuildEntry.JAR_PREFIX)) {
- retrieveContainers(name, IBuildEntry.JAR_PREFIX, buildEntry);
- } else if (name.startsWith(EXTRA_PREFIX)) {
- retrieveContainers(name, EXTRA_PREFIX, buildEntry);
- }
- }
- }
- }
+ createContainersFromProjectModel(model, this, fPathToOutputContainers, fOutputLocationToContainer);
}
return super.createApiTypeContainers();
}
return Collections.emptyList();
}
- private void retrieveContainers(String name, String prefix, IBuildEntry buildEntry) throws CoreException {
+ private static void createContainersFromProjectModel(IPluginModelBase model, ProjectComponent project,
+ Map<String, IApiTypeContainer> pathToOutputContainers,
+ Map<IPath, IApiTypeContainer> outputLocationToContainer) throws CoreException {
+ IBuildModel buildModel = PluginRegistry.createBuildModel(model);
+ if (buildModel == null) {
+ return;
+ }
+ IBuild build = buildModel.getBuild();
+ IBuildEntry entry = build.getEntry(ENTRY_CUSTOM);
+ if (entry != null) {
+ String[] tokens = entry.getTokens();
+ if (tokens.length == 1 && tokens[0].equals("true")) { //$NON-NLS-1$
+ // hack : add the current output location for each
+ // classpath entries
+ IClasspathEntry[] classpathEntries = project.fProject.getRawClasspath();
+ List<IApiTypeContainer> containers = new ArrayList<>();
+ for (IClasspathEntry classpathEntrie : classpathEntries) {
+ IClasspathEntry classpathEntry = classpathEntrie;
+ switch (classpathEntry.getEntryKind())
+ {
+ case IClasspathEntry.CPE_SOURCE:
+ String containerPath = classpathEntry.getPath().removeFirstSegments(1).toString();
+ IApiTypeContainer container = getApiTypeContainer(containerPath, project,
+ outputLocationToContainer);
+ if (container != null && !containers.contains(container)) {
+ containers.add(container);
+ }
+ break;
+ case IClasspathEntry.CPE_VARIABLE:
+ classpathEntry = JavaCore.getResolvedClasspathEntry(classpathEntry);
+ //$FALL-THROUGH$
+ case IClasspathEntry.CPE_LIBRARY:
+ IPath path = classpathEntry.getPath();
+ if (Util.isArchive(path.lastSegment())) {
+ IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+ if (resource != null) {
+ // jar inside the workspace
+ containers.add(
+ new ArchiveApiTypeContainer(project, resource.getLocation().toOSString()));
+ } else {
+ // external jar
+ containers.add(new ArchiveApiTypeContainer(project, path.toOSString()));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (!containers.isEmpty()) {
+ IApiTypeContainer cfc = null;
+ if (containers.size() == 1) {
+ cfc = containers.get(0);
+ } else {
+ cfc = new CompositeApiTypeContainer(project, containers);
+ }
+ pathToOutputContainers.put(".", cfc); //$NON-NLS-1$
+ }
+ }
+ } else {
+ IBuildEntry[] entries = build.getBuildEntries();
+ int length = entries.length;
+ for (int i = 0; i < length; i++) {
+ IBuildEntry buildEntry = entries[i];
+ String name = buildEntry.getName();
+ if (name.startsWith(IBuildEntry.JAR_PREFIX)) {
+ retrieveContainers(name, IBuildEntry.JAR_PREFIX, buildEntry, project, pathToOutputContainers,
+ outputLocationToContainer);
+ } else if (name.startsWith(EXTRA_PREFIX)) {
+ retrieveContainers(name, EXTRA_PREFIX, buildEntry, project, pathToOutputContainers,
+ outputLocationToContainer);
+ }
+ }
+ }
+ }
+
+ private static void retrieveContainers(String name, String prefix, IBuildEntry buildEntry, ProjectComponent project,
+ Map<String, IApiTypeContainer> pathToOutputContainers,
+ Map<IPath, IApiTypeContainer> outputLocationToContainer) throws CoreException {
String jar = name.substring(prefix.length());
String[] tokens = buildEntry.getTokens();
if (tokens.length == 1) {
- IApiTypeContainer container = getApiTypeContainer(tokens[0], this);
+ IApiTypeContainer container = getApiTypeContainer(tokens[0], project, outputLocationToContainer);
if (container != null) {
- IApiTypeContainer existingContainer = this.fPathToOutputContainers.get(jar);
+ IApiTypeContainer existingContainer = pathToOutputContainers.get(jar);
if (existingContainer != null) {
// concat both containers
List<IApiTypeContainer> allContainers = new ArrayList<>();
allContainers.add(existingContainer);
allContainers.add(container);
- IApiTypeContainer apiTypeContainer = new CompositeApiTypeContainer(this, allContainers);
- fPathToOutputContainers.put(jar, apiTypeContainer);
+ IApiTypeContainer apiTypeContainer = new CompositeApiTypeContainer(project, allContainers);
+ pathToOutputContainers.put(jar, apiTypeContainer);
} else {
- fPathToOutputContainers.put(jar, container);
+ pathToOutputContainers.put(jar, container);
}
}
} else {
List<IApiTypeContainer> containers = new ArrayList<>();
for (String currentToken : tokens) {
- IApiTypeContainer container = getApiTypeContainer(currentToken, this);
+ IApiTypeContainer container = getApiTypeContainer(currentToken, project, outputLocationToContainer);
if (container != null && !containers.contains(container)) {
containers.add(container);
}
}
if (!containers.isEmpty()) {
- IApiTypeContainer existingContainer = this.fPathToOutputContainers.get(jar);
+ IApiTypeContainer existingContainer = pathToOutputContainers.get(jar);
if (existingContainer != null) {
// concat both containers
containers.add(existingContainer);
@@ -320,21 +339,21 @@
if (containers.size() == 1) {
cfc = containers.get(0);
} else {
- cfc = new CompositeApiTypeContainer(this, containers);
+ cfc = new CompositeApiTypeContainer(project, containers);
}
- fPathToOutputContainers.put(jar, cfc);
+ pathToOutputContainers.put(jar, cfc);
}
}
}
@Override
protected IApiTypeContainer createApiTypeContainer(String path) throws CoreException {
- if (this.fPathToOutputContainers == null) {
+ if (isDisposed() || this.fPathToOutputContainers == null) {
baselineDisposed(getBaseline());
}
IApiTypeContainer container = fPathToOutputContainers.get(path);
if (container == null) {
- // could be a binary jar included in the plug-in, just look for it
+ // could be a binary jar included in the plug-in, just look for it
container = findApiTypeContainer(path);
}
return container;
@@ -368,22 +387,21 @@
* @param location project relative path to the source folder
* @return {@link IApiTypeContainer} or <code>null</code>
*/
- private IApiTypeContainer getApiTypeContainer(String location, IApiComponent component) throws CoreException {
- if (this.fOutputLocationToContainer == null) {
- baselineDisposed(getBaseline());
- }
- IResource res = fProject.getProject().findMember(new Path(location));
+ private static IApiTypeContainer getApiTypeContainer(String location, ProjectComponent component,
+ Map<IPath, IApiTypeContainer> outputLocationToContainer) throws CoreException {
+ IJavaProject project = component.fProject;
+ IResource res = project.getProject().findMember(new Path(location));
if (res != null) {
- IPackageFragmentRoot root = fProject.getPackageFragmentRoot(res);
+ IPackageFragmentRoot root = project.getPackageFragmentRoot(res);
if (root.exists()) {
if (root.getKind() == IPackageFragmentRoot.K_BINARY) {
if (res.getType() == IResource.FOLDER) {
// class file folder
IPath location2 = res.getLocation();
- IApiTypeContainer cfc = fOutputLocationToContainer.get(location2);
+ IApiTypeContainer cfc = outputLocationToContainer.get(location2);
if (cfc == null) {
cfc = new ProjectTypeContainer(component, (IContainer) res);
- fOutputLocationToContainer.put(location2, cfc);
+ outputLocationToContainer.put(location2, cfc);
}
return cfc;
}
@@ -391,20 +409,20 @@
IClasspathEntry entry = root.getRawClasspathEntry();
IPath outputLocation = entry.getOutputLocation();
if (outputLocation == null) {
- outputLocation = fProject.getOutputLocation();
+ outputLocation = project.getOutputLocation();
}
- IApiTypeContainer cfc = fOutputLocationToContainer.get(outputLocation);
+ IApiTypeContainer cfc = outputLocationToContainer.get(outputLocation);
if (cfc == null) {
- IPath projectFullPath = fProject.getProject().getFullPath();
+ IPath projectFullPath = project.getProject().getFullPath();
IContainer container = null;
if (projectFullPath.equals(outputLocation)) {
// The project is its own output location
- container = fProject.getProject();
+ container = project.getProject();
} else {
- container = fProject.getProject().getWorkspace().getRoot().getFolder(outputLocation);
+ container = project.getProject().getWorkspace().getRoot().getFolder(outputLocation);
}
cfc = new ProjectTypeContainer(component, container);
- fOutputLocationToContainer.put(outputLocation, cfc);
+ outputLocationToContainer.put(outputLocation, cfc);
}
return cfc;
}
@@ -435,11 +453,14 @@
*/
public IApiTypeContainer getTypeContainer(IPackageFragmentRoot root) throws CoreException {
if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
+ if (isDisposed()) {
+ baselineDisposed(getBaseline());
+ }
getApiTypeContainers(); // ensure initialized
IResource resource = root.getResource();
if (resource != null) {
String location = resource.getProjectRelativePath().toString();
- return getApiTypeContainer(location, this);
+ return getApiTypeContainer(location, this, fOutputLocationToContainer);
}
}
return null;
diff --git a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/WorkspaceBaseline.java b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/WorkspaceBaseline.java
index 9d173a2..046d62e 100644
--- a/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/WorkspaceBaseline.java
+++ b/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/model/WorkspaceBaseline.java
@@ -13,9 +13,12 @@
*******************************************************************************/
package org.eclipse.pde.api.tools.internal.model;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.osgi.service.resolver.State;
@@ -35,7 +38,12 @@
// If workspace is changed, then WorkspaceBaseline is disposed and a
// new WorkspaceBaseline is created, hence mismatch problem can be stored
// with a workspace baseline
- public HashMap<IApiBaseline, IApiProblem> mismatch = new HashMap<>();
+ public Map<IApiBaseline, IApiProblem> mismatch = new ConcurrentHashMap<>();
+
+ private static final IApiProblem NULL_PROBLEM = (IApiProblem) Proxy.newProxyInstance(
+ IApiProblem.class.getClassLoader(), new Class<?>[] { IApiProblem.class },
+ (InvocationHandler) (proxy, method, args) -> null);
+
/**
* Constructor
*/
@@ -61,12 +69,16 @@
// can be null showing no problem
public IApiProblem getProblem(IApiBaseline b) {
- return mismatch.get(b);
+ IApiProblem problem = mismatch.get(b);
+ return problem == NULL_PROBLEM ? null : problem;
}
public void putMismatchInfo(IApiBaseline baseline, IApiProblem problem) {
- mismatch.put(baseline, problem);
-
+ if (problem == null) {
+ mismatch.put(baseline, NULL_PROBLEM);
+ } else {
+ mismatch.put(baseline, problem);
+ }
}
@Override