1
1
/*******************************************************************************
2
- * Copyright (c) 2008, 2011 Sonatype Inc. and others.
2
+ * Copyright (c) 2008, 2022 Sonatype Inc. and others.
3
3
* All rights reserved. This program and the accompanying materials
4
4
* are made available under the terms of the Eclipse Public License v1.0
5
5
* which accompanies this distribution, and is available at
6
6
* http://www.eclipse.org/legal/epl-v10.html
7
7
*
8
8
* Contributors:
9
9
* Sonatype Inc. - initial API and implementation
10
+ * Christoph Läubrich - Issue #663 - Access to the tycho .cache directory is not properly synchronized
10
11
*******************************************************************************/
11
12
package org .eclipse .tycho .core .osgitools ;
12
13
18
19
import java .nio .file .StandardCopyOption ;
19
20
import java .util .Enumeration ;
20
21
import java .util .HashMap ;
21
- import java .util .HashSet ;
22
22
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 ;
24
26
import java .util .jar .JarFile ;
25
27
import java .util .zip .ZipEntry ;
26
28
import java .util .zip .ZipFile ;
34
36
@ Component (role = BundleReader .class )
35
37
public class DefaultBundleReader extends AbstractLogEnabled implements BundleReader {
36
38
39
+ private static final long LOCK_TIMEOUT = Long .getLong ("tycho.bundlereader.lock.timeout" , 5 * 60 * 1000L );
37
40
public static final String CACHE_PATH = ".cache/tycho" ;
38
41
private final Map <String , OsgiManifest > manifestCache = new HashMap <>();
39
42
40
43
private File cacheDir ;
41
- private Set <String > extractedFiles = new HashSet <>();
44
+ private ConcurrentMap <String , Optional < File >> extractedFiles = new ConcurrentHashMap <>();
42
45
43
46
@ Requirement
44
47
private FileLockService fileLockService ;
@@ -109,32 +112,42 @@ public File getEntry(File bundleLocation, String path) {
109
112
getLogger ().warn ("Ignoring Bundle-ClassPath entry '" + path + "' of bundle " + bundleLocation );
110
113
return null ;
111
114
}
112
- final File result ;
115
+ final Optional < File > result ;
113
116
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
+ }
115
123
} else {
124
+ String cacheKey ;
125
+ File outputDirectory = new File (cacheDir , bundleLocation .getName ());
126
+ File cacheFile = new File (outputDirectory , path );
116
127
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 ();
132
129
} 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 );
134
131
}
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
+ });
135
148
}
136
- if (result .exists ()) {
137
- return result ;
149
+ if (result .isPresent ()) {
150
+ return result . get () ;
138
151
} else {
139
152
getLogger ().warn ("Bundle-ClassPath entry " + path + " does not exist in " + bundleLocation );
140
153
return null ;
@@ -147,16 +160,16 @@ private void extractZipEntries(File bundleLocation, String path, File outputDire
147
160
InputStream singleEntryStream ;
148
161
if (singleEntry != null && !singleEntry .isDirectory ()
149
162
&& (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 ());
153
166
} 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)
155
168
for (Enumeration <? extends ZipEntry > entries = zip .entries (); entries .hasMoreElements ();) {
156
169
ZipEntry zipEntry = entries .nextElement ();
157
170
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 ());
160
173
}
161
174
}
162
175
}
0 commit comments