/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package net.java.btrace.server; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import net.java.btrace.api.core.BTraceLogger; /** * This class represents various strategies available for dumping BTrace * output to a file. * * @author Jaroslav Bachorik */ abstract public class TraceOutputWriter extends Writer { static private class SimpleFileOutput extends TraceOutputWriter { final private FileWriter delegate; public SimpleFileOutput(File output) throws IOException { output.getCanonicalFile().getParentFile().mkdirs(); delegate = new FileWriter(output); } @Override public void close() throws IOException { delegate.close(); } @Override public void flush() throws IOException { delegate.flush(); } @Override public void write(char[] cbuf, int off, int len) throws IOException { delegate.write(cbuf, off, len); } } static abstract private class RollingFileWriter extends TraceOutputWriter { final private ReentrantReadWriteLock writerLock = new ReentrantReadWriteLock(); // @GuardedBy writerLock private FileWriter currentFileWriter; private String path, baseName; private int counter = 1; private int maxRolls; public RollingFileWriter(File output) throws IOException { this(output, 5); } public RollingFileWriter(File output, int maxRolls) throws IOException { output.getParentFile().mkdirs(); currentFileWriter = new FileWriter(output); path = output.getParentFile().getAbsolutePath(); baseName = output.getName(); this.maxRolls = maxRolls; } @Override final public void close() throws IOException { try { writerLock.readLock().lock(); currentFileWriter.close(); } finally { writerLock.readLock().unlock(); } } @Override final public void flush() throws IOException { try { writerLock.readLock().lock(); currentFileWriter.flush(); } finally { writerLock.readLock().unlock(); } if (needsRoll()) { nextWriter(); } } @Override final public void write(char[] cbuf, int off, int len) throws IOException { try { writerLock.readLock().lock(); currentFileWriter.write(cbuf, off, len); } finally { writerLock.readLock().unlock(); } } private void nextWriter() { try { writerLock.writeLock().lock(); currentFileWriter = getNextWriter(); } catch (IOException e) { BTraceLogger.debugPrint(e); } finally { writerLock.writeLock().unlock(); } } private FileWriter getNextWriter() throws IOException { currentFileWriter.close(); File scriptOutputFile_renameFrom = new File(path + File.separator + baseName); File scriptOutputFile_renameTo = new File(path + File.separator + baseName + "." + (counter++)); if (scriptOutputFile_renameTo.exists()) { scriptOutputFile_renameTo.delete(); } scriptOutputFile_renameFrom.renameTo(scriptOutputFile_renameTo); scriptOutputFile_renameFrom = new File(path + File.separator + baseName); if (counter > maxRolls) { counter = 1; } return new FileWriter(scriptOutputFile_renameFrom); } abstract protected boolean needsRoll(); } static private class TimeBasedRollingFileWriter extends RollingFileWriter { private long lastTimeStamp = System.currentTimeMillis(); private long interval; private TimeUnit unit; public TimeBasedRollingFileWriter(File output, int maxRolls, long interval, TimeUnit unit) throws IOException { super(output, maxRolls); this.interval = interval; this.unit = unit; } public TimeBasedRollingFileWriter(File output, long interval, TimeUnit unit) throws IOException { super(output); this.interval = interval; this.unit = unit; } protected boolean needsRoll() { long currTime = System.currentTimeMillis(); long myInterval = currTime- lastTimeStamp; if (unit.convert(myInterval, TimeUnit.MILLISECONDS) >= interval) { lastTimeStamp = currTime; return true; } return false; } } /** * Plain file writer - all output will go to one specified file * @param output The file to put the output to * @return Returns an appropriate {@linkplain TraceOutputWriter} instance or NULL */ public static TraceOutputWriter fileWriter(File output) { TraceOutputWriter instance = null; try { instance = new SimpleFileOutput(output); } catch (IOException e) { BTraceLogger.debugPrint(e); } return instance; } /** * Time based rolling file writer. Defaults to 100 allowed output chunks. * @param output The file to put the output to * @param interval The interval between rolling the output file * @param unit The {@linkplain TimeUnit} value the interval is represented in * @return Returns an appropriate {@linkplain TraceOutputWriter} instance or NULL */ public static TraceOutputWriter rollingFileWriter(File output, long interval, TimeUnit unit) { TraceOutputWriter instance = null; try { instance = new TimeBasedRollingFileWriter(null, interval, unit); } catch (IOException e) { BTraceLogger.debugPrint(e); } return instance; } /** * Time based rolling file writer. * @param output The file to put the output to * @param maxRolls Maximum number of roll chunks * @param interval The interval between rolling the output file * @param unit The {@linkplain TimeUnit} value the interval is represented in * @return Returns an appropriate {@linkplain TraceOutputWriter} instance or NULL */ public static TraceOutputWriter rollingFileWriter(File output, int maxRolls, long interval, TimeUnit unit) { TraceOutputWriter instance = null; try { instance = new TimeBasedRollingFileWriter(output, maxRolls, interval, unit); } catch (IOException e) { BTraceLogger.debugPrint(e); } return instance; } private static void ensurePathExists(File f) { if (f == null || f.exists()) return; ensurePathExists(f.getParentFile()); System.err.println("continuing"); if (!f.getName().endsWith(".btrace")) { // not creating the actual file try { System.err.println("creating new folder " + f.toString()); f.createNewFile(); } catch (IOException e) { e.printStackTrace(); // ignore and continue } } } }