/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package alma.acs.logging.dialogs.error; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Timer; import java.util.TimerTask; /** * <code>ErrorLogFile</code> encapsulates a file adding methods to close * the file when it has not been since used for long time. * <P> * In case of error while writing or creating the temporary file, * the behavior depends on the value of <code>retryOnError</code>. * If it is <code>true</code>. * <P> * The purpose of this class is to avoid having a file open when no I/O * is performed for a long time. * This is achieved: * <UL> * <LI>by creating the file only when the first writing is requested * <LI>closing the file when no I/O is performed for a defined time interval * </UL> * The used file is identified by its name; * * @author acaproni * */ public class ErrorLogFile extends TimerTask { /** * The timer to check the time before two * flush. */ private Timer timer = new Timer("ErrorLogFile",true); /** * The number of seconds before closing the file */ private final int timeout; /** * The time when the last written has been performed. * <P> * This can be indirectly used to know if the file is empty as it happens * in <code>copy()</code>. */ private long lastWriteTime=-1; /** * If <code>true</code> the file will be deleted when the application terminates. */ private final boolean deleteOnExit; /** * If <code>true</code> the application tries to perform I/O in the file * even if a previous attempt resulted in an error. */ private final boolean retryOnError; /** * The prefix of the temporary file */ private final String prefix; /** * The suffix of the temporary file */ private final String suffix; /** * The folder where the temporary file must be written */ private final String folder; /** * The name of the file for I/O. */ private String fileName; /** * The file stream for output. */ private FileOutputStream outFile=null; /** * Remember if there was an error associated to the temporary file for * flushing errors. * <P> *The behavior of object of this class when an error happens depends *on the value of <code>retryonError</code>. * */ private boolean fileError=false; /** * <code>true</code> if the object has been closed */ private boolean closed=false; /** * Constructor. * * @param timeout The number of seconds before closing the file * @param prefix The prefix of the temporary file * @param suffix The suffix of the temporary file * @param folder The name of the folder where the new file must be created; * if it is <code>null</code> or empty, then the current * folder is used * @param deleteOnExit if <code>true</code> the file s deleted when the * application exits * @param retryOnError if <code>true</code> tries to open the file even if * a previous attempt failed */ public ErrorLogFile(int timeout, String prefix, String suffix, String folder, boolean deleteOnExit, boolean retryOnError) { if (timeout<=0) { throw new IllegalArgumentException("The timeout msut be greater then 0"); } if (prefix==null || prefix.length()==0) { throw new IllegalArgumentException("Invalid prefix"); } if (suffix==null || suffix.length()==0) { throw new IllegalArgumentException("Invalid suffix"); } if (folder==null || folder.length()==0) { this.folder="."; } else { this.folder=folder; } this.timeout=timeout; this.deleteOnExit=deleteOnExit; this.retryOnError=retryOnError; this.prefix=prefix; this.suffix=suffix; timer.schedule(this, 1000*timeout, 1000*timeout); } /** * The method executed by the <code>Timer</code>. * * @see {@link Timer} */ @Override public synchronized void run() { if (lastWriteTime<0 || outFile==null) { // Nothing has been written in the file yet return; } if (lastWriteTime+timeout*1000<System.currentTimeMillis()) { // timeout elapsed ==>> close the output strem try { outFile.flush(); outFile.close(); } catch (IOException e) { fileError=true; } outFile=null; } } /** * Create the temporary file for flushing the log. * * @throws IOException */ private void initTmpFile() throws IOException { // Get a name for the file File tmpFile = File.createTempFile(prefix, suffix, new File(folder)); if (deleteOnExit) { tmpFile.deleteOnExit(); } fileName = tmpFile.getAbsolutePath(); } /** * Append a string to the temporary file. * * @param str The string to append in the file */ public synchronized void append(String str) throws IOException { if (str==null || str.length()==0) { throw new IllegalArgumentException("The string to write can't be null nor empty"); } if (closed) { return; } if (fileError && !retryOnError) { return; } if (fileName==null) { initTmpFile(); } if (outFile==null) { outFile=new FileOutputStream(fileName,true); } outFile.write(str.getBytes()); lastWriteTime=System.currentTimeMillis(); } /** * Copy the file in the passed output stream * * @param file The stream to copy the content of the file into */ public synchronized void copy(OutputStream file) throws FileNotFoundException, IOException { if (file==null) { throw new IllegalArgumentException("The file can't be null"); } if (lastWriteTime<0) { // The file is empty return; } FileInputStream inF = new FileInputStream(fileName); try { byte[] buffer = new byte[512]; int bytesRead; do { bytesRead=inF.read(buffer); if (bytesRead>0) { file.write(buffer, 0, bytesRead); } } while (bytesRead>=0); } finally { inF.close(); } } /** * Close the file(s) freeing all the resources * <P> * This is the last method executed by this class */ public synchronized void clear() { if (outFile!=null) { try { outFile.close(); } catch (Throwable t) { System.err.println("Ignored error while clearing the temporary file "+fileName+": "+t.getMessage()); t.printStackTrace(); } outFile=null; } lastWriteTime=-1; fileName=null; fileError=false; } /** * Flush and close the file when the object is destroyed by the GC */ @Override protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } } /** * Close clear the file and stop the timer. * <P> * This is the last method to execute. */ public synchronized void close() { closed=true; timer.cancel(); clear(); } /** * @return the fileName */ public synchronized String getFileName() { return fileName; } }