GRPC Java logging in tests - java

So, I am using grpc-testin library to make some integration tests. When I use this code:
#Rule
public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
#Test
public void doTest() throws IOException {
String serverName = InProcessServerBuilder.generateName();
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(service).build().start());
SomeServerGrpc.SomeServerBlockingStub blockingStub = SomeServerGrpc.newBlockingStub(
grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()));
...
whenever there is an exception in code, I get
io.grpc.StatusRuntimeException: UNKNOWN
at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:233)
at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:214)
at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:139)
How to make grpc to return real reason for the exception and not only UNKNOWN?

OK, I finally found a way:
grpcCleanup.register(InProcessServerBuilder.forName(serverName).intercept(si).directExecutor().addService(service).build().start());
and
private ServerInterceptor si = new ServerInterceptor() {
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
ServerCall<ReqT, RespT> wrappedCall = new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(call) {
#Override
public void sendMessage(RespT message) {
super.sendMessage(message);
}
#Override
public void close(Status status, Metadata trailers) {
System.out.println("Interceptor: " + (status.getCause() == null ? "null" : status.getCause().getClass().getName()));
if (status.getCode() == Status.Code.UNKNOWN
&& status.getDescription() == null
&& status.getCause() != null) {
Throwable e = status.getCause();
status = Status.INTERNAL
.withDescription(e.getMessage())
.augmentDescription(stacktraceToString(e));
}
super.close(status, trailers);
}
};
return next.startCall(wrappedCall, headers);
}
private String stacktraceToString(Throwable e) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
return stringWriter.toString();
}
};

Related

How to read request body in HandlerInterceptor?

