001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: welker $' 006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 007 * '$Revision: 24234 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.io; 031 032import java.io.BufferedReader; 033import java.io.File; 034import java.io.FileReader; 035import java.io.FileWriter; 036import java.io.IOException; 037import java.io.PrintWriter; 038import java.text.SimpleDateFormat; 039import java.util.HashSet; 040import java.util.Hashtable; 041import java.util.Iterator; 042 043import org.apache.commons.logging.Log; 044import org.apache.commons.logging.LogFactory; 045 046////////////////////////////////////////////////////////////////////////// 047//// MappedLog 048 049/** 050 * Log a string (single-line) into a file but also keep all text in a HashSet so 051 * that the strings can quickly looked up. This class is useful to create simple 052 * checkpoint mechanism. 053 * 054 * At first call, MappedLog looks for the specified file and reads it into 055 * memory if exists. 056 * 057 * At each call, the MappedLog checks if a the input line already is in the set. 058 * If not, it writes the line into the set and the file. It returns the boolean 059 * flag indicating whether the line was already found (true) or not (false). The 060 * check and write is an atomic operation, so two actors cannot mix up this 061 * behaviour. 062 * 063 * All actors can write into the same file, if their parameter points to the 064 * same file. This allows checking if others already did (and logged) something. 065 * 066 * Query only (not writing out a line, but only checking its existence) can be 067 * achieved by setting the boolean flag 'checkOnly'. 068 * 069 * If the line is empty (or only white spaces), nothing will be written and 070 * false will be returned. 071 * 072 * @author Norbert Podhorszki 073 * @version $Id: MappedLog.java 24234 2010-05-06 05:21:26Z welker $ 074 * @since Ptolemy II 5.0.1 075 */ 076public class MappedLog { 077 078 public MappedLog() { 079 } 080 081 public boolean check(File logfile, String logText) { 082 return checkOrAdd(logfile, logText, true); 083 } 084 085 public boolean add(File logfile, String logText) { 086 return checkOrAdd(logfile, logText, false); 087 } 088 089 /** 090 * Close all opened log file in a synchronized way. The hashtable elements 091 * cannot be removed during the iterator, because that is fail-fast. So we 092 * clear the hashtable at the end. 093 */ 094 public static void closeAll() throws IOException { 095 096 synchronized (logFiles) { 097 Iterator logs = logFiles.keySet().iterator(); 098 while (logs.hasNext()) { 099 String path = (String) logs.next(); 100 MappedLogFile lf = (MappedLogFile) logFiles.get(path); 101 lf.close(); 102 if (isDebugging) 103 log.debug("Closed log " + path); 104 } 105 logFiles.clear(); // remove all elements 106 } 107 } 108 109 private boolean checkOrAdd(File logfile, String logText, boolean checkOnly) { 110 // if empty string, we do not do anything 111 if (logText.trim().length() == 0) { 112 if (isDebugging) 113 log.debug("Empty input. do nothing. return false."); 114 return false; 115 } 116 117 try { 118 MappedLogFile lf = getMappedLogFile(logfile); // get the log file or 119 // create it 120 if (checkOnly) 121 return lf.contains(logText); 122 else 123 return lf.print(logText); 124 125 } catch (Exception ex) { 126 log.error(ex); 127 return false; 128 } 129 } 130 131 private static SimpleDateFormat dateformat = new SimpleDateFormat( 132 "MMM dd yyyy HH:mm:ss.SSS"); 133 private boolean _xmlFormat = false; 134 private String _header; 135 136 // apache commons log for the source code logging. 137 private static final Log log = LogFactory.getLog(MappedLog.class.getName()); 138 private static final boolean isDebugging = log.isDebugEnabled(); 139 140 /* 141 * The log files already created (key = their absolute path as String), 142 * value=the MappedLog 143 */ 144 private static Hashtable logFiles = new Hashtable(); 145 146 /** 147 * Get the mapped log. If it was not yet requested, open create it. 148 */ 149 private MappedLogFile getMappedLogFile(File file) throws IOException { 150 151 String path = file.getAbsolutePath(); 152 MappedLogFile lf = null; 153 154 synchronized (logFiles) { 155 lf = (MappedLogFile) logFiles.get(path); 156 if (lf == null) { 157 lf = new MappedLogFile(file); 158 logFiles.put(path, lf); 159 } 160 } 161 return lf; 162 } 163 164 /** 165 * Record of PrintWriter and HashSet to store what we need about a mapped 166 * log in the hash table. 167 */ 168 private class MappedLogFile { 169 private HashSet set; 170 private PrintWriter writer; 171 172 public MappedLogFile(File f) throws IOException { 173 set = new HashSet(); 174 int nLines = 0; 175 if (f.exists()) { 176 // read file contents into hash set 177 try { 178 BufferedReader in = new BufferedReader(new FileReader(f)); 179 if (!in.ready()) 180 throw new IOException(); 181 String line; 182 while ((line = in.readLine()) != null) { 183 set.add(line); 184 nLines++; 185 } 186 in.close(); 187 } catch (IOException e) { 188 log.error("Error at reading the file " + f + ": " + e); 189 System.out.println("Error at reading the file " + f + ": " 190 + e); 191 } 192 } 193 writer = new PrintWriter(new FileWriter(f, true)); // append mode 194 if (isDebugging) 195 log.info("New MappedLogFile created for file " + f + " with " 196 + nLines + " lines of existing text"); 197 } 198 199 /** 200 * Print line if it is not already in the set. Returns true if line was 201 * already in the set, false otherwise. Synchronized on the PrintWriter 202 * object. 203 */ 204 public boolean print(String line) { 205 synchronized (this) { 206 if (isDebugging) 207 log.debug("start print() on " + line); 208 if (!set.contains(line)) { 209 set.add(line); 210 writer.println(line); 211 writer.flush(); 212 return false; 213 } else 214 return true; 215 } 216 } 217 218 /** 219 * Check if the line is in the set. Synchronized on the PrintWriter 220 * object. 221 */ 222 public boolean contains(String line) { 223 if (isDebugging) 224 log.debug("start contains() on " + line); 225 boolean found = false; 226 synchronized (this) { 227 found = set.contains(line); 228 } 229 return found; 230 } 231 232 /** 233 * Close log file and destroy the memory map. 234 */ 235 public void close() { 236 if (writer != null) { 237 writer.close(); 238 writer = null; 239 } 240 if (set != null) { 241 set.clear(); 242 set = null; 243 } 244 } 245 } 246 247}