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

Last change on this file since 1374 was 1374, checked in by pharms, 10 years ago

Initial import.

File size: 9.1 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     * the maximum size of an individual log file
54     */
55    private static final int MAXIMUM_LOG_FILE_SIZE = 50000000;
56
57    /**
58     * the default log base directory if none is provided through the constructor
59     */
60    private static final String DEFAULT_LOG_FILE_BASE_DIR = "logs";
61
62    /**
63     * the currently used log file base directory
64     */
65    private File logFileBaseDir;
66
67    /**
68     * the log file into which all messages are currently written
69     */
70    private File logFile;
71
72    /**
73     * an output writer to be used for writing into the log file
74     */
75    private PrintWriter outputWriter;
76
77    /**
78     * the time stamp of the last action taken on this writer (such as logging a message)
79     */
80    private long lastUpdate;
81   
82    /**
83     * <p>
84     * initializes the writer with the log file base directory.
85     * </p>
86     *
87     * @param logFileBaseDir the log file base directory, or null if the default directory shall
88     *                       be taken
89     */
90    public HttpMonitorOutputWriter(String logFileBaseDir) {
91        if (logFileBaseDir == null) {
92            this.logFileBaseDir = new File(DEFAULT_LOG_FILE_BASE_DIR);
93        }
94        else {
95            this.logFileBaseDir = new File(logFileBaseDir);
96        }
97       
98        lastUpdate = System.currentTimeMillis();
99    }
100
101    /* (non-Javadoc)
102     * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#init()
103     */
104    @Override
105    public synchronized void init() throws HttpMonitorException {
106        if (outputWriter != null) {
107            throw new IllegalStateException("already initialized. Call close() first");
108        }
109       
110        synchronized (HttpMonitorOutputWriter.class) {
111            try {
112                if (!logFileBaseDir.exists()) {
113                    if (!logFileBaseDir.mkdirs()) {
114                        throw new HttpMonitorException("log file directory " + logFileBaseDir +
115                                                       " can not be created");
116                    }
117                }
118                else if (!logFileBaseDir.isDirectory()) {
119                    throw new HttpMonitorException("log file directory " + logFileBaseDir +
120                                                   " already exists as a file");
121                }
122               
123                logFile = new File(logFileBaseDir, getLogFileName(-1));
124               
125                if (logFile.exists()) {
126                    rotateLogFile();
127                }
128           
129                createLogWriter();
130            }
131            catch (IOException e) {
132                throw new HttpMonitorException("could not open logfile " + logFile, e);
133            }
134        }
135       
136        lastUpdate = System.currentTimeMillis();
137    }
138
139    /**
140     * <p>
141     * used to calculate a log file name. If the provided index is smaller 0, then no index
142     * is added to the file name. A filename is e.g. "httpmonitor_001.log".
143     * </p>
144     *
145     * @param index the index of the log file or -1 one, if no index shall be added
146     *
147     * @return the file name as described
148     */
149    private String getLogFileName(int index) {
150        String result = "httpmonitor";
151       
152        if (index >= 0) {
153            result += "_" + new DecimalFormat("000" ).format(index);
154        }
155       
156        result += ".log";
157       
158        return result;
159    }
160
161    /* (non-Javadoc)
162     * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#start()
163     */
164    @Override
165    public synchronized void start() throws IllegalStateException, HttpMonitorException {
166        lastUpdate = System.currentTimeMillis();
167    }
168
169    /* (non-Javadoc)
170     * @see HttpMonitorMessageListener#handleHttpMessage()
171     */
172    @Override
173    public synchronized void handleHttpExchange(HttpExchange httpExchange) {
174        if (outputWriter == null) {
175            throw new IllegalStateException("not initialized. Call init() first");
176        }
177       
178        try {
179            JAXBContext jaxbContext =
180                JAXBContext.newInstance(HttpExchange.class.getPackage().getName());
181            Marshaller marshaller = jaxbContext.createMarshaller();
182
183            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
184            marshaller.marshal(new ObjectFactory().createHttpExchange(httpExchange), outputWriter);
185        }
186        catch (JAXBException e) {
187            Console.traceln(Level.WARNING, "could not dump exchange to log file: " + e);
188            Console.logException(e);
189        }
190
191        outputWriter.flush();
192       
193        try {
194            considerLogRotate();
195        }
196        catch (IOException e) {
197            throw new IllegalStateException("could not perform log rotation: " + e, e);
198        }
199       
200        lastUpdate = System.currentTimeMillis();
201    }
202
203    /**
204     * <p>
205     * checks, if the log file exceeded the {@link #MAXIMUM_LOG_FILE_SIZE}. If so, the current
206     * log file is closed, the next log file name is determined and this new file is opened for
207     * writing.
208     * </p>
209     */
210    private synchronized void considerLogRotate() throws IOException {
211        if (logFile.length() > MAXIMUM_LOG_FILE_SIZE) {
212            closeLogWriter();
213            rotateLogFile();
214            createLogWriter();
215        }
216    }
217
218    /**
219     * <p>
220     * renames the current log file to a new log file with the next available index. It further
221     * sets the current log file to the default name, i.e. without index.
222     * </p>
223     */
224    private void rotateLogFile() {
225        File clientLogDir = logFile.getParentFile();
226        File checkFile;
227
228        int logFileIndex = -1;
229        do {
230            logFileIndex++;
231           
232            checkFile = new File(clientLogDir, getLogFileName(logFileIndex));
233        }
234        while (checkFile.exists());
235   
236        if (!logFile.renameTo(checkFile)) {
237            Console.printerrln("could not rename log file " + logFile + " to " + checkFile +
238                               ". Will not perform log rotation.");
239        }
240        else {
241            logFileIndex++;
242            logFile = new File(clientLogDir, getLogFileName(-1));
243        }
244    }
245
246    /**
247     * <p>
248     * instantiates a writer to be used for writing messages into the log file.
249     * </p>
250     */
251    private void createLogWriter() throws IOException {
252        FileOutputStream fis = new FileOutputStream(logFile);
253        outputWriter = new PrintWriter(new OutputStreamWriter(fis, "UTF-8"));
254        outputWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
255        outputWriter.println("<session xmlns=\"http://autoquest.informatik.uni-goettingen.de\" >");
256    }
257
258    /**
259     * <p>
260     * closed the current writer if it is open.
261     * </p>
262     */
263    private void closeLogWriter() {
264        if (outputWriter != null) {
265            outputWriter.println("</session>");
266            outputWriter.flush();
267            outputWriter.close();
268            outputWriter = null;
269        }
270    }
271
272    /* (non-Javadoc)
273     * @see de.ugoe.cs.autoquest.htmlmonitor.HttpMonitorComponent#stop()
274     */
275    @Override
276    public synchronized void stop() {
277        closeLogWriter();
278        rotateLogFile();
279
280        lastUpdate = System.currentTimeMillis();
281    }
282
283    /**
284     * <p>
285     * return the time stamp of the last activity that happened on this writer.
286     * </p>
287     *
288     * @return as described
289     */
290    public long getLastUpdate() {
291        return lastUpdate;
292    }
293}
Note: See TracBrowser for help on using the repository browser.