I have Spring Boot and I need to log user action in DB, so I wrote HandlerInterceptor:
#Component
public class LogInterceptor implements HandlerInterceptor {
#Autovired
private LogUserActionService logUserActionService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws IOException {
String userName = SecurityContextHolder.getContext().getAuthentication().getName();
String url = request.getRequestURI();
String queryString = request.getQueryString() != null ? request.getQueryString() : "";
String body = "POST".equalsIgnoreCase(request.getMethod()) ? new BufferedReader(new InputStreamReader(request.getInputStream())).lines().collect(Collectors.joining(System.lineSeparator())) : queryString;
logUserActionService.logUserAction(userName, url, body);
return true;
}
}
But according to this answer Get RequestBody and ResponseBody at HandlerInterceptor "RequestBody can be read only once", so as I understand I read input stream and then Spring tries to do same, but stream has been read already and I'm getting an error: "Required request body is missing ..."
So I tried different ways to make buffered input stream i.e.:
HttpServletRequest httpServletRequest = new ContentCachingRequestWrapper(request);
new BufferedReader(new InputStreamReader(httpServletRequest.getInputStream())).lines().collect(Collectors.joining(System.lineSeparator()))
Or
InputStream bufferedInputStream = new BufferedInputStream(request.getInputStream());
But nothing helped
Also I tried to use
#ControllerAdvice
public class UserActionRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
But it has only body, no request info like URL or Request parameters
Also tried to use Filters, but result same.
So I need a good way to get information from request like user, URL, parameters, body (if present) and write it to DB.
To log HTTP Request & Response, you can use RequestBodyAdviceAdapter and ResponseBodyAdvice. here, it is using in my way.
CustomRequestBodyAdviceAdapter.java
#ControllerAdvice
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
#Autowired
HttpServletRequest httpServletRequest;
#Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
#Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
// here you can full log httpServletRequest and body.
return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
}
CustomResponseBodyAdviceAdapter.java
#ControllerAdvice
public class CustomResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {
#Autowired
private LoggingService loggingService;
#Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
#Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (serverHttpRequest instanceof ServletServerHttpRequest && serverHttpResponse instanceof ServletServerHttpResponse) {
// here you can full log httpServletRequest and body.
}
return o;
}
}
Above AdviceAdapter cannot handle the GET request. So, you can use HandlerInterceptor.
CustomWebConfigurerAdapter.java
#Component
public class CustomWebConfigurerAdapter implements WebMvcConfigurer {
#Autowired
private CustomLogInterceptor httpServiceInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(httpServiceInterceptor);
}
}
CustomLogInterceptor.java
#Component
public class CustomLogInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (DispatcherType.REQUEST.name().equals(request.getDispatcherType().name()) && request.getMethod().equals(HttpMethod.GET.name())) {
// here you can full log httpServletRequest and body for GET Request.
}
return true;
}
}
Here you can reference full source code in my git.
springboot-http-request-response-loging-with-json-logger
+Feature => It is already have Integration with ELK (Elasticsearch, Logstash, Kibana)
You can use Filter to log request body.
public class LoggingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
try {
chain.doFilter(wrappedRequest, res);
} finally {
logRequestBody(wrappedRequest);
}
}
private static void logRequestBody(ContentCachingRequestWrapper request) {
byte[] buf = request.getContentAsByteArray();
if (buf.length > 0) {
try {
String requestBody = new String(buf, 0, buf.length, request.getCharacterEncoding());
System.out.println(requestBody);
} catch (Exception e) {
System.out.println("error in reading request body");
}
}
}
}
The main thing to note here is that you have to pass object of ContentCachingRequestWrapper in filter chain otherwise you won't get request content in it.
In above example, if you use chain.doFilter(req, res) or chain.doFilter(request, res) then you won't get request body in wrappedRequest object.
You can get the Request Body data using RequestBodyAdviceAdapter for POST/PUT requests. You can use HandlerInterceptorAdapter for GET calls. Here's a working example -
https://frandorado.github.io/spring/2018/11/15/log-request-response-with-body-spring.html
#ControllerAdvice
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter
{
#Autowired
HttpServletRequest httpServletRequest;
private static final Log LOGGER = LogFactory.getLog(CustomRequestBodyAdviceAdapter.class);
private static final Charset DEFAULT_CHARSET = ISO_8859_1;
#Override
public boolean supports(MethodParameter methodParameter, Type type,
Class<? extends HttpMessageConverter<?>> aClass)
{
return true;
}
#Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage,
MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType)
{
Instant startTime = Instant.now();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("REQUEST call Starts :: Start Time : %s ").append(startTime);
try
{
logRequest(httpServletRequest, body);
}
catch (IOException e)
{
LOGGER.info("Exception getting the Request Body into the Log: {}" + e.getMessage());
}
public void logRequest(HttpServletRequest httpServletRequest, Object body) throws IOException
{
StringBuilder stringBuilder = new StringBuilder();
Map<String, String> parameters = buildParametersMap(httpServletRequest);
stringBuilder.append("REQUEST ");
stringBuilder.append("method=[").append(httpServletRequest.getMethod()).append("] ");
stringBuilder.append("path=[").append(httpServletRequest.getRequestURI()).append("] ");
stringBuilder.append("headers=[").append(buildHeadersMap(httpServletRequest)).append("] ");
if (!parameters.isEmpty())
{
stringBuilder.append("parameters=[").append(parameters).append("] ");
}
if (body != null)
{
stringBuilder.append("body=[" + body + "]");
}
ObjectMapper objectMapper = new ObjectMapper();
String jsonInString = null;
try
{
jsonInString = objectMapper.writer().writeValueAsString(body);
}
catch (JsonProcessingException e)
{
throw new RestApiException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage());
}
stringBuilder.append("REQUEST Body = [").append(jsonInString).append("] ");
LOGGER.info("BODY DATA >>>> " + jsonInString);
LOGGER.info("Body - : {}" + stringBuilder);
}
private Map<String, String> buildParametersMap(HttpServletRequest httpServletRequest)
{
Map<String, String> resultMap = new HashMap<>();
Enumeration<String> parameterNames = httpServletRequest.getParameterNames();
while (parameterNames.hasMoreElements())
{
String key = parameterNames.nextElement();
String value = httpServletRequest.getParameter(key);
resultMap.put(key, value);
}
return resultMap;
}
private Map<String, String> buildHeadersMap(HttpServletRequest request)
{
Map<String, String> map = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements())
{
String key = headerNames.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
}
I have used ObjectMapper here because I need the body response as raw JSON object, but the afterBodyRead() is invoked after the body is transformed to Java Object.
I found this solved my problem for copying the request buffer for application/json content types. It also shows how to extend the wrapper as the comments to Harshit solution mentions.
https://levelup.gitconnected.com/how-to-log-the-request-body-in-a-spring-boot-application-10083b70c66
The important pieces are that you need a filter to pass along the new request to the server.
#Component
public class LoggingFilter implements Filter {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
if (Arrays.asList("POST", "PUT").contains(httpRequest.getMethod())) {
CustomHttpRequestWrapper requestWrapper = new CustomHttpRequestWrapper(httpRequest);
requestWrapper.setAttribute("input", requestWrapper.getBodyInStringFormat());
filterChain.doFilter(requestWrapper, servletResponse);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
The logger requires a custom wrapper, and the one spring boot provides seems to be insufficient for application/json type messages.
public class CustomHttpRequestWrapper extends HttpServletRequestWrapper {
public String getBodyInStringFormat() {
return bodyInStringFormat;
}
private final String bodyInStringFormat;
public CustomHttpRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
bodyInStringFormat = readInputStreamInStringFormat(request.getInputStream(), Charset.forName(request.getCharacterEncoding()));
}
private String readInputStreamInStringFormat(InputStream stream, Charset charset) throws IOException {
return getString(stream, charset);
}
static String getString(InputStream stream, Charset charset) throws IOException {
final int MAX_BODY_SIZE = 1024;
final StringBuilder bodyStringBuilder = new StringBuilder();
if (!stream.markSupported()) {
stream = new BufferedInputStream(stream);
}
stream.mark(MAX_BODY_SIZE + 1);
final byte[] entity = new byte[MAX_BODY_SIZE + 1];
final int bytesRead = stream.read(entity);
if (bytesRead != -1) {
bodyStringBuilder.append(new String(entity, 0, Math.min(bytesRead, MAX_BODY_SIZE), charset));
if (bytesRead > MAX_BODY_SIZE) {
bodyStringBuilder.append("...");
}
}
stream.reset();
return bodyStringBuilder.toString();
}
#Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
#Override
public ServletInputStream getInputStream () {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bodyInStringFormat.getBytes());
return new ServletInputStream() {
private boolean finished = false;
#Override
public boolean isFinished() {
return finished;
}
#Override
public int available() {
return byteArrayInputStream.available();
}
#Override
public void close() throws IOException {
super.close();
byteArrayInputStream.close();
}
#Override
public boolean isReady() {
return true;
}
#Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
public int read () {
int data = byteArrayInputStream.read();
if (data == -1) {
finished = true;
}
return data;
}
};
}
}

