diff --git a/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java index 74e271a0..5b5ec8ec 100644 --- a/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java +++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java @@ -549,7 +549,7 @@ public boolean isWriteable(Class type, Type genericType, Annotation[] annotat */ @Override public void writeTo(Object value, Class type, Type genericType, Annotation[] annotations, - MediaType mediaType, + MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException { @@ -616,7 +616,7 @@ public void writeTo(Object value, Class type, Type genericType, Annotation[] // [Issue#32]: allow modification by filter-injectible thing ObjectWriterModifier mod = ObjectWriterInjector.getAndClear(); if (mod != null) { - writer = mod.modify(endpoint, value, writer, g); + writer = mod.modify(endpoint, httpHeaders, value, writer, g); } writer.writeValue(g, value); @@ -724,7 +724,9 @@ public boolean isReadable(Class type, Type genericType, Annotation[] annotati * Method that JAX-RS container calls to deserialize given value. */ @Override - public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) + public Object readFrom(Class type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap httpHeaders, + InputStream entityStream) throws IOException { AnnotationBundleKey key = new AnnotationBundleKey(annotations, type); @@ -757,7 +759,7 @@ public Object readFrom(Class type, Type genericType, Annotation[] annota // [Issue#32]: allow modification by filter-injectible thing ObjectReaderModifier mod = ObjectReaderInjector.getAndClear(); if (mod != null) { - reader = mod.modify(endpoint, resolvedType, reader, jp); + reader = mod.modify(endpoint, httpHeaders, resolvedType, reader, jp); } return reader.readValue(jp); } diff --git a/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/EndpointConfigBase.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/EndpointConfigBase.java index f698939d..e8ad836a 100644 --- a/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/EndpointConfigBase.java +++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/EndpointConfigBase.java @@ -21,21 +21,21 @@ public abstract class EndpointConfigBase> protected Class _activeView; protected String _rootName; - + // // Deserialization-only config protected DeserializationFeature[] _deserEnable; protected DeserializationFeature[] _deserDisable; protected ObjectReader _reader; - + // // Serialization-only config - + protected SerializationFeature[] _serEnable; protected SerializationFeature[] _serDisable; protected ObjectWriter _writer; - + /* /********************************************************** /* Construction @@ -129,6 +129,20 @@ protected THIS initWriter(ObjectWriter writer) /* Accessors /********************************************************** */ + + /** + * @since 2.3 + */ + public String getRootName() { + return _rootName; + } + + /** + * @since 2.3 + */ + public Class getActiveView() { + return _activeView; + } public final ObjectReader getReader() { if (_reader == null) { // sanity check, should never happen diff --git a/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/ObjectReaderModifier.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/ObjectReaderModifier.java index 14bfe98e..9532096e 100644 --- a/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/ObjectReaderModifier.java +++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/ObjectReaderModifier.java @@ -2,8 +2,9 @@ import java.io.IOException; -import com.fasterxml.jackson.core.*; +import javax.ws.rs.core.MultivaluedMap; +import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; /** @@ -16,11 +17,13 @@ public abstract class ObjectReaderModifier * used for reading request objects for specified endpoint. * * @param endpoint End point for which reader is used + * @param httpHeaders HTTP headers sent with request (read-only) * @param resultType Type that input is to be bound to * @param r ObjectReader as constructed for endpoint, type to handle * @param p Parser to use for reading content */ public abstract ObjectReader modify(EndpointConfigBase endpoint, + MultivaluedMap httpHeaders, JavaType resultType, ObjectReader r, JsonParser p) throws IOException; } diff --git a/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/ObjectWriterModifier.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/ObjectWriterModifier.java index fc93702e..8f5a9f76 100644 --- a/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/ObjectWriterModifier.java +++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/ObjectWriterModifier.java @@ -2,8 +2,9 @@ import java.io.IOException; -import com.fasterxml.jackson.core.*; +import javax.ws.rs.core.MultivaluedMap; +import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; /** @@ -14,8 +15,11 @@ public abstract class ObjectWriterModifier /** * Method called to let modifier make any changes it wants to to objects * used for writing response for specified endpoint. + * + * @param responseHeaders HTTP headers being returned with response (mutable) */ public abstract ObjectWriter modify(EndpointConfigBase endpoint, - Object valueToWrite, ObjectWriter r, JsonGenerator g) + MultivaluedMap responseHeaders, + Object valueToWrite, ObjectWriter w, JsonGenerator g) throws IOException; } diff --git a/json/src/test/java/com/fasterxml/jackson/jaxrs/json/JaxrsTestBase.java b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/JaxrsTestBase.java index 1e9eb92f..b7527cbf 100644 --- a/json/src/test/java/com/fasterxml/jackson/jaxrs/json/JaxrsTestBase.java +++ b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/JaxrsTestBase.java @@ -1,18 +1,24 @@ package com.fasterxml.jackson.jaxrs.json; import java.io.IOException; -import java.util.Arrays; +import java.util.*; + +import javax.servlet.DispatcherType; +import javax.servlet.Filter; import org.junit.Assert; + + // JAX-RS (jersey), Jetty stuff: import javax.ws.rs.core.Application; + import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import com.sun.jersey.spi.container.servlet.ServletContainer; +import com.sun.jersey.spi.container.servlet.ServletContainer; import com.fasterxml.jackson.core.*; public abstract class JaxrsTestBase @@ -25,6 +31,12 @@ public abstract class JaxrsTestBase */ protected Server startServer(int port, Class appClass) + { + return startServer(port, appClass, null); + } + + protected Server startServer(int port, Class appClass, + Class filterClass) { Server server = new Server(port); final ContextHandlerCollection contexts = new ContextHandlerCollection(); @@ -33,6 +45,11 @@ protected Server startServer(int port, Class appClass) jaxrs.setInitParameter("javax.ws.rs.Application", appClass.getName()); final ServletContextHandler mainHandler = new ServletContextHandler(contexts, "/", true, false); mainHandler.addServlet(jaxrs, "/*"); + + if (filterClass != null) { + mainHandler.addFilter(filterClass, "/*", java.util.EnumSet.allOf(DispatcherType.class)); + } + server.setHandler(mainHandler); try { server.start(); diff --git a/json/src/test/java/com/fasterxml/jackson/jaxrs/json/ResourceTestBase.java b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/ResourceTestBase.java new file mode 100644 index 00000000..df2168c2 --- /dev/null +++ b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/ResourceTestBase.java @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.jaxrs.json; + +import java.io.*; + +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.core.Application; + +public class ResourceTestBase extends JaxrsTestBase +{ + protected static abstract class JsonApplication extends Application + { + protected final Object _resource; + + protected JsonApplication(Object r) { _resource = r; } + + @Override + public Set getSingletons() { + HashSet singletons = new HashSet(); + singletons.add(new JacksonJsonProvider()); + singletons.add(_resource); + return singletons; + } + } + + protected String aposToQuotes(String json) { + return json.replace("'", "\""); + } + + protected String readUTF8(InputStream in) throws IOException + { + return new String(readAll(in), "UTF-8"); + } + + protected byte[] readAll(InputStream in) throws IOException + { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); + byte[] buffer = new byte[500]; + int count; + + while ((count = in.read(buffer)) > 0) { + bytes.write(buffer, 0, count); + } + in.close(); + return bytes.toByteArray(); + } +} diff --git a/json/src/test/java/com/fasterxml/jackson/jaxrs/json/dw/TestSimpleEndpoint.java b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/dw/TestSimpleEndpoint.java index 1bc5aa35..d21a99c2 100644 --- a/json/src/test/java/com/fasterxml/jackson/jaxrs/json/dw/TestSimpleEndpoint.java +++ b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/dw/TestSimpleEndpoint.java @@ -2,22 +2,21 @@ import java.io.*; import java.net.*; -import java.util.*; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; -import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import org.eclipse.jetty.server.Server; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; -import com.fasterxml.jackson.jaxrs.json.JaxrsTestBase; +import com.fasterxml.jackson.jaxrs.json.ResourceTestBase; -public class TestSimpleEndpoint extends JaxrsTestBase +public class TestSimpleEndpoint extends ResourceTestBase { + final static int TEST_PORT = 6011; + static class Point { public int x, y; @@ -41,21 +40,6 @@ public Point getPoint() { public static class SimpleResourceApp extends JsonApplication { public SimpleResourceApp() { super(new SimpleResource()); } } - - static abstract class JsonApplication extends Application - { - protected final Object _resource; - - protected JsonApplication(Object r) { _resource = r; } - - @Override - public Set getSingletons() { - HashSet singletons = new HashSet(); - singletons.add(new JacksonJsonProvider()); - singletons.add(_resource); - return singletons; - } - } /* /********************************************************** @@ -66,8 +50,8 @@ public Set getSingletons() { public void testStandardJson() throws Exception { final ObjectMapper mapper = new ObjectMapper(); - Server server = startServer(6061, SimpleResourceApp.class); - InputStream in = new URL("http://localhost:6061/point").openStream(); + Server server = startServer(TEST_PORT, SimpleResourceApp.class); + InputStream in = new URL("http://localhost:"+TEST_PORT+"/point").openStream(); Point p; try { diff --git a/json/src/test/java/com/fasterxml/jackson/jaxrs/json/dw/TestWriteModifications.java b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/dw/TestWriteModifications.java new file mode 100644 index 00000000..14603a58 --- /dev/null +++ b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/dw/TestWriteModifications.java @@ -0,0 +1,113 @@ +package com.fasterxml.jackson.jaxrs.json.dw; + +import java.io.IOException; +import java.net.*; + +import javax.servlet.*; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; + +import org.eclipse.jetty.server.Server; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.jaxrs.cfg.EndpointConfigBase; +import com.fasterxml.jackson.jaxrs.cfg.ObjectWriterInjector; +import com.fasterxml.jackson.jaxrs.cfg.ObjectWriterModifier; +import com.fasterxml.jackson.jaxrs.json.ResourceTestBase; + +public class TestWriteModifications extends ResourceTestBase +{ + final static int TEST_PORT = 6021; + + static class Point { + public int x, y; + + protected Point() { } + public Point(int x, int y) { + this.x = x; + this.y = y; + } + } + + @Path("/point") + public static class SimpleResource + { + @GET + @Produces(MediaType.APPLICATION_JSON) + public Point getPoint() { + return new Point(1, 2); + } + } + + public static class SimpleResourceApp extends JsonApplication { + public SimpleResourceApp() { super(new SimpleResource()); } + } + + public static class IndentingModifier extends ObjectWriterModifier + { + public static boolean doIndent = false; + + @Override + public ObjectWriter modify(EndpointConfigBase endpoint, + MultivaluedMap httpHeaders, + Object valueToWrite, ObjectWriter w, JsonGenerator g) + throws IOException + { + if (doIndent) { + g.useDefaultPrettyPrinter(); + } + return w; + } + } + + public static class InjectingFilter implements javax.servlet.Filter + { + @Override + public void init(FilterConfig filterConfig) throws ServletException { } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException + { + ObjectWriterInjector.set(new IndentingModifier()); + chain.doFilter(request, response); + } + + @Override + public void destroy() { } + } + + /* + /********************************************************** + /* Test methods + /********************************************************** + */ + + public void testIndentation() throws Exception + { + // We need a filter to inject modifier that enables + Server server = startServer(TEST_PORT, SimpleResourceApp.class, + InjectingFilter.class); + final URL url = new URL("http://localhost:"+TEST_PORT+"/point"); + + try { + // First, without indent: + IndentingModifier.doIndent = false; + String json = readUTF8(url.openStream()); + assertEquals(aposToQuotes("{'x':1,'y':2}"), json); + + // and then with indentation + IndentingModifier.doIndent = true; + final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.connect(); + json = readUTF8(url.openStream()); + assertEquals(aposToQuotes("{\n 'x' : 1,\n 'y' : 2\n}"), json); + } finally { + server.stop(); + } + } +}