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

Last change on this file since 1206 was 1206, checked in by ftrautsch, 11 years ago

added some comments

File size: 31.7 KB
RevLine 
[927]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
[857]15package de.ugoe.cs.autoquest.htmlmonitor;
16
[879]17import java.io.BufferedReader;
[1019]18import java.io.FileNotFoundException;
[857]19import java.io.IOException;
[879]20import java.io.InputStream;
[857]21import java.io.InputStreamReader;
[879]22import java.io.PrintWriter;
[857]23import java.net.MalformedURLException;
24import java.net.URL;
25import java.util.ArrayList;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29
30import javax.servlet.ServletException;
31import javax.servlet.http.HttpServletRequest;
32import javax.servlet.http.HttpServletResponse;
33
34import org.json.simple.JSONArray;
35import org.json.simple.JSONObject;
36import org.json.simple.JSONValue;
37import org.json.simple.parser.ParseException;
38import org.mortbay.jetty.servlet.DefaultServlet;
39
[1019]40import de.ugoe.cs.util.FileTools;
[857]41import de.ugoe.cs.util.console.Console;
42
43/**
44 * <p>
[879]45 * the servlet deployed in the web server that receives all client messages and returns the client
46 * java script. The messages are parsed, validated, and forwarded to the provided message listener.
47 * If a message is not valid, it is discarded. If an event in a message is not valid, it is
48 * discarded. Messages are only received via the POST HTTP method. The GET HTTP method is only
49 * implemented for returning the client java script.
[857]50 * </p>
51 *
52 * @author Patrick Harms
53 */
[871]54class HtmlMonitorServlet extends DefaultServlet {
[857]55
56    /**  */
57    private static final long serialVersionUID = 1L;
58   
[879]59    /**  */
60    private static final boolean DO_TRACE = false;
61   
[857]62    /**
[1019]63     * <p>
64     * Name and path of the robot filter.
65     * </p>
66     */
67    private static final String ROBOTFILTERFILE = "data/robots/robotfilter.txt";
68
69    /**
70     * <p>
71     * Field that contains a regular expression that matches all robots
72     * contained in {@link #ROBOTFILTERFILE}.
73     * </p>
74     */
75    private String robotRegex = null;
76
77    /**
[871]78     * the message listener to forward received messages to.
[857]79     */
[1075]80    private transient HtmlGUIElementManager guiElementManager = new HtmlGUIElementManager();
[1069]81
82    /**
83     * the message listener to forward received messages to.
84     */
[873]85    private transient HtmlMonitorMessageListener messageListener;
[857]86
87    /**
88     * <p>
[871]89     * initializes the servlet with the message listener to which all events shall be forwarded
[857]90     * </p>
91     *
[871]92     * @param messageListener the message listener that shall receive all client events
[857]93     */
94    HtmlMonitorServlet(HtmlMonitorMessageListener messageListener) {
95        this.messageListener = messageListener;
[1019]96        try {
97            loadRobotRegex();
98        }
99        catch (Exception e) {
100            Console.println
101                ("robot filtering disabled: could not parse robot filter file " + ROBOTFILTERFILE);
102        }
[857]103    }
104
[1206]105    /**
106     * this implements handling of doGet. For this servlet this means that
107     * the autoquest-htmlmonitor.js will be delivered to the instance
108     * which sent the get request.
109     *
110     *
111     *  (non-Javadoc)
[879]112     * @see org.mortbay.jetty.servlet.DefaultServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
113     */
114    @Override
115    protected void doGet(HttpServletRequest request, HttpServletResponse response)
116        throws ServletException, IOException
117    {
118        if ((request.getPathInfo() != null) &&
119            (request.getPathInfo().endsWith("/script/autoquest-htmlmonitor.js")))
120        {
[884]121            BufferedReader reader = null;
[879]122           
123            try {
[884]124                InputStream script = this.getClass().getClassLoader().getResourceAsStream
[879]125                     ("autoquest-htmlmonitor.js");
[1019]126               
[879]127                if (script == null) {
128                    Console.printerrln("could not read autoquest-htmlmonitor.js from classpath");
129                }
130                else {
[884]131                    reader = new BufferedReader(new InputStreamReader(script, "UTF-8"));
[879]132                    PrintWriter output = response.getWriter();
133                    String line;
134                   
135                    while ((line = reader.readLine()) != null) {
136                        output.println(line);
137                    }
138                   
139                    output.close();
140                }
141            }
142            catch (Exception e) {
143                Console.printerrln("could not read autoquest-htmlmonitor.js from classpath: " + e);
144                Console.logException(e);
145            }
146            finally {
[884]147                if (reader != null) {
148                    reader.close();
[879]149                }
150            }
151        }
152    }
153
[1206]154    /**
155     * this implements handling of doPost. For this servlet this means that
156     * the data from the post request will be parsed and validated.
157     *
158     * (non-Javadoc)
[857]159     * @see org.mortbay.jetty.servlet.DefaultServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
160     */
161    @Override
162    protected void doPost(HttpServletRequest request, HttpServletResponse response)
163        throws ServletException, IOException
164    {
165        Object value = null;
166        try {
[1019]167            //InputStream requestInputStream = dumpStreamContent(request.getInputStream());
[998]168            InputStream requestInputStream = request.getInputStream();
[1019]169
[872]170            value = JSONValue.parseWithException
[998]171                (new InputStreamReader(requestInputStream, "UTF-8"));
[857]172           
173            if (!(value instanceof JSONObject)) {
174                Console.printerrln("incoming data is not of the expected type --> discarding it");
175            }
176            else {
177                handleJSONObject((JSONObject) value);
178            }
179        }
180        catch (ParseException e) {
181            Console.printerrln
182                ("could not parse incoming data --> discarding it (" + e.toString() + ")");
183        }
184    }
185
186    /**
187     * <p>
[871]188     * processes a received JSON object and validates it. If the message is ok, it is forwarded
189     * to the message listener
[857]190     * </p>
191     *
[871]192     * @param object the JSON object that contains a client message
[857]193     */
194    private void handleJSONObject(JSONObject object) {
[879]195        if (DO_TRACE) {
196            dumpJSONObject(object, "");
197        }
[857]198       
199        JSONObject message = assertValue(object, "message", JSONObject.class);
200       
201        if (message == null) {
202            Console.printerrln("incoming data is no valid message --> discarding it");
203        }
204        else {
205            HtmlClientInfos clientInfos = extractClientInfos(message);
206
207            if (clientInfos == null) {
208                Console.printerrln
209                    ("incoming message does not contain valid client infos --> discarding it");
210            }
[1019]211            else if (isRobot(clientInfos.getUserAgent())) {
212                Console.printerrln
213                    ("ignoring robot " + clientInfos.getUserAgent());
214            }
[857]215            else {
[1069]216                HtmlGUIElement guiStructure = extractHtmlPageElements(message, clientInfos);
[857]217                HtmlEvent[] events = extractHtmlEvents(message, clientInfos);
[1019]218               
[857]219                if (events == null) {
220                    Console.printerrln
[998]221                        ("incoming message does not contain valid events --> discarding it");
[857]222                }
223                else {
[1019]224                    messageListener.handleMessage(clientInfos, guiStructure, events);
[857]225                }
226            }
227        }
228    }
229
230    /**
231     * <p>
[871]232     * tries to extract the client infos out of the received JSON object. If this is not fully
233     * possible, an appropriate message is dumped and the whole message is discarded (the method
234     * return null).
[857]235     * </p>
236     *
[871]237     * @param message the message to parse the client infos from
238     *
239     * @return the client infos, if the message is valid in this respect, or null if not
[857]240     */
241    private HtmlClientInfos extractClientInfos(JSONObject message) {
242        HtmlClientInfos clientInfos = null;
243       
244        JSONObject infos = assertValue(message, "clientInfos", JSONObject.class);
245       
246        if (infos != null) {
247            String clientId = assertValue((JSONObject) infos, "clientId", String.class);
248            String userAgent = assertValue((JSONObject) infos, "userAgent", String.class);
249            URL url = assertValue((JSONObject) infos, "url", URL.class);
250            String title = assertValue((JSONObject) infos, "title", String.class);
251           
252            if (clientId == null) {
253                Console.printerrln("client infos do not contain a valid client id");
254            }
255            else if (userAgent == null) {
256                Console.printerrln("client infos do not contain a valid user agent");
257            }
258            else if (url == null) {
259                Console.printerrln("client infos do not contain a valid URL");
260            }
261            else if (title == null) {
262                Console.printerrln("client infos do not contain a valid title");
263            }
264            else {
265                clientInfos = new HtmlClientInfos(clientId, userAgent, url, title);
266            }
267        }
268       
269        return clientInfos;
270    }
271
272    /**
273     * <p>
[871]274     * tries to extract the events out of the received JSON object. If this is not fully
275     * possible, an appropriate message is dumped and the errorprone event is discarded. If no
276     * valid event is found, the whole message is discarded.
[857]277     * </p>
278     *
[871]279     * @param object      the message to parse the events from
280     * @param clientInfos the infos about the client that send the events
281     * 
282     * @return the valid events stored in the message, or null if there are none
[857]283     */
284    private HtmlEvent[] extractHtmlEvents(JSONObject object, HtmlClientInfos clientInfos) {
285        List<HtmlEvent> events = null;
286       
287        JSONArray eventArray = assertValue(object, "events", JSONArray.class);
288       
289        if (eventArray != null) {
290            events = new ArrayList<HtmlEvent>();
291           
[1069]292            HtmlServer server = getServerElement(clientInfos);
293            HtmlDocument document = getPageElementRepresentingWebPage(clientInfos, server);
[1019]294
[857]295            for (int i = 0; i < eventArray.size(); i++) {
296                Object eventObj = eventArray.get(i);
297                if (!(eventObj instanceof JSONObject)) {
298                    Console.printerrln("event number " + (i + 1) + " is not a valid event object");
299                }
300                else {
301                    Long time = assertValue(((JSONObject) eventObj), "time", Long.class);
[1069]302                    String domPath = assertValue(((JSONObject) eventObj), "path", String.class);
[857]303                    String eventType =
304                        assertValue(((JSONObject) eventObj), "eventType", String.class);
305                    Integer[] coordinates =
306                        assertValue(((JSONObject) eventObj), "coordinates", Integer[].class);
307                    Integer key = assertValue(((JSONObject) eventObj), "key", Integer.class);
[942]308                    Integer[] scrollPosition =
309                        assertValue(((JSONObject) eventObj), "scrollPosition", Integer[].class);
310                    String selectedValue =
311                            assertValue(((JSONObject) eventObj), "selectedValue", String.class);
[857]312                   
[998]313                    if (eventType == null) {
314                        Console.printerrln("event number " + (i + 1) + " has no valid event type");
[857]315                    }
[998]316                    else if (time == null) {
317                        Console.printerrln(eventType + " event has no valid timestamp");
318                    }
[1069]319                    else if (domPath == null) {
320                        Console.printerrln(eventType + " event has no valid DOM path");
[857]321                    }
322                    else if ((coordinates != null) && (coordinates.length != 2)) {
[998]323                        Console.printerrln(eventType + " event has no valid coordinates");
[857]324                    }
325                    else if (checkEventParameterCombinations
[1174]326                                (eventType, coordinates, key, scrollPosition, selectedValue, domPath))
[857]327                    {
[1069]328                        HtmlPageElement target =
329                            guiElementManager.getPageElement(document, domPath);
330                       
331                        if (target != null) {
332                            events.add(new HtmlEvent(clientInfos, time, target, eventType,
333                                                     coordinates, key, scrollPosition,
334                                                     selectedValue));
335                        }
336                        else {
337                            events.add(new HtmlEvent(clientInfos, time, document, domPath,
338                                                     eventType, coordinates, key, scrollPosition,
339                                                     selectedValue));
340                        }
[857]341                    }
342                    else {
[998]343                        Console.printerrln(eventType + " event has no valid parameter combination");
[857]344                    }
345                }
346            }
347           
348        }
349       
350        if ((events != null) && (events.size() > 0)) {
351            return events.toArray(new HtmlEvent[events.size()]);
352        }
353        else {
354            return null;
355        }
356    }
357
358    /**
359     * <p>
[1022]360     * extracts the GUI structure from the provided JSON object.
[1019]361     * </p>
362     *
[1022]363     * @param object      the JSON object to extract the GUI structure from
364     * @param clientInfos infos about the client who send the data
365     *
366     * @return the GUI structure extracted from the JSON object of which the root node is a
367     *         representation of the server of the HTML page that was observed
[1019]368     */
[1069]369    private HtmlServer extractHtmlPageElements(JSONObject      object,
370                                               HtmlClientInfos clientInfos)
[1019]371    {
[1069]372        HtmlServer server = getServerElement(clientInfos);
373        HtmlDocument document = getPageElementRepresentingWebPage(clientInfos, server);
[1019]374
375        JSONObject jsonPageElement = assertValue(object, "guiModel", JSONObject.class);
[1069]376        document.addChild(convert(jsonPageElement, document, null));
[1019]377       
378        return server;
379    }
380
381    /**
382     * <p>
[1022]383     * instantiates an element of the GUI structure representing the server of the observed
384     * web page
[1019]385     * </p>
386     *
[1022]387     * @param clientInfos infos about the client who send the data
388     *
389     * @return as described
[1019]390     */
[1069]391    private HtmlServer getServerElement(HtmlClientInfos clientInfos) {
392        String host = clientInfos.getUrl().getHost();
393        int port = 80;
394       
[1019]395        if (clientInfos.getUrl().getPort() > -1) {
[1069]396            port = clientInfos.getUrl().getPort();
[1019]397        }
398       
[1069]399        return guiElementManager.createHtmlServer(host, port);
[1019]400    }
401
402    /**
403     * <p>
[1022]404     * instantiates an element of the GUI structure representing the observed web page. Adds
405     * this element to the provided server as child.
[1019]406     * </p>
407     *
[1022]408     * @param clientInfos infos about the client who send the data
409     * @param server      the server on which the page represented by the return value resists
410     *
411     * @return as described
[1019]412     */
[1069]413    private HtmlDocument getPageElementRepresentingWebPage(HtmlClientInfos clientInfos,
414                                                           HtmlServer      server)
[1019]415    {
[1069]416        String path = clientInfos.getUrl().getPath();
417        String query = null;
[1019]418       
419        if (clientInfos.getUrl().getQuery() != null) {
[1069]420            query = "?" + clientInfos.getUrl().getQuery();
[1019]421        }
422       
[1069]423        HtmlDocument document = guiElementManager.createHtmlDocument
424            (server, path, query, clientInfos.getTitle());
[1019]425       
[1069]426        server.addChild(document);
[1019]427       
[1069]428        return document;
[1019]429    }
430
431    /**
432     * <p>
[1022]433     * converts a JSON object representing an HTML page element to an HTML page element. Calls
434     * itself recursively to also convert the children of the element, if any.
[1019]435     * </p>
436     *
[1022]437     * @param jsonPageElement the JSON object to be converted
[1069]438     * @param document        the document to which the page element belongs
439     * @param parent          the parent page element of the converted element, of null, if none
440     *                        is present. In this case the document is considered the parent
441     *                        element.
[1022]442     *                       
443     * @return as described.
[1019]444     */
[1069]445    private HtmlPageElement convert(JSONObject      jsonPageElement,
446                                    HtmlDocument    document,
447                                    HtmlPageElement parent)
448    {
[1019]449        HtmlPageElement result = null;
450
451        if (jsonPageElement != null) {
452            String tagName = assertValue(jsonPageElement, "tagName", String.class);
[1069]453            String htmlid = assertValue(jsonPageElement, "htmlId", String.class);
[1019]454            Integer index = assertValue(jsonPageElement, "index", Integer.class);
455
[1069]456            result = guiElementManager.createHtmlPageElement
457                (document, parent, tagName, htmlid, index);
[1019]458
459            JSONArray childElements = assertValue(jsonPageElement, "children", JSONArray.class);
460           
461            if (childElements != null) {
462                Object jsonChild;
463
464                for (int i = 0; i < childElements.size(); i++) {
465                    jsonChild = childElements.get(i);
466                    if (!(jsonChild instanceof JSONObject)) {
467                        Console.printerrln("child " + (i + 1) + " of HTML page element " + tagName +
468                                           " is no valid HTML page element");
469                    }
470                    else {
[1069]471                        result.addChild(convert((JSONObject) jsonChild, document, result));
[1019]472                    }
473                }
474            }
475           
476        }
477       
478        return result;   
479    }
480
481    /**
482     * <p>
[942]483     * validates if for the given event type the parameter combination of coordinates, key,
484     * scroll position, and selected value is valid. As an example, an onclick event should
485     * usually not have an associated scroll position.
[857]486     * </p>
487     *
[871]488     * @param eventType      the type of the event
489     * @param coordinates    the coordinates of the event
490     * @param key            the key of the event
491     * @param scrollPosition the scroll position of the event
[942]492     * @param selectedValue  the value selected through a specific event
[1174]493     * @param domPath            the path through the DOM of the document of the HTML element on which
494     *                       the event was executed
[871]495     *
496     * @return true, if the combination of the parameters is valid, false else
[857]497     */
498    private boolean checkEventParameterCombinations(String    eventType,
499                                                    Integer[] coordinates,
500                                                    Integer   key,
[942]501                                                    Integer[] scrollPosition,
[1174]502                                                    String    selectedValue,
503                                                    String    domPath)
[857]504    {
505        boolean result = false;
506       
507        if ("onscroll".equals(eventType)) {
[942]508            if ((coordinates == null) && (key == null) &&
509                (scrollPosition != null) && (selectedValue == null))
510            {
[857]511                result = true;
512            }
513            else {
514                Console.printerrln(eventType + " event has invalid parameters");
515            }
516        }
517        else if ("onclick".equals(eventType) || "ondblclick".equals(eventType)) {
[942]518            if ((coordinates != null) && (key == null) &&
519                (scrollPosition == null) && (selectedValue == null))
520            {
[857]521                result = true;
522            }
523            else {
524                Console.printerrln(eventType + " event has invalid parameters");
525            }
526        }
[942]527        else if ("onchange".equals(eventType)) {
[1174]528            // "input_password" dont have a selectedValue
529            if (domPath.contains("input_password")) {
530                if ((coordinates == null) && (key == null) && (scrollPosition == null)) {
531                    result = true;
532                }
533                else {
534                    Console.printerrln(eventType + " event has invalid parameters");
535                }
[942]536            }
537            else {
[1174]538                if ((coordinates == null) && (key == null) && (scrollPosition == null) &&
539                    (selectedValue != null))
540                {
541                    result = true;
542                }
543                else {
544                    Console.printerrln(eventType + " event has invalid parameters");
545                }
[942]546            }
[1174]547
[942]548        }
[857]549        else if ("onkeypress".equals(eventType) || "onkeydown".equals(eventType) ||
550                 "onkeyup".equals(eventType))
551        {
[942]552            if ((coordinates == null) && (key != null) &&
553                (scrollPosition == null) && (selectedValue == null))
554            {
[857]555                result = true;
556            }
557            else {
558                Console.printerrln(eventType + " event has invalid parameters");
559            }
560        }
561        else if ("onfocus".equals(eventType) || "onmouseout".equals(eventType) ||
[998]562                 "onmousemove".equals(eventType) || "onload".equals(eventType) ||
563                 "onunload".equals(eventType) || "onbeforeunload".equals(eventType) ||
564                 "onpagehide".equals(eventType) || "onpageshow".equals(eventType) ||
565                 "onabort".equals(eventType) || "onsubmit".equals(eventType) ||
566                 "onplaying".equals(eventType) || "onpause".equals(eventType) ||
567                 "ontimeupdate".equals(eventType) || "onerror".equals(eventType) ||
568                 "onundo".equals(eventType) || "onreset".equals(eventType) ||
569                 "onselect".equals(eventType))
[857]570        {
[942]571            if ((coordinates == null) && (key == null) &&
572                (scrollPosition == null) && (selectedValue == null))
573            {
[857]574                result = true;
575            }
576            else {
577                Console.printerrln(eventType + " event has invalid parameters");
578            }
579        }
580        else {
581            Console.printerrln("'" + eventType + "' is not a valid event type");
582        }
583       
584        return result;
585    }
586
587    /**
588     * <p>
[871]589     * converts a value in the provided object matching the provided key to the provided type. If
590     * there is no value with the key or if the value can not be transformed to the provided type,
591     * the method returns null.
[857]592     * </p>
593     *
[871]594     * @param object the object to read the value from
595     * @param key    the key of the value
596     * @param clazz  the type to which the value should be transformed
597     *
598     * @return the value or null if either the value does not exist or if it can not be transformed
599     *         to the expected type
[857]600     */
601    @SuppressWarnings("unchecked")
602    private <T> T assertValue(JSONObject object, String key, Class<T> clazz) {
603        Object value = object.get(key);
604        T result = null;
605       
606        if (clazz.isInstance(value)) {
607            result = (T) value;
608        }
609        else if (value instanceof String) {
610            if (URL.class.equals(clazz)) {
611                try {
612                    result = (T) new URL((String) value);
613                }
614                catch (MalformedURLException e) {
615                    e.printStackTrace();
616                    Console.printerrln("retrieved malformed URL for key '" + key + "': " + value +
617                                       " (" + e.toString() + ")");
618                }
619            }
620            else if ((int.class.equals(clazz)) || (Integer.class.equals(clazz))) {
621                try {
[872]622                    result = (T) Integer.valueOf(Integer.parseInt((String) value));
[857]623                }
624                catch (NumberFormatException e) {
625                    Console.printerrln
626                        ("retrieved malformed integer for key '" + key + "': " + value);
627                }
628            }
629            else if ((long.class.equals(clazz)) || (Long.class.equals(clazz))) {
630                try {
[872]631                    result = (T) Long.valueOf(Long.parseLong((String) value));
[857]632                }
633                catch (NumberFormatException e) {
634                    Console.printerrln
635                        ("retrieved malformed long for key '" + key + "': " + value);
636                }
637            }
638        }
639        else if (value instanceof Long) {
640            if ((int.class.equals(clazz)) || (Integer.class.equals(clazz))) {
641                result = (T) (Integer) ((Long) value).intValue();
642            }
643        }
644        else if (value instanceof JSONArray) {
645            if ((int[].class.equals(clazz)) || (Integer[].class.equals(clazz))) {
646                Integer[] resultArray = new Integer[((JSONArray) value).size()];
647                boolean allCouldBeParsed = true;
648               
649                for (int i = 0; i < ((JSONArray) value).size(); i++) {
650                    try {
651                        if (((JSONArray) value).get(i) instanceof Long) {
652                            resultArray[i] = (int) (long) (Long) ((JSONArray) value).get(i);
653                        }
654                        else if (((JSONArray) value).get(i) instanceof String) {
655                            try {
656                                resultArray[i] =
657                                    (int) Long.parseLong((String) ((JSONArray) value).get(i));
658                            }
659                            catch (NumberFormatException e) {
660                                Console.printerrln
661                                    ("retrieved malformed integer array for key '" + key + "': " +
662                                     value);
663                       
664                                allCouldBeParsed = false;
665                                break;
666                            }
667                        }
668                        else {
669                            Console.printerrln
670                                ("can not handle type of value in expected integer array '" + key +
671                                 "': " + value);
672                        }
673                    }
674                    catch (ClassCastException e) {
675                        e.printStackTrace();
676                        Console.printerrln("expected integer array for key '" + key +
677                                           "' but it was something else: " + value);
678                       
679                        allCouldBeParsed = false;
680                        break;
681                    }
682                }
683               
684                if (allCouldBeParsed) {
685                    result = (T) resultArray;
686                }
687            }
688        }
689       
690        return result;
691    }
692
693    /**
694     * <p>
[1019]695     * Checks whether an agent is a robot.
696     * </p>
697     *
698     * @param agent
699     *            agent that is checked
700     * @return true, if the agent is a robot; false otherwise
701     */
702    private boolean isRobot(String agent) {
703        return agent.matches(robotRegex);
704    }
705
706    /**
707     * <p>
708     * Reads {@link #ROBOTFILTERFILE} and creates a regular expression that
709     * matches all the robots defined in the file. The regular expression is
710     * stored in the field {@link #robotRegex}.
711     * </p>
712     *
713     * @throws IOException
714     *             thrown if there is a problem reading the robot filter
715     * @throws FileNotFoundException
716     *             thrown if the robot filter is not found
717     */
718    private void loadRobotRegex() throws IOException, FileNotFoundException {
719        String[] lines = FileTools.getLinesFromFile(ROBOTFILTERFILE);
720        StringBuilder regex = new StringBuilder();
721        for (int i = 0; i < lines.length; i++) {
722            regex.append("(.*" + lines[i] + ".*)");
723            if (i != lines.length - 1) {
724                regex.append('|');
725            }
726        }
727        robotRegex = regex.toString();
728    }
729
730    /**
731     * <p>
[998]732     * convenience method for dumping the content of a stream and returning a new stream
733     * containing the same data.
734     * </p>
735     *
736     * @param inputStream the stream to be dumped and copied
737     * @return the copy of the stream
738     *
739     * @throws IOException if the stream can not be read
740     */
[1019]741/*    private InputStream dumpStreamContent(ServletInputStream inputStream) throws IOException {
[998]742        List<Byte> bytes = new ArrayList<Byte>();
743        int buf;
744       
745        while ((buf = inputStream.read()) >= 0) {
746            bytes.add((byte) buf);
747        }
748       
749        byte[] byteArray = new byte[bytes.size()];
750        for (int i = 0; i < bytes.size(); i++) {
751            byteArray[i] = bytes.get(i);
752        }
753       
754        System.out.println(new String(byteArray, "UTF-8"));
755       
756        return new ByteArrayInputStream(byteArray);
757    }*/
758
759    /**
760     * <p>
[871]761     * convenience method for dumping an object to std out. If the object is a JSON object, it is
762     * deeply analyzed and its internal structure is dumped as well.
[857]763     * </p>
764     *
[871]765     * @param object the object to dump
766     * @param indent the indentation to be used.
[857]767     */
768    private void dumpJSONObject(Object object, String indent) {
769        if (object instanceof JSONArray) {
770            boolean arrayContainsJSONObjects = false;
771            for (Object arrayElem : (JSONArray) object) {
772                if (arrayElem instanceof JSONObject) {
773                    arrayContainsJSONObjects = true;
774                    break;
775                }               
776            }
777           
778            if (arrayContainsJSONObjects) {
779                System.out.println();
780                System.out.print(indent);
781                System.out.println('[');
782                System.out.print(indent);
783                System.out.print(' ');
784            }
785            else {
786                System.out.print(' ');
787                System.out.print('[');
788            }
789           
790            int index = 0;
791            for (Object arrayElem : (JSONArray) object) {
792                if (index++ > 0) {
793                    System.out.print(",");
794                    if (arrayContainsJSONObjects) {
795                        System.out.println();
796                        System.out.print(indent);
797                    }
798
799                    System.out.print(' ');
800                }
801
802                dumpJSONObject(arrayElem, indent + "  ");
803            }
804           
805            if (arrayContainsJSONObjects) {
806                System.out.println();
807                System.out.print(indent);
808            }
809           
810            System.out.print(']');
811        }
812        else if (object instanceof JSONObject) {
813            System.out.println(" {");
814           
815            @SuppressWarnings("unchecked")
816            Set<Map.Entry<?,?>> entrySet = ((JSONObject) object).entrySet();
817           
818            int index = 0;
819            for (Map.Entry<?,?> entry : entrySet) {
820                if (index++ > 0) {
821                    System.out.println(",");
822                }
823                System.out.print(indent);
824                System.out.print("  \"");
825                System.out.print(entry.getKey());
826                System.out.print("\":");
827                dumpJSONObject(entry.getValue(), indent + "  ");
828            }
829           
830            System.out.println();
831            System.out.print(indent);
832            System.out.print('}');
833        }
834        else {
835            System.out.print('"');
836            System.out.print(object);
837            System.out.print('"');
838        }
839    }
840
841}
Note: See TracBrowser for help on using the repository browser.