[jackson-jaxrs-providers] 19/162: Refactoring

Timo Aaltonen tjaalton at moszumanska.debian.org
Mon Sep 8 22:16:24 UTC 2014


This is an automated email from the git hooks/post-receive script.

tjaalton pushed a commit to branch master
in repository jackson-jaxrs-providers.

commit b4768d07f3e63b3577ae2d63cdae1002efe41b9b
Author: Tatu Saloranta <tsaloranta at gmail.com>
Date:   Fri Mar 15 22:46:34 2013 -0700

    Refactoring
---
 .../fasterxml/jackson/jaxrs/base/ProviderBase.java | 534 ++++++++-----------
 .../jackson/jaxrs/base/cfg/EndpointConfigBase.java | 165 ++++++
 .../jackson/jaxrs/json/JacksonJsonProvider.java    | 591 ++-------------------
 .../jaxrs/json/annotation/EndpointConfig.java      | 153 +-----
 4 files changed, 439 insertions(+), 1004 deletions(-)

diff --git a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java
similarity index 65%
copy from json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
copy to base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java
index 65ec884..61f59ff 100644
--- a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
+++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java
@@ -1,77 +1,39 @@
-package com.fasterxml.jackson.jaxrs.json;
+package com.fasterxml.jackson.jaxrs.base;
 
 import java.io.*;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
 
-import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.StreamingOutput;
-import javax.ws.rs.ext.*;
+import javax.ws.rs.core.*;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.util.LRUMap;
 
+import com.fasterxml.jackson.jaxrs.base.cfg.EndpointConfigBase;
+import com.fasterxml.jackson.jaxrs.base.cfg.MapperConfiguratorBase;
 import com.fasterxml.jackson.jaxrs.base.util.AnnotationBundleKey;
 import com.fasterxml.jackson.jaxrs.base.util.ClassKey;
 
