Lines 7-49
Link Here
|
7 |
* http://www.sun.com/ |
7 |
* http://www.sun.com/ |
8 |
* |
8 |
* |
9 |
* The Original Code is NetBeans. The Initial Developer of the Original |
9 |
* The Original Code is NetBeans. The Initial Developer of the Original |
10 |
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun |
10 |
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun |
11 |
* Microsystems, Inc. All Rights Reserved. |
11 |
* Microsystems, Inc. All Rights Reserved. |
12 |
*/ |
12 |
*/ |
13 |
|
13 |
|
14 |
package org.netbeans.core.projects; |
14 |
package org.netbeans.core.projects; |
15 |
|
15 |
|
16 |
import java.beans.*; |
16 |
import java.beans.*; |
17 |
import java.io.File; |
17 |
import java.io.*; |
18 |
import java.io.IOException; |
18 |
import java.net.*; |
19 |
import java.net.URL; |
|
|
20 |
import java.util.*; |
19 |
import java.util.*; |
21 |
|
20 |
|
|
|
21 |
import org.xml.sax.SAXException; |
22 |
|
23 |
import org.openide.ErrorManager; |
22 |
import org.openide.TopManager; |
24 |
import org.openide.TopManager; |
|
|
25 |
import org.openide.filesystems.*; |
23 |
import org.openide.filesystems.FileSystem; |
26 |
import org.openide.filesystems.FileSystem; |
24 |
import org.openide.filesystems.MultiFileSystem; |
27 |
|
25 |
import org.openide.filesystems.XMLFileSystem; |
28 |
import org.netbeans.core.perftool.StartLog; |
26 |
import org.openide.ErrorManager; |
|
|
27 |
|
29 |
|
28 |
/** Layered file system serving itself as either the user or installation layer. |
30 |
/** Layered file system serving itself as either the user or installation layer. |
29 |
* Holds one layer of a writable system directory, and some number |
31 |
* Holds one layer of a writable system directory, and some number |
30 |
* of module layers. |
32 |
* of module layers. |
31 |
* @author Jesse Glick |
33 |
* @author Jesse Glick, Jaroslav Tulach |
32 |
*/ |
34 |
*/ |
33 |
public class ModuleLayeredFileSystem extends MultiFileSystem { |
35 |
public class ModuleLayeredFileSystem extends MultiFileSystem { |
34 |
/** serial version UID */ |
36 |
/** serial version UID */ |
35 |
private static final long serialVersionUID = 782910986724201983L; |
37 |
private static final long serialVersionUID = 782910986724201983L; |
|
|
38 |
|
39 |
static final ErrorManager err = ErrorManager.getDefault().getInstance("org.netbeans.core.projects"); // NOI18N |
40 |
|
41 |
/** current list of URLs - r/o; or null if not yet set */ |
42 |
private List urls; // List<URL> |
43 |
/** cache file, or null */ |
44 |
private final File cacheFile; |
36 |
|
45 |
|
37 |
/** Create layered filesystem based on a supplied writable layer. |
46 |
/** Create layered filesystem based on a supplied writable layer. |
38 |
* @param writableLayer the writable layer to use, typically a LocalFileSystem |
47 |
* @param writableLayer the writable layer to use, typically a LocalFileSystem |
|
|
48 |
* @param cacheDir a directory in which to store a cache, or null for no caching |
39 |
*/ |
49 |
*/ |
40 |
ModuleLayeredFileSystem (FileSystem writableLayer) { |
50 |
ModuleLayeredFileSystem (FileSystem writableLayer, File cacheDir) throws IOException, SAXException { |
41 |
super (new FileSystem[] { writableLayer, new XMLFileSystem () }); |
51 |
this(writableLayer, false, cacheFile(cacheDir)); |
|
|
52 |
} |
53 |
|
54 |
private ModuleLayeredFileSystem(FileSystem writableLayer, boolean ignored, File cacheFile) throws IOException, SAXException { |
55 |
super(new FileSystem[] { |
56 |
writableLayer, |
57 |
(cacheFile != null && cacheFile.isFile()) ? |
58 |
loadCache(cacheFile) : |
59 |
new XMLFileSystem() |
60 |
}); |
61 |
this.cacheFile = cacheFile; |
62 |
|
42 |
// Wish to permit e.g. a user-installed module to mask files from a |
63 |
// Wish to permit e.g. a user-installed module to mask files from a |
43 |
// root-installed module, so propagate masks up this high. |
64 |
// root-installed module, so propagate masks up this high. |
44 |
// SystemFileSystem leaves this off, so that the final file system |
65 |
// SystemFileSystem leaves this off, so that the final file system |
45 |
// will not show them if there are some left over. |
66 |
// will not show them if there are some left over. |
46 |
setPropagateMasks (true); |
67 |
setPropagateMasks (true); |
|
|
68 |
|
69 |
urls = null; |
70 |
} |
71 |
|
72 |
private static XMLFileSystem loadCache(File cacheFile) throws IOException, SAXException { |
73 |
StartLog.logStart("Loading " + cacheFile); |
74 |
XMLFileSystem xmlfs = new XMLFileSystem(cacheFile.toURL()); |
75 |
StartLog.logEnd("Loading " + cacheFile); |
76 |
return xmlfs; |
77 |
} |
78 |
|
79 |
// See #20168 for information on layer caching. |
80 |
private static File cacheFile(File cacheDir) { |
81 |
if (cacheDir != null && Boolean.getBoolean("netbeans.cache.layers")) { |
82 |
try { |
83 |
if (!cacheDir.isDirectory()) { |
84 |
if (!cacheDir.mkdirs()) { |
85 |
throw new IOException("Could not make dir: " + cacheDir); // NOI18N |
86 |
} |
87 |
} |
88 |
File f = new File(cacheDir, "all-layers.xml"); // NOI18N |
89 |
err.log("Using layer cache in " + f); |
90 |
return f; |
91 |
} catch (IOException ex) { |
92 |
err.notify(ex); |
93 |
} |
94 |
} |
95 |
// Misleading: |
96 |
//err.log("Not using any layer cache"); |
97 |
return null; |
47 |
} |
98 |
} |
48 |
|
99 |
|
49 |
/** Get all layers. |
100 |
/** Get all layers. |
Lines 60-72
Link Here
|
60 |
return getDelegates ()[0]; |
111 |
return getDelegates ()[0]; |
61 |
} |
112 |
} |
62 |
|
113 |
|
63 |
/** Get the XML layer. |
|
|
64 |
* @return the XML layer |
65 |
*/ |
66 |
final XMLFileSystem getXMLLayer () { |
67 |
return (XMLFileSystem)getDelegates ()[1]; |
68 |
} |
69 |
|
70 |
/** Creates the system file system. |
114 |
/** Creates the system file system. |
71 |
*/ |
115 |
*/ |
72 |
public static FileSystem create (File x, File y) |
116 |
public static FileSystem create (File x, File y) |
Lines 99-155
Link Here
|
99 |
} |
143 |
} |
100 |
|
144 |
|
101 |
/** Change the list of module layers URLs. |
145 |
/** Change the list of module layers URLs. |
102 |
* @param urls the urls describing module layers to use. List (List (URL)) |
146 |
* @param urls the urls describing module layers to use. List<URL> |
103 |
*/ |
147 |
*/ |
104 |
public void setURLs (final List urls) throws Exception { |
148 |
public void setURLs (final List urls) throws Exception { |
105 |
if (urls.contains(null)) throw new NullPointerException("urls=" + urls); // NOI18N |
149 |
if (urls.contains(null)) throw new NullPointerException("urls=" + urls); // NOI18N |
|
|
150 |
if (err.isLoggable(ErrorManager.INFORMATIONAL)) { |
151 |
err.log("setURLs: " + urls); |
152 |
} |
153 |
if (this.urls != null && urls.equals(this.urls)) { |
154 |
err.log("no-op"); |
155 |
return; |
156 |
} |
157 |
|
158 |
StartLog.logStart("setURLs"); |
159 |
|
160 |
final XMLFileSystem xmlfs = (XMLFileSystem)getDelegates()[1]; |
161 |
|
162 |
final File stampFile; |
163 |
final Stamp stamp; |
164 |
if (cacheFile != null) { |
165 |
stampFile = new File(cacheFile.getParentFile(), "layer-stamp.txt"); // NOI18N |
166 |
stamp = new Stamp(urls); |
167 |
} else { |
168 |
stampFile = null; |
169 |
stamp = null; |
170 |
} |
171 |
if (cacheFile != null && cacheFile.isFile() && stampFile.isFile()) { |
172 |
err.log("Stamp of new URLs: " + stamp.getHash()); |
173 |
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(stampFile), "UTF-8")); // NOI18N |
174 |
try { |
175 |
String line = r.readLine(); |
176 |
long hash = Long.parseLong(line); |
177 |
err.log("Stamp in the cache: " + hash); |
178 |
if (hash == stamp.getHash()) { |
179 |
err.log("Cache hit!"); |
180 |
this.urls = urls; |
181 |
StartLog.logEnd("setURLs"); |
182 |
return; |
183 |
} |
184 |
} finally { |
185 |
r.close(); |
186 |
} |
187 |
} |
106 |
|
188 |
|
107 |
// #17656: don't hold synch lock while firing changes, it could be dangerous... |
189 |
// #17656: don't hold synch lock while firing changes, it could be dangerous... |
108 |
runAtomicAction(new AtomicAction() { |
190 |
runAtomicAction(new AtomicAction() { |
109 |
public void run() throws IOException { |
191 |
public void run() throws IOException { |
110 |
synchronized (ModuleLayeredFileSystem.this) { |
192 |
synchronized (ModuleLayeredFileSystem.this) { |
111 |
try { |
193 |
try { |
112 |
getXMLLayer().setXmlUrls((URL[])urls.toArray(new URL[urls.size()])); |
194 |
if (cacheFile != null) { |
|
|
195 |
err.log("Rewriting cache in " + cacheFile); |
196 |
// Cf. XMLFileSystem impl. |
197 |
System.setProperty("netbeans.cache.layers.filename", cacheFile.getAbsolutePath()); |
198 |
if (cacheFile.isFile() && !cacheFile.delete()) { |
199 |
throw new IOException("Deletion failed: " + cacheFile); // NOI18N |
200 |
} |
201 |
} |
202 |
xmlfs.setXmlUrls((URL[])urls.toArray(new URL[urls.size()])); |
113 |
} catch (PropertyVetoException pve) { |
203 |
} catch (PropertyVetoException pve) { |
114 |
IOException ioe = new IOException(pve.toString()); |
204 |
IOException ioe = new IOException(pve.toString()); |
115 |
ErrorManager.getDefault().annotate(ioe, pve); |
205 |
err.annotate(ioe, pve); |
116 |
throw ioe; |
206 |
throw ioe; |
|
|
207 |
} finally { |
208 |
if (cacheFile != null) { |
209 |
System.setProperty("netbeans.cache.layers.filename", ""); |
210 |
if (cacheFile.isFile()) { |
211 |
// Write out new stamp too. |
212 |
Writer wr = new OutputStreamWriter(new FileOutputStream(stampFile), "UTF-8"); // NOI18N |
213 |
try { |
214 |
wr.write(String.valueOf(stamp.getHash())); |
215 |
wr.write("\nLine above is identifying hash key, do not edit!\nBelow is metadata about all-layer.xml, for debugging purposes.\n"); // NOI18N |
216 |
wr.write(stamp.toString()); |
217 |
} finally { |
218 |
wr.close(); |
219 |
} |
220 |
} else { |
221 |
err.log(ErrorManager.WARNING, "WARNING - XMLFileSystem did not manage to create " + cacheFile); |
222 |
} |
223 |
} |
117 |
} |
224 |
} |
118 |
} |
225 |
} |
119 |
} |
226 |
} |
120 |
}); |
227 |
}); |
121 |
|
228 |
|
|
|
229 |
this.urls = urls; |
122 |
firePropertyChange ("layers", null, null); // NOI18N |
230 |
firePropertyChange ("layers", null, null); // NOI18N |
|
|
231 |
|
232 |
StartLog.logEnd("setURLs"); |
123 |
} |
233 |
} |
124 |
|
234 |
|
125 |
/** Adds few URLs. |
235 |
/** Adds few URLs. |
126 |
*/ |
236 |
*/ |
127 |
public void addURLs (Collection urls) throws Exception { |
237 |
public void addURLs(Collection urls) throws Exception { |
128 |
if (urls.contains(null)) throw new NullPointerException("urls=" + urls); // NOI18N |
238 |
if (urls.contains(null)) throw new NullPointerException("urls=" + urls); // NOI18N |
129 |
FileSystem[] delegates = getDelegates (); |
239 |
ArrayList arr = new ArrayList(); |
130 |
XMLFileSystem fs = delegates.length < 2 ? null : (XMLFileSystem)delegates[1]; |
240 |
if (this.urls != null) arr.addAll(this.urls); |
131 |
ArrayList arr = new ArrayList (); |
241 |
arr.addAll(urls); |
132 |
if (fs != null) { |
242 |
setURLs(arr); |
133 |
arr.addAll (Arrays.asList (fs.getXmlUrls ())); |
|
|
134 |
} |
135 |
arr.addAll (urls); |
136 |
setURLs (arr); |
137 |
} |
243 |
} |
138 |
|
244 |
|
139 |
/** Removes few URLs. |
245 |
/** Removes few URLs. |
140 |
* |
|
|
141 |
*/ |
246 |
*/ |
142 |
public void removeURLs (Collection urls) throws Exception { |
247 |
public void removeURLs(Collection urls) throws Exception { |
143 |
FileSystem[] delegates = getDelegates (); |
248 |
if (urls.contains(null)) throw new NullPointerException("urls=" + urls); // NOI18N |
144 |
XMLFileSystem fs = delegates.length < 2 ? null : (XMLFileSystem)delegates[1]; |
249 |
ArrayList arr = new ArrayList(); |
145 |
if (fs == null) { |
250 |
if (this.urls != null) arr.addAll(this.urls); |
146 |
// no filesystem available |
251 |
arr.removeAll(urls); |
147 |
return; |
252 |
setURLs(arr); |
|
|
253 |
} |
254 |
|
255 |
/** Represents a hash of a bunch of jar: URLs and the associated JAR timestamps. |
256 |
*/ |
257 |
private static final class Stamp implements Comparator { |
258 |
private final List urls; // List<URL> |
259 |
private final long[] times; |
260 |
private final long hash; |
261 |
public Stamp(List urls) throws IOException { |
262 |
this.urls = new ArrayList(urls); |
263 |
Collections.sort(this.urls, this); |
264 |
times = new long[this.urls.size()]; |
265 |
long x = 17L; |
266 |
Iterator it = this.urls.iterator(); |
267 |
int i = 0; |
268 |
while (it.hasNext()) { |
269 |
URL u = (URL)it.next(); |
270 |
String s = u.toString(); |
271 |
x += 3199876987199633L; |
272 |
x ^= s.hashCode(); |
273 |
File extracted = findFile(s); |
274 |
if (extracted != null) { |
275 |
x ^= (times[i++] = extracted.lastModified()); |
276 |
} else { |
277 |
times[i++] = 0L; |
278 |
} |
279 |
} |
280 |
hash = x; |
281 |
} |
282 |
private static File findFile(String u) throws IOException { |
283 |
if (u.startsWith("jar:") && u.lastIndexOf("!/") != -1) { // NOI18N |
284 |
u = u.substring(4, u.lastIndexOf("!/")); |
285 |
} |
286 |
if (u.startsWith("file:")) { // NOI18N |
287 |
//return new File(u.substring(5)); |
288 |
// XXX is this algorithm really portable? Unfortunately Java |
289 |
// platform provides no better way that I know of. |
290 |
URL fu = new URL(u); |
291 |
String path = URLDecoder.decode(fu.getPath()); |
292 |
if (File.separatorChar != '/') { |
293 |
path = path.replace('/', File.separatorChar); |
294 |
} |
295 |
return new File(path); |
296 |
} else { |
297 |
return null; |
298 |
} |
299 |
} |
300 |
public long getHash() { |
301 |
return hash; |
302 |
} |
303 |
public String toString() { |
304 |
StringBuffer buf = new StringBuffer(); |
305 |
Iterator it = urls.iterator(); |
306 |
int i = 0; |
307 |
while (it.hasNext()) { |
308 |
long t = times[i++]; |
309 |
if (t == 0L) { |
310 |
buf.append("<file not found>"); // NOI18N |
311 |
} else { |
312 |
buf.append(new Date(t)); |
313 |
} |
314 |
buf.append('\t'); |
315 |
buf.append(it.next()); |
316 |
buf.append('\n'); |
317 |
} |
318 |
return buf.toString(); |
319 |
} |
320 |
public int compare(Object o1, Object o2) { |
321 |
return ((URL)o1).toString().compareTo(((URL)o2).toString()); |
148 |
} |
322 |
} |
149 |
ArrayList arr = new ArrayList (); |
|
|
150 |
arr.addAll (Arrays.asList (fs.getXmlUrls ())); |
151 |
arr.removeAll (urls); |
152 |
setURLs (arr); |
153 |
} |
323 |
} |
154 |
|
324 |
|
155 |
} |
325 |
} |