Add ability to see raw response from server when something goes wrong. (OC Transpo...
authorChris Jaekl <cejaekl@yahoo.com>
Sun, 21 Dec 2014 03:24:59 +0000 (22:24 -0500)
committerChris Jaekl <cejaekl@yahoo.com>
Sun, 21 Dec 2014 03:24:59 +0000 (22:24 -0500)
cov.sh
go.sh
prod/net/jaekl/qd/http/InvalidResponseException.java
prod/net/jaekl/qd/http/RequestBroker.java
prod/net/jaekl/qd/util/InputStreamWrapper.java [new file with mode: 0644]
test/net/jaekl/qd/http/HttpServletRequestMock.java
test/net/jaekl/qd/http/RequestBrokerTest.java
test/net/jaekl/qd/util/InputStreamWrapperTest.java [new file with mode: 0644]

diff --git a/cov.sh b/cov.sh
index c9fb54cbc110bcf011c01f3c9ac446a0c2c9b532..a9fe22c662900aff624490fc446031892e01eed6 100755 (executable)
--- a/cov.sh
+++ b/cov.sh
@@ -4,8 +4,8 @@ INSTR_DIR="${WEB_ROOT}/../instr"
 
 #####################
 echo Compiling...
-find "${WEB_ROOT}/prod" -name "*.java" | xargs javac
-find "${WEB_ROOT}/test" -name "*.java" | xargs javac -classpath ${WEB_ROOT}/prod:${CLASSPATH}
+find "${WEB_ROOT}/prod" -name "*.java" | xargs javac -Xlint:deprecation
+find "${WEB_ROOT}/test" -name "*.java" | xargs javac -classpath ${WEB_ROOT}/prod:${CLASSPATH} -Xlint:deprecation
 
 #####################
 echo Cleaning old coverage files...
diff --git a/go.sh b/go.sh
index 5822733d970f353861b5ab23d7cc30a5b220d102..c11280489f0898a6b7320a5e6202bfb07cba2089 100755 (executable)
--- a/go.sh
+++ b/go.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 WEB_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 echo Compiling...
-find "${WEB_ROOT}/prod" -name "*.java" | xargs javac -classpath ${WEB_ROOT}/prod:${CLASSPATH}
+find "${WEB_ROOT}/prod" -name "*.java" | xargs javac -classpath ${WEB_ROOT}/prod:${CLASSPATH} -Xlint:deprecation
 cp -r ${WEB_ROOT}/prod/* ${WEB_ROOT}/WEB-INF/classes/
 find "${WEB_ROOT}/prod" -name '*.class' -exec rm {} \;
 echo Launching...
index 525772e4627789dc2e72b4c650c20f1e8f180419..7d0bdc85d8a21c5bff5c9877d7f9b820883fb874 100644 (file)
@@ -7,12 +7,14 @@ public class InvalidResponseException extends QDException {
 
        String m_url;
        String m_method;
+       String m_response;      // first few bytes of the actual response
        
-       public InvalidResponseException(String url, String method, Throwable cause) {
+       public InvalidResponseException(Throwable cause, String url, String method, String response) {
                super(cause);
                
                m_url = url;
                m_method = method;
+               m_response = response;
        }
        
        public String getUrl() { return m_url; }
@@ -20,6 +22,6 @@ public class InvalidResponseException extends QDException {
        
        @Override
        public String toString() {
-               return getClass().getName() + "; " + m_url + "/" + m_method; 
+               return getClass().getName() + "; " + m_url + "/" + m_method + "=>\"" + m_response + "\""
        }
 }
index d0630089eead3805ba77b73a2c6bcf64d873f637..b11128900be8b38d084872ab9a0dc17b864001c6 100644 (file)
@@ -8,10 +8,12 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 
 import net.jaekl.qd.QDException;
 import net.jaekl.qd.util.ExceptionUtils;
+import net.jaekl.qd.util.InputStreamWrapper;
 import net.jaekl.qd.xml.ParseErrorHandler;
 import net.jaekl.qd.xml.ParseHandler;
 import net.jaekl.qd.xml.ParseResult;
@@ -120,25 +122,31 @@ public class RequestBroker
        throws QDException
        {
                ParseResult result = null;
-               InputStream is = null;
+               InputStreamWrapper isw = null;
+               Charset utf8 = null;
                
                try {
+                       utf8 = Charset.forName(UTF_8);
                        if (null == rootTagName) {
                                result = (ParseResult) rootParserClass.newInstance();
                        } 
                        else {
                                result = (ParseResult) rootParserClass.getDeclaredConstructor(String.class).newInstance(rootTagName);
                        }
-                       is = doSubmit(method, passedParams);
+                       isw = new InputStreamWrapper(doSubmit(method, passedParams));
                        XMLReader reader = XMLReaderFactory.createXMLReader();
                        ParseHandler ph = new ParseHandler(result);
                        ParseErrorHandler peh = new ParseErrorHandler();
                        reader.setContentHandler(ph);
                        reader.setErrorHandler(peh);
-                       reader.parse(new InputSource(is));
+                       reader.parse(new InputSource(isw));
                } 
                catch ( SAXParseException saxpe ) {
-                       throw new InvalidResponseException(m_gatewayUrl, method, saxpe);
+                       String response = "<n/a>";
+                       if (null != isw) {
+                               response = new String(isw.getHeadBytes(), utf8);
+                       }
+                       throw new InvalidResponseException(saxpe, m_gatewayUrl, method, response);
                }
                catch ( InstantiationException
                                | InvocationTargetException
@@ -151,7 +159,7 @@ public class RequestBroker
                        throw new QDException(e);
                } 
                finally {
-                       ExceptionUtils.tryClose(is);
+                       ExceptionUtils.tryClose(isw);
                }
                
                return result;
diff --git a/prod/net/jaekl/qd/util/InputStreamWrapper.java b/prod/net/jaekl/qd/util/InputStreamWrapper.java
new file mode 100644 (file)
index 0000000..f78cf61
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (C) 2014 Christian Jaekl
+
+// Wrap an inputstream, keeping a copy of the first few bytes that are read from it.
+// This is useful when passing an inputstream directly to the SAX parser, 
+// because we may want to do a post-mortem examination of the input being parsed
+// if parsing fails.
+
+package net.jaekl.qd.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+public class InputStreamWrapper extends InputStream {
+       final static int HEAD_MAX = 1024;
+       
+       InputStream m_is;       // the stream being wrapped
+       byte[] m_head;          // the first (up to HEAD_MAX) bytes that were read from the wrapped stream
+       int    m_headBytes;     // number of bytes stored in m_head
+       
+       public InputStreamWrapper(InputStream is)
+       {
+               super();
+               
+               m_is = is;
+               m_head = new byte[HEAD_MAX];
+               m_headBytes = 0;
+       }
+       
+       @Override
+       public int read() throws IOException {
+               int b = m_is.read();
+               if ((-1) == b) {
+                       // end-of-stream
+                       return b;
+               }
+               if (m_headBytes < HEAD_MAX) {
+                       m_head[m_headBytes] = (byte)b;
+                       m_headBytes++;
+               }
+               return b;
+       }
+       
+       public byte[] getHeadBytes() {
+               return Arrays.copyOf(m_head, m_headBytes);
+       }
+}
index 8d22b03a123d0b1287efef14ac87d638c615d001..456bc822f53e412db403ddf1322c4bd63d9b1c13 100644 (file)
@@ -125,6 +125,7 @@ public class HttpServletRequestMock implements HttpServletRequest {
                return null;
        }
 
+       @Deprecated
        @Override
        public String getRealPath(String arg0) {
                // TODO Auto-generated method stub
@@ -330,6 +331,7 @@ public class HttpServletRequestMock implements HttpServletRequest {
                return false;
        }
 
+       @Deprecated
        @Override
        public boolean isRequestedSessionIdFromUrl() {
                // TODO Auto-generated method stub
index 2892a1775dc667afd5deb76ed13e64d841d930ee..307fe90f93ba675d6d47fef394fd5ca94f31bd62 100644 (file)
@@ -189,7 +189,8 @@ public class RequestBrokerTest {
        
        @Test
        public void test_submitAndParse_throwsInvalidResultException() {
-               InvalidResponseException ire = new InvalidResponseException(GATEWAY, METHOD, new SAXParseException("dummy", null));
+               final String RESPONSE = "Must specify stop number.";
+               InvalidResponseException ire = new InvalidResponseException(new SAXParseException("dummy", null), GATEWAY, METHOD, RESPONSE);
                ArrayList<NameValuePair> emptyParams = new ArrayList<NameValuePair>();
                
                RequestBrokerMock rbm = new RequestBrokerMock(GATEWAY, emptyParams);
@@ -204,6 +205,7 @@ public class RequestBrokerTest {
                        Assert.assertEquals(ire, qde);
                        Assert.assertTrue(ire.toString().contains(GATEWAY));
                        Assert.assertTrue(ire.toString().contains(METHOD));
+                       Assert.assertTrue(ire.toString().contains(RESPONSE));
                }
        }
 }
diff --git a/test/net/jaekl/qd/util/InputStreamWrapperTest.java b/test/net/jaekl/qd/util/InputStreamWrapperTest.java
new file mode 100644 (file)
index 0000000..9db2252
--- /dev/null
@@ -0,0 +1,90 @@
+package net.jaekl.qd.util;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class InputStreamWrapperTest {
+       static final String[] DATA = {
+               "", 
+               "<?xml encoding=\"utf-8\"?><Root/>\n",
+               "古池\n蛙飛び込む\n水の音\n",
+               
+               "arma virumque cano, Troiae qui primus ab oris\n" +
+               "Italiam fato profugus Laviniaque venit\n" +
+               "litora, multum ille et terris iactatus et alto\n" +
+               "vi superum, saevae memorem Iunonis ob iram,\n" +
+               "multa quoque et bello passus, dum conderet urbem\n" +
+               "inferretque deos Latio; genus unde\n" +
+               "Albanique patres atque altae moenia Romae.\n" +
+               "Musa, mihi causas memora, quo numine laeso\n" +
+               "quidve dolens regina deum tot volvere\n" +
+               "insignem pietate virum, tot adire labores\n" +
+               "impulerit. tantaene animis caelestibus irae?\n" +
+               "urbs antiqua fuit (Tyrii tenuere coloni)\n" +
+               "Karthago, Italiam contra Tiberinaque longe\n" +
+               "ostia, dives opum studiisque asperrima belli,\n" +
+               "quam Iuno fertur terris magis omnibus unam\n" +
+               "posthabita coluisse Samo. hic illius arma,\n" +
+               "hic currus fuit; hoc regnum dea gentibus esse,\n" +
+               "si qua fata sinant, iam tum tenditque fovetque.\n" +
+               "progeniem sed enim Troiano a sanguine duci\n" +
+               "audierat Tyrias olim quae verteret arces;\n" +
+               "hinc populum late regem belloque superbum\n" +
+               "venturum excidio Libyae; sic volvere Parcas.\n" +
+               "id metuens veterisque memor Saturnia belli,\n" +
+               "prima quod ad Troiam pro caris gesserat Argis_\n" +
+               "necdum etiam causae irarum saevique dolores\n" +
+               "exciderant animo; manet alta mente repostum\n" +
+               "iudicium Paridis spretaeque iniuria formae\n" +
+               "et genus invisum et rapti Ganymedis honores:\n" +
+               "his accensa super iactatos aequore toto\n" +
+               "Troas, reliquias Danaum atque immitis Achilli,\n" +
+               "arcebat longe Latio, multosque per annos\n" +
+               "errabant acti fatis maria omnia circum.\n"
+       };
+
+       @Test
+       public void testRead() throws IOException {
+               Charset utf8 = Charset.forName("UTF-8");
+               StringBuilder sb = new StringBuilder();
+               ByteArrayInputStream bais = null;
+               BufferedReader br = null;
+               String line;
+               for (String datum : DATA) {
+                       sb.setLength(0);        // reset the builder
+                       int end = datum.length();
+                       if (end > InputStreamWrapper.HEAD_MAX) {
+                               end = InputStreamWrapper.HEAD_MAX;
+                       }
+                       String expected = datum.substring(0, end);
+                       
+                       try {
+                               bais = new ByteArrayInputStream(datum.getBytes(utf8));
+                               InputStreamWrapper isw = new InputStreamWrapper(bais);
+                               br = new BufferedReader(new InputStreamReader(isw));
+                               line = br.readLine();
+                               while (null != line) {
+                                       sb.append(line).append("\n");
+                                       line = br.readLine();
+                               }
+                               String actualRead = sb.toString();
+                               String actualHead = new String(isw.getHeadBytes(), utf8);
+
+                               Assert.assertEquals(datum, actualRead);
+                               Assert.assertEquals(expected, actualHead);
+                       }
+                       finally {
+                               if (null != br) {
+                                       br.close();
+                               }
+                       }
+               }
+       }
+}