blob: ea01606fa09191563f3a180f508fbaf45753f971 [file] [log] [blame]
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2010 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*
* Contributor(s):
*
* Portions Copyrighted 2008 Sun Microsystems, Inc.
*/
package org.netbeans.modules.jackpot30.remotingapi;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
import org.openide.modules.Places;
import org.openide.util.Exceptions;
import org.openide.util.RequestProcessor;
/**
*
* @author Tomas Zezula
*/
public final class CacheFolder {
private static final Logger LOG = Logger.getLogger(CacheFolder.class.getName());
private static final RequestProcessor RP = new RequestProcessor(CacheFolder.class.getName(), 1, false, false);
private static final RequestProcessor.Task SAVER = RP.create(new Saver());
private static final int SLIDING_WINDOW = 500;
private static final String SEGMENTS_FILE = "segments"; //NOI18N
private static final String SLICE_PREFIX = "s"; //NOI18N
//@GuardedBy("CacheFolder.class")
private static FileObject cacheFolder;
//@GuardedBy("CacheFolder.class")
private static Properties segments;
//@GuardedBy("CacheFolder.class")
private static Map<String, String> invertedSegments;
//@GuardedBy("CacheFolder.class")
private static int index = 0;
//@NotThreadSafe
@org.netbeans.api.annotations.common.SuppressWarnings(
value="LI_LAZY_INIT_UPDATE_STATIC"
/*,justification="Caller already holds a monitor"*/)
private static void loadSegments(FileObject folder) throws IOException {
assert Thread.holdsLock(CacheFolder.class);
if (segments == null) {
assert folder != null;
segments = new Properties ();
invertedSegments = new HashMap<String,String> ();
final FileObject segmentsFile = folder.getFileObject(SEGMENTS_FILE);
if (segmentsFile!=null) {
final InputStream in = segmentsFile.getInputStream();
try {
segments.load (in);
} finally {
in.close();
}
}
for (Map.Entry entry : segments.entrySet()) {
String segment = (String) entry.getKey();
String root = (String) entry.getValue();
invertedSegments.put(root,segment);
try {
index = Math.max (index,Integer.parseInt(segment.substring(SLICE_PREFIX.length())));
} catch (NumberFormatException nfe) {
LOG.log(Level.FINE, null, nfe);
}
}
}
}
private static void storeSegments(FileObject folder) throws IOException {
assert Thread.holdsLock(CacheFolder.class);
assert folder != null;
//It's safer to use FileUtil.createData(File) than FileUtil.createData(FileObject, String)
//see issue #173094
final File _file = FileUtil.toFile(folder);
assert _file != null;
final FileObject segmentsFile = FileUtil.createData(new File(_file, SEGMENTS_FILE));
final OutputStream out = segmentsFile.getOutputStream();
try {
segments.store(out,null);
} finally {
out.close();
}
}
public static synchronized URL getSourceRootForDataFolder (final FileObject dataFolder) {
final FileObject segFolder = dataFolder.getParent();
if (segFolder == null || !segFolder.equals(cacheFolder)) {
return null;
}
String source = segments.getProperty(dataFolder.getName());
if (source != null) {
try {
return new URL (source);
} catch (IOException ioe) {
LOG.log(Level.FINE, null, ioe);
}
}
return null;
}
public static FileObject getDataFolder (final URL root) throws IOException {
return getDataFolder(root, false);
}
public static FileObject getDataFolder (final URL root, final boolean onlyIfAlreadyExists) throws IOException {
final String rootName = root.toExternalForm();
final FileObject _cacheFolder = getCacheFolder();
String slice;
synchronized (CacheFolder.class) {
loadSegments(_cacheFolder);
slice = invertedSegments.get (rootName);
if (slice == null) {
if (onlyIfAlreadyExists) {
return null;
}
slice = SLICE_PREFIX + (++index);
while (segments.getProperty(slice) != null) {
slice = SLICE_PREFIX + (++index);
}
segments.put (slice,rootName);
invertedSegments.put(rootName, slice);
SAVER.schedule(SLIDING_WINDOW);
}
}
assert slice != null;
if (onlyIfAlreadyExists) {
return cacheFolder.getFileObject(slice);
} else {
return FileUtil.createFolder(_cacheFolder, slice);
}
}
public static synchronized Iterable<? extends FileObject> findRootsWithCacheUnderFolder(FileObject folder) throws IOException {
URL folderURL = folder.toURL();
String prefix = folderURL.toExternalForm();
final FileObject _cacheFolder = getCacheFolder();
List<FileObject> result = new LinkedList<FileObject>();
loadSegments(_cacheFolder);
for (Entry<String, String> e : invertedSegments.entrySet()) {
if (e.getKey().startsWith(prefix)) {
FileObject fo = URLMapper.findFileObject(new URL(e.getKey()));
if (fo != null) {
result.add(fo);
}
}
}
return result;
}
public static synchronized FileObject getCacheFolder () {
if (cacheFolder == null) {
File cache = Places.getCacheSubdirectory("index"); // NOI18N
if (!cache.isDirectory()) {
throw new IllegalStateException("Indices cache folder " + cache.getAbsolutePath() + " is not a folder"); //NOI18N
}
if (!cache.canRead()) {
throw new IllegalStateException("Can't read from indices cache folder " + cache.getAbsolutePath()); //NOI18N
}
if (!cache.canWrite()) {
throw new IllegalStateException("Can't write to indices cache folder " + cache.getAbsolutePath()); //NOI18N
}
cacheFolder = FileUtil.toFileObject(cache);
if (cacheFolder == null) {
throw new IllegalStateException("Can't convert indices cache folder " + cache.getAbsolutePath() + " to FileObject"); //NOI18N
}
}
return cacheFolder;
}
/**
* Only for unit tests! It's used also by CslTestBase, which is not in the
* same package, hence the public keyword.
*
*/
public static void setCacheFolder (final FileObject folder) {
SAVER.schedule(0);
SAVER.waitFinished();
synchronized (CacheFolder.class) {
assert folder != null && folder.canRead() && folder.canWrite();
cacheFolder = folder;
segments = null;
invertedSegments = null;
index = 0;
}
}
private CacheFolder() {
// no-op
}
private static class Saver implements Runnable {
@Override
public void run() {
try {
final FileObject cf = getCacheFolder();
// #170182 - preventing filesystem events being fired from under the CacheFolder.class lock
cf.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() {
@Override
public void run() throws IOException {
synchronized (CacheFolder.class) {
if (segments == null) return ;
storeSegments(cf);
}
}
});
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
}
}
}
}