001: /*
002: * @(#)CacheDirChannelLogger.java
003: *
004: * Copyright (C) 2003-2004 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.codecoverage.v2.logger;
028:
029: import net.sourceforge.groboutils.util.datastruct.v1.HashCache;
030:
031: import java.io.BufferedWriter;
032: import java.io.File;
033: import java.io.FileWriter;
034: import java.io.IOException;
035: import java.io.Writer;
036:
037: /**
038: * This is the same as the DirectoryChannelLogger, except that the open
039: * files are cached for access.
040: * <P>
041: * Besides some other issues with this (see open bugs), it can cause
042: * issues if multiple loggers are trying to access the same file. This
043: * could happen if the CoverageLogger class is loaded by different class
044: * loaders; as a result, multiple CoverageLogger instances will be created,
045: * allowing for multiple channel loggers to write to the same area.
046: *
047: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
048: * @version $Date: 2004/07/07 09:39:10 $
049: * @since May 9, 2003
050: */
051: public class CacheDirChannelLogger extends DirectoryChannelLogger
052: implements Runnable, HashCache.ObjectManager {
053: private final HashCache openedFiles;
054:
055: public CacheDirChannelLogger(File baseDir, int cacheSize) {
056: super (baseDir);
057: if (baseDir != null) {
058: // even though the logic in the factory shouldn't allow this,
059: // it apparently can happen.
060: // See bug 923349
061: if (cacheSize <= 0) {
062: cacheSize = CacheDirChannelLoggerFactory.DEFAULT_CACHESIZE;
063: }
064: this .openedFiles = new HashCache(this , cacheSize);
065: addShutdownHook();
066: } else {
067: this .openedFiles = null;
068: }
069: }
070:
071: /**
072: * Records a coverage of a marked bytecode instruction. This method should
073: * never throw an exception.
074: *
075: * @param classSignature a signature of the class file being covered.
076: * this signature includes the fully-qualified name of the class,
077: * along with a checksum to uniquely identify it.
078: * @param methodIndex index for a method within the class. The meta-data
079: * store will know how to translate the index to a method signature.
080: * @param markIndex the index of the bytecode instruction mark for this
081: * particular channel.
082: */
083: public void cover(String classSignature, short methodIndex,
084: short markIndex) {
085: if (this .baseDir != null) {
086: File f = getClassFile(this .baseDir, classSignature);
087: Writer w = null;
088: try {
089: char[] out = createCoverString(methodIndex, markIndex);
090: synchronized (this ) {
091: w = (Writer) this .openedFiles.get(f);
092: if (w != null) {
093: w.write(out);
094: }
095: }
096: } catch (IOException ioe) {
097: // gasp!!!!
098: ioe.printStackTrace();
099:
100: // prevent these errors from occuring again
101: this .baseDir = null;
102: }
103: }
104: }
105:
106: /**
107: * Called when the shutdown hook is called.
108: */
109: public void run() {
110: synchronized (this ) {
111: this .openedFiles.clear();
112: }
113: }
114:
115: /**
116: * Called by the HashCache
117: */
118: public Object createObject(Object key) {
119: File f = (File) key;
120: try {
121: // bug 907800
122: BufferedWriter bw = new BufferedWriter(new FileWriter(f
123: .toString(), true));
124: return bw;
125: } catch (IOException ioe) {
126: // gasp!!!!
127: ioe.printStackTrace();
128:
129: // prevent these errors from occuring again
130: this .baseDir = null;
131:
132: // we have to return something...
133: return null;
134: }
135: }
136:
137: /**
138: * Called by HashCache when the given object is being removed from the
139: * cache.
140: *
141: * @param key the key associated with the object.
142: * @param obj the object being cleaned up.
143: */
144: public void cleanUpObject(Object key, Object obj) {
145: if (obj != null) {
146: Writer w = (Writer) obj;
147: try {
148: w.flush();
149: w.close();
150: } catch (IOException ioe) {
151: ioe.printStackTrace();
152: }
153: }
154: }
155:
156: protected void finalize() throws Throwable {
157: // our cleanup method is the "run" method. Of course!
158: run();
159:
160: super .finalize();
161: }
162:
163: protected void addShutdownHook() {
164: Class c = Runtime.class;
165: try {
166: java.lang.reflect.Method m = c.getMethod("addShutdownHook",
167: new Class[] { Thread.class });
168: Thread t = new Thread(this , this .getClass().getName()
169: + " shutdown hook");
170: m.invoke(Runtime.getRuntime(), new Object[] { t });
171: } catch (Exception ex) {
172: // prolly JDK 1.3 not supported.
173: System.err
174: .println(this .getClass().getName()
175: + " should only be run in a JDK 1.3 compatible JVM.");
176: ex.printStackTrace();
177: }
178: }
179: }
|