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}