Unit testing respository with in MVVM pattern android

I am trying to write unit tests for repository while using MVVM pattern in android.
What i have is a repository which fetched data from the network using retrofit
public class ValidateCbuRepository {
private static ValidateCbuRepository single_instance = null;
private MutableLiveData<CBUValidationImageResponse> data = new MutableLiveData<>();
public static ValidateCbuRepository getInstance() {
if (single_instance == null)
single_instance = new ValidateCbuRepository();
return single_instance;
}
public MutableLiveData<CBUValidationImageResponse> processImage(String encodedString) {
JsonObject postParam = new JsonObject();
postParam.addProperty("image", encodedString);
Api service = RetrofitClientInstance.getRetrofitInstance().create(Api.class);
data.setValue(null);
HttpUrl httpUrl = HttpUrl.parse("some url");
Call<CBUValidationImageResponse> responseCall = service.getProcessedImage_cbu_validation(httpUrl.toString(),postParam);
responseCall.enqueue(new Callback<CBUValidationImageResponse>() {
#Override
public void onResponse(Call<CBUValidationImageResponse> call, Response<CBUValidationImageResponse> response) {
if(response.isSuccessful()) {
CBUValidationImageResponse res = response.body();
CBUValidationImageResponse cbuValidationImageResponse = res;
Log.i("CBU response ",""+cbuValidationImageResponse.toString());
cbuValidationImageResponse.setSuccess(true);
cbuValidationImageResponse.setShowProgres(false);
cbuValidationImageResponse.setError(false);
data.setValue(cbuValidationImageResponse);
}
}
#Override
public void onFailure(Call<CBUValidationImageResponse> call, Throwable t) {
CBUValidationImageResponse cbuValidationImageResponse = new CBUValidationImageResponse();
cbuValidationImageResponse.setError(true);
cbuValidationImageResponse.setShowProgres(false);
data.setValue(cbuValidationImageResponse);
t.printStackTrace();
}
});
return data;
}
}
The unit test part
#Mock
private Observer<CBUValidationImageResponse> observer;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testApiResponse_success() {
Api mockedApiInterface = Mockito.mock(Api.class);
Call<CBUValidationImageResponse> mockedCall = Mockito.mock(Call.class);
Mockito.when(mockedApiInterface.getProcessedImage_cbu_validation(any(),any())).thenReturn(mockedCall);
try {
Mockito.doAnswer(new Answer() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Callback<CBUValidationImageResponse> callback = invocation.getArgument(0);
CBUValidationImageResponse cbuValidationImageResponse = new CBUValidationImageResponse();
cbuValidationImageResponse.setCBU_code("some code");
cbuValidationImageResponse.setHeight(7);
cbuValidationImageResponse.setBreadth(7);
cbuValidationImageResponse.setLength(7);
callback.onResponse(mockedCall, Response.success(cbuValidationImageResponse));
// or callback.onResponse(mockedCall, Response.error(404. ...);
// or callback.onFailure(mockedCall, new IOException());
return null;
}
}).when(mockedCall).enqueue(any(Callback.class));
ValidateCbuRepository validateCbuRepository = new ValidateCbuRepository();
String encodedString= "";
validateCbuRepository.processImage(encodedString).observeForever(observer);
Getting a null pointer exception at validateCbuRepository.processImage(encodedString).observeForever(observer). Next step is to verify the observer.
I expect the test to pass. What am i doing wrong here?. I did something similar foe view model and the test passes with 100% code coverage.
The retrofit call is asynchronous. Is that the reason why it fails?
Edit : It seems livedata is null while testing causing NPE.

