ServiceResponseDecoder implements HttpResponseDecoder<ServiceResponse<T>> { @Override public ServiceResponse<T> decode(HttpResponse httpResponse) throws Exception { Map<String, String> headers = getHeaders(httpResponse); collectedHeaders.forEach((k, v) -> Optional.ofNullable(localCollectedHeaders.get(k)).ifPresent(v::addAll)); int statusCode = httpResponse.getStatusLine().getStatusCode(); String statusCodeString = String.valueOf(statusCode); if (statusCode >= 200 && statusCode <= 299) { if (statusCode == 204) { return new ServiceResponse<T>((T) null, headers); } else { try { final ServiceResponseInfo serviceResponseInfo = serviceResponseInfoMap.get(String.valueOf(statusCode)); final JavaType javaType; if (serviceResponseInfo != null) { javaType = serviceResponseInfo.getType(); } else { javaType = serviceResponseInfoMap.get("200").getType(); } if (javaType == null) { return new ServiceResponse<T>((T) null, headers); } if (String.class.isAssignableFrom(javaType.getRawClass())) { return new ServiceResponse<T>((T) IOUtils.toString(httpResponse.getEntity().getContent()), headers); } if (byte[].class.isAssignableFrom(javaType.getRawClass())) { return new ServiceResponse<T>((T) IOUtils.toByteArray(httpResponse.getEntity().getContent()), headers); } return new ServiceResponse<T>(objectMapper.<T>readValue(httpResponse.getEntity().getContent(), javaType), headers); } catch (JsonMappingException e) { if (e.getMessage().contains("No content to map due to end-of-input")) { return new ServiceResponse<T>((T) null, headers); } else { logger.error("Error de-serializing response object exception: {}", e.getMessage()); throw new IOException("Response object de-serialization error", e); } } catch (Exception e) { logger.error("Error de-serializing response object exception: {}", e.getMessage()); throw new IOException("Response object de-serialization error", e); } } } else { String serviceResponse = StringUtils.convertStreamToString(httpResponse.getEntity().getContent()); Object errorResponse = null; ServiceResponseInfo responseInfo = Optional.ofNullable(serviceResponseInfoMap.get(statusCodeString)).orElseGet(() -> serviceResponseInfoMap.get("default")); JavaType errorType = responseInfo.getType(); if (errorType != null) { try { errorResponse = objectMapper.readValue(serviceResponse, errorType); } catch (Exception e) { logger.warn("Error while de-serializing non 200 response to given errorType statusCode:{} exception: {}", statusCodeString, e.getMessage()); } } Class<? extends ServiceClientException> exceptionClass = responseInfo.getExceptionClass(); String exceptionMessage = statusCodeString + " " + serviceResponse; ServiceClientException serviceClientException = exceptionClass.getConstructor(String.class, Object.class).newInstance(exceptionMessage, errorResponse); if (statusCode >= 500 && statusCode <= 599) { logger.error("Non 200 response statusCode: {} response: {}", statusCodeString, serviceResponse); throw serviceClientException; } else { logger.debug("Non 200 response statusCode: {} response: {}", statusCodeString, serviceResponse); return new ServiceResponse<T>(serviceClientException, headers); } } } @Deprecated  ServiceResponseDecoder(ObjectMapper objectMapper, JavaType javaType, JavaType errorType, Logger logger, Map<String, Class<? extends ServiceClientException>> exceptions); @Deprecated  ServiceResponseDecoder(ObjectMapper objectMapper, JavaType javaType, JavaType errorType, Logger logger, Map<String, Class<? extends ServiceClientException>> exceptions, Map<String, Queue<String>> collectedHeaders);  ServiceResponseDecoder(ObjectMapper objectMapper, Logger logger, Map<String, ServiceResponseInfo> serviceResponseInfoMap, Map<String, Queue<String>> collectedHeaders); @Override ServiceResponse<T> decode(HttpResponse httpResponse); @Override ServiceResponse<T> decode(String stringResponse); @Override ServiceResponse<T> decode(byte[] byteResponse); @Override ServiceResponse<T> decode(InputStream is);  }
@Test public void testDecodeHttpResponse() throws Exception { HttpResponse mockHttpResponse = mock(HttpResponse.class); StatusLine mockStatusLine = mock(StatusLine.class); HttpEntity mockEntity = mock(HttpEntity.class); InputStream stream = mock(InputStream.class); mockStatic(IOUtils.class); when(mockStatusLine.getStatusCode()).thenReturn(200); when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine); when(mockHttpResponse.getEntity()).thenReturn(mockEntity); when(mockEntity.getContent()).thenReturn(stream); BDDMockito.when(IOUtils.toString(stream)).thenReturn("success"); when(mockJavaType.getRawClass()).thenReturn(responseClass); ServiceResponseDecoder decoder = spy(new ServiceResponseDecoder(mockMapper, mockJavaType, mockErrorType, mockLogger , exceptions, collectedHeaders)); ServiceResponse response = decoder.decode(mockHttpResponse); assertEquals("success", response.getDataList().get(0)); Mockito.verify(mockLogger, Mockito.never()); }
@Test public void testDecodeHttpResponseExceptionWhileDecode() throws Exception { HttpResponse mockHttpResponse = mock(HttpResponse.class); StatusLine mockStatusLine = mock(StatusLine.class); HttpEntity mockEntity = mock(HttpEntity.class); InputStream stream = mock(InputStream.class); when(mockStatusLine.getStatusCode()).thenReturn(200); when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine); when(mockHttpResponse.getEntity()).thenReturn(mockEntity); when(mockEntity.getContent()).thenReturn(stream); Mockito.doThrow(IOException.class).when(mockMapper).readValue(stream, responseClass); exception.expect(IOException.class); exception.expectMessage(equalTo("Response object de-serialization error")); ServiceResponseDecoder decoder = spy(new ServiceResponseDecoder(mockMapper, mockJavaType, mockErrorType, mockLogger , exceptions, collectedHeaders)); ServiceResponse response = decoder.decode(mockHttpResponse); assertNotNull(response); }
@Test public void testDecodeHttpResponseReturnException() throws Exception { HttpResponse mockHttpResponse = mock(HttpResponse.class); StatusLine mockStatusLine = mock(StatusLine.class); HttpEntity mockEntity = mock(HttpEntity.class); InputStream stream = mock(InputStream.class); String errorString = "{\"error\":\"testing error\"}"; Map<String, Class<? extends ServiceClientException>> mockExceptions = new HashMap<>(); mockExceptions.put("404", ServiceClientException.class); ServiceResponseDecoder decoder = spy(new ServiceResponseDecoder(mockMapper, mockJavaType, mockErrorType, mockLogger, mockExceptions, new HashMap<>())); when(mockStatusLine.getStatusCode()).thenReturn(404); when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine); when(mockHttpResponse.getEntity()).thenReturn(mockEntity); when(mockEntity.getContent()).thenReturn(stream); mockStatic(StringUtils.class); when(StringUtils.convertStreamToString(stream)).thenReturn(errorString); ServiceResponse response = decoder.decode(mockHttpResponse); assertThat(response.getException(), instanceOf(ServiceClientException.class)); assertThat(response.getException().getErrorResponse(), instanceOf(mockErrorType.getRawClass())); Mockito.verify(mockLogger).debug("Non 200 response statusCode: {} response: {}", "404", errorString); }
@Test public void testDecodeHttpResponseReturnExceptionNoErrorType() throws Exception { HttpResponse mockHttpResponse = mock(HttpResponse.class); StatusLine mockStatusLine = mock(StatusLine.class); HttpEntity mockEntity = mock(HttpEntity.class); InputStream stream = mock(InputStream.class); String errorString = "{\"error\":\"testing error\"}"; Map<String, Class<? extends ServiceClientException>> mockExceptions = new HashMap<>(); mockExceptions.put("404", ServiceClientException.class); ServiceResponseDecoder decoder = spy(new ServiceResponseDecoder(mockMapper, mockJavaType, null, mockLogger, mockExceptions, new HashMap<>())); when(mockStatusLine.getStatusCode()).thenReturn(404); when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine); when(mockHttpResponse.getEntity()).thenReturn(mockEntity); when(mockEntity.getContent()).thenReturn(stream); mockStatic(StringUtils.class); when(StringUtils.convertStreamToString(stream)).thenReturn(errorString); ServiceResponse response = decoder.decode(mockHttpResponse); assertThat(response.getException(), instanceOf(ServiceClientException.class)); assertEquals(response.getException().getErrorResponse(), null); Mockito.verify(mockLogger).debug("Non 200 response statusCode: {} response: {}", "404", errorString); }
@Test public void testDecodeHttpResponseReturnExceptionInvalidErrorType() throws Exception { HttpResponse mockHttpResponse = mock(HttpResponse.class); StatusLine mockStatusLine = mock(StatusLine.class); HttpEntity mockEntity = mock(HttpEntity.class); InputStream stream = mock(InputStream.class); String errorString = "{\"nonsense\":\"testing error\"}"; Map<String, Class<? extends ServiceClientException>> mockExceptions = new HashMap<>(); mockExceptions.put("404", ServiceClientException.class); ServiceResponseDecoder decoder = spy(new ServiceResponseDecoder(mockMapper, mockJavaType, mockErrorType, mockLogger, mockExceptions, new HashMap<>())); when(mockStatusLine.getStatusCode()).thenReturn(404); when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine); when(mockHttpResponse.getEntity()).thenReturn(mockEntity); when(mockEntity.getContent()).thenReturn(stream); mockStatic(StringUtils.class); when(StringUtils.convertStreamToString(stream)).thenReturn(errorString); ServiceResponse response = decoder.decode(mockHttpResponse); assertThat(response.getException(), instanceOf(ServiceClientException.class)); assertEquals(response.getException().getErrorResponse(), null); Mockito.verify(mockLogger).debug("Non 200 response statusCode: {} response: {}", "404", errorString); Mockito.verify(mockLogger).warn(anyString(), anyString(), any(Object.class)); }
@Test public void testDecodeHttpResponseDefaultResponse() throws Exception { HttpResponse mockHttpResponse = mock(HttpResponse.class); StatusLine mockStatusLine = mock(StatusLine.class); HttpEntity mockEntity = mock(HttpEntity.class); InputStream stream = mock(InputStream.class); when(mockStatusLine.getStatusCode()).thenReturn(500); when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine); when(mockHttpResponse.getEntity()).thenReturn(mockEntity); when(mockEntity.getContent()).thenReturn(stream); mockStatic(StringUtils.class); when(StringUtils.convertStreamToString(stream)).thenReturn("error"); exceptions.put("default", ServiceClientException.class); try { ServiceResponseDecoder decoder = spy(new ServiceResponseDecoder(mockMapper, mockJavaType, mockErrorType, mockLogger , exceptions, collectedHeaders)); ServiceResponse response = decoder.decode(mockHttpResponse); fail("Decoder should throw exception for 5xx status, but didn't!"); } catch(Exception e) { assertThat(e, instanceOf(ServiceClientException.class)); } Mockito.verify(mockLogger).error("Non 200 response statusCode: {} response: {}","500", "error"); }
@Test public void testDecoderMultipleExceptionClassesOldConstructor() throws Exception { HttpResponse mockHttpResponse = mock(HttpResponse.class); StatusLine mockStatusLine = mock(StatusLine.class); HttpEntity mockEntity = mock(HttpEntity.class); InputStream stream = mock(InputStream.class); Map<String, Class<? extends ServiceClientException>> mockExceptions = new HashMap<>(); mockExceptions.put("400", ServiceClientExceptionTest.class); mockExceptions.put("default", ServiceClientException.class); ServiceResponseDecoder decoder = spy(new ServiceResponseDecoder(mockMapper, mockJavaType, mockErrorType, mockLogger , mockExceptions, collectedHeaders)); when(mockStatusLine.getStatusCode()).thenReturn(500); when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine); when(mockHttpResponse.getEntity()).thenReturn(mockEntity); when(mockEntity.getContent()).thenReturn(stream); mockStatic(StringUtils.class); when(StringUtils.convertStreamToString(stream)).thenReturn("error"); try { decoder.decode(mockHttpResponse); fail("Decoder should throw exception for 5xx status, but didn't!"); } catch(Exception e) { assertThat(e, instanceOf(ServiceClientException.class)); } when(mockStatusLine.getStatusCode()).thenReturn(400); ServiceResponse response = decoder.decode(mockHttpResponse); assertEquals(response.getException().getClass(), ServiceClientExceptionTest.class); assertNull(response.getException().getErrorResponse()); Mockito.verify(mockLogger).error("Non 200 response statusCode: {} response: {}","500", "error"); }