001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.mercurial;
042:
043: import org.netbeans.modules.turbo.TurboProvider;
044: import org.openide.filesystems.FileUtil;
045: import org.openide.filesystems.Repository;
046:
047: import org.netbeans.modules.mercurial.Mercurial;
048: import java.util.logging.Level;
049:
050: import java.io.*;
051: import java.util.*;
052:
053: /**
054: * Storage of file attributes with shortcut to retrieve all stored values.
055: *
056: * @author Maros Sandor
057: */
058: class DiskMapTurboProvider implements TurboProvider {
059:
060: static final String ATTR_STATUS_MAP = "mercurial.STATUS_MAP"; // NOI18N
061:
062: private static final int STATUS_VALUABLE = FileInformation.STATUS_MANAGED
063: & ~FileInformation.STATUS_VERSIONED_UPTODATE;
064: private static final String CACHE_DIRECTORY = "mercurialcache"; // NOI18N
065:
066: private File cacheStore;
067: private int storeSerial;
068:
069: private int cachedStoreSerial = -1;
070: private Map<File, FileInformation> cachedValues;
071:
072: DiskMapTurboProvider() {
073: initCacheStore();
074: }
075:
076: synchronized Map<File, FileInformation> getAllModifiedValues() {
077: if (cachedStoreSerial != storeSerial || cachedValues == null) {
078: cachedValues = new HashMap<File, FileInformation>();
079: File[] files = cacheStore.listFiles();
080: for (int i = 0; i < files.length; i++) {
081: File file = files[i];
082: if (file.getName().endsWith(".bin") == false) { // NOI18N
083: // on windows list returns already deleted .new files
084: continue;
085: }
086: DataInputStream dis = null;
087: try {
088: int retry = 0;
089: while (true) {
090: try {
091: dis = new DataInputStream(
092: new BufferedInputStream(
093: new FileInputStream(file)));
094: break;
095: } catch (IOException ioex) {
096: retry++;
097: if (retry > 7) {
098: throw ioex;
099: }
100: Thread.sleep(retry * 30);
101: }
102: }
103:
104: for (;;) {
105: int pathLen = dis.readInt();
106: dis.readInt();
107: String path = readChars(dis, pathLen);
108: Map value = readValue(dis, path);
109: for (Iterator j = value.keySet().iterator(); j
110: .hasNext();) {
111: File f = (File) j.next();
112: FileInformation info = (FileInformation) value
113: .get(f);
114: if ((info.getStatus() & DiskMapTurboProvider.STATUS_VALUABLE) != 0) {
115: cachedValues.put(f, info);
116: }
117: }
118: }
119: } catch (EOFException e) {
120: // reached EOF, no entry for this key
121: } catch (Exception e) {
122: Mercurial.LOG.log(Level.WARNING, null, e);
123: } finally {
124: if (dis != null)
125: try {
126: dis.close();
127: } catch (IOException e) {
128: }
129: }
130: }
131: cachedStoreSerial = storeSerial;
132: cachedValues = Collections.unmodifiableMap(cachedValues);
133: }
134: return cachedValues;
135: }
136:
137: public boolean recognizesAttribute(String name) {
138: return DiskMapTurboProvider.ATTR_STATUS_MAP.equals(name);
139: }
140:
141: public boolean recognizesEntity(Object key) {
142: return key instanceof File;
143: }
144:
145: public synchronized Object readEntry(Object key, String name,
146: MemoryCache memoryCache) {
147: assert key instanceof File;
148: assert name != null;
149:
150: boolean readFailed = false;
151: File dir = (File) key;
152: File store = getStore(dir);
153: if (!store.isFile()) {
154: return null;
155: }
156:
157: String dirPath = dir.getAbsolutePath();
158: int dirPathLen = dirPath.length();
159: DataInputStream dis = null;
160: try {
161:
162: int retry = 0;
163: while (true) {
164: try {
165: dis = new DataInputStream(new BufferedInputStream(
166: new FileInputStream(store)));
167: break;
168: } catch (IOException ioex) {
169: retry++;
170: if (retry > 7) {
171: throw ioex;
172: }
173: Thread.sleep(retry * 30);
174: }
175: }
176:
177: for (;;) {
178: int pathLen = dis.readInt();
179: int mapLen = dis.readInt();
180: if (pathLen != dirPathLen) {
181: skip(dis, pathLen * 2 + mapLen);
182: } else {
183: String path = readChars(dis, pathLen);
184: if (dirPath.equals(path)) {
185: return readValue(dis, path);
186: } else {
187: skip(dis, mapLen);
188: }
189: }
190: }
191: } catch (EOFException e) {
192: // reached EOF, no entry for this key
193: } catch (Exception e) {
194: Mercurial.LOG.log(Level.INFO, null, e);
195: readFailed = true;
196: } finally {
197: if (dis != null)
198: try {
199: dis.close();
200: } catch (IOException e) {
201: }
202: }
203: if (readFailed)
204: store.delete();
205: return null;
206: }
207:
208: public synchronized boolean writeEntry(Object key, String name,
209: Object value) {
210: assert key instanceof File;
211: assert name != null;
212:
213: if (value != null) {
214: if (!(value instanceof Map))
215: return false;
216: if (!isValuable(value))
217: value = null;
218: }
219:
220: File dir = (File) key;
221: String dirPath = dir.getAbsolutePath();
222: int dirPathLen = dirPath.length();
223: File store = getStore(dir);
224:
225: if (value == null && !store.exists())
226: return true;
227:
228: File storeNew = new File(store.getParentFile(), store.getName()
229: + ".new"); // NOI18N
230:
231: DataOutputStream oos = null;
232: DataInputStream dis = null;
233: try {
234: oos = new DataOutputStream(new BufferedOutputStream(
235: new FileOutputStream(storeNew)));
236: if (value != null) {
237: writeEntry(oos, dirPath, value);
238: }
239: if (store.exists()) {
240: int retry = 0;
241: while (true) {
242: try {
243: dis = new DataInputStream(
244: new BufferedInputStream(
245: new FileInputStream(store)));
246: break;
247: } catch (IOException ioex) {
248: retry++;
249: if (retry > 7) {
250: throw ioex;
251: }
252: Thread.sleep(retry * 30);
253: }
254: }
255:
256: for (;;) {
257: int pathLen;
258: try {
259: pathLen = dis.readInt();
260: } catch (EOFException e) {
261: break;
262: }
263: int mapLen = dis.readInt();
264: if (pathLen == dirPathLen) {
265: String path = readChars(dis, pathLen);
266: if (dirPath.equals(path)) {
267: skip(dis, mapLen);
268: } else {
269: oos.writeInt(pathLen);
270: oos.writeInt(mapLen);
271: oos.writeChars(path);
272: DiskMapTurboProvider.copyStreams(oos, dis,
273: mapLen);
274: }
275: } else {
276: oos.writeInt(pathLen);
277: oos.writeInt(mapLen);
278: DiskMapTurboProvider.copyStreams(oos, dis,
279: mapLen + pathLen * 2);
280: }
281: }
282: }
283: } catch (Exception e) {
284: Mercurial.LOG.log(Level.WARNING,
285: "writeEntry(): Copy: {0} to: {1}", new Object[] {
286: store.getAbsolutePath(),
287: storeNew.getAbsolutePath() }); //NOI18N
288: return true;
289: } finally {
290: if (oos != null)
291: try {
292: oos.close();
293: } catch (IOException e) {
294: }
295: if (dis != null)
296: try {
297: dis.close();
298: } catch (IOException e) {
299: }
300: }
301: storeSerial++;
302: store.delete();
303: storeNew.renameTo(store);
304: return true;
305: }
306:
307: private void skip(InputStream is, long len) throws IOException {
308: while (len > 0) {
309: long n = is.skip(len);
310: if (n < 0)
311: throw new EOFException("Missing " + len + " bytes."); // NOI18N
312: len -= n;
313: }
314: }
315:
316: private String readChars(DataInputStream dis, int len)
317: throws IOException {
318: StringBuffer sb = new StringBuffer(len);
319: while (len-- > 0) {
320: sb.append(dis.readChar());
321: }
322: return sb.toString();
323: }
324:
325: private Map<File, FileInformation> readValue(DataInputStream dis,
326: String dirPath) throws IOException {
327: Map<File, FileInformation> map = new HashMap<File, FileInformation>();
328: int len = dis.readInt();
329: while (len-- > 0) {
330: int nameLen = dis.readInt();
331: String name = readChars(dis, nameLen);
332: File file = new File(dirPath, name);
333: int status = dis.readInt();
334: FileInformation info = new FileInformation(status & 65535,
335: status > 65535);
336: map.put(file, info);
337: }
338: return map;
339: }
340:
341: private void writeEntry(DataOutputStream dos, String dirPath,
342: Object value) throws IOException {
343:
344: Map map = (Map) value;
345: Set set = map.keySet();
346: ByteArrayOutputStream baos = new ByteArrayOutputStream(set
347: .size() * 50);
348: DataOutputStream temp = new DataOutputStream(baos);
349:
350: temp.writeInt(set.size());
351: for (Iterator i = set.iterator(); i.hasNext();) {
352: File file = (File) i.next();
353: FileInformation info = (FileInformation) map.get(file);
354: temp.writeInt(file.getName().length());
355: temp.writeChars(file.getName());
356: temp.writeInt(info.getStatus()
357: + (info.isDirectory() ? 65536 : 0));
358: }
359: temp.close();
360: byte[] valueBytes = baos.toByteArray();
361:
362: dos.writeInt(dirPath.length());
363: dos.writeInt(valueBytes.length);
364: dos.writeChars(dirPath);
365: dos.write(valueBytes);
366: }
367:
368: private boolean isValuable(Object value) {
369: Map map = (Map) value;
370: for (Iterator i = map.values().iterator(); i.hasNext();) {
371: FileInformation info = (FileInformation) i.next();
372: if ((info.getStatus() & DiskMapTurboProvider.STATUS_VALUABLE) != 0)
373: return true;
374: }
375: return false;
376: }
377:
378: private File getStore(File dir) {
379: String dirPath = dir.getAbsolutePath();
380: int dirHash = dirPath.hashCode();
381: return new File(cacheStore, Integer
382: .toString(dirHash % 173 + 172)
383: + ".bin"); // NOI18N
384: }
385:
386: private void initCacheStore() {
387: String userDir = System.getProperty("netbeans.user"); // NOI18N
388: if (userDir != null) {
389: cacheStore = new File(new File(new File(userDir, "var"),
390: "cache"), DiskMapTurboProvider.CACHE_DIRECTORY); // NOI18N
391: } else {
392: File cachedir = FileUtil.toFile(Repository.getDefault()
393: .getDefaultFileSystem().getRoot());
394: cacheStore = new File(cachedir,
395: DiskMapTurboProvider.CACHE_DIRECTORY); // NOI18N
396: }
397: cacheStore.mkdirs();
398: }
399:
400: private static void copyStreams(OutputStream out, InputStream in,
401: int len) throws IOException {
402: byte[] buffer = new byte[4096];
403: for (;;) {
404: int n = (len <= 4096) ? len : 4096;
405: n = in.read(buffer, 0, n);
406: if (n < 0)
407: throw new EOFException("Missing " + len + " bytes."); // NOI18N
408: out.write(buffer, 0, n);
409: if ((len -= n) == 0)
410: break;
411: }
412: out.flush();
413: }
414: }
|