-import com.fasterxml.jackson.jaxrs.json.annotation.EndpointConfig;
-import com.fasterxml.jackson.jaxrs.json.cfg.MapperConfigurator;
-
-/**
- * Basic implementation of JAX-RS abstractions ({@link MessageBodyReader},
- * {@link MessageBodyWriter}) needed for binding
- * JSON ("application/json") content to and from Java Objects ("POJO"s).
- *<p>
- * Actual data binding functionality is implemented by {@link ObjectMapper}:
- * mapper to use can be configured in multiple ways:
- * <ul>
- *  <li>By explicitly passing mapper to use in constructor
- *  <li>By explictly setting mapper to use by {@link #setMapper}
- *  <li>By defining JAX-RS <code>Provider</code> that returns {@link ObjectMapper}s.
- *  <li>By doing none of above, in which case a default mapper instance is
- *     constructed (and configured if configuration methods are called)
- * </ul>
- * The last method ("do nothing specific") is often good enough; explicit passing
- * of Mapper is simple and explicit; and Provider-based method may make sense
- * with Depedency Injection frameworks, or if Mapper has to be configured differently
- * for different media types.
- *<p>
- * Note that the default mapper instance will be automatically created if
- * one of explicit configuration methods (like {@link #configure})
- * is called: if so, Provider-based introspection is <b>NOT</b> used, but the
- * resulting Mapper is used as configured.
- *<p>
- * Note: version 1.3 added a sub-class ({@link JacksonJaxbJsonProvider}) which
- * is configured by default to use both Jackson and JAXB annotations for configuration
- * (base class when used as-is defaults to using just Jackson annotations)
- *
- * @author Tatu Saloranta
- */
- at Provider
- at Consumes(MediaType.WILDCARD) // NOTE: required to support "non-standard" JSON variants
- at Produces(MediaType.WILDCARD)
-public class JacksonJsonProvider
+public abstract class ProviderBase<
+    THIS extends ProviderBase<THIS, MAPPER, ANN, EP_CONFIG, MAPPER_CONFIG>,
+    MAPPER extends ObjectMapper,
+    ANN extends Enum<ANN>,
+    EP_CONFIG extends EndpointConfigBase<EP_CONFIG>,
+    MAPPER_CONFIG extends MapperConfiguratorBase<MAPPER_CONFIG,MAPPER,ANN>
+>
     implements
         MessageBodyReader<Object>,
         MessageBodyWriter<Object>,
         Versioned
 {
     /**
-     * Default annotation sets to use, if not explicitly defined during
-     * construction: only Jackson annotations are used for the base
-     * class. Sub-classes can use other settings.
-     */
-    public final static Annotations[] BASIC_ANNOTATIONS = {
-        Annotations.JACKSON
-    };
-
-    /**
      * Looks like we need to worry about accidental
      *   data binding for types we shouldn't be handling. This is
      *   probably not a very good way to do it, but let's start by
@@ -80,16 +42,16 @@ public class JacksonJsonProvider
      *  (why ClassKey? since plain old Class has no hashCode() defined,
      *  lookups are painfully slow)
      */
-    public final static HashSet<ClassKey> _untouchables = new HashSet<ClassKey>();
+    protected final static HashSet<ClassKey> DEFAULT_UNTOUCHABLES = new HashSet<ClassKey>();
     static {
         // First, I/O things (direct matches)
-        _untouchables.add(new ClassKey(java.io.InputStream.class));
-        _untouchables.add(new ClassKey(java.io.Reader.class));
-        _untouchables.add(new ClassKey(java.io.OutputStream.class));
-        _untouchables.add(new ClassKey(java.io.Writer.class));
+        DEFAULT_UNTOUCHABLES.add(new ClassKey(java.io.InputStream.class));
+        DEFAULT_UNTOUCHABLES.add(new ClassKey(java.io.Reader.class));
+        DEFAULT_UNTOUCHABLES.add(new ClassKey(java.io.OutputStream.class));
+        DEFAULT_UNTOUCHABLES.add(new ClassKey(java.io.Writer.class));
 
         // then some primitive types
-        _untouchables.add(new ClassKey(char[].class));
+        DEFAULT_UNTOUCHABLES.add(new ClassKey(char[].class));
 
         /* 28-Jan-2012, tatu: 1.x excluded some additional types;
          *   but let's relax these a bit:
@@ -98,15 +60,15 @@ public class JacksonJsonProvider
          *   [https://github.com/FasterXML/jackson-jaxrs-json-provider/issues/12]
          *  better revert this back, to make them untouchable again.
          */
-        _untouchables.add(new ClassKey(String.class));
-        _untouchables.add(new ClassKey(byte[].class));
+        DEFAULT_UNTOUCHABLES.add(new ClassKey(String.class));
+        DEFAULT_UNTOUCHABLES.add(new ClassKey(byte[].class));
     }
 
     /**
      * These are classes that we never use for reading
      * (never try to deserialize instances of these types).
      */
-    public final static Class<?>[] _unreadableClasses = new Class<?>[] {
+    public final static Class<?>[] DEFAULT_UNREADABLES = new Class<?>[] {
         InputStream.class, Reader.class
     };
 
@@ -114,40 +76,16 @@ public class JacksonJsonProvider
      * These are classes that we never use for writing
      * (never try to serialize instances of these types).
      */
-    public final static Class<?>[] _unwritableClasses = new Class<?>[] {
+    public final static Class<?>[] DEFAULT_UNWRITABLES = new Class<?>[] {
         OutputStream.class, Writer.class,
         StreamingOutput.class, Response.class
     };
 
     /*
     /**********************************************************
-    /* Bit of caching
-    /**********************************************************
-     */
-
-    /**
-     * Cache for resolved endpoint configurations when reading JSON data
-     */
-    protected final LRUMap<AnnotationBundleKey, EndpointConfig> _readers
-        = new LRUMap<AnnotationBundleKey, EndpointConfig>(16, 120);
-
-    /**
-     * Cache for resolved endpoint configurations when writing JSON data
-     */
-    protected final LRUMap<AnnotationBundleKey, EndpointConfig> _writers
-        = new LRUMap<AnnotationBundleKey, EndpointConfig>(16, 120);
-    
-    /*
-    /**********************************************************
     /* General configuration
     /**********************************************************
      */
-    
-    /**
-     * Helper object used for encapsulating configuration aspects
-     * of {@link ObjectMapper}
-     */
-    protected final MapperConfigurator _mapperConfig;
 
     /**
      * Set of types (classes) that provider should ignore for data binding
@@ -155,34 +93,6 @@ public class JacksonJsonProvider
     protected HashSet<ClassKey> _cfgCustomUntouchables;
 
     /**
-     * JSONP function name to use for automatic JSONP wrapping, if any;
-     * if null, no JSONP wrapping is done.
-     * Note that this is the default value that can be overridden on
-     * per-endpoint basis.
-     */
-    protected String _jsonpFunctionName;
-    
-    /*
-    /**********************************************************
-    /* Context configuration
-    /**********************************************************
-     */
-
-    /**
-     * Injectable context object used to locate configured
-     * instance of {@link ObjectMapper} to use for actual
-     * serialization.
-     */
-    @Context
-    protected Providers _providers;
-
-    /*
-    /**********************************************************
-    /* Configuration
-    /**********************************************************
-     */
-
-    /**
      * Whether we want to actually check that Jackson has
      * a serializer for given type. Since this should generally
      * be the case (due to auto-discovery) and since the call
@@ -200,52 +110,48 @@ public class JacksonJsonProvider
 
     /*
     /**********************************************************
-    /* Construction
+    /* Excluded types
     /**********************************************************
      */
+    
+    public final static HashSet<ClassKey> _untouchables = DEFAULT_UNTOUCHABLES;
 
-    /**
-     * Default constructor, usually used when provider is automatically
-     * configured to be used with JAX-RS implementation.
+    public final static Class<?>[] _unreadableClasses = DEFAULT_UNREADABLES;
+
+    public final static Class<?>[] _unwritableClasses = DEFAULT_UNWRITABLES;
+
+    /*
+    /**********************************************************
+    /* Bit of caching
+    /**********************************************************
      */
-    public JacksonJsonProvider()
-    {
-        this(null, BASIC_ANNOTATIONS);
-    }
 
     /**
-     * @param annotationsToUse Annotation set(s) to use for configuring
-     *    data binding
+     * Cache for resolved endpoint configurations when reading JSON data
      */
-    public JacksonJsonProvider(Annotations... annotationsToUse)
-    {
-        this(null, annotationsToUse);
-    }
+    protected final LRUMap<AnnotationBundleKey, EP_CONFIG> _readers
+        = new LRUMap<AnnotationBundleKey, EP_CONFIG>(16, 120);
 
-    public JacksonJsonProvider(ObjectMapper mapper)
-    {
-        this(mapper, BASIC_ANNOTATIONS);
-    }
-    
     /**
-     * Constructor to use when a custom mapper (usually components
-     * like serializer/deserializer factories that have been configured)
-     * is to be used.
-     * 
-     * @param annotationsToUse Sets of annotations (Jackson, JAXB) that provider should
-     *   support
+     * Cache for resolved endpoint configurations when writing JSON data
      */
-    public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse)
-    {
-        _mapperConfig = new MapperConfigurator(mapper, annotationsToUse);
-    }
-
+    protected final LRUMap<AnnotationBundleKey, EP_CONFIG> _writers
+        = new LRUMap<AnnotationBundleKey, EP_CONFIG>(16, 120);
+    
+    /*
+    /**********************************************************
+    /* General configuration
+    /**********************************************************
+     */
+    
     /**
-     * Method that will return version information stored in and read from jar
-     * that contains this class.
+     * Helper object used for encapsulating configuration aspects
+     * of {@link ObjectMapper}
      */
-    public Version version() {
-        return PackageVersion.VERSION;
+    protected final MAPPER_CONFIG _mapperConfig;
+
+    protected ProviderBase(MAPPER_CONFIG mconfig) {
+        _mapperConfig = mconfig;
     }
     
     /*
@@ -269,6 +175,23 @@ public class JacksonJsonProvider
     public void checkCanSerialize(boolean state) { _cfgCheckCanSerialize = state; }
 
     /**
+     * Method for marking specified type as "untouchable", meaning that provider
+     * will not try to read or write values of this type (or its subtypes).
+     * 
+     * @param type Type to consider untouchable; can be any kind of class,
+     *   including abstract class or interface. No instance of this type
+     *   (including subtypes, i.e. types assignable to this type) will
+     *   be read or written by provider
+     */
+    public void addUntouchable(Class<?> type)
+    {
+        if (_cfgCustomUntouchables == null) {
+            _cfgCustomUntouchables = new HashSet<ClassKey>();
+        }
+        _cfgCustomUntouchables.add(new ClassKey(type));
+    }
+
+    /**
      * Method for configuring which annotation sets to use (including none).
      * Annotation sets are defined in order decreasing precedence; that is,
      * first one has the priority over following ones.
@@ -276,7 +199,7 @@ public class JacksonJsonProvider
      * @param annotationsToUse Ordered list of annotation sets to use; if null,
      *    default
      */
-    public void setAnnotationsToUse(Annotations[] annotationsToUse) {
+    public void setAnnotationsToUse(ANN[] annotationsToUse) {
         _mapperConfig.setAnnotationsToUse(annotationsToUse);
     }
     
@@ -285,174 +208,96 @@ public class JacksonJsonProvider
      * for serialization and deserialization; if null, will use the standard
      * provider discovery from context instead. Default setting is null.
      */
-    public void setMapper(ObjectMapper m) {
+    public void setMapper(MAPPER m) {
         _mapperConfig.setMapper(m);
     }
 
-    public JacksonJsonProvider configure(DeserializationFeature f, boolean state) {
+    public THIS configure(DeserializationFeature f, boolean state) {
         _mapperConfig.configure(f, state);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider configure(SerializationFeature f, boolean state) {
+    public THIS configure(SerializationFeature f, boolean state) {
         _mapperConfig.configure(f, state);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider configure(JsonParser.Feature f, boolean state) {
+    public THIS configure(JsonParser.Feature f, boolean state) {
         _mapperConfig.configure(f, state);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider configure(JsonGenerator.Feature f, boolean state) {
+    public THIS configure(JsonGenerator.Feature f, boolean state) {
         _mapperConfig.configure(f, state);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider enable(DeserializationFeature f, boolean state) {
+    public THIS enable(DeserializationFeature f, boolean state) {
         _mapperConfig.configure(f, true);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider enable(SerializationFeature f, boolean state) {
+    public THIS enable(SerializationFeature f, boolean state) {
         _mapperConfig.configure(f, true);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider enable(JsonParser.Feature f, boolean state) {
+    public THIS enable(JsonParser.Feature f, boolean state) {
         _mapperConfig.configure(f, true);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider enable(JsonGenerator.Feature f, boolean state) {
+    public THIS enable(JsonGenerator.Feature f, boolean state) {
         _mapperConfig.configure(f, true);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider disable(DeserializationFeature f, boolean state) {
+    public THIS disable(DeserializationFeature f, boolean state) {
         _mapperConfig.configure(f, false);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider disable(SerializationFeature f, boolean state) {
+    public THIS disable(SerializationFeature f, boolean state) {
         _mapperConfig.configure(f, false);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider disable(JsonParser.Feature f, boolean state) {
+    public THIS disable(JsonParser.Feature f, boolean state) {
         _mapperConfig.configure(f, false);
-        return this;
+        return _this();
     }
 
-    public JacksonJsonProvider disable(JsonGenerator.Feature f, boolean state) {
+    public THIS disable(JsonGenerator.Feature f, boolean state) {
         _mapperConfig.configure(f, false);
-        return this;
+        return _this();
     }
 
-    /**
-     * Method for marking specified type as "untouchable", meaning that provider
-     * will not try to read or write values of this type (or its subtypes).
-     * 
-     * @param type Type to consider untouchable; can be any kind of class,
-     *   including abstract class or interface. No instance of this type
-     *   (including subtypes, i.e. types assignable to this type) will
-     *   be read or written by provider
-     */
-    public void addUntouchable(Class<?> type)
-    {
-        if (_cfgCustomUntouchables == null) {
-            _cfgCustomUntouchables = new HashSet<ClassKey>();
-        }
-        _cfgCustomUntouchables.add(new ClassKey(type));
-    }
-
-    public void setJSONPFunctionName(String fname) {
-        _jsonpFunctionName = fname;
-    }
-    
     /*
     /**********************************************************
-    /* MessageBodyReader impl
+    /* Abstract methods sub-classes need to implement
     /**********************************************************
      */
-
-    /**
-     * Method that JAX-RS container calls to try to check whether
-     * values of given type (and media type) can be deserialized by
-     * this provider.
-     * Implementation will first check that expected media type is
-     * a JSON type (via call to {@link #isJsonType}; then verify
-     * that type is not one of "untouchable" types (types we will never
-     * automatically handle), and finally that there is a deserializer
-     * for type (iff {@link #checkCanDeserialize} has been called with
-     * true argument -- otherwise assumption is there will be a handler)
-     */
-    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
-    {
-        if (!isJsonType(mediaType)) {
-            return false;
-        }
-
-        /* Ok: looks like we must weed out some core types here; ones that
-         * make no sense to try to bind from JSON:
-         */
-        if (_untouchables.contains(new ClassKey(type))) {
-            return false;
-        }
-        // and there are some other abstract/interface types to exclude too:
-        for (Class<?> cls : _unreadableClasses) {
-            if (cls.isAssignableFrom(type)) {
-                return false;
-            }
-        }
-        // as well as possible custom exclusions
-        if (_containedIn(type, _cfgCustomUntouchables)) {
-            return false;
-        }
-
-        // Finally: if we really want to verify that we can serialize, we'll check:
-        if (_cfgCheckCanSerialize) {
-            ObjectMapper mapper = locateMapper(type, mediaType);
-            if (!mapper.canDeserialize(mapper.constructType(type))) {
-                return false;
-            }
-        }
-        return true;
-    }
     
     /**
-     * Method that JAX-RS container calls to deserialize given value.
+     * Helper method used to check whether given media type
+     * is JSON type or sub type.
+     * Current implementation essentially checks to see whether
+     * {@link MediaType#getSubtype} returns "json" or something
+     * ending with "+json".
+     * 
+     * @since 2.2
      */
-    public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,String> httpHeaders, InputStream entityStream) 
-        throws IOException
-    {
+    protected abstract boolean hasMatchingMediaType(MediaType mediaType);
 
-        AnnotationBundleKey key = new AnnotationBundleKey(annotations);
-        EndpointConfig endpoint;
-        synchronized (_readers) {
-            endpoint = _readers.get(key);
-        }
-        // not yet resolved (or not cached any more)? Resolve!
-        if (endpoint == null) {
-            ObjectMapper mapper = locateMapper(type, mediaType);
-            endpoint = EndpointConfig.forReading(mapper, annotations);
-            // and cache for future reuse
-            synchronized (_readers) {
-                _readers.put(key.immutableKey(), endpoint);
-            }
-        }
-        ObjectReader reader = endpoint.getReader();
-        
-        JsonParser jp = reader.getFactory().createParser(entityStream);
-        if (jp.nextToken() == null) {
-           return null;
-        }
-        return reader.withType(genericType).readValue(jp);
-    }
+    protected abstract MAPPER _locateMapperViaProvider(Class<?> type, MediaType mediaType);
 
+    protected abstract EP_CONFIG _configForReading(MAPPER mapper, Annotation[] annotations);
+
+    protected abstract EP_CONFIG _configForWriting(MAPPER mapper, Annotation[] annotations);
+    
     /*
     /**********************************************************
-    /* MessageBodyWriter impl
+    /* Partial MessageBodyWriter impl
     /**********************************************************
      */
 
@@ -471,7 +316,7 @@ public class JacksonJsonProvider
          */
         return -1;
     }
-
+    
     /**
      * Method that JAX-RS container calls to try to check whether
      * given value (of specified type) can be serialized by
@@ -485,7 +330,7 @@ public class JacksonJsonProvider
      */
     public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
     {
-        if (!isJsonType(mediaType)) {
+        if (!hasMatchingMediaType(mediaType)) {
             return false;
         }
 
@@ -523,15 +368,14 @@ public class JacksonJsonProvider
         throws IOException
     {
         AnnotationBundleKey key = new AnnotationBundleKey(annotations);
-        EndpointConfig endpoint;
+        EP_CONFIG endpoint;
         synchronized (_writers) {
             endpoint = _writers.get(key);
         }
         // not yet resolved (or not cached any more)? Resolve!
         if (endpoint == null) {
-            ObjectMapper mapper = locateMapper(type, mediaType);
-            endpoint = EndpointConfig.forWriting(mapper, annotations,
-                    this._jsonpFunctionName);
+            MAPPER mapper = locateMapper(type, mediaType);
+            endpoint = _configForWriting(mapper, annotations);
             // and cache for future reuse
             synchronized (_writers) {
                 _writers.put(key.immutableKey(), endpoint);
@@ -579,9 +423,7 @@ public class JacksonJsonProvider
         if (rootType != null) {
             writer = writer.withType(rootType);
         }
-        // and finally, JSONP wrapping, if any:
-        value = endpoint.applyJSONP(value);
-        
+        value = endpoint.modifyBeforeWrite(value);
         writer.writeValue(jg, value);
     }
 
@@ -593,39 +435,92 @@ public class JacksonJsonProvider
     {
         return JsonEncoding.UTF8;
     }
-    
+
     /*
     /**********************************************************
-    /* Public helper methods
+    /* MessageBodyReader impl
     /**********************************************************
      */
-
+    
     /**
-     * Helper method used to check whether given media type
-     * is JSON type or sub type.
-     * Current implementation essentially checks to see whether
-     * {@link MediaType#getSubtype} returns "json" or something
-     * ending with "+json".
+     * Method that JAX-RS container calls to try to check whether
+     * values of given type (and media type) can be deserialized by
+     * this provider.
+     * Implementation will first check that expected media type is
+     * a JSON type (via call to {@link #isJsonType}; then verify
+     * that type is not one of "untouchable" types (types we will never
+     * automatically handle), and finally that there is a deserializer
+     * for type (iff {@link #checkCanDeserialize} has been called with
+     * true argument -- otherwise assumption is there will be a handler)
      */
-    protected boolean isJsonType(MediaType mediaType)
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
     {
-        /* As suggested by Stephen D, there are 2 ways to check: either
-         * being as inclusive as possible (if subtype is "json"), or
-         * exclusive (major type "application", minor type "json").
-         * Let's start with inclusive one, hard to know which major
-         * types we should cover aside from "application".
-         */
-        if (mediaType != null) {
-            // Ok: there are also "xxx+json" subtypes, which count as well
-            String subtype = mediaType.getSubtype();
-            return "json".equalsIgnoreCase(subtype) || subtype.endsWith("+json");
+        if (!hasMatchingMediaType(mediaType)) {
+            return false;
         }
-        /* Not sure if this can happen; but it seems reasonable
-         * that we can at least produce json without media type?
+
+        /* Ok: looks like we must weed out some core types here; ones that
+         * make no sense to try to bind from JSON:
          */
+        if (_untouchables.contains(new ClassKey(type))) {
+            return false;
+        }
+        // and there are some other abstract/interface types to exclude too:
+        for (Class<?> cls : _unreadableClasses) {
+            if (cls.isAssignableFrom(type)) {
+                return false;
+            }
+        }
+        // as well as possible custom exclusions
+        if (_containedIn(type, _cfgCustomUntouchables)) {
+            return false;
+        }
+
+        // Finally: if we really want to verify that we can serialize, we'll check:
+        if (_cfgCheckCanSerialize) {
+            ObjectMapper mapper = locateMapper(type, mediaType);
+            if (!mapper.canDeserialize(mapper.constructType(type))) {
+                return false;
+            }
+        }
         return true;
     }
-
+    
+    /**
+     * Method that JAX-RS container calls to deserialize given value.
+     */
+    public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,String> httpHeaders, InputStream entityStream) 
+        throws IOException
+    {
+        AnnotationBundleKey key = new AnnotationBundleKey(annotations);
+        EP_CONFIG endpoint;
+        synchronized (_readers) {
+            endpoint = _readers.get(key);
+        }
+        // not yet resolved (or not cached any more)? Resolve!
+        if (endpoint == null) {
+            MAPPER mapper = locateMapper(type, mediaType);
+            endpoint = _configForReading(mapper, annotations);
+            // and cache for future reuse
+            synchronized (_readers) {
+                _readers.put(key.immutableKey(), endpoint);
+            }
+        }
+        ObjectReader reader = endpoint.getReader();
+        
+        JsonParser jp = reader.getFactory().createParser(entityStream);
+        if (jp.nextToken() == null) {
+           return null;
+        }
+        return reader.withType(genericType).readValue(jp);
+    }
+    
+    /*
+    /**********************************************************
+    /* Private/sub-class helper methods
+    /**********************************************************
+     */
+    
     /**
      * Method called to locate {@link ObjectMapper} to use for serialization
      * and deserialization. If an instance has been explicitly defined by
@@ -646,26 +541,13 @@ public class JacksonJsonProvider
      *   not used by this method,
      *   but will be passed to {@link ContextResolver} as is.
      */
-    public ObjectMapper locateMapper(Class<?> type, MediaType mediaType)
+    public MAPPER locateMapper(Class<?> type, MediaType mediaType)
     {
         // First: were we configured with a specific instance?
-        ObjectMapper m = _mapperConfig.getConfiguredMapper();
+        MAPPER m = _mapperConfig.getConfiguredMapper();
         if (m == null) {
             // If not, maybe we can get one configured via context?
-            if (_providers != null) {
-                ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
-                /* Above should work as is, but due to this bug
-                 *   [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
-                 * in Jersey, it doesn't. But this works until resolution of
-                 * the issue:
-                 */
-                if (resolver == null) {
-                    resolver = _providers.getContextResolver(ObjectMapper.class, null);
-                }
-                if (resolver != null) {
-                    m = resolver.getContext(type);
-                }
-            }
+            m = _locateMapperViaProvider(type, mediaType);
             if (m == null) {
                 // If not, let's get the fallback default instance
                 m = _mapperConfig.getDefaultMapper();
@@ -673,13 +555,7 @@ public class JacksonJsonProvider
         }
         return m;
     }
-
-    /*
-    /**********************************************************
-    /* Private/sub-class helper methods
-    /**********************************************************
-     */
-
+    
     protected static boolean _containedIn(Class<?> mainType, HashSet<ClassKey> set)
     {
         if (set != null) {
@@ -695,18 +571,18 @@ public class JacksonJsonProvider
         return false;
     }
 
-    private static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore)
+    protected static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore)
     {
         return findSuperTypes(cls, endBefore, new ArrayList<Class<?>>(8));
     }
 
-    private static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore, List<Class<?>> result)
+    protected static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore, List<Class<?>> result)
     {
         _addSuperTypes(cls, endBefore, result, false);
         return result;
     }
     
-    private static void _addSuperTypes(Class<?> cls, Class<?> endBefore, Collection<Class<?>> result, boolean addClassItself)
+    protected static void _addSuperTypes(Class<?> cls, Class<?> endBefore, Collection<Class<?>> result, boolean addClassItself)
     {
         if (cls == endBefore || cls == null || cls == Object.class) {
             return;
@@ -723,4 +599,8 @@ public class JacksonJsonProvider
         _addSuperTypes(cls.getSuperclass(), endBefore, result, true);
     }
 
+    @SuppressWarnings("unchecked")
+    private final THIS _this() {
+        return (THIS) this;
+    }
 }
diff --git a/base/src/main/java/com/fasterxml/jackson/jaxrs/base/cfg/EndpointConfigBase.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/cfg/EndpointConfigBase.java
new file mode 100644
index 0000000..da7ec2a
--- /dev/null
+++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/cfg/EndpointConfigBase.java
@@ -0,0 +1,165 @@
+package com.fasterxml.jackson.jaxrs.base.cfg;
+
+import java.lang.annotation.Annotation;
+
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+import com.fasterxml.jackson.annotation.JsonRootName;
+import com.fasterxml.jackson.annotation.JsonView;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Container class for figuring out annotation-based configuration
+ * for JAX-RS end points.
+ */
+public abstract class EndpointConfigBase<THIS extends EndpointConfigBase<THIS>>
+{
+    // // General configuration
+    
+    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
+    /**********************************************************
+     */
+
+    protected EndpointConfigBase() { }
+    
+    @SuppressWarnings("unchecked")
+    protected THIS add(Annotation[] annotations, boolean forWriting)
+    {
+        if (annotations != null) {
+            for (Annotation annotation : annotations) {
+                addAnnotation(annotation.annotationType(), annotation, forWriting);
+            }
+        }
+        return (THIS) this;
+    }
+
+    protected void addAnnotation(Class<? extends Annotation> type,
+            Annotation annotation, boolean forWriting)
+    {
+        if (type == JsonView.class) {
+            // Can only use one view; but if multiple defined, use first (no exception)
+            Class<?>[] views = ((JsonView) annotation).value();
+            _activeView = (views.length > 0) ? views[0] : null;
+        } else if (type == JsonRootName.class) {
+            _rootName = ((JsonRootName) annotation).value();
+        } else if (type == JacksonAnnotationsInside.class) {
+            // skip; processed below (in parent), so encountering here is of no use
+        } else {
+            // For all unrecognized types, check meta-annotation(s) to see if they are bundles
+            JacksonAnnotationsInside inside = type.getAnnotation(JacksonAnnotationsInside.class);
+            if (inside != null) {
+                add(type.getAnnotations(), forWriting);
+            }
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected THIS initReader(ObjectMapper mapper)
+    {
+        // first common config
+        if (_activeView != null) {
+            _reader = mapper.readerWithView(_activeView);
+        } else {
+            _reader = mapper.reader();
+        }
+
+        if (_rootName != null) {
+            _reader = _reader.withRootName(_rootName);
+        }
+        // Then deser features
+        if (_deserEnable != null) {
+            _reader = _reader.withFeatures(_deserEnable);
+        }
+        if (_deserDisable != null) {
+            _reader = _reader.withoutFeatures(_deserDisable);
+        }
+        /* Important: we are NOT to close the underlying stream after
+         * mapping, so we need to instruct parser:
+         */
+        _reader.getFactory().disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
+        
+        return (THIS) this;
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected THIS initWriter(ObjectMapper mapper)
+    {
+        // first common config
+        if (_activeView != null) {
+            _writer = mapper.writerWithView(_activeView);
+        } else {
+            _writer = mapper.writer();
+        }
+        if (_rootName != null) {
+            _writer = _writer.withRootName(_rootName);
+        }
+        // Then features
+        if (_serEnable != null) {
+            _writer = _writer.withFeatures(_serEnable);
+        }
+        if (_serDisable != null) {
+            _writer = _writer.withoutFeatures(_serDisable);
+        }
+        // then others
+
+        // Finally: couple of features we always set
+
+        /* Important: we are NOT to close the underlying stream after
+         * mapping, so we need to instruct parser:
+         */
+        _writer.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
+        
+        return (THIS) this;
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    public final ObjectReader getReader() {
+        if (_reader == null) { // sanity check, should never happen
+            throw new IllegalStateException();
+        }
+        return _reader;
+    }
+
+    public final ObjectWriter getWriter() {
+        if (_writer == null) { // sanity check, should never happen
+            throw new IllegalStateException();
+        }
+        return _writer;
+    }
+
+    /*
+    /**********************************************************
+    /* Value modifications
+    /**********************************************************
+     */
+
+    public abstract Object modifyBeforeWrite(Object value);
+}
diff --git a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java b/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
index 65ec884..9a211db 100644
--- a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
+++ b/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
@@ -1,24 +1,15 @@
 package com.fasterxml.jackson.jaxrs.json;
 
-import java.io.*;
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.*;
 
 import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.core.*;
 import javax.ws.rs.ext.*;
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.util.LRUMap;
 
-import com.fasterxml.jackson.jaxrs.base.util.AnnotationBundleKey;
-import com.fasterxml.jackson.jaxrs.base.util.ClassKey;
+import com.fasterxml.jackson.jaxrs.base.ProviderBase;
 
 import com.fasterxml.jackson.jaxrs.json.annotation.EndpointConfig;
 import com.fasterxml.jackson.jaxrs.json.cfg.MapperConfigurator;
@@ -57,10 +48,9 @@ import com.fasterxml.jackson.jaxrs.json.cfg.MapperConfigurator;
 @Consumes(MediaType.WILDCARD) // NOTE: required to support "non-standard" JSON variants
 @Produces(MediaType.WILDCARD)
 public class JacksonJsonProvider
-    implements
-        MessageBodyReader<Object>,
-        MessageBodyWriter<Object>,
-        Versioned
+    extends ProviderBase<JacksonJsonProvider,
+        ObjectMapper, Annotations,
+        EndpointConfig, MapperConfigurator>
 {
     /**
      * Default annotation sets to use, if not explicitly defined during
@@ -70,89 +60,12 @@ public class JacksonJsonProvider
     public final static Annotations[] BASIC_ANNOTATIONS = {
         Annotations.JACKSON
     };
-
-    /**
-     * Looks like we need to worry about accidental
-     *   data binding for types we shouldn't be handling. This is
-     *   probably not a very good way to do it, but let's start by
-     *   blacklisting things we are not to handle.
-     *<p>
-     *  (why ClassKey? since plain old Class has no hashCode() defined,
-     *  lookups are painfully slow)
-     */
-    public final static HashSet<ClassKey> _untouchables = new HashSet<ClassKey>();
-    static {
-        // First, I/O things (direct matches)
-        _untouchables.add(new ClassKey(java.io.InputStream.class));
-        _untouchables.add(new ClassKey(java.io.Reader.class));
-        _untouchables.add(new ClassKey(java.io.OutputStream.class));
-        _untouchables.add(new ClassKey(java.io.Writer.class));
-
-        // then some primitive types
-        _untouchables.add(new ClassKey(char[].class));
-
-        /* 28-Jan-2012, tatu: 1.x excluded some additional types;
-         *   but let's relax these a bit:
-         */
-        /* 27-Apr-2012, tatu: Ugh. As per
-         *   [https://github.com/FasterXML/jackson-jaxrs-json-provider/issues/12]
-         *  better revert this back, to make them untouchable again.
-         */
-        _untouchables.add(new ClassKey(String.class));
-        _untouchables.add(new ClassKey(byte[].class));
-    }
-
-    /**
-     * These are classes that we never use for reading
-     * (never try to deserialize instances of these types).
-     */
-    public final static Class<?>[] _unreadableClasses = new Class<?>[] {
-        InputStream.class, Reader.class
-    };
-
-    /**
-     * These are classes that we never use for writing
-     * (never try to serialize instances of these types).
-     */
-    public final static Class<?>[] _unwritableClasses = new Class<?>[] {
-        OutputStream.class, Writer.class,
-        StreamingOutput.class, Response.class
-    };
-
-    /*
-    /**********************************************************
-    /* Bit of caching
-    /**********************************************************
-     */
-
-    /**
-     * Cache for resolved endpoint configurations when reading JSON data
-     */
-    protected final LRUMap<AnnotationBundleKey, EndpointConfig> _readers
-        = new LRUMap<AnnotationBundleKey, EndpointConfig>(16, 120);
-
-    /**
-     * Cache for resolved endpoint configurations when writing JSON data
-     */
-    protected final LRUMap<AnnotationBundleKey, EndpointConfig> _writers
-        = new LRUMap<AnnotationBundleKey, EndpointConfig>(16, 120);
     
     /*
     /**********************************************************
     /* General configuration
     /**********************************************************
      */
-    
-    /**
-     * Helper object used for encapsulating configuration aspects
-     * of {@link ObjectMapper}
-     */
-    protected final MapperConfigurator _mapperConfig;
-
-    /**
-     * Set of types (classes) that provider should ignore for data binding
-     */
-    protected HashSet<ClassKey> _cfgCustomUntouchables;
 
     /**
      * JSONP function name to use for automatic JSONP wrapping, if any;
@@ -178,28 +91,6 @@ public class JacksonJsonProvider
 
     /*
     /**********************************************************
-    /* Configuration
-    /**********************************************************
-     */
-
-    /**
-     * Whether we want to actually check that Jackson has
-     * a serializer for given type. Since this should generally
-     * be the case (due to auto-discovery) and since the call
-     * to check availability can be bit expensive, defaults to false.
-     */
-    protected boolean _cfgCheckCanSerialize = false;
-
-    /**
-     * Whether we want to actually check that Jackson has
-     * a deserializer for given type. Since this should generally
-     * be the case (due to auto-discovery) and since the call
-     * to check availability can be bit expensive, defaults to false.
-     */
-    protected boolean _cfgCheckCanDeserialize = false;
-
-    /*
-    /**********************************************************
     /* Construction
     /**********************************************************
      */
@@ -208,8 +99,7 @@ public class JacksonJsonProvider
      * Default constructor, usually used when provider is automatically
      * configured to be used with JAX-RS implementation.
      */
-    public JacksonJsonProvider()
-    {
+    public JacksonJsonProvider() {
         this(null, BASIC_ANNOTATIONS);
     }
 
@@ -217,13 +107,11 @@ public class JacksonJsonProvider
      * @param annotationsToUse Annotation set(s) to use for configuring
      *    data binding
      */
-    public JacksonJsonProvider(Annotations... annotationsToUse)
-    {
+    public JacksonJsonProvider(Annotations... annotationsToUse) {
         this(null, annotationsToUse);
     }
 
-    public JacksonJsonProvider(ObjectMapper mapper)
-    {
+    public JacksonJsonProvider(ObjectMapper mapper) {
         this(mapper, BASIC_ANNOTATIONS);
     }
     
@@ -235,9 +123,8 @@ public class JacksonJsonProvider
      * @param annotationsToUse Sets of annotations (Jackson, JAXB) that provider should
      *   support
      */
-    public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse)
-    {
-        _mapperConfig = new MapperConfigurator(mapper, annotationsToUse);
+    public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse) {
+        super(new MapperConfigurator(mapper, annotationsToUse));
     }
 
     /**
@@ -250,364 +137,30 @@ public class JacksonJsonProvider
     
     /*
     /**********************************************************
-    /* Configuring
+    /* JSON-specific configuration
     /**********************************************************
      */
 
-    /**
-     * Method for defining whether actual detection for existence of
-     * a deserializer for type should be done when {@link #isReadable}
-     * is called.
-     */
-    public void checkCanDeserialize(boolean state) { _cfgCheckCanDeserialize = state; }
-
-    /**
-     * Method for defining whether actual detection for existence of
-     * a serializer for type should be done when {@link #isWriteable}
-     * is called.
-     */
-    public void checkCanSerialize(boolean state) { _cfgCheckCanSerialize = state; }
-
-    /**
-     * Method for configuring which annotation sets to use (including none).
-     * Annotation sets are defined in order decreasing precedence; that is,
-     * first one has the priority over following ones.
-     * 
-     * @param annotationsToUse Ordered list of annotation sets to use; if null,
-     *    default
-     */
-    public void setAnnotationsToUse(Annotations[] annotationsToUse) {
-        _mapperConfig.setAnnotationsToUse(annotationsToUse);
-    }
-    
-    /**
-     * Method that can be used to directly define {@link ObjectMapper} to use
-     * for serialization and deserialization; if null, will use the standard
-     * provider discovery from context instead. Default setting is null.
-     */
-    public void setMapper(ObjectMapper m) {
-        _mapperConfig.setMapper(m);
-    }
-
-    public JacksonJsonProvider configure(DeserializationFeature f, boolean state) {
-        _mapperConfig.configure(f, state);
-        return this;
-    }
-
-    public JacksonJsonProvider configure(SerializationFeature f, boolean state) {
-        _mapperConfig.configure(f, state);
-        return this;
-    }
-
-    public JacksonJsonProvider configure(JsonParser.Feature f, boolean state) {
-        _mapperConfig.configure(f, state);
-        return this;
-    }
-
-    public JacksonJsonProvider configure(JsonGenerator.Feature f, boolean state) {
-        _mapperConfig.configure(f, state);
-        return this;
-    }
-
-    public JacksonJsonProvider enable(DeserializationFeature f, boolean state) {
-        _mapperConfig.configure(f, true);
-        return this;
-    }
-
-    public JacksonJsonProvider enable(SerializationFeature f, boolean state) {
-        _mapperConfig.configure(f, true);
-        return this;
-    }
-
-    public JacksonJsonProvider enable(JsonParser.Feature f, boolean state) {
-        _mapperConfig.configure(f, true);
-        return this;
-    }
-
-    public JacksonJsonProvider enable(JsonGenerator.Feature f, boolean state) {
-        _mapperConfig.configure(f, true);
-        return this;
-    }
-
-    public JacksonJsonProvider disable(DeserializationFeature f, boolean state) {
-        _mapperConfig.configure(f, false);
-        return this;
-    }
-
-    public JacksonJsonProvider disable(SerializationFeature f, boolean state) {
-        _mapperConfig.configure(f, false);
-        return this;
-    }
-
-    public JacksonJsonProvider disable(JsonParser.Feature f, boolean state) {
-        _mapperConfig.configure(f, false);
-        return this;
-    }
-
-    public JacksonJsonProvider disable(JsonGenerator.Feature f, boolean state) {
-        _mapperConfig.configure(f, false);
-        return this;
-    }
-
-    /**
-     * Method for marking specified type as "untouchable", meaning that provider
-     * will not try to read or write values of this type (or its subtypes).
-     * 
-     * @param type Type to consider untouchable; can be any kind of class,
-     *   including abstract class or interface. No instance of this type
-     *   (including subtypes, i.e. types assignable to this type) will
-     *   be read or written by provider
-     */
-    public void addUntouchable(Class<?> type)
-    {
-        if (_cfgCustomUntouchables == null) {
-            _cfgCustomUntouchables = new HashSet<ClassKey>();
-        }
-        _cfgCustomUntouchables.add(new ClassKey(type));
-    }
-
     public void setJSONPFunctionName(String fname) {
         _jsonpFunctionName = fname;
     }
-    
-    /*
-    /**********************************************************
-    /* MessageBodyReader impl
-    /**********************************************************
-     */
-
-    /**
-     * Method that JAX-RS container calls to try to check whether
-     * values of given type (and media type) can be deserialized by
-     * this provider.
-     * Implementation will first check that expected media type is
-     * a JSON type (via call to {@link #isJsonType}; then verify
-     * that type is not one of "untouchable" types (types we will never
-     * automatically handle), and finally that there is a deserializer
-     * for type (iff {@link #checkCanDeserialize} has been called with
-     * true argument -- otherwise assumption is there will be a handler)
-     */
-    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
-    {
-        if (!isJsonType(mediaType)) {
-            return false;
-        }
-
-        /* Ok: looks like we must weed out some core types here; ones that
-         * make no sense to try to bind from JSON:
-         */
-        if (_untouchables.contains(new ClassKey(type))) {
-            return false;
-        }
-        // and there are some other abstract/interface types to exclude too:
-        for (Class<?> cls : _unreadableClasses) {
-            if (cls.isAssignableFrom(type)) {
-                return false;
-            }
-        }
-        // as well as possible custom exclusions
-        if (_containedIn(type, _cfgCustomUntouchables)) {
-            return false;
-        }
-
-        // Finally: if we really want to verify that we can serialize, we'll check:
-        if (_cfgCheckCanSerialize) {
-            ObjectMapper mapper = locateMapper(type, mediaType);
-            if (!mapper.canDeserialize(mapper.constructType(type))) {
-                return false;
-            }
-        }
-        return true;
-    }
-    
-    /**
-     * Method that JAX-RS container calls to deserialize given value.
-     */
-    public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,String> httpHeaders, InputStream entityStream) 
-        throws IOException
-    {
-
-        AnnotationBundleKey key = new AnnotationBundleKey(annotations);
-        EndpointConfig endpoint;
-        synchronized (_readers) {
-            endpoint = _readers.get(key);
-        }
-        // not yet resolved (or not cached any more)? Resolve!
-        if (endpoint == null) {
-            ObjectMapper mapper = locateMapper(type, mediaType);
-            endpoint = EndpointConfig.forReading(mapper, annotations);
-            // and cache for future reuse
-            synchronized (_readers) {
-                _readers.put(key.immutableKey(), endpoint);
-            }
-        }
-        ObjectReader reader = endpoint.getReader();
-        
-        JsonParser jp = reader.getFactory().createParser(entityStream);
-        if (jp.nextToken() == null) {
-           return null;
-        }
-        return reader.withType(genericType).readValue(jp);
-    }
 
     /*
     /**********************************************************
-    /* MessageBodyWriter impl
+    /* Abstract method impl
     /**********************************************************
      */
 
     /**
-     * Method that JAX-RS container calls to try to figure out
-     * serialized length of given value. Since computation of
-     * this length is about as expensive as serialization itself,
-     * implementation will return -1 to denote "not known", so
-     * that container will determine length from actual serialized
-     * output (if needed).
-     */
-    public long getSize(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
-    {
-        /* In general figuring output size requires actual writing; usually not
-         * worth it to write everything twice.
-         */
-        return -1;
-    }
-
-    /**
-     * Method that JAX-RS container calls to try to check whether
-     * given value (of specified type) can be serialized by
-     * this provider.
-     * Implementation will first check that expected media type is
-     * a JSON type (via call to {@link #isJsonType}; then verify
-     * that type is not one of "untouchable" types (types we will never
-     * automatically handle), and finally that there is a serializer
-     * for type (iff {@link #checkCanSerialize} has been called with
-     * true argument -- otherwise assumption is there will be a handler)
+     * @deprecated Since 2.2 use {@link #hasMatchingMediaType(MediaType)} instead
      */
-    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
-    {
-        if (!isJsonType(mediaType)) {
-            return false;
-        }
-
-        /* Ok: looks like we must weed out some core types here; ones that
-         * make no sense to try to bind from JSON:
-         */
-        if (_untouchables.contains(new ClassKey(type))) {
-            return false;
-        }
-        // but some are interface/abstract classes, so
-        for (Class<?> cls : _unwritableClasses) {
-            if (cls.isAssignableFrom(type)) {
-                return false;
-            }
-        }
-        // and finally, may have additional custom types to exclude
-        if (_containedIn(type, _cfgCustomUntouchables)) {
-            return false;
-        }
-
-        // Also: if we really want to verify that we can deserialize, we'll check:
-        if (_cfgCheckCanSerialize) {
-            if (!locateMapper(type, mediaType).canSerialize(type)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Method that JAX-RS container calls to serialize given value.
-     */
-    public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
-            MultivaluedMap<String,Object> httpHeaders, OutputStream entityStream) 
-        throws IOException
-    {
-        AnnotationBundleKey key = new AnnotationBundleKey(annotations);
-        EndpointConfig endpoint;
-        synchronized (_writers) {
-            endpoint = _writers.get(key);
-        }
-        // not yet resolved (or not cached any more)? Resolve!
-        if (endpoint == null) {
-            ObjectMapper mapper = locateMapper(type, mediaType);
-            endpoint = EndpointConfig.forWriting(mapper, annotations,
-                    this._jsonpFunctionName);
-            // and cache for future reuse
-            synchronized (_writers) {
-                _writers.put(key.immutableKey(), endpoint);
-            }
-        }
-
-        ObjectWriter writer = endpoint.getWriter();
-        
-        /* 27-Feb-2009, tatu: Where can we find desired encoding? Within
-         *   HTTP headers?
-         */
-        JsonEncoding enc = findEncoding(mediaType, httpHeaders);
-        JsonGenerator jg = writer.getFactory().createGenerator(entityStream, enc);
-
-        // Want indentation?
-        if (writer.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
-            jg.useDefaultPrettyPrinter();
-        }
-        // 04-Mar-2010, tatu: How about type we were given? (if any)
-        JavaType rootType = null;
-        
-        if (genericType != null && value != null) {
-            /* 10-Jan-2011, tatu: as per [JACKSON-456], it's not safe to just force root
-             *    type since it prevents polymorphic type serialization. Since we really
-             *    just need this for generics, let's only use generic type if it's truly
-             *    generic.
-             */
-            if (genericType.getClass() != Class.class) { // generic types are other impls of 'java.lang.reflect.Type'
-                /* This is still not exactly right; should root type be further
-                 * specialized with 'value.getClass()'? Let's see how well this works before
-                 * trying to come up with more complete solution.
-                 */
-                rootType = writer.getTypeFactory().constructType(genericType);
-                /* 26-Feb-2011, tatu: To help with [JACKSON-518], we better recognize cases where
-                 *    type degenerates back into "Object.class" (as is the case with plain TypeVariable,
-                 *    for example), and not use that.
-                 */
-                if (rootType.getRawClass() == Object.class) {
-                    rootType = null;
-                }
-            }
-        }
-        // Most of the configuration now handled through EndpointConfig, ObjectWriter
-        // but we may need to force root type:
-        if (rootType != null) {
-            writer = writer.withType(rootType);
-        }
-        // and finally, JSONP wrapping, if any:
-        value = endpoint.applyJSONP(value);
-        
-        writer.writeValue(jg, value);
-    }
-
-    /**
-     * Helper method to use for determining desired output encoding.
-     * For now, will always just use UTF-8...
-     */
-    protected JsonEncoding findEncoding(MediaType mediaType, MultivaluedMap<String,Object> httpHeaders)
-    {
-        return JsonEncoding.UTF8;
+    @Deprecated
+    protected boolean isJsonType(MediaType mediaType) {
+        return hasMatchingMediaType(mediaType);
     }
     
-    /*
-    /**********************************************************
-    /* Public helper methods
-    /**********************************************************
-     */
-
-    /**
-     * Helper method used to check whether given media type
-     * is JSON type or sub type.
-     * Current implementation essentially checks to see whether
-     * {@link MediaType#getSubtype} returns "json" or something
-     * ending with "+json".
-     */
-    protected boolean isJsonType(MediaType mediaType)
+    @Override
+    protected boolean hasMatchingMediaType(MediaType mediaType)
     {
         /* As suggested by Stephen D, there are 2 ways to check: either
          * being as inclusive as possible (if subtype is "json"), or
@@ -626,101 +179,33 @@ public class JacksonJsonProvider
         return true;
     }
 
-    /**
-     * Method called to locate {@link ObjectMapper} to use for serialization
-     * and deserialization. If an instance has been explicitly defined by
-     * {@link #setMapper} (or non-null instance passed in constructor), that
-     * will be used. 
-     * If not, will try to locate it using standard JAX-RS
-     * {@link ContextResolver} mechanism, if it has been properly configured
-     * to access it (by JAX-RS runtime).
-     * Finally, if no mapper is found, will return a default unconfigured
-     * {@link ObjectMapper} instance (one constructed with default constructor
-     * and not modified in any way)
-     *
-     * @param type Class of object being serialized or deserialized;
-     *   not checked at this point, since it is assumed that unprocessable
-     *   classes have been already weeded out,
-     *   but will be passed to {@link ContextResolver} as is.
-     * @param mediaType Declared media type for the instance to process:
-     *   not used by this method,
-     *   but will be passed to {@link ContextResolver} as is.
-     */
-    public ObjectMapper locateMapper(Class<?> type, MediaType mediaType)
+    @Override
+    protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType)
     {
-        // First: were we configured with a specific instance?
-        ObjectMapper m = _mapperConfig.getConfiguredMapper();
-        if (m == null) {
-            // If not, maybe we can get one configured via context?
-            if (_providers != null) {
-                ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
-                /* Above should work as is, but due to this bug
-                 *   [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
-                 * in Jersey, it doesn't. But this works until resolution of
-                 * the issue:
-                 */
-                if (resolver == null) {
-                    resolver = _providers.getContextResolver(ObjectMapper.class, null);
-                }
-                if (resolver != null) {
-                    m = resolver.getContext(type);
-                }
-            }
-            if (m == null) {
-                // If not, let's get the fallback default instance
-                m = _mapperConfig.getDefaultMapper();
+        if (_providers != null) {
+            ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
+            /* Above should work as is, but due to this bug
+             *   [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
+             * in Jersey, it doesn't. But this works until resolution of
+             * the issue:
+             */
+            if (resolver == null) {
+                resolver = _providers.getContextResolver(ObjectMapper.class, null);
             }
-        }
-        return m;
-    }
-
-    /*
-    /**********************************************************
-    /* Private/sub-class helper methods
-    /**********************************************************
-     */
-
-    protected static boolean _containedIn(Class<?> mainType, HashSet<ClassKey> set)
-    {
-        if (set != null) {
-            ClassKey key = new ClassKey(mainType);
-            // First: type itself?
-            if (set.contains(key)) return true;
-            // Then supertypes (note: will not contain Object.class)
-            for (Class<?> cls : findSuperTypes(mainType, null)) {
-                key.reset(cls);
-                if (set.contains(key)) return true;
+            if (resolver != null) {
+                return resolver.getContext(type);
             }
         }
-        return false;
+        return null;
     }
 
-    private static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore)
-    {
-        return findSuperTypes(cls, endBefore, new ArrayList<Class<?>>(8));
+    @Override
+    protected EndpointConfig _configForReading(ObjectMapper mapper, Annotation[] annotations) {
+        return EndpointConfig.forReading(mapper, annotations);
     }
 
-    private static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore, List<Class<?>> result)
-    {
-        _addSuperTypes(cls, endBefore, result, false);
-        return result;
-    }
-    
-    private static void _addSuperTypes(Class<?> cls, Class<?> endBefore, Collection<Class<?>> result, boolean addClassItself)
-    {
-        if (cls == endBefore || cls == null || cls == Object.class) {
-            return;
-        }
-        if (addClassItself) {
-            if (result.contains(cls)) { // already added, no need to check supers
-                return;
-            }
-            result.add(cls);
-        }
-        for (Class<?> intCls : cls.getInterfaces()) {
-            _addSuperTypes(intCls, endBefore, result, true);
-        }
-        _addSuperTypes(cls.getSuperclass(), endBefore, result, true);
+    @Override
+    protected EndpointConfig _configForWriting(ObjectMapper mapper, Annotation[] annotations) {
+        return EndpointConfig.forWriting(mapper, annotations, _jsonpFunctionName);
     }
-
 }
diff --git a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/annotation/EndpointConfig.java b/json/src/main/java/com/fasterxml/jackson/jaxrs/json/annotation/EndpointConfig.java
index ea7d099..ee10375 100644
--- a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/annotation/EndpointConfig.java
+++ b/json/src/main/java/com/fasterxml/jackson/jaxrs/json/annotation/EndpointConfig.java
@@ -2,42 +2,23 @@ package com.fasterxml.jackson.jaxrs.json.annotation;
 
 import java.lang.annotation.Annotation;
 
-import com.fasterxml.jackson.annotation.*;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.util.JSONPObject;
 import com.fasterxml.jackson.databind.util.JSONWrappedObject;
 
+import com.fasterxml.jackson.jaxrs.base.cfg.EndpointConfigBase;
+
 /**
  * Container class for figuring out annotation-based configuration
  * for JAX-RS end points.
  */
 public class EndpointConfig
+    extends EndpointConfigBase<EndpointConfig>
 {
-    // // General configuration
-    
-    protected Class<?> _activeView;
-
-    protected String _rootName;
-
-    // // Deserialization-only config
-    
-    protected DeserializationFeature[] _deserEnable;
-    protected DeserializationFeature[] _deserDisable;
-
-    protected ObjectReader _reader;
-    
     // // Serialization-only config
 
     protected JSONP.Def _jsonp;
     
-    protected SerializationFeature[] _serEnable;
-    protected SerializationFeature[] _serDisable;
-
-    protected ObjectWriter _writer;
-    
     /*
     /**********************************************************
     /* Construction
@@ -45,7 +26,7 @@ public class EndpointConfig
      */
 
     protected EndpointConfig() { }
-
+    
     public static EndpointConfig forReading(ObjectMapper mapper, Annotation[] annotations)
     {
         return new EndpointConfig()
@@ -65,122 +46,46 @@ public class EndpointConfig
             .initWriter(mapper)
         ;
     }
+
+    /*
+    /**********************************************************
+    /* Abstract method impls, overrides
+    /**********************************************************
+     */
     
-    protected EndpointConfig add(Annotation[] annotations, boolean forWriting)
+    @Override
+    protected void addAnnotation(Class<? extends Annotation> type,
+            Annotation annotation, boolean forWriting)
     {
-    	if (annotations != null) {
-          for (Annotation annotation : annotations) {
-            Class<?> type = annotation.annotationType();
-            if (type == JSONP.class) {
-                if (forWriting) {
-                    _jsonp = new JSONP.Def((JSONP) annotation);
-                }
-            } else if (type == JsonView.class) {
-                // Can only use one view; but if multiple defined, use first (no exception)
-                Class<?>[] views = ((JsonView) annotation).value();
-                _activeView = (views.length > 0) ? views[0] : null;
-            } else if (type == JsonRootName.class) {
-                _rootName = ((JsonRootName) annotation).value();
-            } else if (type == JacksonFeatures.class) {
-                JacksonFeatures feats = (JacksonFeatures) annotation;
-                if (forWriting) {
-                    _serEnable = nullIfEmpty(feats.serializationEnable());
-                    _serDisable = nullIfEmpty(feats.serializationDisable());
-                } else {
-                    _deserEnable = nullIfEmpty(feats.deserializationEnable());
-                    _deserDisable = nullIfEmpty(feats.deserializationDisable());
-                }
-            } else if (type == JacksonAnnotationsInside.class) {
-                // skip; processed below (in parent), so encountering here is of no use
+        if (type == JSONP.class) {
+            if (forWriting) {
+                _jsonp = new JSONP.Def((JSONP) annotation);
+            }
+        } else if (type == JacksonFeatures.class) {
+            JacksonFeatures feats = (JacksonFeatures) annotation;
+            if (forWriting) {
+                _serEnable = nullIfEmpty(feats.serializationEnable());
+                _serDisable = nullIfEmpty(feats.serializationDisable());
             } else {
-                // For all unrecognized types, check meta-annotation(s) to see if they are bundles
-                JacksonAnnotationsInside inside = type.getAnnotation(JacksonAnnotationsInside.class);
-                if (inside != null) {
-                    add(type.getAnnotations(), forWriting);
-                }
+                _deserEnable = nullIfEmpty(feats.deserializationEnable());
+                _deserDisable = nullIfEmpty(feats.deserializationDisable());
             }
-          }
-    	}
-        return this;
-    }
-
-    protected EndpointConfig initReader(ObjectMapper mapper)
-    {
-        // first common config
-        if (_activeView != null) {
-            _reader = mapper.readerWithView(_activeView);
         } else {
-            _reader = mapper.reader();
+            super.addAnnotation(type, annotation, forWriting);
         }
-
-        if (_rootName != null) {
-            _reader = _reader.withRootName(_rootName);
-        }
-        // Then deser features
-        if (_deserEnable != null) {
-            _reader = _reader.withFeatures(_deserEnable);
-        }
-        if (_deserDisable != null) {
-            _reader = _reader.withoutFeatures(_deserDisable);
-        }
-        /* Important: we are NOT to close the underlying stream after
-         * mapping, so we need to instruct parser:
-         */
-        _reader.getFactory().disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
-        
-        return this;
     }
-    
-    protected EndpointConfig initWriter(ObjectMapper mapper)
-    {
-        // first common config
-        if (_activeView != null) {
-            _writer = mapper.writerWithView(_activeView);
-        } else {
-            _writer = mapper.writer();
-        }
-        if (_rootName != null) {
-            _writer = _writer.withRootName(_rootName);
-        }
-        // Then features
-        if (_serEnable != null) {
-            _writer = _writer.withFeatures(_serEnable);
-        }
-        if (_serDisable != null) {
-            _writer = _writer.withoutFeatures(_serDisable);
-        }
-        // then others
-
-        // Finally: couple of features we always set
 
-        /* Important: we are NOT to close the underlying stream after
-         * mapping, so we need to instruct parser:
-         */
-        _writer.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
-        
-        return this;
+    @Override
+    public Object modifyBeforeWrite(Object value) {
+        return applyJSONP(value);
     }
-
+    
     /*
     /**********************************************************
     /* Accessors
     /**********************************************************
      */
 
-    public ObjectReader getReader() {
-        if (_reader == null) { // sanity check, should never happen
-            throw new IllegalStateException();
-        }
-        return _reader;
-    }
-
-    public ObjectWriter getWriter() {
-        if (_writer == null) { // sanity check, should never happen
-            throw new IllegalStateException();
-        }
-        return _writer;
-    }
-
     /**
      * Method that will add JSONP wrapper object, if and as
      * configured by collected annotations.

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jackson-jaxrs-providers.git



More information about the pkg-java-commits mailing list