i have multiple jax-rs services built using cxf/spring. want control output payload response size of services. simplicity sake, let's none of api's in of services should ever return json response payload more 500 characters , want control in 1 place instead of relying on individual services adhere requirement. (we have other features built custom framework/base component services depend on).
i have tried implementing using jax-rs's writerinterceptor
, containerresponsefilter
, cxf's phase interceptor
, none of approaches seem satisfy requirement. more details on i've done far:
option 1: (writerinteceptor) in overridden method, ouputstream , set max size of cache 500. when invoke api returns more 500 characters in response payload, http 400
bad request status, response body contains entire json payload.
@provider public class responsepayloadinterceptor implements writerinterceptor { private static final logger logger = loggerfactory.getlogger(responsepayloadinterceptor.class); @override public void aroundwriteto(writerinterceptorcontext context) throws ioexception, webapplicationexception { final outputstream outputstream = context.getoutputstream(); cacheandwriteoutputstream cacheandwriteoutputstream = new cacheandwriteoutputstream(outputstream); cacheandwriteoutputstream.setmaxsize(500); context.setoutputstream(cacheandwriteoutputstream); context.proceed(); } }
option 2a: (cxf phase inteceptor) in overridden method, response string ouputstream , check it's size. if it's greater 500, create new response object data too data , set in message. if response > 500 characters, http 200
ok status entire json. when use phase post_marshal
or later phase, i'm able hold of json response , check it's length, time response has been streamed client.
@provider public class responsepayloadinterceptor extends abstractphaseinterceptor<message> { private static final logger logger = loggerfactory.getlogger(responsepayloadinterceptor.class); public responsepayloadinterceptor() { super(phase.post_marshal); } @override public void handlemessage(message message) throws fault { logger.info("handlemessage() - response intercepted"); try { outputstream outputstream = message.getcontent(outputstream.class); ... cachedoutputstream cachedoutputstream = (cachedoutputstream) outputstream; string responsebody = ioutils.tostring(cachedoutputstream.getinputstream(), "utf-8"); ... logger.info("handlemessage() - response: {}", responsebody); logger.info("handlemessage() - response length: {}", responsebody.length()); if (responsebody.length() > 500) { response response = response.status(response.status.bad_request) .entity("too data").build(); message.getexchange().put(response.class, response); } } catch (ioexception e) { logger.error("handlemessage() - error"); e.printstacktrace(); } } }
option 2b: (cxf phase inteceptor) same above, contents of if block changed. if response length greater 500, create new output stream string too data , set in message. if response payload > 500 characters, still http 200
ok status invalid json response (entire json + additional text) i.e., response looks this: [{"data":"", ...}, {...}]too data
(the text 'too data' appended json)
if (responsebody.length() > 500) { inputstream inputstream = new bytearrayinputstream("too data".getbytes("utf-8")); outputstream.flush(); ioutils.copy(inputstream, outputstream); outputstream out = new cachedoutputstream(); out.write("too data".getbytes("utf-8")); message.setcontent(outputstream.class, out); }
option 3: (containerresponsefilter) using containerresponsefilter, added content-length
response header value 500. if response length > 500, http 200
ok status invalid json response (truncated 500 characters). if response length < 500, still http 200
ok status, client waits more data returned server (as expected) , times out, isn't desirable solution.
@provider public class responsepayloadfilter implements containerresponsefilter { private static final logger logger = loggerfactory.getlogger(responsepayloadfilter.class); @override public void filter(containerrequestcontext requestcontext, containerresponsecontext responsecontext) throws ioexception { logger.info("filter() - response intercepted"); cachedoutputstream cos = (cachedoutputstream) responsecontext.getentitystream(); stringbuilder responsepayload = new stringbuilder(); bytearrayoutputstream out = new bytearrayoutputstream(); if (cos.getinputstream().available() > 0) { ioutils.copy(cos.getinputstream(), out); byte[] responseentity = out.tobytearray(); responsepayload.append(new string(responseentity)); } logger.info("filter() - content: {}", responsepayload.tostring()); responsecontext.getheaders().add("content-length", "500"); } }
any suggestions on how can tweak above approaches want or other different pointers?
i resolved partially using answer. partially because i'm able control payload, not response status code. ideally, if response length greater 500 , modify message content, send different response status code (other 200 ok). enough solution me proceed @ point. if figure out how update status code well, i'll come , update answer.
import org.apache.commons.io.ioutils; import org.apache.cxf.interceptor.fault; import org.apache.cxf.io.cachedoutputstream; import org.apache.cxf.message.message; import org.apache.cxf.phase.abstractphaseinterceptor; import org.apache.cxf.phase.phase; import org.slf4j.logger; import org.slf4j.loggerfactory; import java.io.ioexception; import java.io.inputstream; import java.io.outputstream; public class responsepayloadinterceptor extends abstractphaseinterceptor<message> { private static final logger logger = loggerfactory.getlogger(responsepayloadinterceptor.class); public responsepayloadinterceptor() { super(phase.pre_stream); } @override public void handlemessage(message message) throws fault { logger.info("handlemessage() - response intercepted"); try { outputstream outputstream = message.getcontent(outputstream.class); cachedoutputstream cachedoutputstream = new cachedoutputstream(); message.setcontent(outputstream.class, cachedoutputstream); message.getinterceptorchain().dointercept(message); cachedoutputstream.flush(); cachedoutputstream.close(); cachedoutputstream newcachedoutputstream = (cachedoutputstream) message.getcontent(outputstream.class); string currentresponse = ioutils.tostring(newcachedoutputstream.getinputstream(), "utf-8"); newcachedoutputstream.flush(); newcachedoutputstream.close(); if (currentresponse != null) { logger.info("handlemessage() - response: {}", currentresponse); logger.info("handlemessage() - response length: {}", currentresponse.length()); if (currentresponse.length() > 500) { inputstream replaceinputstream = ioutils.toinputstream("{\"message\":\"too data\"}", "utf-8"); ioutils.copy(replaceinputstream, outputstream); replaceinputstream.close(); message.setcontent(outputstream.class, outputstream); outputstream.flush(); outputstream.close(); } else { inputstream replaceinputstream = ioutils.toinputstream(currentresponse, "utf-8"); ioutils.copy(replaceinputstream, outputstream); replaceinputstream.close(); message.setcontent(outputstream.class, outputstream); outputstream.flush(); outputstream.close(); } } } catch (ioexception e) { logger.error("handlemessage() - error", e); throw new runtimeexception(e); } }