package log; import java.io.*; /** * Represents a log file. * * <p>There is one global writable log file in the whole system. An instance * of {@link LogCreator} is used to create the global log file as well as any local * log file.</p> * * <p>Anything loggable must suit the {@link Loggable} interface and must be able to * generate an instance of {@link LogEntry}</p> * * <p>To read in log files see {@link LogInputStream}.</p> * * @author Steffen Zschaler * @version 1.0 * @since v1.0 */ public class Log extends Object implements LogContext { /** * The log files output stream. * * @see Log#changeOutputStream * @see Log#log */ protected ObjectOutputStream ooOutput = null; /** * Construct a new log file. * * @param os the outputstream to write to. */ public Log (OutputStream os) { super(); try { changeOutputStream (os); } catch (IOException ioe) {} } /** * Closes this Log file. * * @exception IOException if an error occurred while closing the underlying stream. * * @see Log#closeGlobalLog * * @override Never */ public synchronized void closeLog() throws IOException { changeOutputStream (null); } /** * Called by the garbage collector on an object when garbage collection * determines that there are no more references to the object. * * <p>Disposes of this log file. If this is the global log file, calls * {@link #closeGlobalLog()}, else calls {@link #closeLog()}.</p> * * @exception IOException if an error occurs while closing the underlying stream. * * @see Log#closeLog * @see Log#closeGlobalLog * * @override Never */ protected void finalize() throws IOException { if (this == theGlobalLog) closeGlobalLog(); else closeLog(); } /** * Change this log's outputstream. * * <p>If an outputstream exists it is closed prior to setting the new outputstream.</p> * * @param os the new output stream. * * @exception IOException if an error occured while closing the old stream. * * @see Log#setGlobalOutputStream * * @override Never */ public synchronized void changeOutputStream (OutputStream os) throws IOException { if (ooOutput != null) { logCloseLog(); ooOutput.close(); } if (os != null) { os.write (0); // provide for appended logs ooOutput = new ObjectOutputStream (os); } else { ooOutput = null; } if (ooOutput != null) { logOpenLog(); } } /** * Add a log entry when closing the log file. * * <p>Currently does nothing. You can override this to write a log entry when * the log file is being closed.</p> * * @see Log#closeLog * * @override Sometimes Override this method if you want to add a log entry when the log file is closed. */ protected void logCloseLog() {} /** * Add a log entry when opening the log file. * * <p>Currently does nothing. You can override this to write a log entry when * the log file is being opened.</p> * * @see Log#changeOutputStream * * @override Sometimes Override this method if you want to add a log entry when the log file is opened. */ protected void logOpenLog() {} /** * Adds one entry to the log file. Calls l.getLogData(). * * @param l the event to be logged. * @see Loggable * * @exception LogNoOutputStreamException if no OutputStream has been * specified. * * @exception IOException if an IOException occurs when writing to the * stream. * * @override Never */ public synchronized void log (Loggable l) throws LogNoOutputStreamException, IOException { if (ooOutput == null) throw new LogNoOutputStreamException("on Log.log ( "+l+" )"); LogEntry le = l.getLogData(); ooOutput.writeObject (le); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // STATIC PART //////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * The global log file to create a <i>Singleton</i>. */ private static Log theGlobalLog = null; /** * The global Log creator. */ private static LogCreator theLogCreator = null; static { theLogCreator = new LogCreator() { public Log createLog (OutputStream os) { return new Log (os); } }; } /** * Reference to the global output stream. * * <p><STRONG>Read Only</STRONG></p> * * @see Log#setGlobalOutputStream */ protected static OutputStream theGlobalOutputStream = null; /** * Returns the current global log file. * * <p>If no log file exists, creates it using the Outputstream as specified * by {@link #setGlobalOutputStream}</p> * * @see #setGlobalOutputStream * @see #closeGlobalLog * * @exception LogNoOutputStreamException if <code>setGlobalOutputStream()</code> * has not been called yet. */ public synchronized static Log getGlobalLog() throws LogNoOutputStreamException { if (theGlobalOutputStream == null) throw new LogNoOutputStreamException ("On Log.getGlobalLog()"); if (theGlobalLog == null) theGlobalLog = createLog (theGlobalOutputStream); return theGlobalLog; } /** * Create a new Log file using the current Log creator. * * <p>You should prefer calling this method to directly creating a new Log * file as this method will provide an easy interface for adapting to new * log classes.</p> * * @param os the OutputStream to be used. */ public static Log createLog (OutputStream os) { return theLogCreator.createLog (os); } /** * Change the Log creator. * * <p>Call to provide support for descended Log classes.</p> * * @param lc the log creator to be used when creating log files. * * @see Log#getGlobalLog */ public static void setLogCreator (LogCreator lc) { theLogCreator = lc; } /** * Closes the global log file if any log file was open. * * <p>If no log file exists no exception is thrown. * Closes the log file <B>and</B> it's OutputStream.</p> * * @exception IOException if an error occurs while closing the underlying stream. * * @see Log#getGlobalLog * @see Log#setGlobalOutputStream */ public synchronized static void closeGlobalLog() throws IOException { if (theGlobalLog != null) { theGlobalLog.closeLog(); theGlobalLog = null; theGlobalOutputStream = null; } } /** * Changes the current OutputStream of the global log file. * * <p>This method <strong>must</strong> be called at least once before any global log * operation takes place.</p> * * <p>If an OutputStream exists it will be closed automatically. To close * the entire global log file use {@link #closeGlobalLog()}.</p> * * @param newOutputStream the new global output stream * * @exception IOException if an error occurs while closing the original stream. * * @see #getGlobalLog * @see #closeGlobalLog * @see #changeOutputStream */ public synchronized static void setGlobalOutputStream (OutputStream newOutputStream) throws IOException { OutputStream os = theGlobalOutputStream ; theGlobalOutputStream = newOutputStream; if (theGlobalLog != null) { theGlobalLog.changeOutputStream (newOutputStream); } else { if (os != null) { os.close(); } } } }