Skip to content

Commit

Permalink
Avoid closing entityStream in JsonBProvider
Browse files Browse the repository at this point in the history
This is required by the spec (see MBW's javadoc) that we must not close
the entity stream in the writeTo method. The toJson(obj, stream) method
will always close the stream, so this cannot be used.

This commit includes a test case that verifies that the client response
filter is only called once per response.  The prior behavior invoked
the filter twice - once as a result of the toJson(obj, stream) method
closing the stream and again when the response was actually processed.
  • Loading branch information
andymc12 committed Jan 2, 2020
1 parent c2051b9 commit 548bcf2
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 15 deletions.
@@ -1,5 +1,5 @@
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=false
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=java;javax;org;com;
org.eclipse.jdt.ui.javadoc=true
Expand Down Expand Up @@ -40,6 +40,8 @@ sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_redundant_modifiers=false
sp_cleanup.remove_redundant_semicolons=false
sp_cleanup.remove_redundant_type_arguments=false
sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
Expand Down
Expand Up @@ -193,8 +193,13 @@ public void CTSTestClientMethodLinkUsedInInvocation() throws Exception {
}

@Test
public void testGetSetEntityStream() throws Exception {
this.runTestOnServer(target, "testGetSetEntityStream", null, "ENTITY_STREAM_WORKS");
public void testGetSetEntityStreamOnRequestFilter() throws Exception {
this.runTestOnServer(target, "testGetSetEntityStreamOnRequestFilter", null, "ENTITY_STREAM_WORKS");
}

@Test
public void testGetSetEntityStreamOnResponseFilter() throws Exception {
this.runTestOnServer(target, "testGetSetEntityStreamOnResponseFilter", null, "ENTITY_STREAM_WORKS1");
}

@Test
Expand Down
Expand Up @@ -10,7 +10,10 @@
*******************************************************************************/
package com.ibm.ws.jaxrs20.client.ComplexClientTest.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
Expand All @@ -23,6 +26,7 @@
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
Expand All @@ -37,6 +41,8 @@
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.Invocation.Builder;
Expand Down Expand Up @@ -543,7 +549,7 @@ public void filter(ClientRequestContext ctx) throws IOException {
ret.append(result);
}

public void testGetSetEntityStream(Map<String, String> param, StringBuilder ret) throws Exception {
public void testGetSetEntityStreamOnRequestFilter(Map<String, String> param, StringBuilder ret) throws Exception {
String serverIP = param.get("serverIP");
String serverPort = param.get("serverPort");
String message = "ENTITY_STREAM_WORKS";
Expand All @@ -563,6 +569,40 @@ public void filter(ClientRequestContext context) throws IOException {
ret.append(response.readEntity(String.class));
}

public void testGetSetEntityStreamOnResponseFilter(Map<String, String> param, StringBuilder ret) throws Exception {
String serverIP = param.get("serverIP");
String serverPort = param.get("serverPort");
final String message = "ENTITY_STREAM_WORKS";
String entity = message.replace('T', 'X');
final AtomicInteger filterInvocationCount = new AtomicInteger(0);

Client client = ClientBuilder.newClient();
client.register(new ClientResponseFilter() {
@Override
public void filter(ClientRequestContext reqContext, ClientResponseContext respContext) throws IOException {
filterInvocationCount.incrementAndGet();
ByteArrayInputStream bais = new ByteArrayInputStream(message.getBytes());
respContext.setEntityStream(bais);
}
});

WebTarget target = client.target("http://" + serverIP + ":" + serverPort + "/" + moduleName + "/ComplexClientTest/SimpleResource/post");
System.out.println("entity=" + entity);
Response response = target.request().post(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE));

ret.append(response.readEntity(String.class)).append(filterInvocationCount.get());
}

private byte[] readInputStreamBytes(InputStream entityStream) throws IOException {
final ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = entityStream.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
return result.toByteArray();
}

public void testTargetTemplateVariable(Map<String, String> param, StringBuilder ret) throws Exception {
String serverIP = param.get("serverIP");
String serverPort = param.get("serverPort");
Expand Down
Expand Up @@ -11,9 +11,12 @@
package mpRestClient10.basicCdi;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.sql.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.enterprise.context.ApplicationScoped;
Expand Down Expand Up @@ -87,17 +90,43 @@ public void testMaps409Exception(HttpServletRequest req, HttpServletResponse res
@Test
public void testFiltersInvoked(HttpServletRequest req, HttpServletResponse resp) throws Exception {
try {
MyFilter.requestFilterInvoked = false;
MyFilter.responseFilterInvoked = false;
MyFilter.requestFilterInvocationCount = 0;
MyFilter.responseFilterInvocationCount = 0;

client.createNewWidget(new Widget("Erasers", 10, 0.8));
assertTrue("Request filter was not invoked", MyFilter.requestFilterInvoked);
assertTrue("Response filter was not invoked", MyFilter.responseFilterInvoked);
assertEquals("Request filter was not invoked or invoked more than once", 1, MyFilter.requestFilterInvocationCount);
assertEquals("Response filter was not invoked or invoked more than once", 1, MyFilter.responseFilterInvocationCount);
assertTrue("POSTed widget does not show up in query", client.getWidgetNames().contains("Erasers"));

} finally {
//ensure we delete so as to not throw off other tests
client.removeWidget("Erasers");
}
}

@Test
public void testSqlDateTypeAndFiltersInvokedCorrectly(HttpServletRequest req, HttpServletResponse resp) throws Exception {
// this test is not applicable to MP Rest Client 1.0 or 1.1 - if we are running these older versions, just abort the test
try {
Class.forName("org.eclipse.microprofile.rest.client.annotation.ClientHeadersParam");
} catch (Throwable t) {
LOG.log(Level.INFO, "Could not load MP Rest Client 1.2+ class - skipping test - expected in MP Rest Client 1.0 or 1.1", t);
return;
}
MyFilter.requestFilterInvocationCount = 0;
MyFilter.responseFilterInvocationCount = 0;

Date currentDate = client.getCurrentDate();
LOG.info("Client received from getCurrentDate(): " + currentDate);
assertNotNull("Date returned was null", currentDate);
assertEquals("Request filter was not invoked or invoked more than once", 1, MyFilter.requestFilterInvocationCount);
assertEquals("Response filter was not invoked or invoked more than once", 1, MyFilter.responseFilterInvocationCount);

Date echoedDate = client.echoDate(currentDate);
LOG.info("Client received from echoDate(" + currentDate + "): " + echoedDate);
assertNotNull("Date returned was null", echoedDate);
assertEquals("Echoed date does not match passed-in date", currentDate.getTime(), echoedDate.getTime());
assertEquals("Request filter was not invoked or invoked more than once", 2, MyFilter.requestFilterInvocationCount);
assertEquals("Response filter was not invoked or invoked more than once", 2, MyFilter.responseFilterInvocationCount);
}
}
Expand Up @@ -10,6 +10,7 @@
*******************************************************************************/
package mpRestClient10.basicCdi;

import java.sql.Date;
import java.util.Set;

import javax.ws.rs.Consumes;
Expand Down Expand Up @@ -50,4 +51,11 @@ public interface BasicServiceClient {
@Path("/{name}")
Widget removeWidget(@PathParam("name") String name) throws UnknownWidgetException;

@GET
@Path("/date")
Date getCurrentDate();

@POST
@Path("/date")
Date echoDate(Date d);
}
Expand Up @@ -19,8 +19,8 @@

public class MyFilter implements ClientRequestFilter, ClientResponseFilter {

static boolean requestFilterInvoked;
static boolean responseFilterInvoked;
static int requestFilterInvocationCount = 0;
static int responseFilterInvocationCount = 0;

/*
* (non-Javadoc)
Expand All @@ -29,7 +29,7 @@ public class MyFilter implements ClientRequestFilter, ClientResponseFilter {
*/
@Override
public void filter(ClientRequestContext arg0, ClientResponseContext arg1) throws IOException {
responseFilterInvoked = true;
responseFilterInvocationCount++;
}

/*
Expand All @@ -39,7 +39,6 @@ public void filter(ClientRequestContext arg0, ClientResponseContext arg1) throws
*/
@Override
public void filter(ClientRequestContext arg0) throws IOException {
requestFilterInvoked = true;
requestFilterInvocationCount++;
}

}
Expand Up @@ -10,11 +10,13 @@
*******************************************************************************/
package remoteApp.basic;

import java.sql.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.Consumes;
Expand All @@ -31,12 +33,13 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;


@Path("/basic")
@ApplicationPath("/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class BasicService extends Application {

private static Logger LOG = Logger.getLogger(BasicService.class.getName());
private static Map<String, Widget> widgets = new HashMap<>();

@GET
Expand Down Expand Up @@ -127,4 +130,20 @@ public Map<String, Widget> removeWidgets(Set<String> names) {
names.stream().forEach(s -> removedWidgets.put(s, widgets.remove(s)));
return removedWidgets;
}

@GET
@Path("/date")
public Date getCurrentDate() {
Date d = new Date(System.currentTimeMillis());
LOG.info("returning " + d);
return d;
}

@POST
@Path("/date")
public Response echoDate(Date d) {
Date d2 = new Date(d.getTime());
LOG.info("given " + d + ", returning " + d2);
return Response.status(202).entity(d2).build();
}
}
@@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2020 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package remoteApp.basic;

import java.io.IOException;
import java.util.logging.Logger;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {

private static Logger LOG = Logger.getLogger(LoggingFilter.class.getName());

@Override
public void filter(ContainerRequestContext arg0) throws IOException {
LOG.info("incoming request " + arg0.getMethod() + " " + arg0.getUriInfo().getPath());
}

@Override
public void filter(ContainerRequestContext arg0, ContainerResponseContext arg1) throws IOException {
LOG.info("outgoing response " + arg0.getMethod() + " " + arg0.getUriInfo().getPath() + " " +
arg1.getStatus() + " " + arg1.getEntity());
}
}
Expand Up @@ -168,7 +168,8 @@ private boolean isJsonType(MediaType mediaType) {
@Override
public void writeTo(Object obj, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
getJsonb().toJson(obj, entityStream);
String json = getJsonb().toJson(obj);
entityStream.write(json.getBytes()); // do not close

if (tc.isDebugEnabled()) {
Tr.debug(tc, "object=" + obj);
Expand Down

0 comments on commit 548bcf2

Please sign in to comment.