source: trunk/autoquest-httpmonitor/src/main/java/de/ugoe/cs/autoquest/httpmonitor/HttpMonitorOutputWriter.java @ 1381

Last change on this file since 1381 was 1381, checked in by pharms, 10 years ago
  • removed find bugs warning
File size: 9.2 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.httpmonitor;
16
17import java.io.File;
18import java.io.FileOutputStream;
19import java.io.IOException;
20import java.io.OutputStreamWriter;
21import java.io.PrintWriter;
22import java.text.DecimalFormat;
23import java.util.logging.Level;
24
25import javax.xml.bind.JAXBContext;
26import javax.xml.bind.JAXBException;
27import javax.xml.bind.Marshaller;
28
29import de.ugoe.cs.autoquest.httpmonitor.exchange.HttpExchange;
30import de.ugoe.cs.autoquest.httpmonitor.exchange.ObjectFactory;
31import de.ugoe.cs.util.console.Console;
32
33/**
34 * <p>
35 * dumps messages to a log file in the provided base log directory. The name of each finished log
36 * file starts with the "httpmonitor_" followed by an index of the log file. An unfinished log file
37 * has no index yet. A log file is finished if
38 * <ul>
39 *   <li>the session is closed by a timeout</li>
40 *   <li>the HTTP monitor or proxy are normally shut down</li>
41 *   <li>on startup an unfinished log file is detected.</li>
42 *   <li>the {@link #MAXIMUM_LOG_FILE_SIZE} is reached</li>
43 * </ul>
44 * </p>
45 *
46 * @author Patrick Harms
47 * @version 1.0
48 *
49 */
50public class HttpMonitorOutputWriter implements HttpMonitorComponent, HttpMonitorExchangeHandler {
51   
52    /**  */
53    private static final long serialVersionUID = 1L;
54
55    /**
56     * the maximum size of an individual log file
57     */
58    private static final int MAXIMUM_LOG_FILE_SIZE = 50000000;
59
60    /**
61     * the default log base directory if none is provided through the constructor
62     */
63    private static final String DEFAULT_LOG_FILE_BASE_DIR = "logs";
64
65    /**
66     * the currently used log file base directory
67     */
68    private File logFileBaseDir;
69
70    /**
71     * the log file into which all messages are currently written
72     */
73    private File logFile;
74
75    /**
76     * an output writer to be used for writing into the log file
77     */
78    private PrintWriter outputWriter;
79
80    /**
81     * the time stamp of the last action taken on this writer (such as logging a message)
82     */
83    private long lastUpdate;
84   
85    /**
86     * <p>
87     * initializes the writer with the log file base directory.
88     * </p>
89     *
90     * @param logFileBaseDir the log file base directory, or null if the default directory shall
91     *                       be taken
92     */
93    public HttpMonitorOutputWriter(String logFileBaseDir) {
94        if (logFileBaseDir == null) {
95            this.logFileBaseDir = new File(DEFAULT_LOG_FILE_BASE_DIR);
96        }
97        else {
98            this.logFileBaseDir = new File(logFileBaseDir);
99        }
100       
101        lastUpdate = System.currentTimeMillis();
102    }
103
104    /* (non-Javadoc)
105     * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#init()
106     */
107    @Override
108    public synchronized void init() throws HttpMonitorException {
109        if (outputWriter != null) {
110            throw new IllegalStateException("already initialized. Call close() first");
111        }
112       
113        synchronized (HttpMonitorOutputWriter.class) {
114            try {
115                if (!logFileBaseDir.exists()) {
116                    if (!logFileBaseDir.mkdirs()) {
117                        throw new HttpMonitorException("log file directory " + logFileBaseDir +
118                                                       " can not be created");
119                    }
120                }
121                else if (!logFileBaseDir.isDirectory()) {
122                    throw new HttpMonitorException("log file directory " + logFileBaseDir +
123                                                   " already exists as a file");
124                }
125               
126                logFile = new File(logFileBaseDir, getLogFileName(-1));
127               
128                if (logFile.exists()) {
129                    rotateLogFile();
130                }
131           
132                createLogWriter();
133            }
134            catch (IOException e) {
135                throw new HttpMonitorException("could not open logfile " + logFile, e);
136            }
137        }
138       
139        lastUpdate = System.currentTimeMillis();
140    }
141
142    /**
143     * <p>
144     * used to calculate a log file name. If the provided index is smaller 0, then no index
145     * is added to the file name. A filename is e.g. "httpmonitor_001.log".
146     * </p>
147     *
148     * @param index the index of the log file or -1 one, if no index shall be added
149     *
150     * @return the file name as described
151     */
152    private String getLogFileName(int index) {
153        String result = "httpmonitor";
154       
155        if (index >= 0) {
156            result += "_" + new DecimalFormat("000" ).format(index);
157        }
158       
159        result += ".log";
160       
161        return result;
162    }
163
164    /* (non-Javadoc)
165     * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#start()
166     */
167    @Override
168    public synchronized void start() throws IllegalStateException, HttpMonitorException {
169        lastUpdate = System.currentTimeMillis();
170    }
171
172    /* (non-Javadoc)
173     * @see HttpMonitorMessageListener#handleHttpMessage()
174     */
175    @Override
176    public synchronized void handleHttpExchange(HttpExchange httpExchange) {
177        if (outputWriter == null) {
178            throw new IllegalStateException("not initialized. Call init() first");
179        }
180       
181        try {
182            JAXBContext jaxbContext =
183                JAXBContext.newInstance(HttpExchange.class.getPackage().getName());
184            Marshaller marshaller = jaxbContext.createMarshaller();
185
186            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
187            marshaller.marshal(new ObjectFactory().createHttpExchange(httpExchange), outputWriter);
188        }
189        catch (JAXBException e) {
190            Console.traceln(Level.WARNING, "could not dump exchange to log file: " + e);
191            Console.logException(e);
192        }
193
194        outputWriter.flush();
195       
196        try {
197            considerLogRotate();
198        }
199        catch (IOException e) {
200            throw new IllegalStateException("could not perform log rotation: " + e, e);
201        }
202       
203        lastUpdate = System.currentTimeMillis();
204    }
205
206    /**
207     * <p>
208     * checks, if the log file exceeded the {@link #MAXIMUM_LOG_FILE_SIZE}. If so, the current
209     * log file is closed, the next log file name is determined and this new file is opened for
210     * writing.
211     * </p>
212     */
213    private synchronized void considerLogRotate() throws IOException {
214        if (logFile.length() > MAXIMUM_LOG_FILE_SIZE) {
215            closeLogWriter();
216            rotateLogFile();
217            createLogWriter();
218        }
219    }
220
221    /**
222     * <p>
223     * renames the current log file to a new log file with the next available index. It further
224     * sets the current log file to the default name, i.e. without index.
225     * </p>
226     */
227    private void rotateLogFile() {
228        File clientLogDir = logFile.getParentFile();
229        File checkFile;
230
231        int logFileIndex = -1;
232        do {
233            logFileIndex++;
234           
235            checkFile = new File(clientLogDir, getLogFileName(logFileIndex));
236        }
237        while (checkFile.exists());
238   
239        if (!logFile.renameTo(checkFile)) {
240            Console.printerrln("could not rename log file " + logFile + " to " + checkFile +
241                               ". Will not perform log rotation.");
242        }
243        else {
244            logFileIndex++;
245            logFile = new File(clientLogDir, getLogFileName(-1));
246        }
247    }
248
249    /**
250     * <p>
251     * instantiates a writer to be used for writing messages into the log file.
252     * </p>
253     */
254    private void createLogWriter() throws IOException {
255        FileOutputStream fis = new FileOutputStream(logFile);
256        outputWriter = new PrintWriter(new OutputStreamWriter(fis, "UTF-8"));
257        outputWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
258        outputWriter.println("<session xmlns=\"http://autoquest.informatik.uni-goettingen.de\" >");
259    }
260
261    /**
262     * <p>
263     * closed the current writer if it is open.
264     * </p>
265     */
266    private void closeLogWriter() {
267        if (outputWriter != null) {
268            outputWriter.println("</session>");
269            outputWriter.flush();
270            outputWriter.close();
271            outputWriter = null;
272        }
273    }
274
275    /* (non-Javadoc)
276     * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#stop()
277     */
278    @Override
279    public synchronized void stop() {
280        closeLogWriter();
281        rotateLogFile();
282
283        lastUpdate = System.currentTimeMillis();
284    }
285
286    /**
287     * <p>
288     * return the time stamp of the last activity that happened on this writer.
289     * </p>
290     *
291     * @return as described
292     */
293    public long getLastUpdate() {
294        return lastUpdate;
295    }
296}
Note: See TracBrowser for help on using the repository browser.