Introducing an API for local caching of remote results.
1.1 --- a/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndex.java Tue Jul 31 21:05:52 2012 +0200
1.2 +++ b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndex.java Wed Aug 08 18:39:56 2012 +0200
1.3 @@ -42,20 +42,15 @@
1.4
1.5 package org.netbeans.modules.jackpot30.impl.duplicates.indexing;
1.6
1.7 -import java.io.File;
1.8 -import java.io.FileOutputStream;
1.9 import java.io.IOException;
1.10 -import java.io.OutputStream;
1.11 import java.net.URI;
1.12 import java.net.URISyntaxException;
1.13 -import java.util.AbstractMap.SimpleEntry;
1.14 import java.util.ArrayList;
1.15 import java.util.Arrays;
1.16 import java.util.BitSet;
1.17 import java.util.Collection;
1.18 import java.util.Collections;
1.19 import java.util.Comparator;
1.20 -import java.util.HashMap;
1.21 import java.util.HashSet;
1.22 import java.util.Iterator;
1.23 import java.util.LinkedHashMap;
1.24 @@ -75,24 +70,21 @@
1.25 import org.apache.lucene.document.Field.Store;
1.26 import org.apache.lucene.index.IndexReader;
1.27 import org.apache.lucene.index.IndexWriter;
1.28 -import org.apache.lucene.index.IndexWriter.MaxFieldLength;
1.29 import org.apache.lucene.index.Term;
1.30 import org.apache.lucene.search.Collector;
1.31 import org.apache.lucene.search.IndexSearcher;
1.32 import org.apache.lucene.search.Query;
1.33 import org.apache.lucene.search.Searcher;
1.34 import org.apache.lucene.search.TermQuery;
1.35 -import org.apache.lucene.store.FSDirectory;
1.36 import org.codeviation.pojson.Pojson;
1.37 -import org.netbeans.modules.jackpot30.common.api.IndexAccess.NoAnalyzer;
1.38 import org.netbeans.modules.jackpot30.common.api.LuceneHelpers.BitSetCollector;
1.39 import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.DuplicateDescription;
1.40 import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.Span;
1.41 +import org.netbeans.modules.jackpot30.remoting.api.LocalCache;
1.42 +import org.netbeans.modules.jackpot30.remoting.api.LocalCache.Task;
1.43 import org.netbeans.modules.jackpot30.remoting.api.RemoteIndex;
1.44 import org.netbeans.modules.jackpot30.remoting.api.WebUtilities;
1.45 -import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
1.46 import org.openide.filesystems.FileObject;
1.47 -import org.openide.filesystems.FileUtil;
1.48 import org.openide.filesystems.URLMapper;
1.49 import org.openide.util.Exceptions;
1.50
1.51 @@ -103,7 +95,6 @@
1.52 @SuppressWarnings("ClassWithMultipleLoggers")
1.53 public class RemoteDuplicatesIndex {
1.54
1.55 - private static final Logger LOG = Logger.getLogger(RemoteDuplicatesIndex.class.getName());
1.56 private static final Logger TIMER = Logger.getLogger("TIMER");
1.57
1.58 public static List<DuplicateDescription> findDuplicates(Map<String, long[]> hashes, FileObject currentFile, AtomicBoolean cancel) throws IOException, URISyntaxException {
1.59 @@ -209,123 +200,60 @@
1.60 }
1.61 }
1.62
1.63 - private static final Map<URI, IndexReader> readerCache = new HashMap<URI, IndexReader>();
1.64 + private static Map<String, Map<String, Collection<? extends String>>> findHashOccurrencesInLocalCache(RemoteIndex ri, final Iterable<? extends String> hashes, AtomicBoolean cancel) throws IOException, URISyntaxException {
1.65 + return LocalCache.runOverLocalCache(ri, new Task<IndexReader, Map<String, Map<String, Collection<? extends String>>>>() {
1.66 + @Override public Map<String, Map<String, Collection<? extends String>>> run(IndexReader reader, AtomicBoolean cancel) throws IOException {
1.67 + Map<String, Map<String, Collection<String>>> result = new LinkedHashMap<String, Map<String, Collection<String>>>();
1.68
1.69 - private static File findLocalCacheDir(RemoteIndex ri) throws IOException {
1.70 - return new File(FileUtil.toFile(FileUtil.createFolder(CacheFolder.getDataFolder(ri.remote), "remote-duplicates")), ri.remoteSegment);
1.71 + for (Entry<String, Collection<? extends String>> e : containsHash(reader, hashes, cancel).entrySet()) {
1.72 + if (cancel.get()) return Collections.emptyMap();
1.73 +
1.74 + Map<String, Collection<String>> forHash = result.get(e.getKey());
1.75 +
1.76 + if (forHash == null) {
1.77 + result.put(e.getKey(), forHash = new LinkedHashMap<String, Collection<String>>());
1.78 + }
1.79 +
1.80 + for (String path : e.getValue()) {
1.81 + String segment = path.substring(0, path.indexOf('/'));
1.82 +
1.83 + path = path.substring(path.indexOf('/') + 1);
1.84 +
1.85 + Collection<String> list = forHash.get(segment);
1.86 +
1.87 + if (list == null) {
1.88 + forHash.put(segment, list = new LinkedList<String>());
1.89 + }
1.90 +
1.91 + list.add(path);
1.92 + }
1.93 + }
1.94 +
1.95 + return (Map)result; //XXX
1.96 + }
1.97 + }, Collections.<String, Map<String, Collection<? extends String>>>emptyMap(), cancel);
1.98 }
1.99
1.100 - private static final long VERSION_CHECK_PERIOD = 60 * 60 * 1000;
1.101 - private static final Map<Entry<URI, String>, Long> lastVersionCheck = new HashMap<Entry<URI, String>, Long>();
1.102 -
1.103 - private static synchronized Map<String, Map<String, Collection<? extends String>>> findHashOccurrencesInLocalCache(RemoteIndex ri, Iterable<? extends String> hashes, AtomicBoolean cancel) throws IOException, URISyntaxException {
1.104 - URI uri = ri.remote.toURI();
1.105 - SimpleEntry<URI, String> versionCheckKey = new SimpleEntry<URI, String>(uri, ri.remoteSegment);
1.106 - Long lastCheck = lastVersionCheck.get(versionCheckKey);
1.107 + private static synchronized void saveToLocalCache(RemoteIndex ri, final Map<String, Map<String, Collection<? extends String>>> what) throws IOException, URISyntaxException {
1.108 + LocalCache.saveToLocalCache(ri, new Task<IndexWriter, Void>() {
1.109 + @Override public Void run(IndexWriter w, AtomicBoolean cancel) throws IOException {
1.110 + for (Entry<String, Map<String, Collection<? extends String>>> e : what.entrySet()) {
1.111 + Document doc = new Document();
1.112
1.113 - if (lastCheck == null || (System.currentTimeMillis() - lastCheck) > VERSION_CHECK_PERIOD) {
1.114 - File dir = findLocalCacheDir(ri);
1.115 - File remoteVersion = new File(dir, "remoteVersion");
1.116 - FileObject remoteVersionFO = FileUtil.toFileObject(remoteVersion);
1.117 - String previousVersion = remoteVersionFO != null ? remoteVersionFO.asText("UTF-8") : null;
1.118 - URI infoURI = new URI(ri.remote.toExternalForm() + "/info?path=" + WebUtilities.escapeForQuery(ri.remoteSegment));
1.119 - String infoContent = WebUtilities.requestStringResponse(infoURI, cancel);
1.120 + doc.add(new Field("hash", e.getKey(), Store.YES, Index.NOT_ANALYZED));
1.121
1.122 - if (cancel.get()) return Collections.emptyMap();
1.123 -
1.124 - if (infoContent != null) {
1.125 - Object buildId = Pojson.load(LinkedHashMap.class, infoContent).get("BUILD_ID");
1.126 -
1.127 - if (buildId != null && !(buildId = buildId.toString()).equals(previousVersion)) {
1.128 - remoteVersion.getParentFile().mkdirs();
1.129 - OutputStream out = new FileOutputStream(remoteVersion);
1.130 - try {
1.131 - out.write(buildId.toString().getBytes("UTF-8"));
1.132 - } finally {
1.133 - out.close();
1.134 + for (Entry<String, Collection<? extends String>> pe : e.getValue().entrySet()) {
1.135 + for (String path : pe.getValue()) {
1.136 + doc.add(new Field("path", pe.getKey() + "/" + path, Store.YES, Index.NO));
1.137 + }
1.138 }
1.139
1.140 - LOG.log(Level.FINE, "Deleting local cache");
1.141 - delete(new File(dir, "index"));
1.142 -
1.143 - IndexReader reader = readerCache.remove(uri);
1.144 - if (reader != null)
1.145 - reader.close();
1.146 -
1.147 - }
1.148 - }
1.149 -
1.150 - lastVersionCheck.put(versionCheckKey, System.currentTimeMillis());
1.151 - }
1.152 -
1.153 - IndexReader reader = readerCache.get(uri);
1.154 -
1.155 - if (reader == null && !cancel.get()) {
1.156 - File dir = new File(findLocalCacheDir(ri), "index");
1.157 -
1.158 - if (dir.listFiles() != null && dir.listFiles().length > 0) {
1.159 - readerCache.put(uri, reader = IndexReader.open(FSDirectory.open(dir), true));
1.160 - }
1.161 - }
1.162 -
1.163 - if (reader == null || cancel.get()) {
1.164 - return Collections.emptyMap();
1.165 - }
1.166 -
1.167 - Map<String, Map<String, Collection<String>>> result = new LinkedHashMap<String, Map<String, Collection<String>>>();
1.168 -
1.169 - for (Entry<String, Collection<? extends String>> e : containsHash(reader, hashes, cancel).entrySet()) {
1.170 - if (cancel.get()) return Collections.emptyMap();
1.171 -
1.172 - Map<String, Collection<String>> forHash = result.get(e.getKey());
1.173 -
1.174 - if (forHash == null) {
1.175 - result.put(e.getKey(), forHash = new LinkedHashMap<String, Collection<String>>());
1.176 - }
1.177 -
1.178 - for (String path : e.getValue()) {
1.179 - String segment = path.substring(0, path.indexOf('/'));
1.180 -
1.181 - path = path.substring(path.indexOf('/') + 1);
1.182 -
1.183 - Collection<String> list = forHash.get(segment);
1.184 -
1.185 - if (list == null) {
1.186 - forHash.put(segment, list = new LinkedList<String>());
1.187 + w.addDocument(doc);
1.188 }
1.189
1.190 - list.add(path);
1.191 + return null;
1.192 }
1.193 - }
1.194 -
1.195 - return (Map)result; //XXX
1.196 - }
1.197 -
1.198 - private static synchronized void saveToLocalCache(RemoteIndex ri, Map<String, Map<String, Collection<? extends String>>> what) throws IOException, URISyntaxException {
1.199 - IndexReader r = readerCache.remove(ri.remote.toURI());
1.200 -
1.201 - if (r != null) {
1.202 - r.close();
1.203 - }
1.204 -
1.205 - IndexWriter w = new IndexWriter(FSDirectory.open(new File(findLocalCacheDir(ri), "index")), new NoAnalyzer(), MaxFieldLength.UNLIMITED);
1.206 -
1.207 - for (Entry<String, Map<String, Collection<? extends String>>> e : what.entrySet()) {
1.208 - Document doc = new Document();
1.209 -
1.210 - doc.add(new Field("hash", e.getKey(), Store.YES, Index.NOT_ANALYZED));
1.211 -
1.212 - for (Entry<String, Collection<? extends String>> pe : e.getValue().entrySet()) {
1.213 - for (String path : pe.getValue()) {
1.214 - doc.add(new Field("path", pe.getKey() + "/" + path, Store.YES, Index.NO));
1.215 - }
1.216 - }
1.217 -
1.218 - w.addDocument(doc);
1.219 - }
1.220 -
1.221 - w.optimize();
1.222 - w.close();
1.223 + });
1.224 }
1.225
1.226 private static List<DuplicateDescription> translate(Map<String, long[]> hashes, Map<String, Map<RemoteIndex, Collection<String>>> occ, FileObject currentFile) {
1.227 @@ -396,35 +324,6 @@
1.228 });
1.229 }
1.230
1.231 - private static boolean subsumes(DuplicateDescription bigger, DuplicateDescription smaller) {
1.232 - Set<FileObject> bFiles = new HashSet<FileObject>();
1.233 -
1.234 - for (Span s : bigger.dupes) {
1.235 - bFiles.add(s.file);
1.236 - }
1.237 -
1.238 - Set<FileObject> sFiles = new HashSet<FileObject>();
1.239 -
1.240 - for (Span s : smaller.dupes) {
1.241 - sFiles.add(s.file);
1.242 - }
1.243 -
1.244 - if (!bFiles.equals(sFiles)) return false;
1.245 -
1.246 - Span testAgainst = bigger.dupes.get(0);
1.247 -
1.248 - for (Span s : smaller.dupes) {
1.249 - if (s.file == testAgainst.file) {
1.250 - if ( (testAgainst.startOff <= s.startOff && testAgainst.endOff > s.endOff)
1.251 - || (testAgainst.startOff < s.startOff && testAgainst.endOff >= s.endOff)) {
1.252 - return true;
1.253 - }
1.254 - }
1.255 - }
1.256 -
1.257 - return false;
1.258 - }
1.259 -
1.260 private static Map<String, Collection<? extends String>> containsHash(IndexReader reader, Iterable<? extends String> hashes, AtomicBoolean cancel) throws IOException {
1.261 Map<String, Collection<? extends String>> result = new LinkedHashMap<String, Collection<? extends String>>();
1.262
1.263 @@ -458,15 +357,4 @@
1.264 return result;
1.265 }
1.266
1.267 - private static void delete(File file) {
1.268 - File[] c = file.listFiles();
1.269 -
1.270 - if (c != null) {
1.271 - for (File cc : c) {
1.272 - delete(cc);
1.273 - }
1.274 - }
1.275 -
1.276 - file.delete();
1.277 - }
1.278 }
2.1 --- a/remoting/ide/api/nbproject/genfiles.properties Tue Jul 31 21:05:52 2012 +0200
2.2 +++ b/remoting/ide/api/nbproject/genfiles.properties Wed Aug 08 18:39:56 2012 +0200
2.3 @@ -3,6 +3,6 @@
2.4 build.xml.stylesheet.CRC32=a56c6a5b@1.47
2.5 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
2.6 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
2.7 -nbproject/build-impl.xml.data.CRC32=e364f985
2.8 +nbproject/build-impl.xml.data.CRC32=f9a67ab1
2.9 nbproject/build-impl.xml.script.CRC32=33cac223
2.10 -nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.49
2.11 +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.53
3.1 --- a/remoting/ide/api/nbproject/project.xml Tue Jul 31 21:05:52 2012 +0200
3.2 +++ b/remoting/ide/api/nbproject/project.xml Wed Aug 08 18:39:56 2012 +0200
3.3 @@ -25,6 +25,23 @@
3.4 </run-dependency>
3.5 </dependency>
3.6 <dependency>
3.7 + <code-name-base>org.netbeans.libs.lucene</code-name-base>
3.8 + <build-prerequisite/>
3.9 + <compile-dependency/>
3.10 + <run-dependency>
3.11 + <release-version>3</release-version>
3.12 + <specification-version>3.8</specification-version>
3.13 + </run-dependency>
3.14 + </dependency>
3.15 + <dependency>
3.16 + <code-name-base>org.netbeans.modules.jackpot30.common</code-name-base>
3.17 + <build-prerequisite/>
3.18 + <compile-dependency/>
3.19 + <run-dependency>
3.20 + <specification-version>1.1</specification-version>
3.21 + </run-dependency>
3.22 + </dependency>
3.23 + <dependency>
3.24 <code-name-base>org.netbeans.modules.options.api</code-name-base>
3.25 <build-prerequisite/>
3.26 <compile-dependency/>
3.27 @@ -90,6 +107,14 @@
3.28 </run-dependency>
3.29 </dependency>
3.30 <dependency>
3.31 + <code-name-base>org.openide.modules</code-name-base>
3.32 + <build-prerequisite/>
3.33 + <compile-dependency/>
3.34 + <run-dependency>
3.35 + <specification-version>7.33</specification-version>
3.36 + </run-dependency>
3.37 + </dependency>
3.38 + <dependency>
3.39 <code-name-base>org.openide.nodes</code-name-base>
3.40 <build-prerequisite/>
3.41 <compile-dependency/>
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/remoting/ide/api/src/org/netbeans/modules/jackpot30/remoting/api/LocalCache.java Wed Aug 08 18:39:56 2012 +0200
4.3 @@ -0,0 +1,173 @@
4.4 +/*
4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4.6 + *
4.7 + * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
4.8 + *
4.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
4.10 + * Other names may be trademarks of their respective owners.
4.11 + *
4.12 + * The contents of this file are subject to the terms of either the GNU
4.13 + * General Public License Version 2 only ("GPL") or the Common
4.14 + * Development and Distribution License("CDDL") (collectively, the
4.15 + * "License"). You may not use this file except in compliance with the
4.16 + * License. You can obtain a copy of the License at
4.17 + * http://www.netbeans.org/cddl-gplv2.html
4.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
4.19 + * specific language governing permissions and limitations under the
4.20 + * License. When distributing the software, include this License Header
4.21 + * Notice in each file and include the License file at
4.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
4.23 + * particular file as subject to the "Classpath" exception as provided
4.24 + * by Oracle in the GPL Version 2 section of the License file that
4.25 + * accompanied this code. If applicable, add the following below the
4.26 + * License Header, with the fields enclosed by brackets [] replaced by
4.27 + * your own identifying information:
4.28 + * "Portions Copyrighted [year] [name of copyright owner]"
4.29 + *
4.30 + * If you wish your version of this file to be governed by only the CDDL
4.31 + * or only the GPL Version 2, indicate your decision by adding
4.32 + * "[Contributor] elects to include this software in this distribution
4.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
4.34 + * single choice of license, a recipient has the option to distribute
4.35 + * your version of this file under either the CDDL, the GPL Version 2 or
4.36 + * to extend the choice of license to its licensees as provided above.
4.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
4.38 + * Version 2 license, then the option applies only if the new code is
4.39 + * made subject to such option by the copyright holder.
4.40 + *
4.41 + * Contributor(s):
4.42 + *
4.43 + * Portions Copyrighted 2012 Sun Microsystems, Inc.
4.44 + */
4.45 +package org.netbeans.modules.jackpot30.remoting.api;
4.46 +
4.47 +import java.io.File;
4.48 +import java.io.FileOutputStream;
4.49 +import java.io.IOException;
4.50 +import java.io.OutputStream;
4.51 +import java.net.URI;
4.52 +import java.net.URISyntaxException;
4.53 +import java.util.AbstractMap.SimpleEntry;
4.54 +import java.util.HashMap;
4.55 +import java.util.LinkedHashMap;
4.56 +import java.util.Map;
4.57 +import java.util.Map.Entry;
4.58 +import java.util.concurrent.atomic.AtomicBoolean;
4.59 +import java.util.logging.Level;
4.60 +import java.util.logging.Logger;
4.61 +import org.apache.lucene.index.IndexReader;
4.62 +import org.apache.lucene.index.IndexWriter;
4.63 +import org.apache.lucene.index.IndexWriter.MaxFieldLength;
4.64 +import org.apache.lucene.store.FSDirectory;
4.65 +import org.codeviation.pojson.Pojson;
4.66 +import org.netbeans.api.annotations.common.CheckForNull;
4.67 +import org.netbeans.modules.jackpot30.common.api.IndexAccess.NoAnalyzer;
4.68 +import org.netbeans.modules.jackpot30.remotingapi.CacheFolder;
4.69 +import org.openide.filesystems.FileObject;
4.70 +import org.openide.filesystems.FileUtil;
4.71 +
4.72 +/**
4.73 + *
4.74 + * @author lahvac
4.75 + */
4.76 +public class LocalCache {
4.77 +
4.78 + private static final Map<URI, IndexReader> readerCache = new HashMap<URI, IndexReader>();
4.79 +
4.80 + private static File findLocalCacheDir(RemoteIndex ri) throws IOException {
4.81 + return new File(FileUtil.toFile(FileUtil.createFolder(CacheFolder.getDataFolder(ri.remote), "remote-duplicates")), ri.remoteSegment);
4.82 + }
4.83 +
4.84 + private static final long VERSION_CHECK_PERIOD = 60 * 60 * 1000;
4.85 + private static final Map<Entry<URI, String>, Long> lastVersionCheck = new HashMap<Entry<URI, String>, Long>();
4.86 +
4.87 + @CheckForNull public static synchronized <R> R runOverLocalCache(RemoteIndex ri, Task<IndexReader, R> task, R empty, AtomicBoolean cancel) throws IOException, URISyntaxException {
4.88 + URI uri = ri.remote.toURI();
4.89 + SimpleEntry<URI, String> versionCheckKey = new SimpleEntry<URI, String>(uri, ri.remoteSegment);
4.90 + Long lastCheck = lastVersionCheck.get(versionCheckKey);
4.91 +
4.92 + if (lastCheck == null || (System.currentTimeMillis() - lastCheck) > VERSION_CHECK_PERIOD) {
4.93 + File dir = findLocalCacheDir(ri);
4.94 + File remoteVersion = new File(dir, "remoteVersion");
4.95 + FileObject remoteVersionFO = FileUtil.toFileObject(remoteVersion);
4.96 + String previousVersion = remoteVersionFO != null ? remoteVersionFO.asText("UTF-8") : null;
4.97 + URI infoURI = new URI(ri.remote.toExternalForm() + "/info?path=" + WebUtilities.escapeForQuery(ri.remoteSegment));
4.98 + String infoContent = WebUtilities.requestStringResponse(infoURI, cancel);
4.99 +
4.100 + if (cancel.get()) return empty;
4.101 +
4.102 + if (infoContent != null) {
4.103 + Object buildId = Pojson.load(LinkedHashMap.class, infoContent).get("BUILD_ID");
4.104 +
4.105 + if (buildId != null && !(buildId = buildId.toString()).equals(previousVersion)) {
4.106 + remoteVersion.getParentFile().mkdirs();
4.107 + OutputStream out = new FileOutputStream(remoteVersion);
4.108 + try {
4.109 + out.write(buildId.toString().getBytes("UTF-8"));
4.110 + } finally {
4.111 + out.close();
4.112 + }
4.113 +
4.114 + LOG.log(Level.FINE, "Deleting local cache");
4.115 + delete(new File(dir, "index"));
4.116 +
4.117 + IndexReader reader = readerCache.remove(uri);
4.118 + if (reader != null)
4.119 + reader.close();
4.120 +
4.121 + }
4.122 + }
4.123 +
4.124 + lastVersionCheck.put(versionCheckKey, System.currentTimeMillis());
4.125 + }
4.126 +
4.127 + IndexReader reader = readerCache.get(uri);
4.128 +
4.129 + if (reader == null && !cancel.get()) {
4.130 + File dir = new File(findLocalCacheDir(ri), "index");
4.131 +
4.132 + if (dir.listFiles() != null && dir.listFiles().length > 0) {
4.133 + readerCache.put(uri, reader = IndexReader.open(FSDirectory.open(dir), true));
4.134 + }
4.135 + }
4.136 +
4.137 + if (reader == null || cancel.get()) {
4.138 + return empty;
4.139 + }
4.140 +
4.141 + return task.run(reader, cancel);
4.142 + }
4.143 +
4.144 + public static synchronized void saveToLocalCache(RemoteIndex ri, Task<IndexWriter, Void> save) throws IOException, URISyntaxException {
4.145 + IndexReader r = readerCache.remove(ri.remote.toURI());
4.146 +
4.147 + if (r != null) {
4.148 + r.close();
4.149 + }
4.150 +
4.151 + IndexWriter w = new IndexWriter(FSDirectory.open(new File(findLocalCacheDir(ri), "index")), new NoAnalyzer(), MaxFieldLength.UNLIMITED);
4.152 +
4.153 + save.run(w, new AtomicBoolean());
4.154 +
4.155 + w.optimize();
4.156 + w.close();
4.157 + }
4.158 +
4.159 + private static final Logger LOG = Logger.getLogger(LocalCache.class.getName());
4.160 +
4.161 + private static void delete(File file) {
4.162 + File[] c = file.listFiles();
4.163 +
4.164 + if (c != null) {
4.165 + for (File cc : c) {
4.166 + delete(cc);
4.167 + }
4.168 + }
4.169 +
4.170 + file.delete();
4.171 + }
4.172 +
4.173 + public interface Task<P, R> {
4.174 + public R run(P p, AtomicBoolean cancel) throws IOException;
4.175 + }
4.176 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/remoting/ide/api/src/org/netbeans/modules/jackpot30/remotingapi/CacheFolder.java Wed Aug 08 18:39:56 2012 +0200
5.3 @@ -0,0 +1,269 @@
5.4 +/*
5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5.6 + *
5.7 + * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
5.8 + *
5.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
5.10 + * Other names may be trademarks of their respective owners.
5.11 + *
5.12 + * The contents of this file are subject to the terms of either the GNU
5.13 + * General Public License Version 2 only ("GPL") or the Common
5.14 + * Development and Distribution License("CDDL") (collectively, the
5.15 + * "License"). You may not use this file except in compliance with the
5.16 + * License. You can obtain a copy of the License at
5.17 + * http://www.netbeans.org/cddl-gplv2.html
5.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
5.19 + * specific language governing permissions and limitations under the
5.20 + * License. When distributing the software, include this License Header
5.21 + * Notice in each file and include the License file at
5.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
5.23 + * particular file as subject to the "Classpath" exception as provided
5.24 + * by Oracle in the GPL Version 2 section of the License file that
5.25 + * accompanied this code. If applicable, add the following below the
5.26 + * License Header, with the fields enclosed by brackets [] replaced by
5.27 + * your own identifying information:
5.28 + * "Portions Copyrighted [year] [name of copyright owner]"
5.29 + *
5.30 + * If you wish your version of this file to be governed by only the CDDL
5.31 + * or only the GPL Version 2, indicate your decision by adding
5.32 + * "[Contributor] elects to include this software in this distribution
5.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
5.34 + * single choice of license, a recipient has the option to distribute
5.35 + * your version of this file under either the CDDL, the GPL Version 2 or
5.36 + * to extend the choice of license to its licensees as provided above.
5.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
5.38 + * Version 2 license, then the option applies only if the new code is
5.39 + * made subject to such option by the copyright holder.
5.40 + *
5.41 + * Contributor(s):
5.42 + *
5.43 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
5.44 + */
5.45 +
5.46 +package org.netbeans.modules.jackpot30.remotingapi;
5.47 +
5.48 +import java.io.File;
5.49 +import java.io.IOException;
5.50 +import java.io.InputStream;
5.51 +import java.io.OutputStream;
5.52 +import java.net.URL;
5.53 +import java.util.HashMap;
5.54 +import java.util.LinkedList;
5.55 +import java.util.List;
5.56 +import java.util.Map;
5.57 +import java.util.Map.Entry;
5.58 +import java.util.Properties;
5.59 +import java.util.logging.Level;
5.60 +import java.util.logging.Logger;
5.61 +import org.openide.filesystems.FileObject;
5.62 +import org.openide.filesystems.FileSystem;
5.63 +import org.openide.filesystems.FileUtil;
5.64 +import org.openide.filesystems.URLMapper;
5.65 +import org.openide.modules.Places;
5.66 +import org.openide.util.Exceptions;
5.67 +import org.openide.util.RequestProcessor;
5.68 +
5.69 +/**
5.70 + *
5.71 + * @author Tomas Zezula
5.72 + */
5.73 +public final class CacheFolder {
5.74 +
5.75 + private static final Logger LOG = Logger.getLogger(CacheFolder.class.getName());
5.76 + private static final RequestProcessor RP = new RequestProcessor(CacheFolder.class.getName(), 1, false, false);
5.77 + private static final RequestProcessor.Task SAVER = RP.create(new Saver());
5.78 + private static final int SLIDING_WINDOW = 500;
5.79 +
5.80 + private static final String SEGMENTS_FILE = "segments"; //NOI18N
5.81 + private static final String SLICE_PREFIX = "s"; //NOI18N
5.82 +
5.83 + //@GuardedBy("CacheFolder.class")
5.84 + private static FileObject cacheFolder;
5.85 + //@GuardedBy("CacheFolder.class")
5.86 + private static Properties segments;
5.87 + //@GuardedBy("CacheFolder.class")
5.88 + private static Map<String, String> invertedSegments;
5.89 + //@GuardedBy("CacheFolder.class")
5.90 + private static int index = 0;
5.91 +
5.92 +
5.93 + //@NotThreadSafe
5.94 + @org.netbeans.api.annotations.common.SuppressWarnings(
5.95 + value="LI_LAZY_INIT_UPDATE_STATIC"
5.96 + /*,justification="Caller already holds a monitor"*/)
5.97 + private static void loadSegments(FileObject folder) throws IOException {
5.98 + assert Thread.holdsLock(CacheFolder.class);
5.99 + if (segments == null) {
5.100 + assert folder != null;
5.101 + segments = new Properties ();
5.102 + invertedSegments = new HashMap<String,String> ();
5.103 + final FileObject segmentsFile = folder.getFileObject(SEGMENTS_FILE);
5.104 + if (segmentsFile!=null) {
5.105 + final InputStream in = segmentsFile.getInputStream();
5.106 + try {
5.107 + segments.load (in);
5.108 + } finally {
5.109 + in.close();
5.110 + }
5.111 + }
5.112 + for (Map.Entry entry : segments.entrySet()) {
5.113 + String segment = (String) entry.getKey();
5.114 + String root = (String) entry.getValue();
5.115 + invertedSegments.put(root,segment);
5.116 + try {
5.117 + index = Math.max (index,Integer.parseInt(segment.substring(SLICE_PREFIX.length())));
5.118 + } catch (NumberFormatException nfe) {
5.119 + LOG.log(Level.FINE, null, nfe);
5.120 + }
5.121 + }
5.122 + }
5.123 + }
5.124 +
5.125 +
5.126 + private static void storeSegments(FileObject folder) throws IOException {
5.127 + assert Thread.holdsLock(CacheFolder.class);
5.128 + assert folder != null;
5.129 + //It's safer to use FileUtil.createData(File) than FileUtil.createData(FileObject, String)
5.130 + //see issue #173094
5.131 + final File _file = FileUtil.toFile(folder);
5.132 + assert _file != null;
5.133 + final FileObject segmentsFile = FileUtil.createData(new File(_file, SEGMENTS_FILE));
5.134 + final OutputStream out = segmentsFile.getOutputStream();
5.135 + try {
5.136 + segments.store(out,null);
5.137 + } finally {
5.138 + out.close();
5.139 + }
5.140 + }
5.141 +
5.142 + public static synchronized URL getSourceRootForDataFolder (final FileObject dataFolder) {
5.143 + final FileObject segFolder = dataFolder.getParent();
5.144 + if (segFolder == null || !segFolder.equals(cacheFolder)) {
5.145 + return null;
5.146 + }
5.147 + String source = segments.getProperty(dataFolder.getName());
5.148 + if (source != null) {
5.149 + try {
5.150 + return new URL (source);
5.151 + } catch (IOException ioe) {
5.152 + LOG.log(Level.FINE, null, ioe);
5.153 + }
5.154 + }
5.155 + return null;
5.156 + }
5.157 +
5.158 + public static FileObject getDataFolder (final URL root) throws IOException {
5.159 + return getDataFolder(root, false);
5.160 + }
5.161 +
5.162 + public static FileObject getDataFolder (final URL root, final boolean onlyIfAlreadyExists) throws IOException {
5.163 + final String rootName = root.toExternalForm();
5.164 + final FileObject _cacheFolder = getCacheFolder();
5.165 + String slice;
5.166 + synchronized (CacheFolder.class) {
5.167 + loadSegments(_cacheFolder);
5.168 + slice = invertedSegments.get (rootName);
5.169 + if (slice == null) {
5.170 + if (onlyIfAlreadyExists) {
5.171 + return null;
5.172 + }
5.173 + slice = SLICE_PREFIX + (++index);
5.174 + while (segments.getProperty(slice) != null) {
5.175 + slice = SLICE_PREFIX + (++index);
5.176 + }
5.177 + segments.put (slice,rootName);
5.178 + invertedSegments.put(rootName, slice);
5.179 + SAVER.schedule(SLIDING_WINDOW);
5.180 + }
5.181 + }
5.182 + assert slice != null;
5.183 + if (onlyIfAlreadyExists) {
5.184 + return cacheFolder.getFileObject(slice);
5.185 + } else {
5.186 + return FileUtil.createFolder(_cacheFolder, slice);
5.187 + }
5.188 + }
5.189 +
5.190 + public static synchronized Iterable<? extends FileObject> findRootsWithCacheUnderFolder(FileObject folder) throws IOException {
5.191 + URL folderURL = folder.toURL();
5.192 + String prefix = folderURL.toExternalForm();
5.193 + final FileObject _cacheFolder = getCacheFolder();
5.194 + List<FileObject> result = new LinkedList<FileObject>();
5.195 + loadSegments(_cacheFolder);
5.196 + for (Entry<String, String> e : invertedSegments.entrySet()) {
5.197 + if (e.getKey().startsWith(prefix)) {
5.198 + FileObject fo = URLMapper.findFileObject(new URL(e.getKey()));
5.199 +
5.200 + if (fo != null) {
5.201 + result.add(fo);
5.202 + }
5.203 + }
5.204 + }
5.205 +
5.206 + return result;
5.207 + }
5.208 +
5.209 + public static synchronized FileObject getCacheFolder () {
5.210 + if (cacheFolder == null) {
5.211 + File cache = Places.getCacheSubdirectory("index"); // NOI18N
5.212 + if (!cache.isDirectory()) {
5.213 + throw new IllegalStateException("Indices cache folder " + cache.getAbsolutePath() + " is not a folder"); //NOI18N
5.214 + }
5.215 + if (!cache.canRead()) {
5.216 + throw new IllegalStateException("Can't read from indices cache folder " + cache.getAbsolutePath()); //NOI18N
5.217 + }
5.218 + if (!cache.canWrite()) {
5.219 + throw new IllegalStateException("Can't write to indices cache folder " + cache.getAbsolutePath()); //NOI18N
5.220 + }
5.221 +
5.222 + cacheFolder = FileUtil.toFileObject(cache);
5.223 + if (cacheFolder == null) {
5.224 + throw new IllegalStateException("Can't convert indices cache folder " + cache.getAbsolutePath() + " to FileObject"); //NOI18N
5.225 + }
5.226 + }
5.227 + return cacheFolder;
5.228 + }
5.229 +
5.230 +
5.231 + /**
5.232 + * Only for unit tests! It's used also by CslTestBase, which is not in the
5.233 + * same package, hence the public keyword.
5.234 + *
5.235 + */
5.236 + public static void setCacheFolder (final FileObject folder) {
5.237 + SAVER.schedule(0);
5.238 + SAVER.waitFinished();
5.239 + synchronized (CacheFolder.class) {
5.240 + assert folder != null && folder.canRead() && folder.canWrite();
5.241 + cacheFolder = folder;
5.242 + segments = null;
5.243 + invertedSegments = null;
5.244 + index = 0;
5.245 + }
5.246 + }
5.247 +
5.248 + private CacheFolder() {
5.249 + // no-op
5.250 + }
5.251 +
5.252 + private static class Saver implements Runnable {
5.253 + @Override
5.254 + public void run() {
5.255 + try {
5.256 + final FileObject cf = getCacheFolder();
5.257 + // #170182 - preventing filesystem events being fired from under the CacheFolder.class lock
5.258 + cf.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() {
5.259 + @Override
5.260 + public void run() throws IOException {
5.261 + synchronized (CacheFolder.class) {
5.262 + if (segments == null) return ;
5.263 + storeSegments(cf);
5.264 + }
5.265 + }
5.266 + });
5.267 + } catch (IOException ioe) {
5.268 + Exceptions.printStackTrace(ioe);
5.269 + }
5.270 + }
5.271 + }
5.272 +}