001 package log; 002 003 import java.io.*; 004 import java.util.Iterator; 005 006 /** 007 * Represents a log file. 008 * 009 * <p>There is one global writable log file in the whole system. An instance 010 * of {@link LogCreator} is used to create the global log file as well as any local 011 * log file.</p> 012 * 013 * <p>Anything loggable must suit the {@link Loggable} interface and must be able to 014 * generate an instance of {@link LogEntry}</p> 015 * 016 * <p>To read in log files see {@link LogInputStream} and {@link LogFileContent}.</p> 017 * 018 * @author Steffen Zschaler 019 * @version 1.0 020 * @since v1.0 021 */ 022 public class Log extends Object implements LogContext { 023 024 /** 025 * The log file's output stream. 026 * 027 * @see Log#changeOutputStream 028 * @see Log#log 029 */ 030 protected ObjectOutputStream ooOutput = null; 031 032 /** 033 * Save log file to persistence file or not. 034 */ 035 protected boolean saveToPersistence = false; 036 037 /** 038 * Construct a new log file. 039 * 040 * @param os the outputstream to write to. 041 */ 042 public Log(OutputStream os) { 043 super(); 044 try { 045 changeOutputStream(os); 046 } 047 catch (IOException ioe) {} 048 } 049 050 /** 051 * Closes this Log file. 052 * 053 * @exception IOException if an error occurred while closing the underlying stream. 054 * 055 * @see Log#closeGlobalLog 056 * 057 * @override Never 058 */ 059 public synchronized void closeLog() throws IOException { 060 changeOutputStream(null); 061 } 062 063 /** 064 * Called by the garbage collector on an object when garbage collection 065 * determines that there are no more references to the object. 066 * 067 * <p>Disposes this log file. If this is the global log file, calls 068 * {@link #closeGlobalLog()}, else calls {@link #closeLog()}.</p> 069 * 070 * @exception IOException if an error occurs while closing the underlying stream. 071 * 072 * @see Log#closeLog 073 * @see Log#closeGlobalLog 074 * 075 * @override Never 076 */ 077 protected void finalize() throws IOException { 078 if (this == theGlobalLog) { 079 closeGlobalLog(); 080 } else { 081 closeLog(); 082 } 083 } 084 085 /** 086 * Change this log's outputstream. 087 * 088 * <p>If an outputstream exists it is closed prior to setting the new outputstream.</p> 089 * 090 * @param os the new output stream. 091 * @exception IOException if an error occured while closing the old stream. 092 * 093 * @see Log#setGlobalOutputStream 094 * 095 * @override Never 096 */ 097 public synchronized void changeOutputStream(OutputStream os) throws IOException { 098 if (ooOutput != null) { 099 logCloseLog(); 100 ooOutput.close(); 101 } 102 103 if (os != null) { 104 os.write(0); // provide for appended logs 105 ooOutput = new ObjectOutputStream(os); 106 } else { 107 ooOutput = null; 108 } 109 110 if (ooOutput != null) { 111 logOpenLog(); 112 } 113 } 114 115 /** 116 * Add a log entry when closing the log file. 117 * 118 * <p>Currently does nothing. You can override this to write a log entry when 119 * the log file is being closed.</p> 120 * 121 * @see Log#closeLog 122 * 123 * @override Sometimes Override this method if you want to add a log entry when the log file is closed. 124 */ 125 protected void logCloseLog() {} 126 127 /** 128 * Add a log entry when opening the log file. 129 * 130 * <p>Currently does nothing. You can override this to write a log entry when 131 * the log file is being opened.</p> 132 * 133 * @see Log#changeOutputStream 134 * 135 * @override Sometimes Override this method if you want to add a log entry when the log file is opened. 136 */ 137 protected void logOpenLog() {} 138 139 /** 140 * Adds one entry to the log file. Calls l.getLogData(). 141 * 142 * @param l the loggable event to be logged. 143 * @see Loggable 144 * 145 * @exception LogNoOutputStreamException if no OutputStream has been 146 * specified. 147 * 148 * @exception IOException if an IOException occurs when writing to the 149 * stream. 150 * 151 * @override Never 152 */ 153 public synchronized void log(Loggable l) throws LogNoOutputStreamException, IOException { 154 if (ooOutput == null) { 155 throw new LogNoOutputStreamException("on Log.log ( " + l + " )"); 156 } 157 158 LogEntry le = l.getLogData(); 159 ooOutput.writeObject(le); 160 } 161 162 /** 163 * Appends all LogEntries that are saved in <code>lfc</code> to the current log. This method is used 164 * when restoring a log file from a persistence file. 165 * 166 * @param lfc contains the LogEntries to be added 167 * @throws LogNoOutputStreamException if no OutputStream is specified 168 * @throws IOException if a log entry cannot be written zu the output stream 169 */ 170 public void addLogEntries(LogFileContent lfc) throws LogNoOutputStreamException, IOException { 171 if (ooOutput == null) { 172 throw new LogNoOutputStreamException("on Log.addLogEntries"); 173 } 174 for (Iterator it = lfc.getContentList().iterator(); it.hasNext();) { 175 LogEntry le = (LogEntry)it.next(); 176 ooOutput.writeObject(le); 177 } 178 } 179 180 //////////////////////////////////////////////////////////////////////////////////////////////////////////// 181 // STATIC PART 182 //////////////////////////////////////////////////////////////////////////////////////////////////////////// 183 184 /** 185 * The global log to create a <i>Singleton</i>. 186 */ 187 private static Log theGlobalLog = null; 188 189 /** 190 * The global log file. This is the acutual file object to be written to. 191 */ 192 private static File globalLogFile = null; 193 194 /** 195 * The global Log creator. It creates a log and returns it. Used by {@link createLog}. 196 */ 197 private static LogCreator theLogCreator = null; 198 static { 199 theLogCreator = new LogCreator() { 200 public Log createLog(OutputStream os) { 201 return new Log(os); 202 } 203 }; 204 } 205 206 /** 207 * Reference to the global output stream. 208 * 209 * <p><STRONG>Read Only</STRONG></p> 210 * 211 * @see Log#setGlobalOutputStream 212 */ 213 protected static OutputStream theGlobalOutputStream = null; 214 215 /** 216 * Returns the current global log. 217 * 218 * <p>If no log exists, one is created using the Outputstream as specified 219 * by {@link #setGlobalOutputStream}</p> 220 * 221 * @see #setGlobalOutputStream 222 * @see #closeGlobalLog 223 * 224 * @exception LogNoOutputStreamException if <code>setGlobalOutputStream()</code> 225 * has not been called yet. 226 */ 227 public synchronized static Log getGlobalLog() throws LogNoOutputStreamException { 228 if (theGlobalOutputStream == null) { 229 throw new LogNoOutputStreamException("On Log.getGlobalLog()"); 230 } 231 232 if (theGlobalLog == null) { 233 theGlobalLog = createLog(theGlobalOutputStream); 234 235 } 236 return theGlobalLog; 237 } 238 239 /** 240 * Create a new Log file using the current Log creator. 241 * 242 * <p>You should prefer calling this method to directly creating a new Log 243 * file as this method will provide an easy interface for adapting to new 244 * log classes.</p> 245 * 246 * @param os the OutputStream to be used. 247 */ 248 public static Log createLog(OutputStream os) { 249 return theLogCreator.createLog(os); 250 } 251 252 /** 253 * Change the Log creator. 254 * 255 * <p>Call to provide support for descended Log classes.</p> 256 * 257 * @param lc the log creator to be used when creating log files. 258 * 259 * @see Log#getGlobalLog 260 */ 261 public static void setLogCreator(LogCreator lc) { 262 theLogCreator = lc; 263 } 264 265 /** 266 * Closes the global log file if any log file was open. 267 * 268 * <p>If no log file exists no exception is thrown. 269 * Closes the log file <B>and</B> it's OutputStream.</p> 270 * 271 * @exception IOException if an error occurs while closing the underlying stream. 272 * 273 * @see Log#getGlobalLog 274 * @see Log#setGlobalOutputStream 275 */ 276 public synchronized static void closeGlobalLog() throws IOException { 277 if (theGlobalLog != null) { 278 theGlobalLog.closeLog(); 279 280 theGlobalLog = null; 281 theGlobalOutputStream = null; 282 } 283 } 284 285 /** 286 * Changes the current OutputStream of the global log file. 287 * 288 * <p>This method <strong>must</strong> be called at least once before any global log 289 * operation takes place.</p> 290 * 291 * <p>If an OutputStream exists it will be closed automatically. To close 292 * the entire global log file use {@link #closeGlobalLog()}.</p> 293 * 294 * @param newOutputStream the new global output stream 295 * 296 * @exception IOException if an error occurs while closing the original stream. 297 * 298 * @see #getGlobalLog 299 * @see #closeGlobalLog 300 * @see #changeOutputStream 301 */ 302 public synchronized static void setGlobalOutputStream(OutputStream newOutputStream) throws IOException { 303 OutputStream os = theGlobalOutputStream; 304 theGlobalOutputStream = newOutputStream; 305 if (theGlobalLog != null) { 306 theGlobalLog.changeOutputStream(newOutputStream); 307 } else { 308 if (os != null) { 309 os.close(); 310 } 311 } 312 } 313 314 /** 315 * Sets the global log file and assigns a FileOutputStream to it. 316 * 317 * @param filename the file to be used as global log file. 318 * @param overwrite if <code>true</code>, an existing file with the name <code>fileName</code> will be 319 * overwritten, if <code>false</code>, the LogEntries will be appended to that existing file.<br> 320 * With this flag one can decide if consecutive runs of the application should be logged in one file or if the old log 321 * file should always be overwritten. 322 * @param save if <code>true</code>, the global log file will be written to the persistence file when 323 * the Shop is saved and accordingly loaded when the Shop's state is loaded. When that happens, the 324 * current log file will be deleted, even if <code>overwrite</code> is set to <code>false</code>. 325 */ 326 public synchronized static void setGlobalLogFile(String filename, boolean overwrite, boolean save) 327 throws IOException { 328 329 if (overwrite) { 330 File f = new File(filename); 331 if (f.exists()) { 332 //set to null to allow the existing file to be deleted 333 setGlobalOutputStream(null); 334 f.delete(); 335 } 336 } 337 setGlobalOutputStream(new FileOutputStream(filename, true)); 338 globalLogFile = new File(filename); 339 try { 340 getGlobalLog().saveToPersistence = save; 341 } 342 catch (LogNoOutputStreamException ex) { 343 } 344 } 345 346 /** 347 * Sets the global log file and assigns a FileOutputStream to it.<br> 348 * This method calls <code>setGlobalLogFile(filename, false, false)</code>, that is, Log entries will 349 * be append to older log files, if they exist. The log files will not be saved/restored when the Shop 350 * is saved/restored. 351 * @param filename the file to be used as global log file. 352 */ 353 public synchronized static void setGlobalLogFile(String filename) throws IOException { 354 setGlobalLogFile(filename, false, false); 355 } 356 357 /** 358 * @return the global log file, if set. Otherwise <code>null</null> 359 */ 360 public static File getGlobalLogFile() { 361 return globalLogFile; 362 } 363 364 /** 365 * States if the log file should be saved to the persistence file. 366 * @return <code>true</code> if the log file should be saved to the persistence file, otherwise 367 * <code>false</code>. 368 */ 369 public static boolean getSaveToPersistence() { 370 try { 371 return getGlobalLog().saveToPersistence; 372 } 373 catch (Exception ex) { 374 return false; 375 } 376 } 377 378 }