2013-10-12 8 views
1

У меня есть статический вспомогательный метод, отвечающий за получение сжатой строки JSON из нашего приложения Rails и декомпрессии данных перед возвратом представления String.GZIPInputStream - поврежденный трейлер GZIP

Я написал два теста JUnit, которые проверяют правильность разбора JSON, и еще один базовый тест, который определяет, возвращается ли строка с длиной больше нуля с сервера.

Проблема: При запуске тестового набора, то первый метод испытания успешно правильно, а другой терпит неудачу с IOException и сообщением «Corrupt GZIP трейлера» (см код ниже). Я решил, что это не сам тест, который терпит неудачу, так как «успешный» тест отменяется, когда я запускаю тесты в противоположном порядке (другими словами, несмотря ни на что, это всегда второй тест, который терпит неудачу, независимо от того, какой из двух тестов выполняется вторым).

Это вспомогательный метод:

public static String doHTTPGet(String urlString) throws IOException{ 
    URL weatherAPI = new URL(urlString); 
    HttpURLConnection apiConnection = (HttpURLConnection) weatherAPI.openConnection(); 
    apiConnection.setRequestMethod("GET"); 
    apiConnection.setRequestProperty("Accept-Encoding", "gzip"); 

    apiConnection.connect(); 

    BufferedInputStream bufferedInputStream = new BufferedInputStream(apiConnection.getInputStream()); 
    byte[] inputByteBuffer = new byte[10 * 1024]; 
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(10 * 1024); // initialize the output stream with at least one buffer's worth of bytes 
    while(bufferedInputStream.read(inputByteBuffer) > -1){ 
     outputStream.write(inputByteBuffer); 
    } 

    outputStream.close(); 
    bufferedInputStream.close(); 
    apiConnection.disconnect(); 

    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(outputStream.toByteArray()); 
    byteArrayInputStream.close(); 

    GZIPInputStream gis = new GZIPInputStream(byteArrayInputStream); 
    InputStreamReader inputStreamReader = new InputStreamReader(gis, "UTF-8"); 
    BufferedReader reader = new BufferedReader(inputStreamReader); 

    String decompressedResponse = ""; 
    String line; 

    // readLine() is generating the IOException on the second pass. 
    while((line = reader.readLine()) != null){ 
     decompressedResponse += line; 
    } 

    reader.close(); 
    inputStreamReader.close(); 
    gis.close(); 

    return decompressedResponse; 
} 

Ошибка возникает в направлении нижней части вспомогательного метода, на линии while((line = reader.readLine()) != null).... В частности, ошибка возникает на reader.readLine().

И методы два испытания:

@Test 
public void testHttpGet(){ 
    try { 
     // FILTERED_API_URL_WITH_TOKEN is merely the URL with an auth token 
     String apiResponse = HTTPHelper.doHTTPGet(GlobalConstants.FILTERED_API_URL_WITH_TOKEN); 
     assertNotNull(apiResponse); 
     assertTrue("The size of the API response should be greater than zero. It is an empty string.", apiResponse.length() > 0); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     assertTrue("An exception occured while trying to perform the HTTP Get to the api at URL " + GlobalConstants.FILTERED_API_URL_WITH_TOKEN, false); 
    } 
} 

@Test 
public void testAPIContent(){ 
    try { 
     // the getAPIJson() method basically does the same as the testHttpGet 
     // method, but converts the string to a json 
     JSONObject jsonObject = XMLProducerFromAPI.getAPIJson(); 
     System.out.println(jsonObject); 
     assertNotNull(jsonObject); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     assertTrue("An IOException occured. See stack trace", false); 
    } catch (JSONException e) { 
     e.printStackTrace(); 
     assertTrue("A JSONException occured. See stack trace", false); 
    } 
} 

Я прочитал this question и the answer, но я не верю, что его это применимо, (или, может быть, это и я понял, дайте мне знать, если это случай), и я попробовал их подход и получил только одно сообщение.

