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:
042: package org.netbeans.modules.subversion;
043:
044: import org.netbeans.modules.subversion.util.*;
045: import org.netbeans.modules.turbo.TurboProvider;
046: import org.openide.filesystems.FileUtil;
047: import org.openide.filesystems.Repository;
048: import java.io.*;
049: import java.util.*;
050: import java.util.logging.Level;
051:
052: /**
053: * Storage of file attributes with shortcut to retrieve all stored values.
054: *
055: * @author Maros Sandor
056: */
057: class DiskMapTurboProvider implements TurboProvider {
058:
059: static final String ATTR_STATUS_MAP = "subversion.STATUS_MAP"; // NOI18N
060:
061: private static final int STATUS_VALUABLE = FileInformation.STATUS_MANAGED
062: & ~FileInformation.STATUS_VERSIONED_UPTODATE;
063: private static final String CACHE_DIRECTORY = "svncache"; // NOI18N
064:
065: private File cacheStore;
066: private int storeSerial;
067:
068: private int cachedStoreSerial = -1;
069: private Map<File, FileInformation> cachedValues;
070:
071: DiskMapTurboProvider() {
072: initCacheStore();
073: }
074:
075: synchronized Map<File, FileInformation> getAllModifiedValues() {
076: if (cachedStoreSerial != storeSerial || cachedValues == null) {
077: cachedValues = new HashMap<File, FileInformation>();
078: File[] files = cacheStore.listFiles();
079: if (files == null) {
080: return Collections.unmodifiableMap(cachedValues);
081: }
082: for (int i = 0; i < files.length; i++) {
083: File file = files[i];
084: if (file.getName().endsWith(".bin") == false) { // NOI18N
085: // on windows list returns already deleted .new files
086: continue;
087: }
088: DataInputStream dis = null;
089: try {
090: int retry = 0;
091: while (true) {
092: try {
093: dis = new DataInputStream(
094: new BufferedInputStream(
095: new FileInputStream(file)));
096: break;
097: } catch (IOException ioex) {
098: retry++;
099: if (retry > 7) {
100: throw ioex;
101: }
102: Thread.sleep(retry * 30);
103: }
104: }
105:
106: for (;;) {
107: int pathLen = dis.readInt();
108: dis.readInt();
109: String path = readChars(dis, pathLen);
110: Map value = readValue(dis, path);
111: for (Iterator j = value.keySet().iterator(); j
112: .hasNext();) {
113: File f = (File) j.next();
114: FileInformation info = (FileInformation) value
115: .get(f);
116: if ((info.getStatus() & STATUS_VALUABLE) != 0) {
117: cachedValues.put(f, info);
118: }
119: }
120: }
121: } catch (EOFException e) {
122: // reached EOF, no entry for this key
123: } catch (Exception e) {
124: Subversion.LOG.log(Level.SEVERE, null, e);
125: } finally {
126: if (dis != null)
127: try {
128: dis.close();
129: } catch (IOException e) {
130: }
131: }
132: }
133: cachedStoreSerial = storeSerial;
134: cachedValues = Collections.unmodifiableMap(cachedValues);
135: }
136: return cachedValues;
137: }
138:
139: public boolean recognizesAttribute(String name) {
140: return ATTR_STATUS_MAP.equals(name);
141: }
142:
143: public boolean recognizesEntity(Object key) {
144: return key instanceof File;
145: }
146:
147: public synchronized Object readEntry(Object key, String name,
148: MemoryCache memoryCache) {
149: assert key instanceof File;
150: assert name != null;
151:
152: boolean readFailed = false;
153: File dir = (File) key;
154: File store = getStore(dir);
155: if (!store.isFile()) {
156: return null;
157: }
158:
159: String dirPath = dir.getAbsolutePath();
160: int dirPathLen = dirPath.length();
161: DataInputStream dis = null;
162: try {
163:
164: int retry = 0;
165: while (true) {
166: try {
167: dis = new DataInputStream(new BufferedInputStream(
168: new FileInputStream(store)));
169: break;
170: } catch (IOException ioex) {
171: retry++;
172: if (retry > 7) {
173: throw ioex;
174: }
175: Thread.sleep(retry * 30);
176: }
177: }
178:
179: for (;;) {
180: int pathLen = dis.readInt();
181: int mapLen = dis.readInt();
182: if (pathLen != dirPathLen) {
183: skip(dis, pathLen * 2 + mapLen);
184: } else {
185: String path = readChars(dis, pathLen);
186: if (dirPath.equals(path)) {
187: return readValue(dis, path);
188: } else {
189: skip(dis, mapLen);
190: }
191: }
192: }
193: } catch (EOFException e) {
194: // reached EOF, no entry for this key
195: } catch (Exception e) {
196: Subversion.LOG.log(Level.INFO, e.getMessage(), e);
197: readFailed = true;
198: } finally {
199: if (dis != null)
200: try {
201: dis.close();
202: } catch (IOException e) {
203: }
204: }
205: if (readFailed)
206: store.delete();
207: return null;
208: }
209:
210: public synchronized boolean writeEntry(Object key, String name,
211: Object value) {
212: assert key instanceof File;
213: assert name != null;
214:
215: if (value != null) {
216: if (!(value instanceof Map))
217: return false;
218: if (!isValuable(value))
219: value = null;
220: }
221:
222: File dir = (File) key;
223: String dirPath = dir.getAbsolutePath();
224: int dirPathLen = dirPath.length();
225: File store = getStore(dir);
226:
227: if (value == null && !store.exists())
228: return true;
229:
230: File storeNew = new File(store.getParentFile(), store.getName()
231: + ".new"); // NOI18N
232:
233: DataOutputStream oos = null;
234: DataInputStream dis = null;
235: try {
236: oos = new DataOutputStream(new BufferedOutputStream(
237: new FileOutputStream(storeNew)));
238: if (value != null) {
239: writeEntry(oos, dirPath, value);
240: }
241: if (store.exists()) {
242: int retry = 0;
243: while (true) {
244: try {
245: dis = new DataInputStream(
246: new BufferedInputStream(
247: new FileInputStream(store)));
248: break;
249: } catch (IOException ioex) {
250: retry++;
251: if (retry > 7) {
252: throw ioex;
253: }
254: Thread.sleep(retry * 30);
255: }
256: }
257:
258: for (;;) {
259: int pathLen;
260: try {
261: pathLen = dis.readInt();
262: } catch (EOFException e) {
263: break;
264: }
265: int mapLen = dis.readInt();
266: if (pathLen == dirPathLen) {
267: String path = readChars(dis, pathLen);
268: if (dirPath.equals(path)) {
269: skip(dis, mapLen);
270: } else {
271: oos.writeInt(pathLen);
272: oos.writeInt(mapLen);
273: oos.writeChars(path);
274: copyStreams(oos, dis, mapLen);
275: }
276: } else {
277: oos.writeInt(pathLen);
278: oos.writeInt(mapLen);
279: copyStreams(oos, dis, mapLen + pathLen * 2);
280: }
281: }
282: }
283: } catch (Exception e) {
284: Subversion.LOG.log(Level.SEVERE, "Copy: "
285: + store.getAbsolutePath() + " to: "
286: + storeNew.getAbsolutePath(), e); // NOI18N
287: return true;
288: } finally {
289: if (oos != null)
290: try {
291: oos.close();
292: } catch (IOException e) {
293: }
294: if (dis != null)
295: try {
296: dis.close();
297: } catch (IOException e) {
298: }
299: }
300: storeSerial++;
301: try {
302: FileUtils.renameFile(storeNew, store);
303: } catch (IOException ex) {
304: Subversion.LOG.log(Level.SEVERE, null, ex);
305: }
306: return true;
307: }
308:
309: private void skip(InputStream is, long len) throws IOException {
310: while (len > 0) {
311: long n = is.skip(len);
312: if (n < 0)
313: throw new EOFException("Missing " + len + " bytes."); // NOI18N
314: len -= n;
315: }
316: }
317:
318: private String readChars(DataInputStream dis, int len)
319: throws IOException {
320: StringBuffer sb = new StringBuffer(len);
321: while (len-- > 0) {
322: sb.append(dis.readChar());
323: }
324: return sb.toString();
325: }
326:
327: private Map<File, FileInformation> readValue(DataInputStream dis,
328: String dirPath) throws IOException {
329: Map<File, FileInformation> map = new HashMap<File, FileInformation>();
330: int len = dis.readInt();
331: while (len-- > 0) {
332: int nameLen = dis.readInt();
333: String name = readChars(dis, nameLen);
334: File file = new File(dirPath, name);
335: int status = dis.readInt();
336: FileInformation info = new FileInformation(status & 65535,
337: status > 65535);
338: map.put(file, info);
339: }
340: return map;
341: }
342:
343: private void writeEntry(DataOutputStream dos, String dirPath,
344: Object value) throws IOException {
345:
346: Map map = (Map) value;
347: Set set = map.keySet();
348: ByteArrayOutputStream baos = new ByteArrayOutputStream(set
349: .size() * 50);
350: DataOutputStream temp = new DataOutputStream(baos);
351:
352: temp.writeInt(set.size());
353: for (Iterator i = set.iterator(); i.hasNext();) {
354: File file = (File) i.next();
355: FileInformation info = (FileInformation) map.get(file);
356: temp.writeInt(file.getName().length());
357: temp.writeChars(file.getName());
358: temp.writeInt(info.getStatus()
359: + (info.isDirectory() ? 65536 : 0));
360: }
361: temp.close();
362: byte[] valueBytes = baos.toByteArray();
363:
364: dos.writeInt(dirPath.length());
365: dos.writeInt(valueBytes.length);
366: dos.writeChars(dirPath);
367: dos.write(valueBytes);
368: }
369:
370: private boolean isValuable(Object value) {
371: Map map = (Map) value;
372: for (Iterator i = map.values().iterator(); i.hasNext();) {
373: FileInformation info = (FileInformation) i.next();
374: if ((info.getStatus() & STATUS_VALUABLE) != 0)
375: return true;
376: }
377: return false;
378: }
379:
380: private File getStore(File dir) {
381: String dirPath = dir.getAbsolutePath();
382: int dirHash = dirPath.hashCode();
383: return new File(cacheStore, Integer
384: .toString(dirHash % 173 + 172)
385: + ".bin"); // NOI18N
386: }
387:
388: private void initCacheStore() {
389: String userDir = System.getProperty("netbeans.user"); // NOI18N
390: if (userDir != null) {
391: cacheStore = new File(new File(new File(userDir, "var"),
392: "cache"), CACHE_DIRECTORY); // NOI18N
393: } else {
394: File cachedir = FileUtil.toFile(Repository.getDefault()
395: .getDefaultFileSystem().getRoot());
396: cacheStore = new File(cachedir, CACHE_DIRECTORY); // NOI18N
397: }
398: cacheStore.mkdirs();
399: }
400:
401: private static void copyStreams(OutputStream out, InputStream in,
402: int len) throws IOException {
403: byte[] buffer = new byte[4096];
404: for (;;) {
405: int n = (len <= 4096) ? len : 4096;
406: n = in.read(buffer, 0, n);
407: if (n < 0)
408: throw new EOFException("Missing " + len + " bytes."); // NOI18N
409: out.write(buffer, 0, n);
410: if ((len -= n) == 0)
411: break;
412: }
413: out.flush();
414: }
415: }
|