KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > versioning > system > cvss > VersionsCache


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
20 package org.netbeans.modules.versioning.system.cvss;
21
22 import org.netbeans.lib.cvsclient.command.CommandException;
23 import org.netbeans.lib.cvsclient.command.GlobalOptions;
24 import org.netbeans.lib.cvsclient.command.checkout.CheckoutCommand;
25 import org.netbeans.lib.cvsclient.connection.AuthenticationException;
26 import org.netbeans.lib.cvsclient.admin.Entry;
27 import org.netbeans.lib.cvsclient.CVSRoot;
28 import org.netbeans.modules.versioning.system.cvss.util.Utils;
29 import org.openide.filesystems.FileUtil;
30 import org.openide.util.NbBundle;
31
32 import java.io.*;
33
34 /**
35  * Takes care about retrieving various revisions of a file and caching them locally.
36  * HEAD revisions are not cached.
37  * TODO: Files are never deleted from cache, should we address this?
38  * TODO: cache dead files
39  *
40  * @author Maros Sandor
41  */

42 public class VersionsCache {
43     
44     /**
45      * Constant representing the current working revision.
46      */

47     public static final String JavaDoc REVISION_CURRENT = ""; // NOI18N
48

49     /**
50      * Constant representing the base revision of the current working revision (the one in Entries).
51      */

52     public static final String JavaDoc REVISION_BASE = "*"; // NOI18N
53

54     /**
55      * Constant representing the CVS HEAD revision.
56      */

57     public static final String JavaDoc REVISION_HEAD = "HEAD"; // NOI18N
58

59     private static final String JavaDoc CACHE_DIR = "CVS/RevisionCache/"; // NOI18N
60

61     private static VersionsCache instance = new VersionsCache();
62
63     private long purgeTimestamp = Long.MAX_VALUE;
64
65     public static VersionsCache getInstance() {
66         return instance;
67     }
68     
69     private VersionsCache() {
70     }
71
72     /**
73      * Retrieves repository version of the file, either from the local cache of revisions or, it that fails,
74      * from the remote repository. CURRENT revision is the file itself. HEAD revisions are considered volatile
75      * and their cached versions are purged with the {@link #purgeVolatileRevisions()} method.
76      *
77      * @param revision revision to fetch
78      * @param group that carries shared state. Note that this group must not be executed later on. This parameter can be null.
79      * @return File supplied file in the specified revision (locally cached copy) or null if this file does not exist
80      * in the specified revision or the user cancelled file checkout (in this case group.isCancelled() returns true)
81      * @throws java.io.IOException
82      * @throws org.netbeans.modules.versioning.system.cvss.IllegalCommandException
83      * @throws org.netbeans.lib.cvsclient.command.CommandException
84      * @throws org.netbeans.lib.cvsclient.connection.AuthenticationException
85      * @throws org.netbeans.modules.versioning.system.cvss.NotVersionedException
86      */

87     public synchronized File getRemoteFile(File baseFile, String JavaDoc revision, ExecutorGroup group) throws IOException,
88                 IllegalCommandException, CommandException, AuthenticationException, NotVersionedException {
89         return getRemoteFile(baseFile, revision, group, false);
90     }
91
92     public synchronized File getRemoteFile(File baseFile, String JavaDoc revision, ExecutorGroup group, boolean quiet) throws IOException,
93                 IllegalCommandException, CommandException, AuthenticationException, NotVersionedException {
94         String JavaDoc resolvedRevision = resolveRevision(baseFile, revision);
95         if (revision == REVISION_CURRENT) {
96             return baseFile.canRead() ? baseFile : null;
97         }
98         File file = getCachedRevision(baseFile, resolvedRevision);
99         if (file != null) return file;
100         file = checkoutRemoteFile(baseFile, revision, group, quiet);
101         if (file == null) return null;
102         return saveRevision(baseFile, file, resolvedRevision);
103     }
104
105     private String JavaDoc resolveRevision(File baseFile, String JavaDoc revision) throws IOException {
106         if (revision == REVISION_BASE) {
107             return getBaseRevision(baseFile);
108         }
109         if (revision.equals(REVISION_HEAD)) {
110             Entry entry = CvsVersioningSystem.getInstance().getAdminHandler().getEntry(baseFile);
111             if (entry != null && entry.getTag() != null) {
112                 return entry.getTag();
113             }
114         }
115         return revision;
116     }
117
118     /**
119      * Purges volatile revisions (i.g. HEAD) from cache. BEWARE: HEAD revisions are cached but there
120      * are multiple HEADs, each branch has its own HEAD!
121      */

122     public void purgeVolatileRevisions() {
123         purgeTimestamp = System.currentTimeMillis();
124     }
125
126     private File saveRevision(File baseFile, File file, String JavaDoc revision) {
127         // do not create directories if they do not exist (deleted trees)
128
File cacheDir;
129         if (baseFile.getParentFile().isDirectory()) {
130             cacheDir = new File(baseFile.getParentFile(), CACHE_DIR);
131         } else {
132             cacheDir = file.getParentFile();
133         }
134             if (!cacheDir.exists() && !cacheDir.mkdirs()) return file;
135         File destFile = new File(cacheDir, cachedName(baseFile, revision));
136         try {
137             FileInputStream fin = new FileInputStream(file);
138             FileOutputStream fos = new FileOutputStream(destFile);
139             FileUtil.copy(fin, fos);
140             fin.close();
141             fos.close();
142             // eventually delete the checked out file
143
if (file.equals(baseFile)) {
144                 // safety check
145
FileInformation info = CvsVersioningSystem.getInstance().getStatusCache().createFileInformation(baseFile);
146                 if (info.getStatus() != FileInformation.STATUS_VERSIONED_UPTODATE) {
147                     destFile.delete();
148                     return null;
149                 }
150             } else {
151                 file.delete();
152             }
153             return destFile;
154         } catch (IOException e) {
155             // ignore errors, cache is not that much important
156
}
157         return file;
158     }
159
160     private File getCachedRevision(File baseFile, String JavaDoc revision) {
161         File cachedCopy = new File(baseFile.getParentFile(), CACHE_DIR + cachedName(baseFile, revision));
162         if (isVolatile(revision)) {
163             if (cachedCopy.lastModified() < purgeTimestamp) {
164                 cachedCopy.delete();
165             }
166         }
167         if (cachedCopy.canRead()) return cachedCopy;
168         return null;
169     }
170
171     private boolean isVolatile(String JavaDoc revision) {
172         return revision.indexOf('.') == -1;
173     }
174
175     private String JavaDoc getBaseRevision(File file) throws IOException {
176         Entry entry = CvsVersioningSystem.getInstance().getAdminHandler().getEntry(file);
177         if (entry == null) {
178             throw new IllegalArgumentException JavaDoc("Cannot get BASE revision, there is no Entry for the file: " + file.getAbsolutePath());
179         }
180         String JavaDoc rawRev = entry.getRevision();
181         if (rawRev != null && rawRev.startsWith("-")) { // NOI18N
182
// leading - means removed
183
return rawRev.substring(1);
184         }
185         return rawRev;
186     }
187
188     private String JavaDoc cachedName(File baseFile, String JavaDoc revision) {
189         return baseFile.getName() + "#" + revision; // NOI18N
190
}
191
192     private String JavaDoc getRepositoryForDirectory(File directory, String JavaDoc repository) {
193         if (directory == null) return null;
194         if (!directory.exists()) {
195             return getRepositoryForDirectory(directory.getParentFile(), repository) + "/" + directory.getName(); // NOI18N
196
}
197         try {
198             return CvsVersioningSystem.getInstance().getAdminHandler().getRepositoryForDirectory(directory.getAbsolutePath(), repository); // NOI18N
199
} catch (IOException e) {
200             return null;
201         }
202     }
203
204     /**
205      * Gets a specific revision of a file from repository.
206      *
207      * @param baseFile location of the file in local workdir (need not exist)
208      * @param revision revision number to get
209      * @param group that carries shared state. Note that this group must not be executed later on. This parameter can be null.
210      * @param quiet
211      * @return File file on disk (most probably located in some temp diretory) or null if this file does not exist
212      * in repository in the specified revision
213      * @throws IOException if some I/O error occurs during checkout
214      */

215     private File checkoutRemoteFile(File baseFile, String JavaDoc revision, ExecutorGroup group, boolean quiet) throws IOException {
216
217         if (revision == REVISION_BASE) {
218             // be optimistic, use the file available on disk if possible
219
FileInformation info = CvsVersioningSystem.getInstance().getStatusCache().createFileInformation(baseFile);
220             if (info.getStatus() == FileInformation.STATUS_VERSIONED_UPTODATE) {
221                 return baseFile;
222             }
223         }
224         revision = resolveRevision(baseFile, revision);
225         
226         GlobalOptions options = CvsVersioningSystem.createGlobalOptions();
227         String JavaDoc root = Utils.getCVSRootFor(baseFile.getParentFile());
228         CVSRoot cvsRoot = CVSRoot.parse(root);
229         String JavaDoc repository = cvsRoot.getRepository();
230         options.setCVSRoot(root);
231
232         String JavaDoc repositoryPath = getRepositoryForDirectory(baseFile.getParentFile(), repository) + "/" + baseFile.getName(); // NOI18N
233

234         CheckoutCommand cmd = new CheckoutCommand();
235         cmd.setRecursive(false);
236         assert repositoryPath.startsWith(repository) : repositoryPath + " does not start with: " + repository; // NOI18N
237

238         repositoryPath = repositoryPath.substring(repository.length());
239         if (repositoryPath.startsWith("/")) { // NOI18N
240
repositoryPath = repositoryPath.substring(1);
241         }
242         cmd.setModule(repositoryPath);
243         cmd.setPipeToOutput(true);
244         cmd.setCheckoutByRevision(revision);
245         String JavaDoc msg = NbBundle.getMessage(VersionsCache.class, "MSG_VersionsCache_FetchingProgress", revision, baseFile.getName());
246         cmd.setDisplayName(msg);
247
248         VersionsCacheExecutor executor = new VersionsCacheExecutor(cmd, options, quiet);
249         if (group != null) {
250             group.progress(msg);
251             group.addExecutor(executor);
252         }
253         executor.execute();
254         ExecutorSupport.wait(new ExecutorSupport [] { executor });
255         if (group == null) {
256             executor.getGroup().executed();
257         }
258
259         if (executor.isSuccessful()) {
260             return executor.getCheckedOutVersion();
261         } else {
262             if (executor.isCancelled()) {
263                 return null;
264             }
265             // XXX note that executor already handles/notifies failures
266
IOException ioe = new IOException(NbBundle.getMessage(VersionsCache.class, "Bk4001", revision, baseFile.getName()));
267             ioe.initCause(executor.getFailure());
268             throw ioe;
269         }
270
271     }
272 }
273
Popular Tags