source: trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/HTMLLogCompressor.java @ 1230

Last change on this file since 1230 was 1230, checked in by pharms, 11 years ago
  • added support for compressing log files by reducing the redundant occurrence of GUI elements
File size: 10.0 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.plugin.html;
16
17import java.io.File;
18import java.io.FileNotFoundException;
19import java.io.FileOutputStream;
20import java.io.OutputStreamWriter;
21import java.io.PrintWriter;
22import java.io.UnsupportedEncodingException;
23import java.text.DecimalFormat;
24import java.util.HashSet;
25import java.util.LinkedList;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29
30import org.xml.sax.SAXException;
31
32import de.ugoe.cs.util.StringTools;
33import de.ugoe.cs.util.console.Console;
34
35/**
36 * <p>
37 * TODO comment
38 * </p>
39 *
40 * @author Fabian Glaser, Patrick Harms
41 * @version 1.0
42 *
43 */
44public class HTMLLogCompressor extends AbstractDefaultLogParser {
45   
46    /**
47     *
48     */
49    private PrintWriter outputWriter;
50   
51    /**
52     * the GUI elements, that were already logged and need therefore not be logged again into
53     * the same file
54     */
55    private Set<String> loggedGUIElementIds = new HashSet<String>();
56
57    /**
58     * the events that were read
59     */
60    private List<EventEntry> sortedEvents = new LinkedList<EventEntry>();
61
62    /**
63     *
64     */
65    public void compressFilesInDirectory(String directory) {
66        if (directory == null) {
67            throw new IllegalArgumentException("directory must not be null");
68        }
69
70        compressFilesInDirectory(new File(directory));
71    }
72
73    /**
74     *
75     */
76    public void compressFilesInDirectory(File directory) {
77        if (directory == null) {
78            throw new IllegalArgumentException("directory must not be null");
79        }
80       
81        if (!directory.exists()) {
82            throw new IllegalArgumentException("directory must denote an existing directory");
83        }
84       
85        if (!directory.isDirectory()) {
86            throw new IllegalArgumentException("directory must denote a directory");
87        }
88       
89        File[] files = directory.listFiles();
90       
91        if ((files == null) || (files.length == 0)) {
92            Console.println("directory is empty");
93            return;
94        }
95       
96        File outFile = null;
97       
98        int logFileIndex = -1;
99        do {
100            outFile = new File(directory, getLogFileName(directory.getName(), logFileIndex));
101            logFileIndex++;
102        }
103        while (outFile.exists());
104       
105        List<File> compressedFiles = new LinkedList<File>();
106       
107        try {
108            FileOutputStream fis = new FileOutputStream(outFile);
109            outputWriter = new PrintWriter(new OutputStreamWriter(fis, "UTF-8"));
110            outputWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
111            outputWriter.println("<session>");
112
113            loggedGUIElementIds.clear();
114            sortedEvents.clear();
115
116            for (File file : files) {
117                if (file.isFile()) {
118                    try {
119                        super.parseFile(file);
120                        Console.println("compressed " + file);
121                        compressedFiles.add(file);
122                    }
123                    catch (Exception e) {
124                        Console.printerrln("could not parse and compress file " + file);
125                    }
126                }
127            }
128           
129            for (EventEntry event : sortedEvents) {
130                event.dump();
131            }
132
133            outputWriter.println("</session>");
134            outputWriter.flush();
135        }
136        catch (FileNotFoundException e) {
137            Console.printerrln("could not create compressed file " + outFile);
138            compressedFiles.clear();
139        }
140        catch (UnsupportedEncodingException e) {
141            // this should never happen
142            e.printStackTrace();
143            compressedFiles.clear();
144        }
145        finally {
146            if (outputWriter != null) {
147                outputWriter.close();
148                outputWriter = null;
149            }
150        }
151       
152        for (File file : compressedFiles) {
153            if (!file.delete()) {
154                Console.printerrln("could not delete compressed file " + file);
155            }
156        }
157       
158        if (outFile.exists()) {
159            File finalOutFile = null;
160           
161            logFileIndex = -1;
162            do {
163                logFileIndex++;
164                finalOutFile =
165                    new File(directory, getLogFileName(directory.getName(), logFileIndex));
166            }
167            while (finalOutFile.exists());
168
169            outFile.renameTo(finalOutFile);
170            Console.println("created " + finalOutFile);
171        }
172    }
173   
174    /* (non-Javadoc)
175     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#parseFile(java.lang.String)
176     */
177    @Override
178    public void parseFile(String filename) {
179        throw new IllegalStateException("this method must not be called externally");
180    }
181
182    /* (non-Javadoc)
183     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#parseFile(java.io.File)
184     */
185    @Override
186    public void parseFile(File file) {
187        throw new IllegalStateException("this method must not be called externally");
188    }
189
190    /* (non-Javadoc)
191     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleGUIElement(String, Map)
192     */
193    @Override
194    protected boolean handleGUIElement(String id, Map<String, String> parameters)
195        throws SAXException
196    {
197        if (!loggedGUIElementIds.contains(id)) {
198            outputWriter.print("<component id=\"");
199            outputWriter.print(id);
200            outputWriter.println("\">");
201       
202            for (Map.Entry<String, String> param : parameters.entrySet()) {
203                dumpParam(param.getKey(), param.getValue());
204            }
205           
206            outputWriter.println("</component>");
207       
208            loggedGUIElementIds.add(id);
209        }
210       
211        return true;
212    }
213
214    /* (non-Javadoc)
215     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleEvent(java.lang.String, java.util.Map)
216     */
217    @Override
218    protected boolean handleEvent(String type, Map<String, String> parameters) throws SAXException {
219        String timestampStr = parameters.get("timestamp");
220       
221        long timestamp = Long.MAX_VALUE;
222       
223        if (timestampStr != null) {
224            timestamp = Long.parseLong(timestampStr);
225        }
226       
227        EventEntry newEvent = new EventEntry(type, parameters, timestamp);
228       
229        boolean added = false;
230        for (int i = 0; i < sortedEvents.size(); i++) {
231            if (sortedEvents.get(i).timestamp > newEvent.timestamp) {
232                sortedEvents.add(i, newEvent);
233                added = true;
234                break;
235            }
236        }
237       
238        if (!added) {
239            sortedEvents.add(newEvent);
240        }
241
242        return true;
243    }
244
245    /**
246     * <p>
247     * dumps a parameter with the given name and value to the log file. The result is a
248     * tag named param with a name attribute and a value attribute. The value is transformed
249     * to a String if it is no String already. Furthermore, an XML entity replacement is performed
250     * if required.
251     * </p>
252     *
253     * @param name  the name of the parameter to be dumped
254     * @param value the value of the parameter to be dumped
255     */
256    private void dumpParam(String name, Object value) {
257        if (value == null) {
258            return;
259        }
260       
261        String val;
262       
263        if (value instanceof String) {
264            val = (String) value;
265        }
266        else {
267            val = String.valueOf(value);
268        }
269       
270        outputWriter.print(" <param name=\"");
271        outputWriter.print(name);
272        outputWriter.print("\" value=\"");
273        outputWriter.print(StringTools.xmlEntityReplacement(val));
274        outputWriter.println("\"/>");
275    }
276
277
278    /**
279     * <p>
280     * used to calculate a log file name. If the provided index is smaller 0, then no index
281     * is added to the file name. A filename is e.g. "htmlmonitor_12345_001.log".
282     * </p>
283     *
284     * @param index the index of the log file or -1 one, if no index shall be added
285     *
286     * @return the file name as described
287     */
288    private String getLogFileName(String clientId, int index) {
289        String result = "htmlmonitor_" + clientId;
290       
291        if (index >= 0) {
292            result += "_" + new DecimalFormat("000" ).format(index);
293        }
294       
295        result += ".log";
296       
297        return result;
298    }
299   
300    /**
301     *
302     */
303    private class EventEntry {
304       
305        /**
306         *
307         */
308        private String type;
309       
310        /**
311         *
312         */
313        private Map<String, String> parameters;
314       
315        /**
316         *
317         */
318        private long timestamp;
319
320        /**
321         * <p>
322         * TODO: comment
323         * </p>
324         *
325         * @param type
326         * @param parameters
327         * @param timestamp
328         */
329        private EventEntry(String type, Map<String, String> parameters, long timestamp) {
330            this.type = type;
331            this.parameters = parameters;
332            this.timestamp = timestamp;
333        }
334       
335        /**
336         *
337         */
338        private void dump() {
339            outputWriter.print("<event type=\"");
340            outputWriter.print(type);
341            outputWriter.println("\">");
342           
343            for (Map.Entry<String, String> param : parameters.entrySet()) {
344                dumpParam(param.getKey(), param.getValue());
345            }
346           
347            outputWriter.println("</event>");
348        }
349    }
350}
Note: See TracBrowser for help on using the repository browser.