Поскольку метод doHTTPGet является статическим, а созданные объекты выполняются таким образом внутри тела метода, ничто (потоки, объекты соединения и т. Д.) Не должно использоваться повторно. Честно говоря, я в тупике.

Вопрос: Я сделал что-то не так в моем вспомогательном коде или неправильно понял какой-либо объект, что даст сообщение «Corrupt GZIP Trailer»? Короче говоря, что может вызвать эту ошибку в моем сценарии?

Как всегда, пожалуйста, дайте мне знать, если я оставил что-либо из этого вопроса.

EDIT

Это трассировки стека:

java.io.IOException: Corrupt GZIP trailer 
    at java.util.zip.GZIPInputStream.readTrailer(GZIPInputStream.java:200) 
    at java.util.zip.GZIPInputStream.read(GZIPInputStream.java:92) 
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264) 
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306) 
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158) 
    at java.io.InputStreamReader.read(InputStreamReader.java:167) 
    at java.io.BufferedReader.fill(BufferedReader.java:136) 
    at java.io.BufferedReader.readLine(BufferedReader.java:299) 
    at java.io.BufferedReader.readLine(BufferedReader.java:362) 
    at com.weathertx.xmlserver.support.HTTPHelper.doHTTPGet(HTTPHelper.java:60) 
    at com.weathertx.xmlserver.tests.HttpHelperTest.getAPIResponse(HttpHelperTest.java:47) 
    at com.weathertx.xmlserver.tests.HttpHelperTest.testHttpGet(HttpHelperTest.java:21) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309) 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 
+0

К сожалению, API, о котором идет речь, ограничен IP, поэтому, к сожалению, я не могу предоставить доступ, не зная IP каждого. –

ответ

0

Проблема была решена. Чтобы быть откровенным, я не совсем понимаю, почему это не сработало изначально, или что с ним не так (за исключением, очевидно, чрезмерно и излишне сложного). Благодаря this solution, который я как-то пропустил, прежде всего, первый раз, я смог решить проблему, в основном реализуя более или менее точно то, что они сделали. Это мой окончательный код:

public static String doHTTPGet(String urlString) throws IOException{ 
    URL weatherAPI = new URL(urlString); 
    HttpURLConnection apiConnection = (HttpURLConnection) weatherAPI.openConnection(); 
    apiConnection.setRequestMethod("GET"); 
    apiConnection.setRequestProperty("Accept-Encoding", "gzip"); 

    apiConnection.connect(); 

    InputStream gzippedResponse = apiConnection.getInputStream(); 
    InputStream decompressedResponse = new GZIPInputStream(gzippedResponse); 
    Reader reader = new InputStreamReader(decompressedResponse, "UTF-8"); 
    StringWriter writer = new StringWriter(); 

    char[] buffer = new char[10240]; 
    for(int length = 0; (length = reader.read(buffer)) > 0;){ 
     writer.write(buffer, 0, length); 
    } 

    writer.close(); 
    reader.close(); 
    decompressedResponse.close(); 
    gzippedResponse.close(); 
    apiConnection.disconnect(); 

    return writer.toString(); 
} 

Так в конечном счете, мне не нужно передавать данные через потоки байтов массива и повсюду. Если мой оригинальный подход запутан, если кто-то точно знает, почему мой оригинальный алгоритм дал сообщение об ошибке «Corrupted GZIP trailer» после первого вызова этого метода, дайте мне знать.

+1

Сравните вызовы write(). Это верно, используя счетчик чтения. У вашего оригинала нет, – EJP

+0

@EJP Итак, мой оригинал постоянно записывал ровно 10240 байт каждый раз, и этот пишет, сколько бы он ни читал, до 10240, что означает, что последнее чтение может быть на несколько байт меньше (правильно ?). Но после того, как потоки и соединения и все закрыты, когда метод вызывается во второй раз, открываются новые потоки и соединения. Происходит ли переполнение мусора, или что-то в этом роде? –

+0

Вы пишете мусор на последней записи, и вы получаете сообщение, в котором говорится именно это. Это совершенно ясно. – EJP

Смежные вопросы