Skip to content

Commit 8e5c5c8

Browse files
committed
Fix #663 - Access to the tycho .cache directory is not properly
synchronized Signed-off-by: Christoph Läubrich <laeubi@laeubi-soft.de>
1 parent f330d0b commit 8e5c5c8

File tree

1 file changed

+43
-30
lines changed

1 file changed

+43
-30
lines changed

tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/DefaultBundleReader.java

+43-30
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
/*******************************************************************************
2-
* Copyright (c) 2008, 2011 Sonatype Inc. and others.
2+
* Copyright (c) 2008, 2022 Sonatype Inc. and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
66
* http://www.eclipse.org/legal/epl-v10.html
77
*
88
* Contributors:
99
* Sonatype Inc. - initial API and implementation
10+
* Christoph Läubrich - Issue #663 - Access to the tycho .cache directory is not properly synchronized
1011
*******************************************************************************/
1112
package org.eclipse.tycho.core.osgitools;
1213

@@ -18,9 +19,10 @@
1819
import java.nio.file.StandardCopyOption;
1920
import java.util.Enumeration;
2021
import java.util.HashMap;
21-
import java.util.HashSet;
2222
import java.util.Map;
23-
import java.util.Set;
23+
import java.util.Optional;
24+
import java.util.concurrent.ConcurrentHashMap;
25+
import java.util.concurrent.ConcurrentMap;
2426
import java.util.jar.JarFile;
2527
import java.util.zip.ZipEntry;
2628
import java.util.zip.ZipFile;
@@ -34,11 +36,12 @@
3436
@Component(role = BundleReader.class)
3537
public class DefaultBundleReader extends AbstractLogEnabled implements BundleReader {
3638

39+
private static final long LOCK_TIMEOUT = Long.getLong("tycho.bundlereader.lock.timeout", 5 * 60 * 1000L);
3740
public static final String CACHE_PATH = ".cache/tycho";
3841
private final Map<String, OsgiManifest> manifestCache = new HashMap<>();
3942

4043
private File cacheDir;
41-
private Set<String> extractedFiles = new HashSet<>();
44+
private ConcurrentMap<String, Optional<File>> extractedFiles = new ConcurrentHashMap<>();
4245

4346
@Requirement
4447
private FileLockService fileLockService;
@@ -109,32 +112,42 @@ public File getEntry(File bundleLocation, String path) {
109112
getLogger().warn("Ignoring Bundle-ClassPath entry '" + path + "' of bundle " + bundleLocation);
110113
return null;
111114
}
112-
final File result;
115+
final Optional<File> result;
113116
if (bundleLocation.isDirectory()) {
114-
result = new File(bundleLocation, path);
117+
File file = new File(bundleLocation, path);
118+
if (file.exists()) {
119+
result = Optional.of(file);
120+
} else {
121+
result = Optional.empty();
122+
}
115123
} else {
124+
String cacheKey;
125+
File outputDirectory = new File(cacheDir, bundleLocation.getName());
126+
File cacheFile = new File(outputDirectory, path);
116127
try {
117-
File outputDirectory = new File(cacheDir, bundleLocation.getName());
118-
result = new File(outputDirectory, path);
119-
String resultPath = result.getCanonicalPath();
120-
if (extractedFiles.contains(resultPath) && result.exists()) {
121-
return result;
122-
} else {
123-
FileLocker locker = fileLockService.getFileLocker(outputDirectory);
124-
locker.lock(5 * 60 * 1000L);
125-
try {
126-
extractZipEntries(bundleLocation, path, outputDirectory);
127-
} finally {
128-
locker.release();
129-
}
130-
extractedFiles.add(resultPath);
131-
}
128+
cacheKey = cacheFile.getCanonicalPath();
132129
} catch (IOException e) {
133-
throw new RuntimeException("IOException while extracting '" + path + "' from " + bundleLocation, e);
130+
throw new RuntimeException("can't get canonical path for " + cacheFile, e);
134131
}
132+
result = extractedFiles.computeIfAbsent(cacheKey, nil -> {
133+
FileLocker locker = fileLockService.getFileLocker(outputDirectory);
134+
locker.lock(LOCK_TIMEOUT);
135+
try {
136+
extractZipEntries(bundleLocation, path, outputDirectory);
137+
if (cacheFile.exists()) {
138+
return Optional.of(cacheFile);
139+
}
140+
return Optional.empty();
141+
} catch (IOException e) {
142+
throw new RuntimeException(
143+
"Can't extract '" + path + "' from " + bundleLocation + " to " + outputDirectory, e);
144+
} finally {
145+
locker.release();
146+
}
147+
});
135148
}
136-
if (result.exists()) {
137-
return result;
149+
if (result.isPresent()) {
150+
return result.get();
138151
} else {
139152
getLogger().warn("Bundle-ClassPath entry " + path + " does not exist in " + bundleLocation);
140153
return null;
@@ -147,16 +160,16 @@ private void extractZipEntries(File bundleLocation, String path, File outputDire
147160
InputStream singleEntryStream;
148161
if (singleEntry != null && !singleEntry.isDirectory()
149162
&& (singleEntryStream = zip.getInputStream(singleEntry)) != null) {
150-
// fix for performance bug 367098: avoid loop if path is a single zip file entry
151-
copyStreamToFile(singleEntryStream, new File(outputDirectory, singleEntry.getName()),
152-
singleEntry.getTime());
163+
// extract an exact match
164+
File outputFile = new File(outputDirectory, singleEntry.getName());
165+
copyStreamToFile(singleEntryStream, outputFile, singleEntry.getTime());
153166
} else {
154-
// loop over all entries and extract matching
167+
// loop over all entries and extract matching (e.g. in case we wan't a directory entry extracted)
155168
for (Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements();) {
156169
ZipEntry zipEntry = entries.nextElement();
157170
if (!zipEntry.isDirectory() && zipEntry.getName().startsWith(path)) {
158-
copyStreamToFile(zip.getInputStream(zipEntry), new File(outputDirectory, zipEntry.getName()),
159-
zipEntry.getTime());
171+
File outputFile = new File(outputDirectory, zipEntry.getName());
172+
copyStreamToFile(zip.getInputStream(zipEntry), outputFile, zipEntry.getTime());
160173
}
161174
}
162175
}

0 commit comments

Comments
 (0)