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-2007 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.cnd.modelimpl.cache.impl;
043:
044: import java.io.BufferedInputStream;
045: import java.io.BufferedOutputStream;
046: import java.io.File;
047: import java.io.FileInputStream;
048: import java.io.FileOutputStream;
049: import java.io.IOException;
050: import java.io.InputStream;
051: import java.io.ObjectInputStream;
052: import java.io.ObjectOutputStream;
053: import java.io.OutputStream;
054: import java.util.Collection;
055: import java.util.HashMap;
056: import java.util.Iterator;
057: import java.util.Map;
058: import java.util.Map.Entry;
059: import java.util.WeakHashMap;
060: import java.util.logging.Level;
061: import org.netbeans.modules.cnd.api.model.CsmChangeEvent;
062: import org.netbeans.modules.cnd.api.model.CsmFile;
063: import org.netbeans.modules.cnd.api.model.CsmProject;
064: import org.netbeans.modules.cnd.apt.structure.APTFile;
065: import org.netbeans.modules.cnd.apt.support.APTPreprocHandler;
066: import org.netbeans.modules.cnd.apt.utils.APTUtils;
067: import org.netbeans.modules.cnd.modelimpl.cache.FileCache;
068: import org.netbeans.modules.cnd.modelimpl.csm.core.FileImpl;
069: import org.netbeans.modules.cnd.modelimpl.csm.core.ParserThreadManager;
070: import org.netbeans.modules.cnd.modelimpl.debug.DiagnosticExceptoins;
071: import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
072:
073: /**
074: * cache manager implementation
075: * @author Vladimir Voskresensky
076: */
077: public final class CacheManagerImpl {
078: // main cache directory
079: // this directory can be changable:
080: // i.e. we can copy all to tmp for fast access and then to userdir again
081: private String cacheDir;
082:
083: // cache per project
084: private WeakHashMap/*<String(project-key), ProjectCache>*/projectCache = new WeakHashMap();
085: // master index of cache
086: private MasterIndex index = new MasterIndex();
087:
088: public CacheManagerImpl() {
089: if (ParserThreadManager.instance().isStandalone()) {
090: cacheDir = System.getProperty("java.io.tmpdir");// NOI18N
091: } else {
092: cacheDir = System.getProperty("netbeans.user");// NOI18N
093: }
094: cacheDir = cacheDir + File.separatorChar + "var"
095: + File.separatorChar + // NOI18N
096: "cache" + File.separatorChar + "cndcache"; // NOI18N
097: new File(cacheDir).mkdirs();
098: if (TraceFlags.TRACE_CACHE)
099: System.err.println("CACHE: Cache dir:" + cacheDir); // NOI18N
100: load();
101: }
102:
103: public APTFile findAPT(CsmFile file) {
104: return getSyncCacheBridge((FileImpl) file).findAPT();
105: }
106:
107: public APTFile findAPTLight(CsmFile file) {
108: return getSyncCacheBridge((FileImpl) file).findAPTLight();
109: }
110:
111: public FileCache findCacheWithAST(CsmFile file,
112: APTPreprocHandler preprocHandler) {
113: return getSyncCacheBridge((FileImpl) file).findCacheWithAST(
114: preprocHandler);
115: }
116:
117: public void projectOpened(CsmProject project) {
118: // load cache related to project?
119: }
120:
121: public void projectClosed(CsmProject project) {
122: if (projectCache == null || index == null) {
123: return;
124: }
125: synchronized (loadLock) {
126: // save/close cache related to project
127: ProjectCache prjCache = null;
128: synchronized (projectCache) {
129: prjCache = (ProjectCache) projectCache.remove(project);
130: }
131: if (prjCache != null) {
132: assert (index != null);
133: String prjDir = index.getProjectDir(project);
134: assert (prjDir != null);
135: // close project cache
136: prjCache.close(this .cacheDir + File.separatorChar
137: + prjDir);
138: }
139: }
140: }
141:
142: public void modelChanged(CsmChangeEvent e) {
143: // nothings now
144: }
145:
146: public void invalidateFile(CsmFile file) {
147: synchronized (fileLocks) {
148: fileLocks.remove(file.getAbsolutePath());
149: }
150: ProjectCache prjCache = getProjectCache(file.getProject(),
151: false);
152: if (prjCache != null) {
153: prjCache.invalidateFile(file.getAbsolutePath().toString());
154: }
155: }
156:
157: public void invalidateAllFiles(String absPath) {
158: synchronized (fileLocks) {
159: fileLocks.remove(absPath);
160: }
161: ProjectCache[] caches = getProjectCaches();
162: for (int i = 0; i < caches.length; i++) {
163: ProjectCache cur = caches[i];
164: cur.invalidateFile(absPath);
165: }
166: }
167:
168: public void changeDir(String dir) {
169: // here we should move cache from one place to another and
170: // it's better to be locked for this time
171: this .cacheDir = dir;
172: }
173:
174: ////////////////////////////////////////////////////////////////////////////
175: // access to sync project cache
176:
177: private ProjectCache getProjectCache(CsmProject project,
178: boolean create) {
179: load();
180: assert (project != null);
181: ProjectCache prjCache = null;
182: synchronized (projectCache) {
183: // check if already in projects
184: prjCache = (ProjectCache) projectCache.get(project);
185: if (prjCache == null && create) {
186: if (TraceFlags.TRACE_CACHE) {
187: System.err
188: .println("CACHE: creating Project cache for "
189: + project.getName()); // NOI18N
190: }
191: // check if can load from disk
192: // prjDir is non absolute
193: String prjDir = (String) index.getProjectDir(project);
194: if (prjDir == null) {
195: // new project => generate new dir and remember in master index
196: prjDir = index.putProject(project);
197: }
198: // create project cache and pass full cache path to load own cache
199: prjCache = new ProjectCache(cacheDir
200: + File.separatorChar + prjDir);
201: projectCache.put(project, prjCache);
202: }
203: }
204: assert (prjCache != null || !create);
205: return prjCache;
206: }
207:
208: private ProjectCache[] getProjectCaches() {
209: load();
210: ProjectCache[] copy = null;
211: synchronized (projectCache) {
212: // check if already in projects
213: Collection values = projectCache.values();
214: copy = (ProjectCache[]) values
215: .toArray(new ProjectCache[values.size()]);
216: }
217: assert (copy != null);
218: return copy;
219: }
220:
221: ////////////////////////////////////////////////////////////////////////////
222: // access to sync file cache
223: private FileCacheSyncBridge getSyncCacheBridge(FileImpl file) {
224: return getProjectCache(file.getProject(), true)
225: .getSyncCacheBridge(file);
226: }
227:
228: ////////////////////////////////////////////////////////////////////////////
229: // getting valid cache files
230:
231: private File getValidCacheFile(CsmFile file, boolean save) {
232: assert (file != null);
233: assert (cacheDir != null);
234: String prjRelDir = index.getProjectDir(file.getProject());
235: File out = null;
236: if (prjRelDir != null) {
237: String baseDir = cacheDir + File.separatorChar + prjRelDir;
238: CharSequence fileName = getProjectCache(file.getProject(),
239: true).getValidCacheFileName((FileImpl) file, save);
240: if (fileName != null) {
241: out = new File(baseDir, fileName.toString());
242: }
243: } else {
244: if (TraceFlags.TRACE_CACHE) {
245: System.err.println("CACHE: no cache dir for project "
246: + file.getProject().getName()); // NOI18N
247: }
248: }
249: return out;
250: }
251:
252: ////////////////////////////////////////////////////////////////////////////
253: // loading cache
254:
255: public FileCache loadValidCache(CsmFile file) {
256: FileCache loaded = null;
257: File cacheFile = getValidCacheFile(file, false);
258: if (cacheFile != null) {
259: loaded = loadFile(cacheFile);
260: if (TraceFlags.TRACE_CACHE) {
261: if (loaded == null) {
262: System.err.println("CACHE: Failed load cache "
263: + cacheFile.getAbsolutePath() + // NOI18N
264: " for:" + file.getAbsolutePath()); // NOI18N
265: }
266: }
267: } else {
268: if (TraceFlags.TRACE_CACHE) {
269: if (((FileImpl) file).getBuffer().isFileBased()) {
270: System.err
271: .println("CACHE: not found valid cache for:"
272: + file.getAbsolutePath()); // NOI18N
273: } else {
274: System.err
275: .println("CACHE: do not load cache for document based file-buffer:"
276: + file.getAbsolutePath()); // NOI18N
277: }
278: }
279: }
280: return loaded;
281: }
282:
283: private FileCache loadFile(File from) {
284: assert (from != null);
285: FileCache loaded = null;
286: ObjectInputStream ois = null;
287: Object fileLock = getCacheFileLock(from);
288: assert (fileLock != null);
289: synchronized (fileLock) {
290: try {
291: InputStream in = null;
292: try {
293: in = new FileInputStream(from);
294: in = new BufferedInputStream(in);
295: ois = new ObjectInputStream(in);
296: } finally {
297: if (in != null && ois == null) {
298: in.close();
299: }
300: }
301: if (ois != null) {
302: loaded = (FileCache) ois.readObject();
303: }
304: } catch (IOException io) {
305: // null cache
306: } catch (ClassNotFoundException e) {
307: DiagnosticExceptoins.register(e);
308: APTUtils.LOG.log(Level.SEVERE, "load cache file: {0}",
309: new Object[] { e.getMessage() });// NOI18N
310: } finally {
311: if (ois != null) {
312: try {
313: ois.close();
314: } catch (IOException e) {
315: // ignore
316: }
317: }
318: }
319: }
320: return loaded;
321: }
322:
323: ////////////////////////////////////////////////////////////////////////////
324: // saving cache
325:
326: public void saveCache(CsmFile file, FileCache cache) {
327: FileCache loaded = null;
328: File cacheFile = getValidCacheFile(file, true);
329: FileCacheSyncBridge syncCache = getSyncCacheBridge((FileImpl) file);
330: assert (cacheFile != null);
331: if (save2File(cacheFile, cache)) {
332: if (TraceFlags.TRACE_CACHE)
333: System.err.println("CACHE: saved cache for:"
334: + file.getAbsolutePath()); // NOI18N
335: syncCache.updateStorage(cache);
336: } else {
337: if (TraceFlags.TRACE_CACHE) {
338: if (((FileImpl) file).getBuffer().isFileBased()) {
339: System.err.println("CACHE: FAILED saving cache "
340: + cacheFile.getAbsolutePath() + // NOI18N
341: " for:" + file.getAbsolutePath()); // NOI18N
342: } else {
343: System.err
344: .println("CACHE: do not save cache for document based file-buffer:"
345: + file.getAbsolutePath()); // NOI18N
346: }
347: }
348:
349: }
350: }
351:
352: private boolean save2File(File to, FileCache cache) {
353: assert (to != null);
354: ObjectOutputStream oos = null;
355: Object fileLock = getCacheFileLock(to);
356: assert (fileLock != null);
357: synchronized (fileLock) {
358: try {
359: OutputStream out = null;
360: try {
361: out = new FileOutputStream(to);
362: out = new BufferedOutputStream(out);
363: oos = new ObjectOutputStream(out);
364: } finally {
365: if (out != null && oos == null) {
366: out.close();
367: }
368: }
369: if (oos != null) {
370: oos.writeObject(cache);
371: return true;
372: }
373: } catch (IOException io) {
374: // null cache
375: } finally {
376: if (oos != null) {
377: try {
378: oos.close();
379: } catch (IOException e) {
380: // ignore
381: }
382: }
383: }
384: }
385: return false;
386: }
387:
388: ////////////////////////////////////////////////////////////////////////////
389: // save/load file locks
390: private static class FileLock {
391: String path;
392:
393: private FileLock(String path) {
394: this .path = path;
395: }
396:
397: @Override
398: public String toString() {
399: return "read/write cache lock for " + path; // NOI18N
400: }
401: };
402:
403: private Object getCacheFileLock(final File file) {
404: Object fileLock = null;
405: synchronized (fileLocks) {
406: final String path = file.getAbsolutePath();
407: fileLock = fileLocks.get(path);
408: if (fileLock == null) {
409: fileLock = new FileLock(path);
410: fileLocks.put(path, fileLock);
411: }
412: }
413: assert (fileLock != null);
414: return fileLock;
415: }
416:
417: private Map<CharSequence, Object> fileLocks = new HashMap();
418:
419: ///////////////////////////////////////////////////////////////////////////
420: // save load main cache info
421:
422: private boolean loaded = false;
423:
424: // we need exclusive copy of string => use "new String(String)" constructor
425: private final String loadLock = new String(
426: "CacheManagerImpl load lock"); // NOI18N
427:
428: private void load() {
429: if (!loaded) {
430: synchronized (loadLock) {
431: index = new MasterIndex();
432: loadIndex(cacheDir);
433: fileLocks = new HashMap();
434: projectCache = new WeakHashMap();
435: loaded = true;
436: }
437: }
438: }
439:
440: private void store() {
441: if (TraceFlags.TRACE_CACHE) {
442: System.err.println("CACHE: saving cache manager data in "
443: + cacheDir); // NOI18N
444: }
445: synchronized (loadLock) {
446: // store master index
447: storeIndex(cacheDir);
448: // store all opened projects
449: saveProjects();
450: }
451: }
452:
453: // XXX what are the right events to call this?
454: public void close() {
455: synchronized (loadLock) {
456: store();
457:
458: // clean all
459: loaded = false;
460: projectCache = new WeakHashMap();
461: index = new MasterIndex();
462: }
463: }
464:
465: ////////////////////////////////////////////////////////////////////////////
466: // master index file support
467: // this file contains references from project keys to project-directory name
468: // in the main cache directory
469:
470: private void loadIndex(String baseDir) {
471: File file = getMasterIndexFile(baseDir);
472: boolean loaded = index.load(file);
473: if (TraceFlags.TRACE_CACHE) {
474: if (loaded) {
475: System.err.println("CACHE: loaded master index:"
476: + file.getAbsolutePath()); // NOI18N
477: System.err.println("CACHE: index value" + index); // NOI18N
478: } else {
479: System.err.println("CACHE: master index not found:"
480: + file.getAbsolutePath()); // NOI18N
481: }
482: }
483: }
484:
485: private void storeIndex(String baseDir) {
486: if (index == null)
487: return;
488: File file = getMasterIndexFile(baseDir);
489: boolean saved = index.save(file);
490: if (TraceFlags.TRACE_CACHE) {
491: if (saved) {
492: System.err.println("CACHE: saved master index:"
493: + file.getAbsolutePath()); // NOI18N
494: System.err.println("index value:" + index.toString()); // NOI18N
495: } else {
496: System.err
497: .println("CACHE: errors on saving master index:"
498: + file.getAbsolutePath()); // NOI18N
499: }
500: }
501: }
502:
503: private File getMasterIndexFile(String baseDir) {
504: String cache = baseDir + File.separatorChar + "index.dat"; // NOI18N
505: File file = new File(cache);
506: return file;
507: }
508:
509: ////////////////////////////////////////////////////////////////////////////
510: // save project index
511:
512: private void saveProjects() {
513: Map copy = new HashMap();
514: synchronized (projectCache) {
515: copy.putAll(projectCache);
516: }
517: for (Iterator it = copy.entrySet().iterator(); it.hasNext();) {
518: Map.Entry entry = (Entry) it.next();
519: CsmProject project = (CsmProject) entry.getKey();
520: String prjDir = (String) index.getProjectDir(project);
521: if (prjDir != null) {
522: // create project cache and pass full cache path to load own cache
523: String prjCachePath = cacheDir + File.separatorChar
524: + prjDir;
525: ProjectCache cache = (ProjectCache) entry.getValue();
526: assert (cache != null);
527: cache.store(prjCachePath);
528: } else {
529: System.err
530: .println("SEVERE: CACHE (saveProjects): not found cache directory for project "
531: + project.getName()); // NOI18N
532: }
533: }
534: }
535: }
|