RestTemplate response.getBody throws exception on 4** and 5** errors for put and post request but works fine for get requests

I am trying to intercept and log all the request-responses. To make requests i am using RestTemplate.exchange().
When i make a GET request and get an 4** error i can call the ClientHttpResponse.getBody() and can access the response body but for PUT and POST requests ClientHttpResponse.getBody() method throws an exception.
What might be causing this and how can i get the response body for POST and PUT requests as well?
This is where i make the request:
apiResponse = restTemplate.exchange(url, vCloudRequest.getHttpMethod(), entity, responseType);
This is the part of the interceptor that gets the exception:
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
String requestString = new String(body);
String responseString = new
// Below line throws exception
String(ByteStreams.toByteArray(response.getBody()), Charset.forName("UTF-8"));
This is the stack.
Caused by: java.io.IOException: Server returned HTTP response code: 403 for URL: https://176.235.57.11/api/admin/org/bd154aaf-2e7c-446d-91be-f0a45138476b/users
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1876)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
at org.springframework.http.client.SimpleClientHttpResponse.getBody(SimpleClientHttpResponse.java:85)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getBody(BufferingClientHttpResponseWrapper.java:69)
at roma.api_utils.model.Interceptors.RequestLoggingInterceptor.intercept(RequestLoggingInterceptor.java:39)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:70)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)
Update :
When i call response.getStatusCode() before calling response.getBody() it doesn't throw IOException.
Basic knowledge:
HttpURLConnection has two similar fields, errorStream and inputStream. When we invoke its getInputSteam method, it checks whether the response has an error code. If so, it throws an IOException and records it- that's why you got the exception. Furthermore, it also copies the contents in inputStream to errorStream, thus we can get its response body by invoking its getErrorStream method. This is exactly what SimpleClientHttpResponse does with its getBody method:
#Override
public InputStream getBody() throws IOException {
InputStream errorStream = this.connection.getErrorStream();
this.responseStream =
(errorStream != null ? errorStream : this.connection.getInputStream());
return this.responseStream;
}
It first checks if errorStream is not null. If true, it returns it. If false, it calls connection.getInputStream() and returns that.
Now here are the answers
Why does calling response.getBody() not throw an IOException after you called response.getStatusCode()? It is because getStatusCode calls getInputStream internally. Thus, errorStream will be not null when getBody is called.
Why does it not throw an exception when the http method is GET?
See method org.springframework.http.client.SimpleBufferingClientHttpRequest#executeInternal.
.
#Override
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput)
throws IOException {
addHeaders(this.connection, headers);
// JDK <1.8 doesn't support getOutputStream with HTTP DELETE
if (HttpMethod.DELETE == getMethod() && bufferedOutput.length == 0) {
this.connection.setDoOutput(false);
}
if (this.connection.getDoOutput() && this.outputStreaming) {
this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
}
this.connection.connect();
if (this.connection.getDoOutput()) {
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
}
else {
// Immediately trigger the request in a no-output scenario as well
this.connection.getResponseCode();
}
return new SimpleClientHttpResponse(this.connection);
}
It eagerly executes this.connection.getResponseCode(); when the http method is GET.
I had a similar requirement of logging every request and response. I wrote a filter and hooked into the filter chain.
The code looks like something below:
public class CustomRequestFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//No custom initialisation required
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
Map<String, String> requestMap = this
.getTypesafeRequestMap(httpServletRequest);
BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(
httpServletRequest);
BufferedResponseWrapper bufferedResponse = new BufferedResponseWrapper(
httpServletResponse);
final StringBuilder logMessage = new StringBuilder(
"REST Request - ").append("[HTTP METHOD:")
.append(httpServletRequest.getMethod())
.append("] [PATH INFO:")
.append(httpServletRequest.getServletPath())
.append("] [REQUEST PARAMETERS:").append(requestMap)
.append("] [REQUEST BODY:")
.append(bufferedRequest.getRequestBody())
.append("] [REMOTE ADDRESS:")
.append(httpServletRequest.getRemoteAddr()).append("]");
log.info("=======================REQUEST PAYLOAD=================================");
log.info(bufferedRequest.getRequestBody());
log.info("========================================================");
filterChain.doFilter(bufferedRequest, bufferedResponse);
logMessage.append(" [RESPONSE:")
.append(bufferedResponse.getContent()).append("]");
log.info("=======================REST RESPONSE=================================");
log.info(bufferedResponse.getContent());
log.info("========================================================");
} catch (Exception a) {
log.error("Error while filtering ", a);
}
}
private Map<String, String> getTypesafeRequestMap(HttpServletRequest request) {
Map<String, String> typesafeRequestMap = new HashMap<>();
Enumeration<?> requestParamNames = request.getParameterNames();
while (requestParamNames.hasMoreElements()) {
String requestParamName = (String) requestParamNames.nextElement();
String requestParamValue;
if ("password".equalsIgnoreCase(requestParamName)) {
requestParamValue = "********";
} else {
requestParamValue = request.getParameter(requestParamName);
}
typesafeRequestMap.put(requestParamName, requestParamValue);
}
return typesafeRequestMap;
}
#Override
public void destroy() {
//not yet implemented
}
private static final class BufferedRequestWrapper extends
HttpServletRequestWrapper {
private ByteArrayInputStream bais = null;
private ByteArrayOutputStream baos = null;
private BufferedServletInputStream bsis = null;
private byte[] buffer = null;
public BufferedRequestWrapper(HttpServletRequest req)
throws IOException {
super(req);
// Read InputStream and store its content in a buffer.
InputStream is = req.getInputStream();
this.baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int read;
while ((read = is.read(buf)) > 0) {
this.baos.write(buf, 0, read);
}
this.buffer = this.baos.toByteArray();
}
#Override
public ServletInputStream getInputStream() {
this.bais = new ByteArrayInputStream(this.buffer);
this.bsis = new BufferedServletInputStream(this.bais);
return this.bsis;
}
String getRequestBody() throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(
this.getInputStream()));
String line;
StringBuilder inputBuffer = new StringBuilder();
do {
line = reader.readLine();
if (null != line) {
inputBuffer.append(line.trim());
}
} while (line != null);
reader.close();
return inputBuffer.toString().trim();
}
}
private static final class BufferedServletInputStream extends
ServletInputStream {
private ByteArrayInputStream bais;
public BufferedServletInputStream(ByteArrayInputStream bais) {
this.bais = bais;
}
#Override
public int available() {
return this.bais.available();
}
#Override
public int read() {
return this.bais.read();
}
#Override
public int read(byte[] buf, int off, int len) {
return this.bais.read(buf, off, len);
}
#Override
public boolean isFinished() {
return false;
}
#Override
public boolean isReady() {
return true;
}
#Override
public void setReadListener(ReadListener readListener) {
//No specific readListener changes required
}
}
public class TeeServletOutputStream extends ServletOutputStream {
private final TeeOutputStream targetStream;
public TeeServletOutputStream(OutputStream one, OutputStream two) {
targetStream = new TeeOutputStream(one, two);
}
#Override
public void write(int arg0) throws IOException {
this.targetStream.write(arg0);
}
#Override
public void flush() throws IOException {
super.flush();
this.targetStream.flush();
}
#Override
public void close() throws IOException {
super.close();
this.targetStream.close();
}
#Override
public boolean isReady() {
return false;
}
#Override
public void setWriteListener(WriteListener writeListener) {
//not yet implemented
}
}
public class BufferedResponseWrapper implements HttpServletResponse {
HttpServletResponse original;
TeeServletOutputStream tee;
ByteArrayOutputStream bos;
public BufferedResponseWrapper(HttpServletResponse response) {
original = response;
}
public String getContent() {
return bos.toString();
}
#Override
public PrintWriter getWriter() throws IOException {
return original.getWriter();
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
if (tee == null) {
bos = new ByteArrayOutputStream();
tee = new TeeServletOutputStream(original.getOutputStream(),
bos);
}
return tee;
}
#Override
public String getCharacterEncoding() {
return original.getCharacterEncoding();
}
#Override
public String getContentType() {
return original.getContentType();
}
#Override
public void setCharacterEncoding(String charset) {
original.setCharacterEncoding(charset);
}
#Override
public void setContentLength(int len) {
original.setContentLength(len);
}
#Override
public void setContentLengthLong(long l) {
original.setContentLengthLong(l);
}
#Override
public void setContentType(String type) {
original.setContentType(type);
}
#Override
public void setBufferSize(int size) {
original.setBufferSize(size);
}
#Override
public int getBufferSize() {
return original.getBufferSize();
}
#Override
public void flushBuffer() throws IOException {
tee.flush();
}
#Override
public void resetBuffer() {
original.resetBuffer();
}
#Override
public boolean isCommitted() {
return original.isCommitted();
}
#Override
public void reset() {
original.reset();
}
#Override
public void setLocale(Locale loc) {
original.setLocale(loc);
}
#Override
public Locale getLocale() {
return original.getLocale();
}
#Override
public void addCookie(Cookie cookie) {
original.addCookie(cookie);
}
#Override
public boolean containsHeader(String name) {
return original.containsHeader(name);
}
#Override
public String encodeURL(String url) {
return original.encodeURL(url);
}
#Override
public String encodeRedirectURL(String url) {
return original.encodeRedirectURL(url);
}
#SuppressWarnings("deprecation")
#Override
public String encodeUrl(String url) {
return original.encodeUrl(url);
}
#SuppressWarnings("deprecation")
#Override
public String encodeRedirectUrl(String url) {
return original.encodeRedirectUrl(url);
}
#Override
public void sendError(int sc, String msg) throws IOException {
original.sendError(sc, msg);
}
#Override
public void sendError(int sc) throws IOException {
original.sendError(sc);
}
#Override
public void sendRedirect(String location) throws IOException {
original.sendRedirect(location);
}
#Override
public void setDateHeader(String name, long date) {
original.setDateHeader(name, date);
}
#Override
public void addDateHeader(String name, long date) {
original.addDateHeader(name, date);
}
#Override
public void setHeader(String name, String value) {
original.setHeader(name, value);
}
#Override
public void addHeader(String name, String value) {
original.addHeader(name, value);
}
#Override
public void setIntHeader(String name, int value) {
original.setIntHeader(name, value);
}
#Override
public void addIntHeader(String name, int value) {
original.addIntHeader(name, value);
}
#Override
public void setStatus(int sc) {
original.setStatus(sc);
}
#SuppressWarnings("deprecation")
#Override
public void setStatus(int sc, String sm) {
original.setStatus(sc, sm);
}
#Override
public String getHeader(String arg0) {
return original.getHeader(arg0);
}
#Override
public Collection<String> getHeaderNames() {
return original.getHeaderNames();
}
#Override
public Collection<String> getHeaders(String arg0) {
return original.getHeaders(arg0);
}
#Override
public int getStatus() {
return original.getStatus();
}
}
}
For PUT and POST, it depends on your target resource. If your target resource do not add anything in the response's body after a PUT or POST request, it's normal to get an exception. In general, you know the resource that you send with PUT or POST so you can just check the response's status to know if your resource had been created or modified. You do not need to check the response body again.
You can use the following to accesss the response body in the interceptor. I did a quick unit test to confirm it works even on a POST with a 403 response.
However be careful, getBody returns an InputStream. Which means you can only read it once. You won't be able to read the same stream again outside the interceptor unless you provide a new response with a new body.
...
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
final ClientHttpResponse response = execution.execute(request, body);
final InputStream body = response.getBody();
return response;
}
...

