KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > localhistory > store > LocalHistoryStoreImpl


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.localhistory.store;
20
21 import java.beans.PropertyChangeEvent JavaDoc;
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.io.BufferedInputStream JavaDoc;
25 import java.io.BufferedOutputStream JavaDoc;
26 import java.io.DataInputStream JavaDoc;
27 import java.io.DataOutputStream JavaDoc;
28 import java.io.EOFException JavaDoc;
29 import java.io.File JavaDoc;
30 import java.io.FileInputStream JavaDoc;
31 import java.io.FileOutputStream JavaDoc;
32 import java.io.FilenameFilter JavaDoc;
33 import java.io.IOException JavaDoc;
34 import java.io.InputStream JavaDoc;
35 import java.io.OutputStream JavaDoc;
36 import java.security.MessageDigest JavaDoc;
37 import java.security.NoSuchAlgorithmException JavaDoc;
38 import java.util.ArrayList JavaDoc;
39 import java.util.Collections JavaDoc;
40 import java.util.HashMap JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.List JavaDoc;
43 import java.util.Map JavaDoc;
44 import java.util.Map.Entry;
45 import java.util.Set JavaDoc;
46 import org.netbeans.modules.localhistory.Diagnostics;
47 import org.netbeans.modules.localhistory.utils.FileUtils;
48 import org.netbeans.modules.turbo.CustomProviders;
49 import org.netbeans.modules.turbo.Turbo;
50 import org.netbeans.modules.turbo.TurboProvider;
51 import org.netbeans.modules.turbo.TurboProvider;
52 import org.netbeans.modules.turbo.TurboProvider;
53 import org.netbeans.modules.turbo.TurboProvider.MemoryCache;
54 import org.openide.ErrorManager;
55 import org.openide.util.RequestProcessor;
56
57 /**
58  *
59  * @author Tomas Stupka
60  */

