|
What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.core.projects;
import java.beans.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.net.*;
import java.util.*;
import org.openide.ErrorManager;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.MultiFileSystem;
import org.openide.util.NbBundle;
import org.netbeans.core.perftool.StartLog;
import org.netbeans.core.projects.cache.LayerCacheManager;
import org.openide.filesystems.Repository;
import org.openide.util.Utilities;
/** Layered file system serving itself as either the user or installation layer.
* Holds one layer of a writable system directory, and some number
* of module layers.
* @author Jesse Glick, Jaroslav Tulach
*/
public class ModuleLayeredFileSystem extends MultiFileSystem {
/** serial version UID */
private static final long serialVersionUID = 782910986724201983L;
private static final String LAYER_STAMP = "layer-stamp.txt";
static final ErrorManager err = ErrorManager.getDefault().getInstance("org.netbeans.core.projects"); // NOI18N
/** current list of URLs - r/o; or null if not yet set */
private List urls; // List
/** cache manager */
private LayerCacheManager manager;
/** writable layer */
private final FileSystem writableLayer;
/** cache layer */
private FileSystem cacheLayer;
/** other layers */
private final FileSystem[] otherLayers;
/** Create layered filesystem based on a supplied writable layer.
* @param writableLayer the writable layer to use, typically a LocalFileSystem
* @param otherLayers some other layers to use, e.g. LocalFileSystem[]
* @param cacheDir a directory in which to store a cache, or null for no caching
*/
ModuleLayeredFileSystem (FileSystem writableLayer, FileSystem[] otherLayers, File cacheDir) throws IOException {
this(writableLayer, otherLayers, manager(cacheDir));
}
private ModuleLayeredFileSystem(FileSystem writableLayer, FileSystem[] otherLayers, LayerCacheManager mgr) throws IOException {
this(writableLayer, otherLayers, mgr, loadCache(mgr));
}
private ModuleLayeredFileSystem(FileSystem writableLayer, FileSystem[] otherLayers, LayerCacheManager mgr, FileSystem cacheLayer) throws IOException {
super(appendLayers(writableLayer, otherLayers, cacheLayer));
this.manager = mgr;
this.writableLayer = writableLayer;
this.otherLayers = otherLayers;
this.cacheLayer = cacheLayer;
// Wish to permit e.g. a user-installed module to mask files from a
// root-installed module, so propagate masks up this high.
// SystemFileSystem leaves this off, so that the final file system
// will not show them if there are some left over.
setPropagateMasks (true);
urls = null;
}
private static LayerCacheManager manager(File cacheDir) throws IOException {
if (cacheDir != null) {
if (!cacheDir.isDirectory()) {
if (!cacheDir.mkdirs()) {
throw new IOException("Could not make dir: " + cacheDir); // NOI18N
}
}
String defaultManager = "org.netbeans.core.projects.cache.BinaryCacheManager"; // NOI18N
String managerName = System.getProperty("netbeans.cache.layers", defaultManager); // NOI18N
if (managerName.equals("-")) { // NOI18N
err.log("Cache manager disabled");
return LayerCacheManager.emptyManager();
}
try {
Class c = Class.forName(managerName);
Constructor ctor = c.getConstructor(new Class[] {File.class});
LayerCacheManager mgr = (LayerCacheManager)ctor.newInstance(new Object[] {cacheDir});
err.log("Using cache manager of type " + managerName + " in " + cacheDir);
return mgr;
} catch (Exception e) {
IOException ioe = new IOException(e.toString());
err.annotate(ioe, e);
throw ioe;
}
} else {
err.log("No cache manager");
return LayerCacheManager.emptyManager();
}
}
private static FileSystem loadCache(LayerCacheManager mgr) throws IOException {
if (mgr.cacheExists()) {
// XXX use Events to log!
org.openide.awt.StatusDisplayer.getDefault().setStatusText(
NbBundle.getMessage(ModuleLayeredFileSystem.class, "MSG_start_load_cache"));
String msg = "Loading layers from " + mgr.getCacheDirectory(); // NOI18N
StartLog.logStart(msg);
FileSystem fs;
try {
fs = mgr.createLoadedFileSystem();
} catch (IOException ioe) {
err.notify(ErrorManager.INFORMATIONAL, ioe);
mgr.cleanupCache();
cleanStamp(mgr.getCacheDirectory());
fs = mgr.createEmptyFileSystem();
}
org.openide.awt.StatusDisplayer.getDefault().setStatusText(
NbBundle.getMessage(ModuleLayeredFileSystem.class, "MSG_end_load_cache"));
StartLog.logEnd(msg);
return fs;
} else {
return mgr.createEmptyFileSystem();
}
}
private static FileSystem[] appendLayers(FileSystem fs1, FileSystem[] fs2s, FileSystem fs3) {
List l = new ArrayList(fs2s.length + 2);
l.add(fs1);
l.addAll(Arrays.asList(fs2s));
l.add(fs3);
return (FileSystem[])l.toArray(new FileSystem[l.size()]);
}
private static void cleanStamp(File cacheDir) throws IOException {
File stampFile = new File(cacheDir, LAYER_STAMP);
if (stampFile.exists() && ! stampFile.delete()) {
throw new IOException("Could not delete: " + stampFile); // NOI18N
}
}
/** Get all layers.
* @return all filesystems making layers
*/
public/*but just for debugging*/ final FileSystem[] getLayers () {
return getDelegates ();
}
/** Get the writable layer.
* @return the writable layer
*/
final FileSystem getWritableLayer () {
return writableLayer;
}
/** Get the installation layer.
* You can take advantage of the specialized return type
* if working within the core.
*/
public static ModuleLayeredFileSystem getInstallationModuleLayer () {
FileSystem fs = Repository.getDefault ().getDefaultFileSystem();
SystemFileSystem sfs = (SystemFileSystem)fs;
ModuleLayeredFileSystem home = sfs.getInstallationLayer ();
if (home != null)
return home;
else
return sfs.getUserLayer ();
}
/** Get the user layer.
* You can take advantage of the specialized return type
* if working within the core.
*/
public static ModuleLayeredFileSystem getUserModuleLayer () {
SystemFileSystem sfs = (SystemFileSystem)
Repository.getDefault().getDefaultFileSystem();
return sfs.getUserLayer ();
}
/** Change the list of module layers URLs.
* @param urls the urls describing module layers to use. List
*/
public void setURLs (final List urls) throws Exception {
if (urls.contains(null)) throw new NullPointerException("urls=" + urls); // NOI18N
if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
err.log("setURLs: " + urls);
}
if (this.urls != null && urls.equals(this.urls)) {
err.log("no-op");
return;
}
StartLog.logStart("setURLs"); // NOI18N
final File stampFile;
final Stamp stamp;
final File cacheDir = manager.getCacheDirectory();
if (cacheDir != null) {
stampFile = new File(cacheDir, LAYER_STAMP);
stamp = new Stamp(manager.getClass().getName(), urls);
} else {
stampFile = null;
stamp = null;
}
if (cacheDir != null && stampFile.isFile()) {
err.log("Stamp of new URLs: " + stamp.getHash());
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(stampFile), "UTF-8")); // NOI18N
try {
String line = r.readLine();
long hash;
try {
hash = Long.parseLong(line);
} catch (NumberFormatException nfe) {
throw new IOException(nfe.toString());
}
err.log("Stamp in the cache: " + hash);
if (hash == stamp.getHash()) {
err.log("Cache hit!");
this.urls = urls;
StartLog.logEnd("setURLs"); // NOI18N
return;
}
} finally {
r.close();
}
}
// #17656: don't hold synch lock while firing changes, it could be dangerous...
runAtomicAction(new AtomicAction() {
public void run() throws IOException {
synchronized (ModuleLayeredFileSystem.this) {
if (cacheDir != null) {
org.openide.awt.StatusDisplayer.getDefault().setStatusText(
NbBundle.getMessage(ModuleLayeredFileSystem.class, "MSG_start_rewrite_cache"));
err.log("Rewriting cache in " + cacheDir);
} // else if null -> we are using emptyManager, so do not print confusing messages
try {
if (manager.supportsLoad()) {
manager.store(cacheLayer, urls);
} else {
cacheLayer = manager.store(urls);
setDelegates(appendLayers(writableLayer, otherLayers, cacheLayer));
}
} catch (IOException ioe) {
err.notify(ErrorManager.INFORMATIONAL, ioe);
err.log("Abandoning cache manager");
manager.cleanupCache();
cleanStamp(cacheDir);
manager = LayerCacheManager.emptyManager();
// Try again with no-op manager.
try {
if (manager.supportsLoad()) {
cacheLayer = manager.createEmptyFileSystem();
manager.store(cacheLayer, urls);
} else {
cacheLayer = manager.store(urls);
}
setDelegates(appendLayers(writableLayer, otherLayers, cacheLayer));
} catch (IOException ioe2) {
// More serious - should not happen.
err.notify(ioe2);
}
return;
}
if (stampFile != null) {
// Write out new stamp too.
Writer wr = new OutputStreamWriter(new FileOutputStream(stampFile), "UTF-8"); // NOI18N
try {
// Would be nice to write out as zero-padded hex.
// Unfortunately while Long.toHexString works fine,
// Long.parseLong cannot be asked to parse unsigned longs,
// so fails when the high bit is set.
wr.write(Long.toString(stamp.getHash()));
wr.write("\nLine above is identifying hash key, do not edit!\nBelow is metadata about layer cache, for debugging purposes.\n"); // NOI18N
wr.write(stamp.toString());
} finally {
wr.close();
}
}
if (cacheDir != null) {
org.openide.awt.StatusDisplayer.getDefault().setStatusText(
NbBundle.getMessage(ModuleLayeredFileSystem.class, "MSG_end_rewrite_cache"));
err.log("Finished rewriting cache in " + cacheDir);
}
}
}
});
this.urls = urls;
firePropertyChange ("layers", null, null); // NOI18N
StartLog.logEnd("setURLs"); // NOI18N
}
/** Adds few URLs.
*/
public void addURLs(Collection urls) throws Exception {
if (urls.contains(null)) throw new NullPointerException("urls=" + urls); // NOI18N
// Add to the front: #23609.
ArrayList arr = new ArrayList(urls);
if (this.urls != null) arr.addAll(this.urls);
setURLs(arr);
}
/** Removes few URLs.
*/
public void removeURLs(Collection urls) throws Exception {
if (urls.contains(null)) throw new NullPointerException("urls=" + urls); // NOI18N
ArrayList arr = new ArrayList();
if (this.urls != null) arr.addAll(this.urls);
arr.removeAll(urls);
setURLs(arr);
}
/** Represents a hash of a bunch of jar: URLs and the associated JAR timestamps.
*/
private static final class Stamp implements Comparator {
private final String managerName;
private final List urls; // List
private final long[] times;
private final long hash;
public Stamp(String name, List urls) throws IOException {
managerName = name;
this.urls = new ArrayList(urls);
Collections.sort(this.urls, this);
times = new long[this.urls.size()];
long x = 17L ^ managerName.hashCode();
Iterator it = this.urls.iterator();
int i = 0;
while (it.hasNext()) {
URL u = (URL)it.next();
String s = u.toExternalForm();
x += 3199876987199633L;
x ^= s.hashCode();
URL u2;
if (s.startsWith("jar:")) { // NOI18N
int bangSlash = s.lastIndexOf("!/"); // NOI18N
if (bangSlash != -1) {
// underlying URL inside jar:, generally file:
u2 = new URL(s.substring(4, bangSlash));
} else {
err.log(ErrorManager.WARNING, "Weird JAR URL: " + u);
u2 = u;
}
} else {
// something else... plain file: URL?
u2 = u;
}
File extracted = Utilities.toFile(u2);
if (extracted != null) {
// the JAR file containing the layer entry:
x ^= (times[i++] = extracted.lastModified());
} else {
// not a file: or jar:file: URL?
times[i++] = 0L;
}
}
hash = x;
}
public long getHash() {
return hash;
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append(managerName);
buf.append('\n');
Iterator it = urls.iterator();
int i = 0;
while (it.hasNext()) {
long t = times[i++];
if (t == 0L) {
buf.append(""); // NOI18N
} else {
buf.append(new Date(t));
}
buf.append('\t');
buf.append(it.next());
buf.append('\n');
}
return buf.toString();
}
public int compare(Object o1, Object o2) {
return ((URL)o1).toString().compareTo(((URL)o2).toString());
}
}
}
|