source: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorServlet.java @ 857

Last change on this file since 857 was 857, checked in by pharms, 12 years ago
  • initial version of the HTML monitor
File size: 15.8 KB
Line 
1package de.ugoe.cs.autoquest.htmlmonitor;
2
3import java.io.IOException;
4import java.io.InputStreamReader;
5import java.net.MalformedURLException;
6import java.net.URL;
7import java.util.ArrayList;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11
12import javax.servlet.ServletException;
13import javax.servlet.http.HttpServletRequest;
14import javax.servlet.http.HttpServletResponse;
15
16import org.json.simple.JSONArray;
17import org.json.simple.JSONObject;
18import org.json.simple.JSONValue;
19import org.json.simple.parser.ParseException;
20import org.mortbay.jetty.servlet.DefaultServlet;
21
22import de.ugoe.cs.util.console.Console;
23
24/**
25 * <p>
26 * TODO comment
27 * </p>
28 *
29 * @author Patrick Harms
30 */
31public class HtmlMonitorServlet extends DefaultServlet {
32
33    /**  */
34    private static final long serialVersionUID = 1L;
35   
36    /**
37     *
38     */
39    private HtmlMonitorMessageListener messageListener;
40
41    /**
42     * <p>
43     * TODO: comment
44     * </p>
45     *
46     * @param htmlMonitoringListener
47     */
48    HtmlMonitorServlet(HtmlMonitorMessageListener messageListener) {
49        this.messageListener = messageListener;
50    }
51
52    /* (non-Javadoc)
53     * @see org.mortbay.jetty.servlet.DefaultServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
54     */
55    @Override
56    protected void doPost(HttpServletRequest request, HttpServletResponse response)
57        throws ServletException, IOException
58    {
59        Object value = null;
60        try {
61            value = JSONValue.parseWithException(new InputStreamReader(request.getInputStream()));
62           
63            if (!(value instanceof JSONObject)) {
64                Console.printerrln("incoming data is not of the expected type --> discarding it");
65            }
66            else {
67                handleJSONObject((JSONObject) value);
68            }
69        }
70        catch (ParseException e) {
71            Console.printerrln
72                ("could not parse incoming data --> discarding it (" + e.toString() + ")");
73        }
74    }
75
76    /**
77     * <p>
78     * TODO: comment
79     * </p>
80     *
81     * @param object
82     */
83    private void handleJSONObject(JSONObject object) {
84        dumpJSONObject(object, "");
85       
86        JSONObject message = assertValue(object, "message", JSONObject.class);
87       
88        if (message == null) {
89            Console.printerrln("incoming data is no valid message --> discarding it");
90        }
91        else {
92            HtmlClientInfos clientInfos = extractClientInfos(message);
93
94            if (clientInfos == null) {
95                Console.printerrln
96                    ("incoming message does not contain valid client infos --> discarding it");
97            }
98            else {
99                HtmlEvent[] events = extractHtmlEvents(message, clientInfos);
100                if (events == null) {
101                    Console.printerrln
102                    ("incoming message does not contain valid events --> discarding it");
103                }
104                else {
105                    messageListener.handleMessage(clientInfos, events);
106                }
107            }
108        }
109    }
110
111    /**
112     * <p>
113     * TODO: comment
114     * </p>
115     *
116     * @param object
117     * @return
118     */
119    private HtmlClientInfos extractClientInfos(JSONObject message) {
120        HtmlClientInfos clientInfos = null;
121       
122        JSONObject infos = assertValue(message, "clientInfos", JSONObject.class);
123       
124        if (infos != null) {
125            String clientId = assertValue((JSONObject) infos, "clientId", String.class);
126            String userAgent = assertValue((JSONObject) infos, "userAgent", String.class);
127            URL url = assertValue((JSONObject) infos, "url", URL.class);
128            String title = assertValue((JSONObject) infos, "title", String.class);
129           
130            if (clientId == null) {
131                Console.printerrln("client infos do not contain a valid client id");
132            }
133            else if (userAgent == null) {
134                Console.printerrln("client infos do not contain a valid user agent");
135            }
136            else if (url == null) {
137                Console.printerrln("client infos do not contain a valid URL");
138            }
139            else if (title == null) {
140                Console.printerrln("client infos do not contain a valid title");
141            }
142            else {
143                clientInfos = new HtmlClientInfos(clientId, userAgent, url, title);
144            }
145        }
146       
147        return clientInfos;
148    }
149
150    /**
151     * <p>
152     * TODO: comment
153     * </p>
154     *
155     * @param object
156     * @param clientInfos
157     * @return
158     */
159    private HtmlEvent[] extractHtmlEvents(JSONObject object, HtmlClientInfos clientInfos) {
160        List<HtmlEvent> events = null;
161       
162        JSONArray eventArray = assertValue(object, "events", JSONArray.class);
163       
164        if (eventArray != null) {
165            events = new ArrayList<HtmlEvent>();
166           
167            for (int i = 0; i < eventArray.size(); i++) {
168                Object eventObj = eventArray.get(i);
169                if (!(eventObj instanceof JSONObject)) {
170                    Console.printerrln("event number " + (i + 1) + " is not a valid event object");
171                }
172                else {
173                    Long time = assertValue(((JSONObject) eventObj), "time", Long.class);
174                    String path = assertValue(((JSONObject) eventObj), "path", String.class);
175                    String eventType =
176                        assertValue(((JSONObject) eventObj), "eventType", String.class);
177                    Integer[] coordinates =
178                        assertValue(((JSONObject) eventObj), "coordinates", Integer[].class);
179                    Integer key = assertValue(((JSONObject) eventObj), "key", Integer.class);
180                    Integer scrollPosition =
181                        assertValue(((JSONObject) eventObj), "scrollPosition", Integer.class);
182                   
183                    if (time == null) {
184                        Console.printerrln("event number " + (i + 1) + " has no valid timestamp");
185                    }
186                    else if (path == null) {
187                        Console.printerrln("event number " + (i + 1) + " has no valid path");
188                    }
189                    else if (eventType == null) {
190                        Console.printerrln("event number " + (i + 1) + " has no valid event type");
191                    }
192                    else if ((coordinates != null) && (coordinates.length != 2)) {
193                        Console.printerrln("event number " + (i + 1) + " has no valid coordinates");
194                    }
195                    else if (checkEventParameterCombinations
196                                (eventType, coordinates, key, scrollPosition))
197                    {
198                        events.add(new HtmlEvent(clientInfos, time, path, eventType, coordinates,
199                                                 key, scrollPosition));
200                    }
201                    else {
202                        Console.printerrln
203                            ("event number " + (i + 1) + " has no valid parameter combination");
204                    }
205                }
206            }
207           
208        }
209       
210        if ((events != null) && (events.size() > 0)) {
211            return events.toArray(new HtmlEvent[events.size()]);
212        }
213        else {
214            return null;
215        }
216    }
217
218    /**
219     * <p>
220     * TODO: comment
221     * </p>
222     *
223     * @param eventType
224     * @param coordinates
225     * @param key
226     * @param scrollPosition
227     * @return
228     */
229    private boolean checkEventParameterCombinations(String    eventType,
230                                                    Integer[] coordinates,
231                                                    Integer   key,
232                                                    Integer   scrollPosition)
233    {
234        boolean result = false;
235       
236        if ("onscroll".equals(eventType)) {
237            if ((coordinates == null) && (key == null) && (scrollPosition != null)) {
238                result = true;
239            }
240            else {
241                Console.printerrln(eventType + " event has invalid parameters");
242            }
243        }
244        else if ("onclick".equals(eventType) || "ondblclick".equals(eventType)) {
245            if ((coordinates != null) && (key == null) && (scrollPosition == null)) {
246                result = true;
247            }
248            else {
249                Console.printerrln(eventType + " event has invalid parameters");
250            }
251        }
252        else if ("onkeypress".equals(eventType) || "onkeydown".equals(eventType) ||
253                 "onkeyup".equals(eventType))
254        {
255            if ((coordinates == null) && (key != null) && (scrollPosition == null)) {
256                result = true;
257            }
258            else {
259                Console.printerrln(eventType + " event has invalid parameters");
260            }
261        }
262        else if ("onfocus".equals(eventType) || "onmouseout".equals(eventType) ||
263                 "onmousemove".equals(eventType) || "onunload".equals(eventType))
264        {
265            if ((coordinates == null) && (key == null) && (scrollPosition == null)) {
266                result = true;
267            }
268            else {
269                Console.printerrln(eventType + " event has invalid parameters");
270            }
271        }
272        else {
273            Console.printerrln("'" + eventType + "' is not a valid event type");
274        }
275       
276        return result;
277    }
278
279    /**
280     * <p>
281     * TODO: comment
282     * </p>
283     *
284     * @param object
285     * @param string
286     * @param class1
287     * @return
288     */
289    @SuppressWarnings("unchecked")
290    private <T> T assertValue(JSONObject object, String key, Class<T> clazz) {
291        Object value = object.get(key);
292        T result = null;
293       
294        if (clazz.isInstance(value)) {
295            result = (T) value;
296        }
297        else if (value instanceof String) {
298            if (URL.class.equals(clazz)) {
299                try {
300                    result = (T) new URL((String) value);
301                }
302                catch (MalformedURLException e) {
303                    e.printStackTrace();
304                    Console.printerrln("retrieved malformed URL for key '" + key + "': " + value +
305                                       " (" + e.toString() + ")");
306                }
307            }
308            else if ((int.class.equals(clazz)) || (Integer.class.equals(clazz))) {
309                try {
310                    result = (T) new Integer(Integer.parseInt((String) value));
311                }
312                catch (NumberFormatException e) {
313                    Console.printerrln
314                        ("retrieved malformed integer for key '" + key + "': " + value);
315                }
316            }
317            else if ((long.class.equals(clazz)) || (Long.class.equals(clazz))) {
318                try {
319                    result = (T) new Long(Long.parseLong((String) value));
320                }
321                catch (NumberFormatException e) {
322                    Console.printerrln
323                        ("retrieved malformed long for key '" + key + "': " + value);
324                }
325            }
326        }
327        else if (value instanceof Long) {
328            if ((int.class.equals(clazz)) || (Integer.class.equals(clazz))) {
329                result = (T) (Integer) ((Long) value).intValue();
330            }
331        }
332        else if (value instanceof JSONArray) {
333            if ((int[].class.equals(clazz)) || (Integer[].class.equals(clazz))) {
334                Integer[] resultArray = new Integer[((JSONArray) value).size()];
335                boolean allCouldBeParsed = true;
336               
337                for (int i = 0; i < ((JSONArray) value).size(); i++) {
338                    try {
339                        if (((JSONArray) value).get(i) instanceof Long) {
340                            resultArray[i] = (int) (long) (Long) ((JSONArray) value).get(i);
341                        }
342                        else if (((JSONArray) value).get(i) instanceof String) {
343                            try {
344                                resultArray[i] =
345                                    (int) Long.parseLong((String) ((JSONArray) value).get(i));
346                            }
347                            catch (NumberFormatException e) {
348                                Console.printerrln
349                                    ("retrieved malformed integer array for key '" + key + "': " +
350                                     value);
351                       
352                                allCouldBeParsed = false;
353                                break;
354                            }
355                        }
356                        else {
357                            Console.printerrln
358                                ("can not handle type of value in expected integer array '" + key +
359                                 "': " + value);
360                        }
361                    }
362                    catch (ClassCastException e) {
363                        e.printStackTrace();
364                        Console.printerrln("expected integer array for key '" + key +
365                                           "' but it was something else: " + value);
366                       
367                        allCouldBeParsed = false;
368                        break;
369                    }
370                }
371               
372                if (allCouldBeParsed) {
373                    result = (T) resultArray;
374                }
375            }
376        }
377       
378        return result;
379    }
380
381    /**
382     * <p>
383     * TODO: comment
384     * </p>
385     *
386     * @param object
387     */
388    private void dumpJSONObject(Object object, String indent) {
389        if (object instanceof JSONArray) {
390            boolean arrayContainsJSONObjects = false;
391            for (Object arrayElem : (JSONArray) object) {
392                if (arrayElem instanceof JSONObject) {
393                    arrayContainsJSONObjects = true;
394                    break;
395                }               
396            }
397           
398            if (arrayContainsJSONObjects) {
399                System.out.println();
400                System.out.print(indent);
401                System.out.println('[');
402                System.out.print(indent);
403                System.out.print(' ');
404            }
405            else {
406                System.out.print(' ');
407                System.out.print('[');
408            }
409           
410            int index = 0;
411            for (Object arrayElem : (JSONArray) object) {
412                if (index++ > 0) {
413                    System.out.print(",");
414                    if (arrayContainsJSONObjects) {
415                        System.out.println();
416                        System.out.print(indent);
417                    }
418
419                    System.out.print(' ');
420                }
421
422                dumpJSONObject(arrayElem, indent + "  ");
423            }
424           
425            if (arrayContainsJSONObjects) {
426                System.out.println();
427                System.out.print(indent);
428            }
429           
430            System.out.print(']');
431        }
432        else if (object instanceof JSONObject) {
433            System.out.println(" {");
434           
435            @SuppressWarnings("unchecked")
436            Set<Map.Entry<?,?>> entrySet = ((JSONObject) object).entrySet();
437           
438            int index = 0;
439            for (Map.Entry<?,?> entry : entrySet) {
440                if (index++ > 0) {
441                    System.out.println(",");
442                }
443                System.out.print(indent);
444                System.out.print("  \"");
445                System.out.print(entry.getKey());
446                System.out.print("\":");
447                dumpJSONObject(entry.getValue(), indent + "  ");
448            }
449           
450            System.out.println();
451            System.out.print(indent);
452            System.out.print('}');
453        }
454        else {
455            System.out.print('"');
456            System.out.print(object);
457            System.out.print('"');
458        }
459    }
460
461}
Note: See TracBrowser for help on using the repository browser.