// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.httpmonitor; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.text.DecimalFormat; import java.util.logging.Level; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import de.ugoe.cs.autoquest.httpmonitor.exchange.HttpExchange; import de.ugoe.cs.autoquest.httpmonitor.exchange.ObjectFactory; import de.ugoe.cs.util.console.Console; /** *

* dumps messages to a log file in the provided base log directory. The name of each finished log * file starts with the "httpmonitor_" followed by an index of the log file. An unfinished log file * has no index yet. A log file is finished if *

*

* * @author Patrick Harms * @version 1.0 * */ public class HttpMonitorOutputWriter implements HttpMonitorComponent, HttpMonitorExchangeHandler { /** */ private static final long serialVersionUID = 1L; /** * the maximum size of an individual log file */ private static final int MAXIMUM_LOG_FILE_SIZE = 50000000; /** * the default log base directory if none is provided through the constructor */ private static final String DEFAULT_LOG_FILE_BASE_DIR = "logs"; /** * the currently used log file base directory */ private File logFileBaseDir; /** * the log file into which all messages are currently written */ private File logFile; /** * an output writer to be used for writing into the log file */ private PrintWriter outputWriter; /** * the time stamp of the last action taken on this writer (such as logging a message) */ private long lastUpdate; /** *

* initializes the writer with the log file base directory. *

* * @param logFileBaseDir the log file base directory, or null if the default directory shall * be taken */ public HttpMonitorOutputWriter(String logFileBaseDir) { if (logFileBaseDir == null) { this.logFileBaseDir = new File(DEFAULT_LOG_FILE_BASE_DIR); } else { this.logFileBaseDir = new File(logFileBaseDir); } lastUpdate = System.currentTimeMillis(); } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#init() */ @Override public synchronized void init() throws HttpMonitorException { if (outputWriter != null) { throw new IllegalStateException("already initialized. Call close() first"); } synchronized (HttpMonitorOutputWriter.class) { try { if (!logFileBaseDir.exists()) { if (!logFileBaseDir.mkdirs()) { throw new HttpMonitorException("log file directory " + logFileBaseDir + " can not be created"); } } else if (!logFileBaseDir.isDirectory()) { throw new HttpMonitorException("log file directory " + logFileBaseDir + " already exists as a file"); } logFile = new File(logFileBaseDir, getLogFileName(-1)); if (logFile.exists()) { rotateLogFile(); } createLogWriter(); } catch (IOException e) { throw new HttpMonitorException("could not open logfile " + logFile, e); } } lastUpdate = System.currentTimeMillis(); } /** *

* used to calculate a log file name. If the provided index is smaller 0, then no index * is added to the file name. A filename is e.g. "httpmonitor_001.log". *

* * @param index the index of the log file or -1 one, if no index shall be added * * @return the file name as described */ private String getLogFileName(int index) { String result = "httpmonitor"; if (index >= 0) { result += "_" + new DecimalFormat("000" ).format(index); } result += ".log"; return result; } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#start() */ @Override public synchronized void start() throws IllegalStateException, HttpMonitorException { lastUpdate = System.currentTimeMillis(); } /* (non-Javadoc) * @see HttpMonitorMessageListener#handleHttpMessage() */ @Override public synchronized void handleHttpExchange(HttpExchange httpExchange) { if (outputWriter == null) { throw new IllegalStateException("not initialized. Call init() first"); } try { JAXBContext jaxbContext = JAXBContext.newInstance(HttpExchange.class.getPackage().getName()); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); marshaller.marshal(new ObjectFactory().createHttpExchange(httpExchange), outputWriter); } catch (JAXBException e) { Console.traceln(Level.WARNING, "could not dump exchange to log file: " + e); Console.logException(e); } outputWriter.flush(); try { considerLogRotate(); } catch (IOException e) { throw new IllegalStateException("could not perform log rotation: " + e, e); } lastUpdate = System.currentTimeMillis(); } /** *

* checks, if the log file exceeded the {@link #MAXIMUM_LOG_FILE_SIZE}. If so, the current * log file is closed, the next log file name is determined and this new file is opened for * writing. *

*/ private synchronized void considerLogRotate() throws IOException { if (logFile.length() > MAXIMUM_LOG_FILE_SIZE) { closeLogWriter(); rotateLogFile(); createLogWriter(); } } /** *

* renames the current log file to a new log file with the next available index. It further * sets the current log file to the default name, i.e. without index. *

*/ private void rotateLogFile() { File clientLogDir = logFile.getParentFile(); File checkFile; int logFileIndex = -1; do { logFileIndex++; checkFile = new File(clientLogDir, getLogFileName(logFileIndex)); } while (checkFile.exists()); if (!logFile.renameTo(checkFile)) { Console.printerrln("could not rename log file " + logFile + " to " + checkFile + ". Will not perform log rotation."); } else { logFileIndex++; logFile = new File(clientLogDir, getLogFileName(-1)); } } /** *

* instantiates a writer to be used for writing messages into the log file. *

*/ private void createLogWriter() throws IOException { FileOutputStream fis = new FileOutputStream(logFile); outputWriter = new PrintWriter(new OutputStreamWriter(fis, "UTF-8")); outputWriter.println(""); outputWriter.println(""); } /** *

* closed the current writer if it is open. *

*/ private void closeLogWriter() { if (outputWriter != null) { outputWriter.println("
"); outputWriter.flush(); outputWriter.close(); outputWriter = null; } } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#stop() */ @Override public synchronized void stop() { closeLogWriter(); rotateLogFile(); lastUpdate = System.currentTimeMillis(); } /** *

* return the time stamp of the last activity that happened on this writer. *

* * @return as described */ public long getLastUpdate() { return lastUpdate; } }