61 class LocalHistoryStoreImpl implements LocalHistoryStore {
62
63     private static final int DELETED = 0;
64     private static final int TOUCHED = 1;
65         
66     private static final String JavaDoc DATA_FILE = "data"; // NOI18N
67
private static final String JavaDoc HISTORY_FILE = "history"; // NOI18N
68
private static final String JavaDoc LABELS_FILE = "labels"; // NOI18N
69

70     private File JavaDoc storage;
71     private Turbo turbo;
72     private DataFilesTurboProvider cacheProvider;
73     private final PropertyChangeSupport JavaDoc propertyChangeSupport;
74     
75     private static List JavaDoc<HistoryEntry> emptyHistory = new ArrayList JavaDoc<HistoryEntry>(0);
76     private static Map JavaDoc<Long JavaDoc, String JavaDoc> emptyLabels = new HashMap JavaDoc<Long JavaDoc, String JavaDoc>();
77     private static StoreEntry[] emptyStoreEntryArray = new StoreEntry[0];
78     
79     private static FilenameFilter JavaDoc fileEntriesFilter =
80             new FilenameFilter JavaDoc() {
81                 public boolean accept(File JavaDoc dir, String JavaDoc fileName) {
82                     return !( fileName.endsWith(DATA_FILE) ||
83                               fileName.endsWith(HISTORY_FILE) ||
84                               fileName.endsWith(LABELS_FILE));
85                 }
86             };
87     
88     LocalHistoryStoreImpl() {
89         initStorage();
90         
91         propertyChangeSupport = new PropertyChangeSupport JavaDoc(this);
92         
93         cacheProvider = new DataFilesTurboProvider();
94         turbo = Turbo.createCustom(
95                 new CustomProviders() {
96                     private final Set JavaDoc providers = Collections.singleton(cacheProvider);
97                     public Iterator JavaDoc providers() {
98                         return providers.iterator();
99                     }
100                 },
101                 20, -1); // XXX why -1, isn't in such a case a weakhashmap enough?
102
}
103
104     public synchronized void fileCreate(File JavaDoc file, long ts) {
105         try {
106             fileCreateImpl(file, ts, null, file.getAbsolutePath());
107         } catch (IOException JavaDoc ioe) {
108             ErrorManager.getDefault().notify(ErrorManager.WARNING, ioe);
109         }
110     }
111
112     private void fileCreateImpl(File JavaDoc file, long ts, String JavaDoc from, String JavaDoc to) throws IOException JavaDoc {
113        if(lastModified(file) > 0) {
114             return;
115         }
116         String JavaDoc tsString = Long.toString(ts);
117         File JavaDoc storeFile = null;
118         if(file.isFile()) {
119             storeFile = getStoreFile(file, tsString, true); // XXX let's call it a lack of inspiration
120
FileUtils.copy(file, StoreEntry.createStoreFileOutputSteam(storeFile));
121             
122             if(Diagnostics.ON) {
123                 Diagnostics.logCreate(file, storeFile, ts, from, to);
124             }
125             
126         }
127         touch(file, new StoreDataFile(file.getAbsolutePath(), TOUCHED, ts, file.isFile()));
128         File JavaDoc parent = file.getParentFile();
129         if(parent != null) {
130             // XXX consider also touching the parent - yes (collisions, ...)
131
writeHistoryForFile(parent, new HistoryEntry[] {new HistoryEntry(ts, from, to, TOUCHED)});
132         }
133         fireChanged(null, file);
134     }
135     
136     public synchronized void fileChange(File JavaDoc file, long ts) {
137         long lastModified = lastModified(file);
138         if(lastModified == ts) {
139             return;
140         }
141         if(file.isFile()) {
142             try {
143                 File JavaDoc storeFile = getStoreFile(file, Long.toString(ts), true);
144                 FileUtils.copy(file, StoreEntry.createStoreFileOutputSteam(storeFile));
145                 
146                 if(Diagnostics.ON) {
147                     Diagnostics.logChange(file, storeFile, ts);
148                 }
149                 
150                 touch(file, new StoreDataFile(file.getAbsolutePath(), TOUCHED, ts, file.isFile()));
151             } catch (IOException JavaDoc ioe) {
152                 ErrorManager.getDefault().notify(ErrorManager.WARNING, ioe);
153             }
154         } else {
155             try {
156                 touch(file, new StoreDataFile(file.getAbsolutePath(), TOUCHED, ts, file.isFile()));
157             } catch (IOException JavaDoc ioe) {
158                 ErrorManager.getDefault().notify(ErrorManager.WARNING, ioe);
159             }
160         }
161         fireChanged(null, file);
162     }
163     
164     public synchronized void fileDelete(File JavaDoc file, long ts) {
165         try {
166             fileDeleteImpl(file, null, file.getAbsolutePath(), ts);
167         } catch (IOException JavaDoc ioe) {
168             ErrorManager.getDefault().notify(ErrorManager.WARNING, ioe);
169         }
170         fireChanged(null, file);
171     }
172
173     private void fileDeleteImpl(File JavaDoc file, String JavaDoc from, String JavaDoc to, long ts) throws IOException JavaDoc {
174         StoreDataFile data = readStoreData(file, true);
175         // XXX what if already deleted?
176

177         if(data == null) {
178             if(Diagnostics.ON) {
179                 Diagnostics.println("deleting without data for file : " + file);
180             }
181             return;
182         }
183         // copy from previous entry
184
long lastModified = data.getLastModified();
185         boolean isFile = data.isFile();
186         
187         if(Diagnostics.ON) {
188             File JavaDoc storeFile = getDataFile(file);
189             Diagnostics.logDelete(file, storeFile, ts);
190         }
191         
192         touch(file, new StoreDataFile(file.getAbsolutePath(), DELETED, lastModified, isFile));
193         File JavaDoc parent = file.getParentFile();
194         if(parent != null) {
195             // XXX consider also touching the parent
196
writeHistoryForFile(parent, new HistoryEntry[] {new HistoryEntry(ts, from, to, DELETED)});
197         }
198     }
199     
200     public synchronized void fileCreateFromMove(File JavaDoc from, File JavaDoc to, long ts) {
201         if(lastModified(to) > 0) {
202             return;
203         }
204         try {
205             fileCreateImpl(to, ts, from.getAbsolutePath(), to.getAbsolutePath());
206         } catch (IOException JavaDoc ioe) {
207             ErrorManager.getDefault().notify(ErrorManager.WARNING, ioe);
208         }
209         fireChanged(null, to);
210     }
211
212     public synchronized void fileDeleteFromMove(File JavaDoc from, File JavaDoc to, long ts) {
213         try {
214             fileDeleteImpl(from, from.getAbsolutePath(), to.getAbsolutePath(), ts);
215         } catch (IOException JavaDoc ioe) {
216             ErrorManager.getDefault().notify(ErrorManager.WARNING, ioe);
217         }
218         fireChanged(null, from);
219     }
220
221     private long lastModified(File JavaDoc file) {
222         StoreDataFile data = readStoreData(file, true);
223         return data != null && data.getStatus() != DELETED ? data.getLastModified() : -1;
224     }
225     
226     public synchronized StoreEntry[] getStoreEntries(File JavaDoc file) {
227         // XXX file.isFile() won't work for deleted files
228
return getStoreEntriesImpl(file);
229     }
230     
231     private StoreEntry[] getStoreEntriesImpl(File JavaDoc file) {
232         File JavaDoc storeFolder = getStoreFolder(file);
233         File JavaDoc[] storeFiles = storeFolder.listFiles(fileEntriesFilter);
234         if(storeFiles != null && storeFiles.length > 0) {
235             List JavaDoc<StoreEntry> ret = new ArrayList JavaDoc<StoreEntry>(storeFiles.length);
236             if(storeFiles.length > 0) {
237                 Map JavaDoc<Long JavaDoc, String JavaDoc> labels = getLabels(getLabelsFile(file));
238                 for (int i = 0; i < storeFiles.length; i++) {
239                     long ts = Long.parseLong(storeFiles[i].getName());
240                     String JavaDoc label = labels.get(ts);
241                     ret.add(StoreEntry.createStoreEntry(file, storeFiles[i], ts, label));
242                 }
243                 return ret.toArray(new StoreEntry[storeFiles.length]);
244             }
245             return emptyStoreEntryArray;
246         } else {
247             return emptyStoreEntryArray;
248         }
249     }
250
251     public StoreEntry[] getFolderState(File JavaDoc root, File JavaDoc[] files, long ts) {
252
253         // check if the root wasn't deleted to that time
254
File JavaDoc parentFile = root.getParentFile();
255         if(parentFile != null) {
256             List JavaDoc<HistoryEntry> parentHistory = readHistoryForFile(parentFile);
257             if(wasDeleted(root, parentHistory, ts)) {
258                 return emptyStoreEntryArray;
259             }
260         }
261         
262         List JavaDoc<HistoryEntry> history = readHistoryForFile(root);
263         
264         // StoreEntries we will return
265
List JavaDoc<StoreEntry> ret = new ArrayList JavaDoc<StoreEntry>();
266         
267         Map JavaDoc<File JavaDoc, HistoryEntry> beforeRevert = new HashMap JavaDoc<File JavaDoc, HistoryEntry>();
268         Map JavaDoc<File JavaDoc, HistoryEntry> afterRevert = new HashMap JavaDoc<File JavaDoc, HistoryEntry>();
269         
270         for(HistoryEntry he : history) {
271             File JavaDoc file = new File JavaDoc(he.getTo());
272             if(he.getTimestamp() < ts) {
273                 // this is the LAST thing which happened
274
// to a file before the given time
275
beforeRevert.put(file, he);
276             } else {
277                 // this is the FIRST thing which happened
278
// to a file before the given time
279
if(!afterRevert.containsKey(file)) {
280                     afterRevert.put(file, he);
281                 }
282             }
283         }
284
285         for(File JavaDoc file : files) {
286             HistoryEntry before = beforeRevert.get(file);
287             HistoryEntry after = afterRevert.get(file);
288             
289             // lets see what remains when we are throught all existing files
290
beforeRevert.remove(file);
291             afterRevert.remove(file);
292             
293             if(before != null && before.getStatus() == DELETED) {
294                 // the file was deleted to the given time -> delete it!
295
ret.add(StoreEntry.createDeletedStoreEntry(file, ts));
296                 continue;
297             }
298             
299             StoreDataFile data = readStoreData(file, true);
300             if(data == null) {
301                 // XXX ???
302
continue;
303             }
304             if(data.isFile()) {
305                 StoreEntry se = getStoreEntry(file, ts);
306                 if(se != null) {
307                     ret.add(se);
308                 } else {
309                     if(after != null && after.getStatus() == TOUCHED) {
310                         ret.add(StoreEntry.createDeletedStoreEntry(file, ts));
311                     } else {
312                         // XXX is this possible?
313
}
314                     // the file still exists and there is no entry -> uptodate?
315
}
316             } else {
317                 if(after != null && after.getStatus() == TOUCHED) {
318                     ret.add(StoreEntry.createDeletedStoreEntry(file, ts));
319                 } else {
320                     // XXX is this possible?
321
}
322                 // the folder still exists and it wasn't deleted, so do nothing
323
}
324         }
325         
326         
327         for(Entry<File JavaDoc, HistoryEntry> entry : beforeRevert.entrySet()) {
328             
329             File JavaDoc file = entry.getKey();
330             
331             // lets see what remains
332
afterRevert.remove(file);
333             
334             // the file doesn't exist now, but
335
// there was something done to it before the given time
336
if(entry.getValue().getStatus() == DELETED) {
337                 // this is exactly what we have - forget it!
338
continue;
339             }
340                         
341             StoreDataFile data = readStoreData(file, true);
342             if(data != null) {
343                 if(data.isFile()) {
344                     StoreEntry se = getStoreEntry(file, ts);
345                     if(se != null) {
346                         ret.add(se);
347                     } else {
348                         // XXX what now? this should be covered
349
}
350                 } else {
351                     // it must have existed
352
File JavaDoc storeFile = getStoreFolder(root); // XXX why returning the root
353
StoreEntry folderEntry = StoreEntry.createStoreEntry(new File JavaDoc(data.getAbsolutePath()), storeFile, data.getLastModified(), "");
354                     ret.add(folderEntry);
355                 }
356             } else {
357                 // XXX how to cover this?
358
}
359         }
360         
361         // XXX do we even need this
362
// for(Entry<File, HistoryEntry> entry : afterRevert.entrySet()) {
363
//
364
// }
365
return ret.toArray(new StoreEntry[ret.size()]);
366                
367     }
368     
369     private boolean wasDeleted(File JavaDoc file, List JavaDoc<HistoryEntry> history , long ts) {
370         String JavaDoc path = file.getAbsolutePath();
371         boolean deleted = false;
372         
373         for(int i = 0; i < history.size(); i++) {
374             HistoryEntry he = history.get(i);
375             if(he.getTo().equals(path)) {
376                 if(he.getStatus() == DELETED) {
377                     deleted = true;
378                 } else {
379                     deleted = false;
380                 }
381             }
382             if(he.ts >= ts) {
383                 break;
384             }
385         }
386         return deleted;
387     }
388     
389     public synchronized StoreEntry getStoreEntry(File JavaDoc file, long ts) {
390         return getStoreEntryImpl(file, ts, readStoreData(file, true));
391     }
392
393     private StoreEntry getStoreEntryImpl(File JavaDoc file, long ts, StoreDataFile data) {
394         // XXX what if file deleted?
395
StoreEntry entry = null;
396                 
397         if(data == null) {
398             // not in storage?
399
return null;
400         }
401         if(data.isFile()) {
402             StoreEntry[] entries = getStoreEntriesImpl(file);
403             for(StoreEntry se : entries) {
404                 if(se.getTimestamp() <= ts) {
405                     if( entry == null || se.getTimestamp() > entry.getTimestamp() ) {
406                         entry = se;
407                     }
408                 }
409             }
410         } else {
411             // XXX dont implement this for folders as long there is no need
412
}
413         
414         return entry;
415     }
416     
417     public synchronized void deleteEntry(File JavaDoc file, long ts) {
418         File JavaDoc storeFile = getStoreFile(file, Long.toString(ts), false);
419         if(storeFile.exists()) {
420             storeFile.delete();
421         }
422         // XXX delete from parent history
423
fireChanged(file, null);
424     }
425
426     public synchronized StoreEntry[] getDeletedFiles(File JavaDoc root) {
427         if(root.isFile()) {
428             return null;
429         }
430         
431         Map JavaDoc<String JavaDoc, StoreEntry> deleted = new HashMap JavaDoc<String JavaDoc, StoreEntry>();
432         List JavaDoc<HistoryEntry> entries = readHistoryForFile(root);
433                 
434         for(HistoryEntry he : entries) {
435             if(he.getStatus() == DELETED) {
436                 String JavaDoc filePath = he.getTo();
437                 if(!deleted.containsKey(filePath)) {
438                     StoreDataFile data = readStoreData(new File JavaDoc(he.getTo()), true);
439                     if(data != null && data.getStatus() == DELETED) {
440                         File JavaDoc storeFile = data.isFile ?
441                                             getStoreFile(new File JavaDoc(data.getAbsolutePath()), Long.toString(data.getLastModified()), false) :
442                                             getStoreFolder(root); // XXX why returning the root???
443
deleted.put(filePath, StoreEntry.createStoreEntry(new File JavaDoc(data.getAbsolutePath()), storeFile, data.getLastModified(), ""));
444                     }
445                 }
446             }
447         }
448         return deleted.values().toArray(new StoreEntry[deleted.size()]);
449     }
450
451     public synchronized void setLabel(File JavaDoc file, long ts, String JavaDoc label) {
452         File JavaDoc labelsFile = getLabelsFile(file);
453         File JavaDoc parent = labelsFile.getParentFile();
454         if(!parent.exists()) {
455             parent.mkdirs();
456         }
457         
458         File JavaDoc labelsNew = null;
459         DataInputStream JavaDoc dis = null;
460         DataOutputStream JavaDoc oos = null;
461         boolean foundLabel = false;
462         try {
463             if(!labelsFile.exists()) {
464                 oos = new DataOutputStream JavaDoc(new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(labelsFile)));
465                 oos.writeLong(ts);
466                 writeString(oos, label);
467             } else {
468                 labelsNew = new File JavaDoc(labelsFile.getParentFile(), labelsFile.getName() + ".new"); // NOI18N
469
oos = new DataOutputStream JavaDoc(new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(labelsNew)));
470
471                 dis = getInputStream(labelsFile);
472                 long readTs = -1;
473                 try {
474                     while(true) {
475                         readTs = dis.readLong();
476                         if(readTs == ts) {
477                             oos.writeLong(readTs);
478                             writeString(oos, label);
479                             int len = dis.readInt();
480                             skip(dis, len * 2);
481                             copyStreams(oos, dis);
482                             break;
483                         } else {
484                             oos.writeLong(readTs);
485                             String JavaDoc l = readString(dis);
486                             writeString(oos, l);
487                         }
488                     }
489                 } catch (EOFException JavaDoc e) {
490                     if(!foundLabel) {
491                         oos.writeLong(ts);
492                         writeString(oos, label);
493                     }
494                 }
495             }
496             oos.flush();
497         } catch (EOFException JavaDoc e) {
498             // ignore
499
} catch (Exception JavaDoc e) {
500             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
501         } finally {
502             if (dis != null) {
503                 try { dis.close(); } catch (IOException JavaDoc e) { }
504             }
505             if (oos != null) {
506                 try { oos.close(); } catch (IOException JavaDoc e) { }
507             }
508         }
509         
510         try {
511             if(labelsNew != null ) {
512                 FileUtils.renameFile(labelsNew, labelsFile);
513             }
514         } catch (IOException JavaDoc ex) {
515             ErrorManager.getDefault().notify(ex);
516         }
517     
518         return;
519     }
520     
521     public synchronized void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
522         propertyChangeSupport.addPropertyChangeListener(l);
523     }
524     
525     public synchronized void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
526         propertyChangeSupport.removePropertyChangeListener(l);
527     }
528
529     public void cleanUp(final long ttl) {
530         // XXX run only once a day - use the top folder metadata for version and cleanup flag
531
RequestProcessor.getDefault().post(new Runnable JavaDoc() {
532             public void run() {
533                 if(Diagnostics.ON) {
534                     Diagnostics.println("Cleanup Start"); // NOI18N
535
}
536                 
537                 cleanUpImpl(ttl);
538                 
539                 if(Diagnostics.ON) {
540                     Diagnostics.println("Cleanup End"); // NOI18N
541
}
542             }
543         });
544     }
545     
546     private void cleanUpImpl(long ttl) {
547                         
548         // XXX fire events
549

550         long now = System.currentTimeMillis();
551         
552         File JavaDoc[] topLevelFiles = storage.listFiles();
553         if(topLevelFiles == null || topLevelFiles.length == 0) {
554             return;
555         }
556         
557         for(File JavaDoc topLevelFile : topLevelFiles) {
558             File JavaDoc[] secondLevelFiles = topLevelFile.listFiles();
559             if(secondLevelFiles == null || secondLevelFiles.length == 0) {
560                 FileUtils.deleteRecursively(topLevelFile);
561                 continue;
562             }
563             
564             boolean allEmpty = true;
565             for(File JavaDoc secondLevelFile : secondLevelFiles) {
566                 boolean empty = cleanUpFolder(secondLevelFile, ttl, now);
567                 if(empty) {
568                     if(secondLevelFile.exists()) {
569                         FileUtils.deleteRecursively(secondLevelFile);
570                     }
571                 } else {
572                     allEmpty = false;
573                 }
574             }
575             if(allEmpty) {
576                 FileUtils.deleteRecursively(topLevelFile);
577             }
578         }
579     }
580     
581     private synchronized boolean cleanUpFolder(File JavaDoc folder, long ttl, long now) {
582         File JavaDoc dataFile = new File JavaDoc(folder, DATA_FILE);
583         
584         if(!dataFile.exists()) {
585             // it's a folder
586
return cleanUpStoredFolder(folder, ttl, now);
587         }
588         
589         StoreDataFile data = readStoreData(dataFile, false);
590         if(data.getAbsolutePath() == null) {
591             // what's this?
592
return true;
593         }
594         if(data.isFile()) {
595            return cleanUpStoredFile(folder, ttl, now);
596         } else {
597            return cleanUpStoredFolder(folder, ttl, now);
598         }
599     }
600     
601     private boolean cleanUpStoredFile(File JavaDoc store, long ttl, long now) {
602         File JavaDoc dataFile = new File JavaDoc(store, DATA_FILE);
603         
604         if(!dataFile.exists()) {
605             return true;
606         }
607         if(dataFile.lastModified() < now - ttl) {
608             purgeDataFile(dataFile);
609             return true;
610         }
611         
612         File JavaDoc[] files = store.listFiles(fileEntriesFilter);
613         boolean skipped = false;
614         
615         File JavaDoc labelsFile = new File JavaDoc(store, LABELS_FILE);
616         Map JavaDoc<Long JavaDoc, String JavaDoc> labels = emptyLabels;
617         if(labelsFile.exists()) {
618             labels = getLabels(labelsFile);
619         }
620         for(File JavaDoc f : files) {
621             // XXX check the timestamp when touched
622
long ts = Long.parseLong(f.getName());
623             if(ts < now - ttl) {
624                 if(labels.size() > 0) {
625                     labels.remove(ts);
626                 }
627                 f.delete();
628             } else {
629                 skipped = true;
630             }
631         }
632         if(!skipped) {
633             // all entries are gone -> remove also the metadata
634
labelsFile.delete();
635             writeStoreData(dataFile, null, false);
636         } else {
637             if(labels.size() > 0) {
638                 writeLabels(labelsFile, labels);
639             }
640         }
641         return !skipped;
642     }
643     
644     private void writeLabels(File JavaDoc labelsFile, Map JavaDoc<Long JavaDoc, String JavaDoc> labels) {
645         File JavaDoc parent = labelsFile.getParentFile();
646         if(!parent.exists()) {
647             parent.mkdirs();
648         }
649         DataInputStream JavaDoc dis = null;
650         DataOutputStream JavaDoc oos = null;
651         try {
652             for(Entry<Long JavaDoc, String JavaDoc> label : labels.entrySet()) {
653                 oos = new DataOutputStream JavaDoc(new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(labelsFile)));
654                 oos.writeLong(label.getKey());
655                 writeString(oos, label.getValue());
656             }
657             oos.flush();
658         } catch (Exception JavaDoc e) {
659             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
660         } finally {
661             if (dis != null) {
662                 try { dis.close(); } catch (IOException JavaDoc e) { }
663             }
664             if (oos != null) {
665                 try { oos.close(); } catch (IOException JavaDoc e) { }
666             }
667         }
668     }
669     
670     private boolean cleanUpStoredFolder(File JavaDoc store, long ttl, long now) {
671         File JavaDoc historyFile = new File JavaDoc(store, HISTORY_FILE);
672         File JavaDoc dataFile = new File JavaDoc(store, DATA_FILE);
673         
674         boolean dataObsolete = !dataFile.exists() || dataFile.lastModified() < now - ttl;
675         boolean historyObsolete = !historyFile.exists() || historyFile.lastModified() < now - ttl;
676                                
677         if(!historyObsolete) {
678             List JavaDoc<HistoryEntry> entries = readHistory(historyFile);
679             historyFile.delete();
680             List JavaDoc<HistoryEntry> newEntries = new ArrayList JavaDoc<HistoryEntry>();
681             for(HistoryEntry entry : entries) {
682                 // XXX check the timestamp when touched - and you also should to write it with the historywhen
683
if(entry.getTimestamp() > now - ttl) {
684                     newEntries.add(entry);
685                 }
686             }
687             if(newEntries.size() > 0) {
688                 writeHistory(historyFile, newEntries.toArray(new HistoryEntry[newEntries.size()]));
689             } else {
690                 historyObsolete = true;
691             }
692         }
693         if(dataObsolete) {
694             purgeDataFile(dataFile);
695         }
696         if(historyObsolete) {
697             historyFile.delete();
698         }
699         
700         return dataObsolete && historyObsolete;
701     }
702     
703     private void purgeDataFile(File JavaDoc dataFile) {
704         if(dataFile.exists()) {
705             writeStoreData(dataFile, null, false);
706         }
707     }
708     
709     private void fireChanged(File JavaDoc oldValue, File JavaDoc newValue) {
710         propertyChangeSupport.firePropertyChange(
711             new PropertyChangeEvent JavaDoc(
712                     this,
713                     LocalHistoryStore.PROPERTY_CHANGED,
714                     oldValue,
715                     newValue));
716     }
717     
718     private void touch(File JavaDoc file, StoreDataFile data) throws IOException JavaDoc {
719         writeStoreData(file, data, true);
720     }
721        
722     private void initStorage() {
723         String JavaDoc userDir = System.getProperty("netbeans.user"); // NOI18N
724
storage = new File JavaDoc(new File JavaDoc (userDir , "var"), "filehistory"); // NOI18N
725
if(!storage.exists()) {
726             storage.mkdirs();
727         }
728     }
729
730     private File JavaDoc getStoreFolder(File JavaDoc file) {
731         String JavaDoc filePath = file.getAbsolutePath();
732         File JavaDoc storeFolder = getStoreFolderName(filePath);
733         int i = 0;
734         while(storeFolder.exists()) {
735             // check for collisions
736
StoreDataFile data = readStoreData(new File JavaDoc(storeFolder, DATA_FILE), false);
737             if(data == null || data.getAbsolutePath().equals(filePath)) {
738                 break;
739             }
740             storeFolder = getStoreFolderName(filePath + "." + i++);
741         }
742         return storeFolder;
743     }
744     
745     private File JavaDoc getStoreFolderName(String JavaDoc filePath) {
746         int fileHash = filePath.hashCode();
747         String JavaDoc storeFileName = getMD5(filePath);
748         String JavaDoc storeIndex = storage.getAbsolutePath() + "/" + Integer.toString(fileHash % 173 + 172); // NOI18N
749
return new File JavaDoc(storeIndex + "/" + storeFileName); // NOI18N
750
}
751     
752     private String JavaDoc getMD5(String JavaDoc name) {
753         MessageDigest JavaDoc digest;
754         try {
755             digest = MessageDigest.getInstance("MD5"); // NOI18N
756
} catch (NoSuchAlgorithmException JavaDoc e) {
757             // should not happen
758
return null;
759         }
760         digest.update(name.getBytes());
761         byte[] hash = digest.digest();
762         StringBuffer JavaDoc ret = new StringBuffer JavaDoc();
763         for (int i = 0; i < hash.length; i++) {
764             String JavaDoc hex = Integer.toHexString(hash[i] & 0x000000FF);
765             if(hex.length()==1) {
766                 hex = "0" + hex; // NOI18N
767
}
768             ret.append(hex);
769         }
770         return ret.toString();
771     }
772     
773     private File JavaDoc getStoreFile(File JavaDoc file, String JavaDoc name, boolean mkdirs) {
774         File JavaDoc storeFolder = getStoreFolder(file);
775         if(mkdirs && !storeFolder.exists()) {
776             storeFolder.mkdirs();
777         }
778         return new File JavaDoc(storeFolder, name);
779     }
780
781     private File JavaDoc getHistoryFile(File JavaDoc file) {
782         File JavaDoc storeFolder = getStoreFolder(file);
783         if(!storeFolder.exists()) {
784             storeFolder.mkdirs();
785         }
786         return new File JavaDoc(storeFolder, HISTORY_FILE);
787     }
788
789     private File JavaDoc getDataFile(File JavaDoc file) {
790         File JavaDoc storeFolder = getStoreFolder(file);
791         return new File JavaDoc(storeFolder, DATA_FILE);
792     }
793         
794     private File JavaDoc getLabelsFile(File JavaDoc file) {
795         File JavaDoc storeFolder = getStoreFolder(file);
796         return new File JavaDoc(storeFolder, LABELS_FILE);
797     }
798     
799     private Map JavaDoc<Long JavaDoc, String JavaDoc> getLabels(File JavaDoc labelsFile) {
800
801         if(!labelsFile.exists()) {
802             return emptyLabels;
803         }
804         DataInputStream JavaDoc dis = null;
805         Map JavaDoc<Long JavaDoc, String JavaDoc> ret = new HashMap JavaDoc<Long JavaDoc, String JavaDoc>();
806         try {
807             dis = getInputStream(labelsFile);
808             while(true) {
809                 long ts = dis.readLong();
810                 String JavaDoc label = readString(dis);
811                 ret.put(ts, label);
812             }
813         } catch (EOFException JavaDoc e) {
814             return ret;
815         } catch (Exception JavaDoc e) {
816             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
817         } finally {
818             if (dis != null) {
819                 try { dis.close(); } catch (IOException JavaDoc e) { }
820             }
821         }
822         return emptyLabels;
823     }
824                 
825     private void writeHistoryForFile(File JavaDoc file, HistoryEntry[] entries) {
826         if(Diagnostics.ON) {
827             if(getDataFile(file) == null) {
828                 Diagnostics.println("writing history for file without data : " + file); // NOI18N
829
}
830         }
831         File JavaDoc history = getHistoryFile(file);
832         writeHistory(history, entries);
833     }
834     
835     private void writeHistory(File JavaDoc history, HistoryEntry[] entries) {
836         DataOutputStream JavaDoc dos = null;
837         try {
838             dos = getOutputStream(history, true);
839             for(HistoryEntry entry : entries) {
840                 dos.writeLong(entry.getTimestamp());
841                 writeString(dos, entry.getFrom());
842                 writeString(dos, entry.getTo());
843                 dos.writeInt(entry.getStatus());
844             }
845             dos.flush();
846         } catch (Exception JavaDoc e) {
847             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
848             return;
849         }
850         finally {
851             if (dos != null) {
852                 try { dos.close(); } catch (IOException JavaDoc e) { }
853             }
854         }
855     }
856
857     private List JavaDoc<HistoryEntry> readHistoryForFile(File JavaDoc file) {
858         return readHistory(getHistoryFile(file));
859     }
860         
861     private List JavaDoc<HistoryEntry> readHistory(File JavaDoc history) {
862         if(!history.exists()) {
863             return emptyHistory;
864         }
865         DataInputStream JavaDoc dis = null;
866         List JavaDoc<HistoryEntry> entries = new ArrayList JavaDoc<HistoryEntry>();
867         try {
868             dis = getInputStream(history);
869             while(true) {
870                 long ts = dis.readLong();
871                 String JavaDoc from = readString(dis);
872                 String JavaDoc to = readString(dis);
873                 int action = dis.readInt();
874                 entries.add(new HistoryEntry(ts, from, to, action));
875             }
876         } catch (EOFException JavaDoc e) {
877             return entries;
878         } catch (Exception JavaDoc e) {
879             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
880         } finally {
881             if (dis != null) {
882                 try { dis.close(); } catch (IOException JavaDoc e) { }
883             }
884         }
885         return emptyHistory;
886     }
887          
888     private StoreDataFile readStoreData(File JavaDoc file, boolean isOriginalFile) {
889         if(isOriginalFile) {
890             file = getDataFile(file);
891         }
892         return (StoreDataFile) turbo.readEntry(file, DataFilesTurboProvider.ATTR_DATA_FILES);
893     }
894
895     private void writeStoreData(File JavaDoc file, StoreDataFile data, boolean isOriginalFile) {
896         if(isOriginalFile) {
897             file = getDataFile(file);
898         }
899         turbo.writeEntry(file, DataFilesTurboProvider.ATTR_DATA_FILES, data);
900     }
901     
902     private static void writeString(DataOutputStream JavaDoc dos, String JavaDoc str) throws IOException JavaDoc {
903         if(str != null) {
904             dos.writeInt(str.length());
905             dos.writeChars(str);
906         } else {
907             dos.writeInt(0);
908         }
909     }
910     
911     private static String JavaDoc readString(DataInputStream JavaDoc dis) throws IOException JavaDoc {
912         int len = dis.readInt();
913         if(len == 0) {
914             return "";
915         }
916         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
917         while(len-- > 0) {
918             char c = dis.readChar();
919             sb.append(c);
920         }
921         return sb.toString();
922     }
923     
924     private static void skip(InputStream JavaDoc is, long len) throws IOException JavaDoc {
925         while (len > 0) {
926             long n = is.skip(len);
927             if (n < 0) throw new EOFException JavaDoc("Missing " + len + " bytes."); // NOI18N
928
len -= n;
929         }
930     }
931
932     private static void copyStreams(OutputStream JavaDoc out, InputStream JavaDoc in) throws IOException JavaDoc {
933         byte [] buffer = new byte[4096];
934         for (;;) {
935             int n = in.read(buffer);
936             if (n < 0) break;
937             out.write(buffer, 0, n);
938         }
939     }
940     
941     private static DataOutputStream JavaDoc getOutputStream(File JavaDoc file, boolean append) throws IOException JavaDoc, InterruptedException JavaDoc {
942         int retry = 0;
943         while (true) {
944             try {
945                 return new DataOutputStream JavaDoc(new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(file, append)));
946             } catch (IOException JavaDoc ioex) {
947                 retry++;
948                 if (retry > 7) {
949                     throw ioex;
950                 }
951                 Thread.sleep(retry * 30);
952             }
953         }
954     }
955     
956     private static DataInputStream JavaDoc getInputStream(File JavaDoc file) throws IOException JavaDoc, InterruptedException JavaDoc {
957         int retry = 0;
958         while (true) {
959             try {
960                 return new DataInputStream JavaDoc(new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(file)));
961             } catch (IOException JavaDoc ioex) {
962                 retry++;
963                 if (retry > 7) {
964                     throw ioex;
965                 }
966                 Thread.sleep(retry * 30);
967             }
968         }
969     }
970
971     private class HistoryEntry {
972         private long ts;
973         private String JavaDoc from;
974         private String JavaDoc to;
975         private int status;
976         HistoryEntry(long ts, String JavaDoc from, String JavaDoc to, int action) {
977             this.ts = ts;
978             this.from = from;
979             this.to = to;
980             this.status = action;
981         }
982         long getTimestamp() {
983             return ts;
984         }
985         String JavaDoc getFrom() {
986             return from;
987         }
988         String JavaDoc getTo() {
989             return to;
990         }
991         int getStatus() {
992             return status;
993         }
994     }
995     
996     private static class StoreDataFile {
997         private final int status;
998         private final long lastModified;
999         private final String JavaDoc absolutePath;
1000        private final boolean isFile;
1001        
1002        private StoreDataFile(String JavaDoc absolutePath, int action, long lastModified, boolean isFile) {
1003            this.status = action;
1004            this.lastModified = lastModified;
1005            this.absolutePath = absolutePath;
1006            this.isFile = isFile;
1007        }
1008        
1009        int getStatus() {
1010            return status;
1011        }
1012        
1013        long getLastModified() {
1014            return lastModified;
1015        }
1016        
1017        String JavaDoc getAbsolutePath() {
1018            return absolutePath;
1019        }
1020        
1021        boolean isFile() {
1022            return isFile;
1023        }
1024        
1025        static synchronized StoreDataFile read(File JavaDoc storeFile) {
1026            DataInputStream JavaDoc dis = null;
1027            try {
1028                dis = getInputStream(storeFile);
1029                boolean isFile = dis.readBoolean();
1030                int action = dis.readInt();
1031                long modified = dis.readLong();
1032                String JavaDoc fileName = readString(dis);
1033                return new StoreDataFile(fileName, action, modified, isFile);
1034            } catch (Exception JavaDoc e) {
1035                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
1036            } finally {
1037                if (dis != null) {
1038                    try { dis.close(); } catch (IOException JavaDoc e) { }
1039                }
1040            }
1041            return null;
1042        }
1043        
1044        static synchronized void write(File JavaDoc storeFile, StoreDataFile value) {
1045            DataOutputStream JavaDoc dos = null;
1046            try {
1047                dos = getOutputStream(storeFile, false);
1048                StoreDataFile data = (StoreDataFile) value;
1049                dos.writeBoolean(data.isFile);
1050                dos.writeInt(data.getStatus());
1051                dos.writeLong(data.getLastModified());
1052                dos.writeInt(data.getAbsolutePath().length());
1053                dos.writeChars(data.getAbsolutePath());
1054                dos.flush();
1055            } catch (Exception JavaDoc e) {
1056                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
1057            } finally {
1058                if (dos != null) {
1059                    try { dos.close(); } catch (IOException JavaDoc e) { }
1060                }
1061            }
1062        }
1063    }
1064            
1065    private class DataFilesTurboProvider implements TurboProvider {
1066                
1067        static final String JavaDoc ATTR_DATA_FILES = "localhistory.ATTR_DATA_FILES"; // NOI18N
1068

1069        public boolean recognizesAttribute(String JavaDoc name) {
1070            return ATTR_DATA_FILES.equals(name);
1071        }
1072
1073        public boolean recognizesEntity(Object JavaDoc key) {
1074            return key instanceof File JavaDoc;
1075        }
1076
1077        public synchronized Object JavaDoc readEntry(Object JavaDoc key, String JavaDoc name, MemoryCache memoryCache) {
1078            assert key instanceof File JavaDoc;
1079            assert name != null;
1080                                
1081            File JavaDoc storeFile = (File JavaDoc) key;
1082            if(!storeFile.exists()) {
1083                return null;
1084            }
1085            return StoreDataFile.read(storeFile);
1086        }
1087
1088        public synchronized boolean writeEntry(Object JavaDoc key, String JavaDoc name, Object JavaDoc value) {
1089            assert key instanceof File JavaDoc;
1090            assert value == null || value instanceof StoreDataFile;
1091            assert name != null;
1092            
1093            File JavaDoc storeFile = (File JavaDoc) key;
1094            if(value == null) {
1095                if(storeFile.exists()) {
1096                    storeFile.delete();
1097                }
1098                return true;
1099            }
1100            
1101            File JavaDoc parent = storeFile.getParentFile();
1102            if(!parent.exists()) {
1103                parent.mkdirs();
1104            }
1105            StoreDataFile.write(storeFile, (StoreDataFile) value);
1106            return true;
1107        }
1108    }
1109    
1110}
Popular Tags