playframework-2.3 Akka-Async interaction porting?

I have some old playframework 2.2 java webservice that interacts with akka, and now I should port them to playframework 2.3.
However, async has been deprecated and even after reading the doc about the async porting (http://www.playframework.com/documentation/2.3.x/JavaAsync) I wasn't able to understand how to apply it to my case (code below):
I must make the await for a timeout/akka server reply before starting the construction of my reply (ok()), otherwise I will block the thread.
I should make the actorselection async too.
I should make the akka server reply parsing/reply construction async too
I looked around and I wasn't able to find an example of such interactions, even in typesafe templates.
How could I do that?
/* playframework 2.2 code */
public class Resolve extends Controller {
private final static String RESOLVER_ACTOR = play.Play.application().configuration().getString("actor.resolve");
#CorsRest
#VerboseRest
#RequireAuthentication
#BodyParser.Of(BodyParser.Json.class)
public static Result getJsonTree() {
JsonNode json = request().body().asJson();
ProtoBufMessages.ResolveRequest msg;
ResolveRequestInput input;
try {
input = new ResolveRequestInput(json);
} catch (rest.exceptions.MalformedInputException mie) {
return badRequest(mie.getMessage());
}
msg = ((ProtoBufMessages.ResolveRequest)input.getMessage());
ActorSelection resolver = Akka.system().actorSelection(RESOLVER_ACTOR);
Timeout tim = new Timeout(Duration.create(4, "seconds"));
Future<Object> fut = Patterns.ask(resolver, input.getMessage(), tim);
return async (
F.Promise.wrap(fut).map(
new F.Function<Object, Result>() {
public Result apply(Object response) {
ProtoBufMessages.ResolveReply rsp = ((ProtoBufMessages.ResolveReply)response);
ResolveOutput output = new ResolveOutput(rsp);
return ok(output.getJsonReply());
}
}
)
);
}
}
I came out with the code below
public class Resolve extends Controller {
private final static String RESOLVER_ACTOR = play.Play.application().configuration().getString("actor.resolve");
private final static BrainProtoMessages.ResolveReply request_error = BrainProtoMessages.ResolveReply.newBuilder()
.setReturnCode(BResults.REQUEST_FAILED)
.build();
#CorsRest
#VerboseRest
#RequireAuthentication
#BodyParser.Of(BodyParser.Json.class)
public static Result resolve_map() {
final ResolveRequestInput input;
final F.Promise<ActorSelection> selected_target;
final F.Promise<Future<Object>> backend_request;
final F.Promise<BrainProtoMessages.ResolveReply> backend_reply;
final F.Promise<ObjectNode> decode_json;
final F.Promise<Result> ok_result;
final JsonNode json = request().body().asJson();
try {
input = new ResolveRequestInput(json);
} catch (rest.exceptions.MalformedInputException mie) {
return badRequest(mie.getMessage());
}
selected_target = F.Promise.promise(
new F.Function0<ActorSelection>() {
#Override
public ActorSelection apply() throws Throwable {
return Akka.system().actorSelection(RESOLVER_ACTOR);
}
}
);
backend_request =
selected_target.map(
new F.Function<ActorSelection, Future<Object>>() {
#Override
public Future<Object> apply(ActorSelection actorSelection) throws Throwable {
return Patterns.ask(actorSelection, input.getMessage(),new Timeout(Duration.create(4, "seconds")));
}
}
);
backend_reply = backend_request.map(
new F.Function<Future<Object>, BrainProtoMessages.ResolveReply>() {
#Override
public BrainProtoMessages.ResolveReply apply(Future<Object> akka_reply) throws Throwable {
try {
return (BrainProtoMessages.ResolveReply) Await.result(akka_reply, Duration.create(4, "seconds"));
}catch(Exception error)
{
return request_error;
}
}
}
);
decode_json = backend_reply.map(
new F.Function<BrainProtoMessages.ResolveReply, ObjectNode>() {
#Override
public ObjectNode apply(BrainProtoMessages.ResolveReply response) throws Throwable {
return new ResolveOutput(response).getJsonReply();
}
}
);
ok_result = decode_json.map(
new F.Function<ObjectNode, Result>() {
#Override
public Result apply(ObjectNode reply) {
return ok(reply);
}
}
);
try {
return ok_result.get(8000);
}catch(Exception error)
{
return internalServerError();
}
}
}

SocketTimeoutException cxf httpconduit PUT

I have a test that takes a lot of time and ended up making a timeout.
I tried the solution bob and nothing it does not work.
The error occurs just on tests PUT.
My Test :
#Test
public void testUpdateUse(){
setExpectedResponse("{\"id\":1,\"username\":\"test\",\"email\":\"bob#test.ca\"}");
User userToUpdate = new User();
userToUpdate.setEmail("bob#test.ca");
userToUpdate.setUsername("superbob");
userToUpdate.setId(1);
UserOrganization userOrganization = new UserOrganization();
userOrganization.setOrganizationId(1);
List<UserOrganization> userOrganizations = new ArrayList<>();
userOrganizations.add(userOrganization);
UserOrganizationUnit userOrganizationUnit = new UserOrganizationUnit();
userOrganizationUnit.setOrganizationUnitId(1);
List<UserOrganizationUnit> userOrganizationUnits = new ArrayList<>();
userOrganizationUnits.add(userOrganizationUnit);
userToUpdate.setOrganizations(userOrganizations);
userToUpdate.setOrganizationUnits(userOrganizationUnits);
userAPIService.update(1, userToUpdate);
assertLatestRequest("PUT", "/users/1");
}
#Before
public void setUpServer() throws Exception {
Thread.sleep(500);
expectedResponse = null;
final Authenticator authenticator = new BasicAuthenticator("") {
#Override
public boolean checkCredentials(final String username, final String password) {
return getUsername().equals(username) && getPassword().equals(password);
}
};
final HttpHandler handler = new HttpHandler() {
#Override
public void handle(HttpExchange exchange) throws IOException {
final Authenticator.Result authResult = authenticator.authenticate(exchange);
if (authResult instanceof Authenticator.Success || !basicAuthRequired) {
latestExchange = exchange;
StringWriter writer = new StringWriter();
try {
IOUtils.copy(new InputStreamReader(latestExchange.getRequestBody()), writer, 1024);
} catch (IOException e) {
e.printStackTrace();
}
latestRequestBody = writer.toString();
byte[] response = expectedResponse.getBytes();
exchange.getResponseHeaders().add("Content-type", expectedContentType);
exchange.sendResponseHeaders(expectedStatus, response.length);
exchange.getResponseBody().write(response);
}
exchange.close();
expectedResponse = null;
}
};
httpServer = HttpServer.create(new InetSocketAddress(testPort), 0);
httpServer.createContext("/", handler);
httpServer.start();
}
#After
public void tearDownServer() throws Exception {
if (httpServer != null) {
httpServer.stop(0);
}
context.assertIsSatisfied();
Thread.sleep(500);
}
example code on applicationContext.xml:
<http:conduit name="{http://service.clientapi.user.com/}*.http-conduit">
<http:client CacheControl="no-store" Connection="Keep-Alive" AllowChunking="false" ConnectionTimeout="10000" ReceiveTimeout="60000"/>
</http:conduit>
Version CXF : 2.7.3
Version Spring 3
Java 7
My solution was to override the httpconduit in my test context.xml
<http:conduit name="*.http-conduit">
<http:client CacheControl="no-store" Connection="Keep-Alive" AllowChunking="false" ConnectionTimeout="10000" ReceiveTimeout="60000"/>
</http:conduit>

Categories

Resources