http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java 
b/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java
index e5b085e..994eec3 100644
--- a/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java
@@ -14,28 +14,32 @@ package org.apache.juneau.parser;
 
 import static org.apache.juneau.parser.ParserContext.*;
 import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
 
 import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.http.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.transform.*;
+import org.apache.juneau.utils.*;
 
 /**
  * Session object that lives for the duration of a single use of {@link 
Parser}.
  *
  * <p>
- * This class is NOT thread safe.  It is meant to be discarded after one-time 
use.
+ * This class is NOT thread safe.
+ * It is typically discarded after one-time use although it can be reused 
against multiple inputs.
  */
-public class ParserSession extends BeanSession {
+public abstract class ParserSession extends BeanSession {
 
        private final boolean trimStrings, strict;
        private final String inputStreamCharset, fileCharset;
-
        private final Method javaMethod;
        private final Object outer;
-       private final ParserInput input;
+
+       // Writable properties.
        private BeanPropertyMeta currentProperty;
        private ClassMeta<?> currentClass;
        private final ParserListener listener;
@@ -46,6 +50,71 @@ public class ParserSession extends BeanSession {
         * @param ctx
         *      The context creating this session object.
         *      The context contains all the configuration settings for this 
object.
+        * @param args
+        *      Runtime session arguments.
+        */
+       protected ParserSession(ParserContext ctx, ParserSessionArgs args) {
+               super(ctx != null ? ctx : ParserContext.DEFAULT, args != null ? 
args : ParserSessionArgs.DEFAULT);
+               if (ctx == null)
+                       ctx = ParserContext.DEFAULT;
+               if (args == null)
+                       args = ParserSessionArgs.DEFAULT;
+               Class<?> listenerClass;
+               ObjectMap p = getProperties();
+               if (p.isEmpty()) {
+                       trimStrings = ctx.trimStrings;
+                       strict = ctx.strict;
+                       inputStreamCharset = ctx.inputStreamCharset;
+                       fileCharset = ctx.fileCharset;
+                       listenerClass = ctx.listener;
+               } else {
+                       trimStrings = p.getBoolean(PARSER_trimStrings, 
ctx.trimStrings);
+                       strict = p.getBoolean(PARSER_strict, ctx.strict);
+                       inputStreamCharset = 
p.getString(PARSER_inputStreamCharset, ctx.inputStreamCharset);
+                       fileCharset = p.getString(PARSER_fileCharset, 
ctx.fileCharset);
+                       listenerClass = p.get(Class.class, PARSER_listener, 
ctx.listener);
+               }
+               this.javaMethod = args.javaMethod;
+               this.outer = args.outer;
+               this.listener = newInstance(ParserListener.class, 
listenerClass);
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Abstract methods
+       
//--------------------------------------------------------------------------------
+
+       /**
+        * Workhorse method.  Subclasses are expected to implement this method.
+        *
+        * @param pipe Where to get the input from.
+        * @param type
+        *      The class type of the object to create.
+        *      If <jk>null</jk> or <code>Object.<jk>class</jk></code>, object 
type is based on what's being parsed.
+        *      For example, when parsing JSON text, it may return a 
<code>String</code>, <code>Number</code>,
+        *      <code>ObjectMap</code>, etc...
+        * @param <T> The class type of the object to create.
+        * @return The parsed object.
+        * @throws Exception If thrown from underlying stream, or if the input 
contains a syntax error or is malformed.
+        */
+       protected abstract <T> T doParse(ParserPipe pipe, ClassMeta<T> type) 
throws Exception;
+
+       /**
+        * Returns <jk>true</jk> if this parser subclasses from {@link 
ReaderParser}.
+        *
+        * @return <jk>true</jk> if this parser subclasses from {@link 
ReaderParser}.
+        */
+       public abstract boolean isReaderParser();
+
+
+       
//--------------------------------------------------------------------------------
+       // Other methods
+       
//--------------------------------------------------------------------------------
+
+       /**
+        * Wraps the specified input object into a {@link ParserPipe} object so 
that it can be easily converted into
+        * a stream or reader.
+        *
         * @param input
         *      The input.
         *      <br>For character-based parsers, this can be any of the 
following types:
@@ -67,67 +136,11 @@ public class ParserSession extends BeanSession {
         *              <li><code><jk>byte</jk>[]</code>
         *              <li>{@link File}
         *      </ul>
-        * @param op
-        *      The override properties.
-        *      These override any context properties defined in the context.
-        * @param javaMethod The java method that called this parser, usually 
the method in a REST servlet.
-        * @param outer The outer object for instantiating top-level non-static 
inner classes.
-        * @param locale
-        *      The session locale.
-        *      If <jk>null</jk>, then the locale defined on the context is 
used.
-        * @param timeZone
-        *      The session timezone.
-        *      If <jk>null</jk>, then the timezone defined on the context is 
used.
-        * @param mediaType The session media type (e.g. 
<js>"application/json"</js>).
-        */
-       public ParserSession(ParserContext ctx, ObjectMap op, Object input, 
Method javaMethod, Object outer, Locale locale,
-                       TimeZone timeZone, MediaType mediaType) {
-               super(ctx, op, locale, timeZone, mediaType);
-               Class<?> listenerClass;
-               if (op == null || op.isEmpty()) {
-                       trimStrings = ctx.trimStrings;
-                       strict = ctx.strict;
-                       inputStreamCharset = ctx.inputStreamCharset;
-                       fileCharset = ctx.fileCharset;
-                       listenerClass = ctx.listener;
-               } else {
-                       trimStrings = op.getBoolean(PARSER_trimStrings, 
ctx.trimStrings);
-                       strict = op.getBoolean(PARSER_strict, ctx.strict);
-                       inputStreamCharset = 
op.getString(PARSER_inputStreamCharset, ctx.inputStreamCharset);
-                       fileCharset = op.getString(PARSER_fileCharset, 
ctx.fileCharset);
-                       listenerClass = op.get(Class.class, PARSER_listener, 
ctx.listener);
-               }
-               this.input = new ParserInput(input, isDebug(), strict, 
fileCharset, inputStreamCharset);
-               this.javaMethod = javaMethod;
-               this.outer = outer;
-               this.listener = newInstance(ParserListener.class, 
listenerClass);
-       }
-
-       /**
-        * Wraps the specified input object inside an input stream.
-        *
-        * <p>
-        * Subclasses can override this method to implement their own input 
streams.
-        *
-        * @return The input object wrapped in an input stream, or 
<jk>null</jk> if the object is null.
-        * @throws ParseException If object could not be converted to an input 
stream.
-        */
-       public InputStream getInputStream() throws ParseException {
-               return input.getInputStream();
-       }
-
-
-       /**
-        * Wraps the specified input object inside a reader.
-        *
-        * <p>
-        * Subclasses can override this method to implement their own readers.
-        *
-        * @return The input object wrapped in a Reader, or <jk>null</jk> if 
the object is null.
-        * @throws Exception If object could not be converted to a reader.
+        * @return
+        *      A new {@link ParserPipe} wrapper around the specified input 
object.
         */
-       public Reader getReader() throws Exception {
-               return input.getReader();
+       public final ParserPipe createPipe(Object input) {
+               return new ParserPipe(input, isDebug(), strict, fileCharset, 
inputStreamCharset);
        }
 
        /**
@@ -135,8 +148,8 @@ public class ParserSession extends BeanSession {
         *
         * @return A map, typically containing something like 
<code>{line:123,column:456,currentProperty:"foobar"}</code>
         */
-       public Map<String,Object> getLastLocation() {
-               Map<String,Object> m = new LinkedHashMap<String,Object>();
+       public final ObjectMap getLastLocation() {
+               ObjectMap m = new ObjectMap();
                if (currentClass != null)
                        m.put("currentClass", currentClass.toString(true));
                if (currentProperty != null)
@@ -145,15 +158,6 @@ public class ParserSession extends BeanSession {
        }
 
        /**
-        * Returns the raw input object passed into this session.
-        *
-        * @return The raw input object passed into this session.
-        */
-       protected Object getInput() {
-               return input.getRawInput();
-       }
-
-       /**
         * Returns the Java method that invoked this parser.
         *
         * <p>
@@ -162,7 +166,7 @@ public class ParserSession extends BeanSession {
         *
         * @return The Java method that invoked this parser.
        */
-       public final Method getJavaMethod() {
+       protected final Method getJavaMethod() {
                return javaMethod;
        }
 
@@ -174,7 +178,7 @@ public class ParserSession extends BeanSession {
         *
         * @return The outer object.
        */
-       public final Object getOuter() {
+       protected final Object getOuter() {
                return outer;
        }
 
@@ -183,7 +187,7 @@ public class ParserSession extends BeanSession {
         *
         * @param currentProperty The current property being parsed.
         */
-       public void setCurrentProperty(BeanPropertyMeta currentProperty) {
+       protected final void setCurrentProperty(BeanPropertyMeta 
currentProperty) {
                this.currentProperty = currentProperty;
        }
 
@@ -192,7 +196,7 @@ public class ParserSession extends BeanSession {
         *
         * @param currentClass The current class being parsed.
         */
-       public void setCurrentClass(ClassMeta<?> currentClass) {
+       protected final void setCurrentClass(ClassMeta<?> currentClass) {
                this.currentClass = currentClass;
        }
 
@@ -201,7 +205,7 @@ public class ParserSession extends BeanSession {
         *
         * @return The {@link ParserContext#PARSER_trimStrings} setting value 
for this session.
         */
-       public final boolean isTrimStrings() {
+       protected final boolean isTrimStrings() {
                return trimStrings;
        }
 
@@ -210,7 +214,7 @@ public class ParserSession extends BeanSession {
         *
         * @return The {@link ParserContext#PARSER_strict} setting value for 
this session.
         */
-       public final boolean isStrict() {
+       protected final boolean isStrict() {
                return strict;
        }
 
@@ -221,7 +225,7 @@ public class ParserSession extends BeanSession {
         * @return The trimmed string if it's a string.
         */
        @SuppressWarnings("unchecked")
-       public final <K> K trim(K o) {
+       protected final <K> K trim(K o) {
                if (trimStrings && o instanceof String)
                        return (K)o.toString().trim();
                return o;
@@ -234,7 +238,7 @@ public class ParserSession extends BeanSession {
         * @param s The input string to trim.
         * @return The trimmed string, or <jk>null</jk> if the input was 
<jk>null</jk>.
         */
-       public final String trim(String s) {
+       protected final String trim(String s) {
                if (trimStrings && s != null)
                        return s.trim();
                return s;
@@ -249,7 +253,7 @@ public class ParserSession extends BeanSession {
         * @return
         *      The converted bean, or the same map if the <js>"_type"</js> 
entry wasn't found or didn't resolve to a bean.
         */
-       public final Object cast(ObjectMap m, BeanPropertyMeta pMeta, 
ClassMeta<?> eType) {
+       protected final Object cast(ObjectMap m, BeanPropertyMeta pMeta, 
ClassMeta<?> eType) {
 
                String btpn = getBeanTypePropertyName(eType);
 
@@ -288,7 +292,7 @@ public class ParserSession extends BeanSession {
         * @param eType The expected type we're currently parsing.
         * @return The resolved class, or <jk>null</jk> if the type name could 
not be resolved.
         */
-       public final ClassMeta<?> getClassMeta(String typeName, 
BeanPropertyMeta pMeta, ClassMeta<?> eType) {
+       protected final ClassMeta<?> getClassMeta(String typeName, 
BeanPropertyMeta pMeta, ClassMeta<?> eType) {
                BeanRegistry br = null;
 
                // Resolve via @BeanProperty(beanDictionary={})
@@ -313,6 +317,7 @@ public class ParserSession extends BeanSession {
        /**
         * Method that gets called when an unknown bean property name is 
encountered.
         *
+        * @param pipe The parser input.
         * @param propertyName The unknown bean property name.
         * @param beanMap The bean that doesn't have the expected property.
         * @param line The line number where the property was found.  
<code>-1</code> if line numbers are not available.
@@ -322,39 +327,427 @@ public class ParserSession extends BeanSession {
         *      <jk>false</jk>
         * @param <T> The class type of the bean map that doesn't have the 
expected property.
         */
-       public <T> void onUnknownProperty(String propertyName, BeanMap<T> 
beanMap, int line, int col) throws ParseException {
+       protected final <T> void onUnknownProperty(ParserPipe pipe, String 
propertyName, BeanMap<T> beanMap, int line, int col) throws ParseException {
                if 
(propertyName.equals(getBeanTypePropertyName(beanMap.getClassMeta())))
                        return;
                if (! isIgnoreUnknownBeanProperties())
-                       throw new ParseException(this,
+                       throw new ParseException(getLastLocation(),
                                "Unknown property ''{0}'' encountered while 
trying to parse into class ''{1}''", propertyName,
                                beanMap.getClassMeta());
                if (listener != null)
-                       listener.onUnknownBeanProperty(this, propertyName, 
beanMap.getClassMeta().getInnerClass(), beanMap.getBean(),
+                       listener.onUnknownBeanProperty(this, pipe, 
propertyName, beanMap.getClassMeta().getInnerClass(), beanMap.getBean(),
                                line, col);
        }
 
        /**
-        * Returns the input to this parser as a plain string.
+        * Parses input into the specified object type.
+        *
+        * <p>
+        * The type can be a simple type (e.g. beans, strings, numbers) or 
parameterized type (collections/maps).
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      ReaderParser p = JsonParser.<jsf>DEFAULT</jsf>;
+        *
+        *      <jc>// Parse into a linked-list of strings.</jc>
+        *      List l = p.parse(json, LinkedList.<jk>class</jk>, 
String.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a linked-list of beans.</jc>
+        *      List l = p.parse(json, LinkedList.<jk>class</jk>, 
MyBean.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a linked-list of linked-lists of strings.</jc>
+        *      List l = p.parse(json, LinkedList.<jk>class</jk>, 
LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a map of string keys/values.</jc>
+        *      Map m = p.parse(json, TreeMap.<jk>class</jk>, 
String.<jk>class</jk>, String.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a map containing string keys and values of 
lists containing beans.</jc>
+        *      Map m = p.parse(json, TreeMap.<jk>class</jk>, 
String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+        * </p>
+        *
+        * <p>
+        * <code>Collection</code> classes are assumed to be followed by zero 
or one objects indicating the element type.
+        *
+        * <p>
+        * <code>Map</code> classes are assumed to be followed by zero or two 
meta objects indicating the key and value types.
         *
         * <p>
-        * This method only returns a value if {@link BeanContext#BEAN_debug} 
is enabled.
+        * The array can be arbitrarily long to indicate arbitrarily complex 
data structures.
+        *
+        * <h5 class='section'>Notes:</h5>
+        * <ul>
+        *      <li>Use the {@link #parse(Object, Class)} method instead if you 
don't need a parameterized map/collection.
+        * </ul>
         *
-        * @return The input as a string, or <jk>null</jk> if debug mode not 
enabled.
+        * @param <T> The class type of the object to create.
+        * @param input
+        *      The input.
+        *      <br>Character-based parsers can handle the following input 
class types:
+        *      <ul>
+        *              <li><jk>null</jk>
+        *              <li>{@link Reader}
+        *              <li>{@link CharSequence}
+        *              <li>{@link InputStream} containing UTF-8 encoded text 
(or charset defined by
+        *                      {@link ParserContext#PARSER_inputStreamCharset} 
property value).
+        *              <li><code><jk>byte</jk>[]</code> containing UTF-8 
encoded text (or charset defined by
+        *                      {@link ParserContext#PARSER_inputStreamCharset} 
property value).
+        *              <li>{@link File} containing system encoded text (or 
charset defined by
+        *                      {@link ParserContext#PARSER_fileCharset} 
property value).
+        *      </ul>
+        *      <br>Stream-based parsers can handle the following input class 
types:
+        *      <ul>
+        *              <li><jk>null</jk>
+        *              <li>{@link InputStream}
+        *              <li><code><jk>byte</jk>[]</code>
+        *              <li>{@link File}
+        *      </ul>
+        * @param type
+        *      The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType},
+        *      {@link GenericArrayType}
+        * @param args
+        *      The type arguments of the class if it's a collection or map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType},
+        *      {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        * @return The parsed object.
+        * @throws ParseException
+        *      If the input contains a syntax error or is malformed, or is not 
valid for the specified type.
+        * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for 
maps and collections.
         */
-       public String getInputAsString() {
-               return input.getInputAsString();
+       @SuppressWarnings("unchecked")
+       public final <T> T parse(Object input, Type type, Type...args) throws 
ParseException {
+               ParserPipe pipe = createPipe(input);
+               try {
+                       return (T)parseInner(pipe, getClassMeta(type, args));
+               } finally {
+                       pipe.close();
+               }
        }
 
        /**
-        * Perform cleanup on this context object if necessary.
+        * Same as {@link #parse(Object, Type, Type...)} except optimized for a 
non-parameterized class.
+        *
+        * <p>
+        * This is the preferred parse method for simple types since you don't 
need to cast the results.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      ReaderParser p = JsonParser.<jsf>DEFAULT</jsf>;
+        *
+        *      <jc>// Parse into a string.</jc>
+        *      String s = p.parse(json, String.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a bean.</jc>
+        *      MyBean b = p.parse(json, MyBean.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a bean array.</jc>
+        *      MyBean[] ba = p.parse(json, MyBean[].<jk>class</jk>);
+        *
+        *      <jc>// Parse into a linked-list of objects.</jc>
+        *      List l = p.parse(json, LinkedList.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a map of object keys/values.</jc>
+        *      Map m = p.parse(json, TreeMap.<jk>class</jk>);
+        * </p>
+        *
+        * @param <T> The class type of the object being created.
+        * @param input
+        *      The input.
+        *      See {@link #parse(Object, Type, Type...)} for details.
+        * @param type The object type to create.
+        * @return The parsed object.
+        * @throws ParseException
+        *      If the input contains a syntax error or is malformed, or is not 
valid for the specified type.
+        */
+       public final <T> T parse(Object input, Class<T> type) throws 
ParseException {
+               ParserPipe pipe = createPipe(input);
+               try {
+                       return parseInner(pipe, getClassMeta(type));
+               } finally {
+                       pipe.close();
+               }
+       }
+
+       /**
+        * Same as {@link #parse(Object, Type, Type...)} except the type has 
already been converted into a {@link ClassMeta}
+        * object.
+        *
+        * <p>
+        * This is mostly an internal method used by the framework.
+        *
+        * @param <T> The class type of the object being created.
+        * @param input
+        *      The input.
+        *      See {@link #parse(Object, Type, Type...)} for details.
+        * @param type The object type to create.
+        * @return The parsed object.
+        * @throws ParseException
+        *      If the input contains a syntax error or is malformed, or is not 
valid for the specified type.
+        */
+       public final <T> T parse(Object input, ClassMeta<T> type) throws 
ParseException {
+               ParserPipe pipe = createPipe(input);
+               try {
+                       return parseInner(pipe, type);
+               } finally {
+                       pipe.close();
+               }
+       }
+
+       /**
+        * Entry point for all parsing calls.
+        *
+        * <p>
+        * Calls the {@link #doParse(ParserPipe, ClassMeta)} implementation 
class and catches/re-wraps any exceptions
+        * thrown.
+        *
+        * @param pipe The parser input.
+        * @param type The class type of the object to create.
+        * @param <T> The class type of the object to create.
+        * @return The parsed object.
+        * @throws ParseException
+        *      If the input contains a syntax error or is malformed, or is not 
valid for the specified type.
         */
-       @Override
-       public boolean close() {
-               if (super.close()) {
-                       input.close();
-                       return true;
+       private <T> T parseInner(ParserPipe pipe, ClassMeta<T> type) throws 
ParseException {
+               try {
+                       if (type.isVoid())
+                               return null;
+                       return doParse(pipe, type);
+               } catch (ParseException e) {
+                       throw e;
+               } catch (StackOverflowError e) {
+                       throw new ParseException(getLastLocation(), "Depth too 
deep.  Stack overflow occurred.");
+               } catch (IOException e) {
+                       throw new ParseException(getLastLocation(), "I/O 
exception occurred.  exception={0}, message={1}.",
+                               e.getClass().getSimpleName(), 
e.getLocalizedMessage()).initCause(e);
+               } catch (Exception e) {
+                       throw new ParseException(getLastLocation(), "Exception 
occurred.  exception={0}, message={1}.",
+                               e.getClass().getSimpleName(), 
e.getLocalizedMessage()).initCause(e);
+               }
+       }
+
+       /**
+        * Parses the contents of the specified reader and loads the results 
into the specified map.
+        *
+        * <p>
+        * Reader must contain something that serializes to a map (such as text 
containing a JSON object).
+        *
+        * <p>
+        * Used in the following locations:
+        * <ul class='spaced-list'>
+        *      <li>
+        *              The various character-based constructors in {@link 
ObjectMap} (e.g.
+        *              {@link ObjectMap#ObjectMap(CharSequence,Parser)}).
+        * </ul>
+        *
+        * @param <K> The key class type.
+        * @param <V> The value class type.
+        * @param input The input.  See {@link #parse(Object, ClassMeta)} for 
supported input types.
+        * @param m The map being loaded.
+        * @param keyType The class type of the keys, or <jk>null</jk> to 
default to <code>String.<jk>class</jk></code>.
+        * @param valueType The class type of the values, or <jk>null</jk> to 
default to whatever is being parsed.
+        * @return The same map that was passed in to allow this method to be 
chained.
+        * @throws ParseException If the input contains a syntax error or is 
malformed, or is not valid for the specified type.
+        * @throws UnsupportedOperationException If not implemented.
+        */
+       public final <K,V> Map<K,V> parseIntoMap(Object input, Map<K,V> m, Type 
keyType, Type valueType) throws ParseException {
+               ParserPipe pipe = createPipe(input);
+               try {
+                       return doParseIntoMap(pipe, m, keyType, valueType);
+               } catch (ParseException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ParseException(getLastLocation(), e);
+               } finally {
+                       pipe.close();
+               }
+       }
+
+       /**
+        * Implementation method.
+        *
+        * <p>
+        * Default implementation throws an {@link 
UnsupportedOperationException}.
+        *
+        * @param pipe The parser input.
+        * @param m The map being loaded.
+        * @param keyType The class type of the keys, or <jk>null</jk> to 
default to <code>String.<jk>class</jk></code>.
+        * @param valueType The class type of the values, or <jk>null</jk> to 
default to whatever is being parsed.
+        * @return The same map that was passed in to allow this method to be 
chained.
+        * @throws Exception If thrown from underlying stream, or if the input 
contains a syntax error or is malformed.
+        */
+       protected <K,V> Map<K,V> doParseIntoMap(ParserPipe pipe, Map<K,V> m, 
Type keyType, Type valueType) throws Exception {
+               throw new UnsupportedOperationException("Parser 
'"+getClass().getName()+"' does not support this method.");
+       }
+
+       /**
+        * Parses the contents of the specified reader and loads the results 
into the specified collection.
+        *
+        * <p>
+        * Used in the following locations:
+        * <ul class='spaced-list'>
+        *      <li>
+        *              The various character-based constructors in {@link 
ObjectList} (e.g.
+        *              {@link ObjectList#ObjectList(CharSequence,Parser)}.
+        * </ul>
+        *
+        * @param <E> The element class type.
+        * @param input The input.  See {@link #parse(Object, ClassMeta)} for 
supported input types.
+        * @param c The collection being loaded.
+        * @param elementType The class type of the elements, or <jk>null</jk> 
to default to whatever is being parsed.
+        * @return The same collection that was passed in to allow this method 
to be chained.
+        * @throws ParseException
+        *      If the input contains a syntax error or is malformed, or is not 
valid for the specified type.
+        * @throws UnsupportedOperationException If not implemented.
+        */
+       public final <E> Collection<E> parseIntoCollection(Object input, 
Collection<E> c, Type elementType) throws ParseException {
+               ParserPipe pipe = createPipe(input);
+               try {
+                       return doParseIntoCollection(pipe, c, elementType);
+               } catch (ParseException e) {
+                       throw e;
+               } catch (StackOverflowError e) {
+                       throw new ParseException(getLastLocation(), "Depth too 
deep.  Stack overflow occurred.");
+               } catch (IOException e) {
+                       throw new ParseException(getLastLocation(), "I/O 
exception occurred.  exception={0}, message={1}.",
+                               e.getClass().getSimpleName(), 
e.getLocalizedMessage()).initCause(e);
+               } catch (Exception e) {
+                       throw new ParseException(getLastLocation(), "Exception 
occurred.  exception={0}, message={1}.",
+                               e.getClass().getSimpleName(), 
e.getLocalizedMessage()).initCause(e);
+               } finally {
+                       pipe.close();
+               }
+       }
+
+       /**
+        * Implementation method.
+        *
+        * <p>
+        * Default implementation throws an {@link 
UnsupportedOperationException}.
+        *
+        * @param pipe The parser input.
+        * @param c The collection being loaded.
+        * @param elementType The class type of the elements, or <jk>null</jk> 
to default to whatever is being parsed.
+        * @return The same collection that was passed in to allow this method 
to be chained.
+        * @throws Exception If thrown from underlying stream, or if the input 
contains a syntax error or is malformed.
+        */
+       protected <E> Collection<E> doParseIntoCollection(ParserPipe pipe, 
Collection<E> c, Type elementType) throws Exception {
+               throw new UnsupportedOperationException("Parser 
'"+getClass().getName()+"' does not support this method.");
+       }
+
+       /**
+        * Parses the specified array input with each entry in the object 
defined by the {@code argTypes}
+        * argument.
+        *
+        * <p>
+        * Used for converting arrays (e.g. <js>"[arg1,arg2,...]"</js>) into an 
{@code Object[]} that can be passed
+        * to the {@code Method.invoke(target, args)} method.
+        *
+        * <p>
+        * Used in the following locations:
+        * <ul class='spaced-list'>
+        *      <li>
+        *              Used to parse argument strings in the {@link 
PojoIntrospector#invokeMethod(Method, Reader)} method.
+        * </ul>
+        *
+        * @param input The input.  Subclasses can support different input 
types.
+        * @param argTypes Specifies the type of objects to create for each 
entry in the array.
+        * @return An array of parsed objects.
+        * @throws ParseException
+        *      If the input contains a syntax error or is malformed, or is not 
valid for the specified type.
+        */
+       public final Object[] parseArgs(Object input, Type[] argTypes) throws 
ParseException {
+               ParserPipe pipe = createPipe(input);
+               try {
+                       return doParse(pipe, getArgsClassMeta(argTypes));
+               } catch (ParseException e) {
+                       throw e;
+               } catch (StackOverflowError e) {
+                       throw new ParseException(getLastLocation(), "Depth too 
deep.  Stack overflow occurred.");
+               } catch (IOException e) {
+                       throw new ParseException(getLastLocation(), "I/O 
exception occurred.  exception={0}, message={1}.",
+                               e.getClass().getSimpleName(), 
e.getLocalizedMessage()).initCause(e);
+               } catch (Exception e) {
+                       throw new ParseException(getLastLocation(), "Exception 
occurred.  exception={0}, message={1}.",
+                               e.getClass().getSimpleName(), 
e.getLocalizedMessage()).initCause(e);
+               } finally {
+                       pipe.close();
+               }
+       }
+
+       /**
+        * Converts the specified string to the specified type.
+        *
+        * @param outer
+        *      The outer object if we're converting to an inner object that 
needs to be created within the context
+        *      of an outer object.
+        * @param s The string to convert.
+        * @param type The class type to convert the string to.
+        * @return The string converted as an object of the specified type.
+        * @throws Exception If the input contains a syntax error or is 
malformed, or is not valid for the specified type.
+        * @param <T> The class type to convert the string to.
+        */
+       @SuppressWarnings({ "unchecked", "rawtypes", "hiding" })
+       protected final <T> T convertAttrToType(Object outer, String s, 
ClassMeta<T> type) throws Exception {
+               if (s == null)
+                       return null;
+
+               if (type == null)
+                       type = (ClassMeta<T>)object();
+               PojoSwap transform = type.getPojoSwap();
+               ClassMeta<?> sType = type.getSerializedClassMeta();
+
+               Object o = s;
+               if (sType.isChar())
+                       o = s.charAt(0);
+               else if (sType.isNumber())
+                       if (type.canCreateNewInstanceFromNumber(outer))
+                               o = type.newInstanceFromNumber(this, outer, 
parseNumber(s, type.getNewInstanceFromNumberClass()));
+                       else
+                               o = parseNumber(s, (Class<? extends 
Number>)sType.getInnerClass());
+               else if (sType.isBoolean())
+                       o = Boolean.parseBoolean(s);
+               else if (! (sType.isCharSequence() || sType.isObject())) {
+                       if (sType.canCreateNewInstanceFromString(outer))
+                               o = sType.newInstanceFromString(outer, s);
+                       else
+                               throw new ParseException(getLastLocation(), 
"Invalid conversion from string to class ''{0}''", type);
+               }
+
+               if (transform != null)
+                       o = transform.unswap(this, o, type);
+
+               return (T)o;
+       }
+
+       /**
+        * Convenience method for calling the {@link ParentProperty 
@ParentProperty} method on the specified object if it
+        * exists.
+        *
+        * @param cm The class type of the object.
+        * @param o The object.
+        * @param parent The parent to set.
+        * @throws Exception
+        */
+       protected final static void setParent(ClassMeta<?> cm, Object o, Object 
parent) throws Exception {
+               Setter m = cm.getParentProperty();
+               if (m != null)
+                       m.set(o, parent);
+       }
+
+       /**
+        * Convenience method for calling the {@link NameProperty 
@NameProperty} method on the specified object if it exists.
+        *
+        * @param cm The class type of the object.
+        * @param o The object.
+        * @param name The name to set.
+        * @throws Exception
+        */
+       protected final static void setName(ClassMeta<?> cm, Object o, Object 
name) throws Exception {
+               if (cm != null) {
+                       Setter m = cm.getNameProperty();
+                       if (m != null)
+                               m.set(o, name);
                }
-               return false;
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ParserSessionArgs.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/parser/ParserSessionArgs.java 
b/juneau-core/src/main/java/org/apache/juneau/parser/ParserSessionArgs.java
new file mode 100644
index 0000000..65ce23e
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserSessionArgs.java
@@ -0,0 +1,61 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.parser;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.*;
+
+/**
+ * Runtime arguments common to all parser sessions.
+ */
+public final class ParserSessionArgs extends BeanSessionArgs {
+
+       /**
+        * Default session arguments.
+        */
+       protected static final ParserSessionArgs DEFAULT = new 
ParserSessionArgs(ObjectMap.EMPTY_MAP, null, null, null, null, null);
+
+       final Method javaMethod;
+       final Object outer;
+
+       /**
+        * Constructor.
+        *
+        * @param properties
+        *      Session-level properties.
+        *      These override context-level properties.
+        *      Can be <jk>null</jk>.
+        * @param javaMethod
+        *      The java method that called this serializer, usually the method 
in a REST servlet.
+        *      Can be <jk>null</jk>.
+        * @param locale
+        *      The session locale.
+        *      If <jk>null</jk>, then the locale defined on the context is 
used.
+        * @param timeZone
+        *      The session timezone.
+        *      If <jk>null</jk>, then the timezone defined on the context is 
used.
+        * @param mediaType
+        *      The session media type (e.g. <js>"application/json"</js>).
+        *      Can be <jk>null</jk>.
+        * @param outer
+        *      The outer object for instantiating top-level non-static inner 
classes.
+        */
+       public ParserSessionArgs(ObjectMap properties, Method javaMethod, 
Locale locale, TimeZone timeZone, MediaType mediaType, Object outer) {
+               super(properties, locale, timeZone, mediaType);
+               this.javaMethod = javaMethod;
+               this.outer = outer;
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParser.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParser.java 
b/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParser.java
index 9d9c710..a987a5e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParser.java
@@ -45,7 +45,7 @@ public abstract class ReaderParser extends Parser {
        }
 
        @Override /* Parser */
-       public boolean isReaderParser() {
+       public final boolean isReaderParser() {
                return true;
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParserSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParserSession.java 
b/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParserSession.java
new file mode 100644
index 0000000..1c7da89
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/parser/ReaderParserSession.java
@@ -0,0 +1,51 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.parser;
+
+/**
+ * Subclass of parser session objects for character-based parsers.
+ *
+ * <p>
+ * This class is NOT thread safe.  It is typically discarded after one-time 
use.
+ */
+public abstract class ReaderParserSession extends ParserSession {
+
+       /**
+        * Create a new session using properties specified in the context.
+        *
+        * @param ctx
+        *      The context creating this session object.
+        *      The context contains all the configuration settings for this 
object.
+        * @param args
+        *      Runtime session arguments.
+        */
+       protected ReaderParserSession(ParserContext ctx, ParserSessionArgs 
args) {
+               super(ctx, args);
+       }
+
+       /**
+        * Constructor for sessions that don't require context.
+        *
+        * @param args
+        *      Runtime session arguments.
+        */
+       protected ReaderParserSession(ParserSessionArgs args) {
+               this(null, args);
+       }
+
+
+       @Override /* ParserSession */
+       public final boolean isReaderParser() {
+               return true;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java
index c25c2a0..c5a2695 100644
--- a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParser.java
@@ -12,8 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.plaintext;
 
-import static org.apache.juneau.internal.IOUtils.*;
-
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.parser.*;
@@ -67,13 +65,8 @@ public class PlainTextParser extends ReaderParser {
                return new PlainTextParserBuilder(propertyStore);
        }
 
-
-       
//--------------------------------------------------------------------------------
-       // Overridden methods
-       
//--------------------------------------------------------------------------------
-
        @Override /* Parser */
-       protected <T> T doParse(ParserSession session, ClassMeta<T> type) 
throws Exception {
-               return session.convertToType(read(session.getReader()), type);
+       public ReaderParserSession createSession(ParserSessionArgs args) {
+               return new PlainTextParserSession(args);
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParserSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParserSession.java
 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParserSession.java
new file mode 100644
index 0000000..c4731fa
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextParserSession.java
@@ -0,0 +1,43 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.plaintext;
+
+import static org.apache.juneau.internal.IOUtils.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link 
PlainTextParser}.
+ *
+ * <p>
+ * This class is NOT thread safe.
+ * It is typically discarded after one-time use although it can be reused 
against multiple inputs.
+ */
+public class PlainTextParserSession extends ReaderParserSession {
+
+       /**
+        * Create a new session using properties specified in the context.
+        *
+        * @param args
+        *      Runtime session arguments.
+        */
+       protected PlainTextParserSession(ParserSessionArgs args) {
+               super(null, args);
+       }
+
+       @Override /* ParserSession */
+       protected <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws 
Exception {
+               return convertToType(read(pipe.getReader()), type);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java
 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java
index cf0975c..910e2d5 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializer.java
@@ -49,6 +49,7 @@ public class PlainTextSerializer extends WriterSerializer {
        /** Default serializer, all default settings.*/
        public static final PlainTextSerializer DEFAULT = new 
PlainTextSerializer(PropertyStore.create());
 
+       private final SerializerContext ctx;
 
        /**
         * Constructor.
@@ -57,6 +58,7 @@ public class PlainTextSerializer extends WriterSerializer {
         */
        public PlainTextSerializer(PropertyStore propertyStore) {
                super(propertyStore);
+               this.ctx = createContext(SerializerContext.class);
        }
 
        @Override /* CoreObject */
@@ -64,13 +66,8 @@ public class PlainTextSerializer extends WriterSerializer {
                return new PlainTextSerializerBuilder(propertyStore);
        }
 
-
-       
//--------------------------------------------------------------------------------
-       // Overridden methods
-       
//--------------------------------------------------------------------------------
-
        @Override /* Serializer */
-       protected void doSerialize(SerializerSession session, SerializerOutput 
out, Object o) throws Exception {
-               out.getWriter().write(o == null ? "null" : 
session.convertToType(o, String.class));
+       public WriterSerializerSession createSession(SerializerSessionArgs 
args) {
+               return new PlainTextSerializerSession(ctx, args);
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerSession.java
 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerSession.java
new file mode 100644
index 0000000..0e69e09
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerSession.java
@@ -0,0 +1,47 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.plaintext;
+
+import org.apache.juneau.serializer.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link 
PlainTextSerializer}.
+ *
+ * <p>
+ * This class is NOT thread safe.
+ * It is typically discarded after one-time use although it can be reused 
within the same thread.
+ */
+public class PlainTextSerializerSession extends WriterSerializerSession {
+
+       /**
+        * Create a new session using properties specified in the context.
+        *
+        * @param ctx
+        *      The context creating this session object.
+        *      The context contains all the configuration settings for this 
object.
+        * @param args
+        *      Runtime arguments.
+        *      These specify session-level information such as locale and URI 
context.
+        *      It also include session-level properties that override the 
properties defined on the bean and
+        *      serializer contexts.
+        *      <br>If <jk>null</jk>, defaults to {@link 
SerializerSessionArgs#DEFAULT}.
+        */
+       protected PlainTextSerializerSession(SerializerContext ctx, 
SerializerSessionArgs args) {
+               super(ctx, args);
+       }
+
+       @Override /* SerializerSession */
+       protected void doSerialize(SerializerPipe out, Object o) throws 
Exception {
+               out.getWriter().write(o == null ? "null" : convertToType(o, 
String.class));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializer.java
 
b/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializer.java
index 8c743b6..b481f2e 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializer.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializer.java
@@ -14,22 +14,12 @@ package org.apache.juneau.serializer;
 
 import static org.apache.juneau.internal.StringUtils.*;
 
-import java.io.*;
-
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 
 /**
  * Subclass of {@link Serializer} for byte-based serializers.
  *
- * <h5 class='section'>Description:</h5>
- *
- * This class is typically the parent class of all byte-based serializers.
- * It has 1 abstract method to implement...
- * <ul>
- *     <li>{@link #doSerialize(SerializerSession, SerializerOutput, Object)}
- * </ul>
- *
  * <h6 class='topic'>@Produces annotation</h6>
  *
  * The media types that this serializer can produce is specified through the 
{@link Produces @Produces} annotation.
@@ -49,16 +39,24 @@ public abstract class OutputStreamSerializer extends 
Serializer {
                super(propertyStore);
        }
 
-       @Override /* Serializer */
-       public boolean isWriterSerializer() {
-               return false;
-       }
+
+       
//--------------------------------------------------------------------------------
+       // Abstract methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* SerializerSession */
+       public abstract OutputStreamSerializerSession 
createSession(SerializerSessionArgs args);
 
 
        
//--------------------------------------------------------------------------------
        // Other methods
        
//--------------------------------------------------------------------------------
 
+       @Override /* Serializer */
+       public final boolean isWriterSerializer() {
+               return false;
+       }
+
        /**
         * Convenience method for serializing an object to a 
<code><jk>byte</jk></code>.
         *
@@ -68,10 +66,7 @@ public abstract class OutputStreamSerializer extends 
Serializer {
         */
        @Override
        public final byte[] serialize(Object o) throws SerializeException {
-               ByteArrayOutputStream baos = new ByteArrayOutputStream();
-               SerializerOutput out = new SerializerOutput(baos);
-               serialize(createSession(), out, o);
-               return baos.toByteArray();
+               return createSession(null).serialize(o);
        }
 
        /**
@@ -82,9 +77,6 @@ public abstract class OutputStreamSerializer extends 
Serializer {
         * @throws SerializeException If a problem occurred trying to convert 
the output.
         */
        public final String serializeToHex(Object o) throws SerializeException {
-               ByteArrayOutputStream baos = new ByteArrayOutputStream();
-               SerializerOutput out = new SerializerOutput(baos);
-               serialize(createSession(), out, o);
-               return toHex(baos.toByteArray());
+               return toHex(serialize(o));
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializerSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializerSession.java
 
b/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializerSession.java
new file mode 100644
index 0000000..8098566
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/OutputStreamSerializerSession.java
@@ -0,0 +1,75 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.serializer;
+
+import java.io.*;
+
+/**
+ * Subclass of {@link SerializerSession} for stream-based serializers.
+ *
+ * <h5 class='section'>Description:</h5>
+ *
+ * This class is the parent class of all byte-based serializers.
+ * <br>It has 1 abstract method to implement...
+ * <ul>
+ *     <li>{@link #doSerialize(SerializerPipe, Object)}
+ * </ul>
+ */
+public abstract class OutputStreamSerializerSession extends SerializerSession {
+
+       /**
+        * Create a new session using properties specified in the context.
+        *
+        * @param ctx
+        *      The context creating this session object.
+        *      The context contains all the configuration settings for this 
object.
+        * @param args
+        *      Runtime arguments.
+        *      These specify session-level information such as locale and URI 
context.
+        *      It also include session-level properties that override the 
properties defined on the bean and
+        *      serializer contexts.
+        *      <br>If <jk>null</jk>, defaults to {@link 
SerializerSessionArgs#DEFAULT}.
+        */
+       protected OutputStreamSerializerSession(SerializerContext ctx, 
SerializerSessionArgs args) {
+               super(ctx, args);
+       }
+
+       /**
+        * Constructor for sessions that don't require context.
+        *
+        * @param args
+        *      Runtime session arguments.
+        */
+       protected OutputStreamSerializerSession(SerializerSessionArgs args) {
+               this(null, args);
+       }
+
+       @Override /* SerializerSession */
+       public final boolean isWriterSerializer() {
+               return false;
+       }
+
+       /**
+        * Convenience method for serializing an object to a 
<code><jk>byte</jk></code>.
+        *
+        * @param o The object to serialize.
+        * @return The output serialized to a byte array.
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        */
+       @Override /* SerializerSession */
+       public final byte[] serialize(Object o) throws SerializeException {
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               serialize(baos, o);
+               return baos.toByteArray();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/Serializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/Serializer.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/Serializer.java
index bb0e623..d2640c3 100644
--- a/juneau-core/src/main/java/org/apache/juneau/serializer/Serializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/Serializer.java
@@ -16,13 +16,10 @@ import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.internal.ReflectionUtils.*;
 
 import java.io.*;
-import java.lang.reflect.*;
-import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.http.*;
-import org.apache.juneau.soap.*;
 
 /**
  * Parent class for all Juneau serializers.
@@ -32,13 +29,21 @@ import org.apache.juneau.soap.*;
  * Base serializer class that serves as the parent class for all serializers.
  *
  * <p>
- * Subclasses should extend directly from {@link OutputStreamSerializer} or 
{@link WriterSerializer}.
+ * The purpose of this class is:
+ * <ul>
+ *     <li>Maintain a read-only configuration state of a serializer (i.e. 
{@link SerializerContext}).
+ *     <li>Create session objects used for serializing POJOs (i.e. {@link 
SerializerSession}).
+ *     <li>Provide convenience methods for serializing POJOs without having to 
construct session objects.
+ * </ul>
+ *
+ * <p>
+ * Subclasses should extend directly from {@link OutputStreamSerializer} or 
{@link WriterSerializer} depending on
+ * whether it's a stream or character based serializer.
  *
  * <h6 class='topic'>@Produces annotation</h6>
  *
  * The media types that this serializer can produce is specified through the 
{@link Produces @Produces} annotation.
- *
- * <p>
+ * <br>
  * However, the media types can also be specified programmatically by 
overriding the {@link #getMediaTypes()}
  * and {@link #getResponseContentType()} methods.
  */
@@ -46,12 +51,10 @@ public abstract class Serializer extends CoreObject {
 
        private final MediaType[] mediaTypes;
        private final MediaType contentType;
-       private final SerializerContext ctx;
 
        // Hidden constructors to force subclass from OuputStreamSerializer or 
WriterSerializer.
        Serializer(PropertyStore propertyStore) {
                super(propertyStore);
-               this.ctx = createContext(SerializerContext.class);
 
                Produces p = getAnnotation(Produces.class, getClass());
                if (p == null)
@@ -72,83 +75,55 @@ public abstract class Serializer extends CoreObject {
                return new SerializerBuilder(propertyStore);
        }
 
-       /**
-        * Returns <jk>true</jk> if this serializer subclasses from {@link 
WriterSerializer}.
-        *
-        * @return <jk>true</jk> if this serializer subclasses from {@link 
WriterSerializer}.
-        */
-       public abstract boolean isWriterSerializer();
-
        
//--------------------------------------------------------------------------------
        // Abstract methods
        
//--------------------------------------------------------------------------------
 
        /**
-        * Serializes a POJO to the specified output stream or writer.
-        *
-        * <p>
-        * This method should NOT close the context object.
+        * Returns <jk>true</jk> if this serializer subclasses from {@link 
WriterSerializer}.
         *
-        * @param session
-        *      The serializer session object return by {@link 
#createSession(ObjectMap, Method, Locale, TimeZone,
-        *      MediaType, UriContext)}.
-        *      If <jk>null</jk>, session is created using {@link 
#createSession()}.
-        * @param out
-        *      Where to send the output from the serializer.
-        * @param o The object to serialize.
-        * @throws Exception If thrown from underlying stream, or if the input 
contains a syntax error or is malformed.
+        * @return <jk>true</jk> if this serializer subclasses from {@link 
WriterSerializer}.
         */
-       protected abstract void doSerialize(SerializerSession session, 
SerializerOutput out, Object o) throws Exception;
+       public abstract boolean isWriterSerializer();
 
        /**
-        * Shortcut method for serializing objects directly to either a 
<code>String</code> or <code><jk>byte</jk>[]</code>
-        * depending on the serializer type.
-        *
-        * @param o The object to serialize.
+        * Create the session object used for actual serialization of objects.
+        *
+        * @param args
+        *      Runtime arguments.
+        *      These specify session-level information such as locale and URI 
context.
+        *      It also include session-level properties that override the 
properties defined on the bean and serializer
+        *      contexts.
+        *      <br>If <jk>null</jk>, defaults to {@link 
SerializerSessionArgs#DEFAULT}.
         * @return
-        *      The serialized object.
-        *      <br>Character-based serializers will return a 
<code>String</code>
-        *      <br>Stream-based serializers will return a 
<code><jk>byte</jk>[]</code>
-        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        *      The new session object.
+        *      <br>Note that you must call {@link SerializerSession#close()} 
on this object to perform any necessary
+        *      cleanup.
         */
-       public abstract Object serialize(Object o) throws SerializeException;
+       public abstract SerializerSession createSession(SerializerSessionArgs 
args);
+
 
        
//--------------------------------------------------------------------------------
-       // Other methods
+       // Convenience methods
        
//--------------------------------------------------------------------------------
 
        /**
-        * Serialize the specified object using the specified session.
+        * Shortcut for calling <code>createSession(<jk>null</jk>)</code>.
         *
-        * @param session
-        *      The serializer session object return by {@link 
#createSession(ObjectMap, Method, Locale, TimeZone,
-        *      MediaType, UriContext)}.
-        *      If <jk>null</jk>, session is created using {@link 
#createSession()}.
-        * @param out
-        *      Where to send the output from the serializer.
-        * @param o The object to serialize.
-        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        * @return
+        *      The new session object.
+        *      <br>Note that you must call {@link SerializerSession#close()} 
on this object to perform any necessary
+        *      cleanup.
         */
-       public final void serialize(SerializerSession session, SerializerOutput 
out, Object o) throws SerializeException {
-               try {
-                       doSerialize(session, out, o);
-               } catch (SerializeException e) {
-                       throw e;
-               } catch (StackOverflowError e) {
-                       throw new SerializeException(session,
-                               "Stack overflow occurred.  This can occur when 
trying to serialize models containing loops.  It's recommended you use the 
SerializerContext.SERIALIZER_detectRecursions setting to help locate the 
loop.").initCause(e);
-               } catch (Exception e) {
-                       throw new SerializeException(session, e);
-               } finally {
-                       session.close();
-               }
+       public final SerializerSession createSession() {
+               return createSession(null);
        }
 
        /**
         * Serializes a POJO to the specified output stream or writer.
         *
         * <p>
-        * Equivalent to calling <code>serializer.serialize(o, out, 
<jk>null</jk>);</code>
+        * Equivalent to calling <code>serializer.createSession().serialize(o, 
output);</code>
         *
         * @param o The object to serialize.
         * @param output
@@ -168,79 +143,38 @@ public abstract class Serializer extends CoreObject {
         * @throws SerializeException If a problem occurred trying to convert 
the output.
         */
        public final void serialize(Object o, Object output) throws 
SerializeException {
-               SerializerSession session = createSession();
-               SerializerOutput out = new SerializerOutput(output);
-               serialize(session, out, o);
-       }
-
-       /**
-        * Create the session object that will be passed in to the serialize 
method.
-        *
-        * <p>
-        * It's up to implementers to decide what the session object looks 
like, although typically it's going to be a
-        * subclass of {@link SerializerSession}.
-        *
-        * @param op Optional additional properties.
-        * @param javaMethod
-        *      Java method that invoked this serializer.
-        *      When using the REST API, this is the Java method invoked by the 
REST call.
-        *      Can be used to access annotations defined on the method or 
class.
-        * @param locale
-        *      The session locale.
-        *      If <jk>null</jk>, then the locale defined on the context is 
used.
-        * @param timeZone
-        *      The session timezone.
-        *      If <jk>null</jk>, then the timezone defined on the context is 
used.
-        * @param mediaType The session media type (e.g. 
<js>"application/json"</js>).
-        * @param uriContext
-        *      The URI context.
-        *      Identifies the current request URI used for resolution of URIs 
to absolute or root-relative form.
-        * @return The new session.
-        */
-       public SerializerSession createSession(ObjectMap op, Method javaMethod, 
Locale locale,
-                       TimeZone timeZone, MediaType mediaType, UriContext 
uriContext) {
-               return new SerializerSession(ctx, op, javaMethod, locale, 
timeZone, mediaType, uriContext);
-       }
-
-       /**
-        * Create a basic session object without overriding properties or 
specifying <code>javaMethod</code>.
-        *
-        * <p>
-        * Equivalent to calling <code>createSession(<jk>null</jk>, 
<jk>null</jk>)</code>.
-        *
-        * @return The new session.
-        */
-       protected SerializerSession createSession() {
-               return createSession(null, null, null, null, 
getPrimaryMediaType(), null);
+               SerializerSession s = createSession();
+               try {
+                       s.serialize(o, output);
+               } finally {
+                       s.close();
+               }
        }
 
        /**
-        * Converts the contents of the specified object array to a list.
-        *
-        * <p>
-        * Works on both object and primitive arrays.
-        *
-        * <p>
-        * In the case of multi-dimensional arrays, the outgoing list will 
contain elements of type n-1 dimension.
-        * i.e. if {@code type} is <code><jk>int</jk>[][]</code> then {@code 
list} will have entries of type
-        * <code><jk>int</jk>[]</code>.
+        * Shortcut method for serializing objects directly to either a 
<code>String</code> or <code><jk>byte</jk>[]</code>
+        * depending on the serializer type.
         *
-        * @param type The type of array.
-        * @param array The array being converted.
-        * @return The array as a list.
+        * @param o The object to serialize.
+        * @return
+        *      The serialized object.
+        *      <br>Character-based serializers will return a 
<code>String</code>
+        *      <br>Stream-based serializers will return a 
<code><jk>byte</jk>[]</code>
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
         */
-       protected static final List<Object> toList(Class<?> type, Object array) 
{
-               Class<?> componentType = type.getComponentType();
-               if (componentType.isPrimitive()) {
-                       int l = Array.getLength(array);
-                       List<Object> list = new ArrayList<Object>(l);
-                       for (int i = 0; i < l; i++)
-                               list.add(Array.get(array, i));
-                       return list;
+       public Object serialize(Object o) throws SerializeException {
+               SerializerSession s = createSession();
+               try {
+                       return s.serialize(o);
+               } finally {
+                       s.close();
                }
-               return Arrays.asList((Object[])array);
        }
 
+       
//--------------------------------------------------------------------------------
+       // Other methods
+       
//--------------------------------------------------------------------------------
+
        /**
         * Returns the media types handled based on the value of the {@link 
Produces} annotation on the serializer class.
         *
@@ -249,7 +183,7 @@ public abstract class Serializer extends CoreObject {
         *
         * @return The list of media types.  Never <jk>null</jk>.
         */
-       public MediaType[] getMediaTypes() {
+       public final MediaType[] getMediaTypes() {
                return mediaTypes;
        }
 
@@ -258,32 +192,11 @@ public abstract class Serializer extends CoreObject {
         *
         * @return The media type.
         */
-       public MediaType getPrimaryMediaType() {
+       public final MediaType getPrimaryMediaType() {
                return mediaTypes == null || mediaTypes.length == 0 ? null : 
mediaTypes[0];
        }
 
        /**
-        * Optional method that specifies HTTP request headers for this 
serializer.
-        *
-        * <p>
-        * For example, {@link SoapXmlSerializer} needs to set a 
<code>SOAPAction</code> header.
-        *
-        * <p>
-        * This method is typically meaningless if the serializer is being used 
stand-alone (i.e. outside of a REST server
-        * or client).
-        *
-        * @param properties
-        *      Optional run-time properties (the same that are passed to 
{@link WriterSerializer#doSerialize(SerializerSession, SerializerOutput, 
Object)}.
-        *      Can be <jk>null</jk>.
-        * @return
-        *      The HTTP headers to set on HTTP requests.
-        *      Can be <jk>null</jk>.
-        */
-       public ObjectMap getResponseHeaders(ObjectMap properties) {
-               return ObjectMap.EMPTY_MAP;
-       }
-
-       /**
         * Optional method that returns the response <code>Content-Type</code> 
for this serializer if it is different from
         * the matched media type.
         *

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
index 4ab30cf..8d50aa8 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
@@ -21,6 +21,12 @@ import org.apache.juneau.annotation.*;
 public class SerializerContext extends BeanContext {
 
        /**
+        * Default context with all default values.
+        */
+       @SuppressWarnings("hiding")
+       protected static final SerializerContext DEFAULT = new 
SerializerContext(PropertyStore.create());
+
+       /**
         * <b>Configuration property:</b>  Max serialization depth.
         *
         * <ul>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
index f8e192b..1fd700c 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
@@ -88,7 +88,7 @@ public final class SerializerGroup {
         *      to match against media types.
         */
        public SerializerGroup(PropertyStore propertyStore, Serializer[] 
serializers) {
-               this.propertyStore = PropertyStore.create(propertyStore);
+               this.propertyStore = propertyStore.copy();
                this.beanContext = propertyStore.getBeanContext();
                this.serializers = Collections.unmodifiableList(new 
ArrayList<Serializer>(Arrays.asList(serializers)));
 
@@ -198,7 +198,7 @@ public final class SerializerGroup {
         * @return A new copy of the property store passed in to the 
constructor.
         */
        public PropertyStore createPropertyStore() {
-               return PropertyStore.create(propertyStore);
+               return propertyStore.copy();
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerOutput.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerOutput.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerOutput.java
deleted file mode 100644
index 8cc9cc0..0000000
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerOutput.java
+++ /dev/null
@@ -1,174 +0,0 @@
-// 
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                
                                              *
-// *                                                                           
                                              *
-// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
-// *                                                                           
                                              *
-// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the 
License.                                              *
-// 
***************************************************************************************************************************
-package org.apache.juneau.serializer;
-
-import static org.apache.juneau.internal.IOUtils.*;
-
-import java.io.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
-
-/**
- * A wrapper around an object that a serializer sends its output to.
- *
- * <p>
- * For character-based serializers, the output object can be any of the 
following:
- * <ul>
- *     <li>{@link Writer}
- *     <li>{@link OutputStream} - Output will be written as UTF-8 encoded 
stream.
- *     <li>{@link File} - Output will be written as system-default encoded 
stream.
- *     <li>{@link StringBuilder}
- * </ul>
- *
- * <p>
- * For stream-based serializers, the output object can be any of the following:
- * <ul>
- *     <li>{@link OutputStream}
- *     <li>{@link File}
- * </ul>
- */
-public class SerializerOutput {
-
-       private final Object output;
-       private final boolean autoClose;
-       private OutputStream outputStream;
-       private Writer writer, flushOnlyWriter;
-
-       /**
-        * Constructor.
-        *
-        * <p>
-        * Equivalent to calling <code>SerializerOutput(output, 
<jk>true</jk>);</code>.
-        *
-        * @param output The object to pipe the serializer output to.
-        */
-       public SerializerOutput(Object output) {
-               this(output, true);
-       }
-
-       /**
-        * Constructor.
-        *
-        * @param output The object to pipe the serializer output to.
-        * @param autoClose Close the stream or writer at the end of the 
session.
-        */
-       public SerializerOutput(Object output, boolean autoClose) {
-               this.output = output;
-               this.autoClose = autoClose;
-       }
-
-       /**
-        * Wraps the specified output object inside an output stream.
-        *
-        * <p>
-        * Subclasses can override this method to implement their own 
specialized output streams.
-        *
-        * <p>
-        * This method can be used if the output object is any of the following 
class types:
-        * <ul>
-        *      <li>{@link OutputStream}
-        *      <li>{@link File}
-        * </ul>
-        *
-        * @return The output object wrapped in an output stream.
-        * @throws Exception If object could not be converted to an output 
stream.
-        */
-       public OutputStream getOutputStream() throws Exception {
-               if (output == null)
-                       throw new SerializeException("Output cannot be null.");
-               if (output instanceof OutputStream)
-                       return (OutputStream)output;
-               if (output instanceof File) {
-                       if (outputStream == null)
-                               outputStream = new BufferedOutputStream(new 
FileOutputStream((File)output));
-                       return outputStream;
-               }
-               throw new SerializeException("Cannot convert object of type {0} 
to an OutputStream.", output.getClass().getName());
-       }
-
-
-       /**
-        * Wraps the specified output object inside a writer.
-        *
-        * <p>
-        * Subclasses can override this method to implement their own 
specialized writers.
-        *
-        * <p>
-        * This method can be used if the output object is any of the following 
class types:
-        * <ul>
-        *      <li>{@link Writer}
-        *      <li>{@link OutputStream} - Output will be written as UTF-8 
encoded stream.
-        *      <li>{@link File} - Output will be written as system-default 
encoded stream.
-        * </ul>
-        *
-        * @return The output object wrapped in a Writer.
-        * @throws Exception If object could not be converted to a writer.
-        */
-       public Writer getWriter() throws Exception {
-               if (output == null)
-                       throw new SerializeException("Output cannot be null.");
-               if (output instanceof Writer)
-                       return (Writer)output;
-               if (output instanceof OutputStream) {
-                       if (flushOnlyWriter == null)
-                               flushOnlyWriter = new 
OutputStreamWriter((OutputStream)output, UTF8);
-                       return flushOnlyWriter;
-               }
-               if (output instanceof File) {
-                       if (writer == null)
-                               writer = new OutputStreamWriter(new 
BufferedOutputStream(new FileOutputStream((File)output)));
-                       return writer;
-               }
-               if (output instanceof StringBuilder) {
-                       if (writer == null)
-                               writer = new 
StringBuilderWriter((StringBuilder)output);
-                       return writer;
-               }
-               throw new SerializeException("Cannot convert object of type {0} 
to a Writer.", output.getClass().getName());
-       }
-
-       /**
-        * Returns the raw output object passed into this session.
-        *
-        * @return The raw output object passed into this session.
-        */
-       public Object getRawOutput() {
-               return output;
-       }
-
-       /**
-        * Closes the output pipe.
-        */
-       public void close() {
-               try {
-                       if (! autoClose) {
-                               if (outputStream != null)
-                                       outputStream.flush();
-                               if (flushOnlyWriter != null)
-                                       flushOnlyWriter.flush();
-                               if (writer != null)
-                                       writer.flush();
-                       } else {
-                               if (outputStream != null)
-                                       outputStream.close();
-                               if (flushOnlyWriter != null)
-                                       flushOnlyWriter.flush();
-                               if (writer != null)
-                                       writer.close();
-                       }
-               } catch (IOException e) {
-                       throw new BeanRuntimeException(e);
-               }
-       }
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerPipe.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerPipe.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerPipe.java
new file mode 100644
index 0000000..4a39264
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerPipe.java
@@ -0,0 +1,172 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.serializer;
+
+import static org.apache.juneau.internal.IOUtils.*;
+
+import java.io.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * A wrapper around an object that a serializer sends its output to.
+ *
+ * <p>
+ * For character-based serializers, the output object can be any of the 
following:
+ * <ul>
+ *     <li>{@link Writer}
+ *     <li>{@link OutputStream} - Output will be written as UTF-8 encoded 
stream.
+ *     <li>{@link File} - Output will be written as system-default encoded 
stream.
+ *     <li>{@link StringBuilder}
+ * </ul>
+ *
+ * <p>
+ * For stream-based serializers, the output object can be any of the following:
+ * <ul>
+ *     <li>{@link OutputStream}
+ *     <li>{@link File}
+ * </ul>
+ */
+public final class SerializerPipe {
+
+       private final Object output;
+       private final boolean autoClose;
+       
+       private OutputStream outputStream;
+       private Writer writer;
+
+       /**
+        * Constructor.
+        *
+        * @param output The object to pipe the serializer output to.
+        */
+       SerializerPipe(Object output) {
+               this.output = output;
+               this.autoClose = (output instanceof File);
+       }
+
+       /**
+        * Wraps the specified output object inside an output stream.
+        *
+        * <p>
+        * Subclasses can override this method to implement their own 
specialized output streams.
+        *
+        * <p>
+        * This method can be used if the output object is any of the following 
class types:
+        * <ul>
+        *      <li>{@link OutputStream}
+        *      <li>{@link File}
+        * </ul>
+        *
+        * @return The output object wrapped in an output stream.
+        * @throws IOException If object could not be converted to an output 
stream.
+        */
+       public OutputStream getOutputStream() throws IOException {
+               if (output == null)
+                       throw new IOException("Output cannot be null.");
+
+               if (output instanceof OutputStream)
+                       outputStream = (OutputStream)output;
+               else if (output instanceof File)
+                       outputStream = new BufferedOutputStream(new 
FileOutputStream((File)output));
+               else
+                       throw new IOException("Cannot convert object of type 
"+output.getClass().getName()+" to an OutputStream.");
+
+               return outputStream;
+       }
+
+
+       /**
+        * Wraps the specified output object inside a writer.
+        *
+        * <p>
+        * Subclasses can override this method to implement their own 
specialized writers.
+        *
+        * <p>
+        * This method can be used if the output object is any of the following 
class types:
+        * <ul>
+        *      <li>{@link Writer}
+        *      <li>{@link OutputStream} - Output will be written as UTF-8 
encoded stream.
+        *      <li>{@link File} - Output will be written as system-default 
encoded stream.
+        * </ul>
+        *
+        * @return The output object wrapped in a Writer.
+        * @throws IOException If object could not be converted to a writer.
+        */
+       public Writer getWriter() throws IOException {
+               if (output == null)
+                       throw new IOException("Output cannot be null.");
+
+               if (output instanceof Writer)
+                       writer = (Writer)output;
+               else if (output instanceof OutputStream)
+                       writer = new OutputStreamWriter((OutputStream)output, 
UTF8);
+               else if (output instanceof File)
+                       writer = new OutputStreamWriter(new 
BufferedOutputStream(new FileOutputStream((File)output)));
+               else if (output instanceof StringBuilder)
+                       writer = new StringBuilderWriter((StringBuilder)output);
+               else
+                       throw new IOException("Cannot convert object of type 
"+output.getClass().getName()+" to a Writer.");
+
+               return writer;
+       }
+
+       /**
+        * Overwrites the writer in this pipe.
+        *
+        * <p>
+        * Used when wrapping the writer returned by {@link #getWriter()} so 
that the wrapped writer will be flushed
+        * and closed when {@link #close()} is called.
+        *
+        * @param writer The wrapped writer.
+        */
+       public void setWriter(Writer writer) {
+               this.writer = writer;
+       }
+
+       /**
+        * Overwrites the output stream in this pipe.
+        *
+        * <p>
+        * Used when wrapping the stream returned by {@link #getOutputStream()} 
so that the wrapped stream will be flushed
+        * and closed when {@link #close()} is called.
+        *
+        * @param outputStream The wrapped stream.
+        */
+       public void setOutputStream(OutputStream outputStream) {
+               this.outputStream = outputStream;
+       }
+
+       /**
+        * Returns the raw output object passed into this session.
+        *
+        * @return The raw output object passed into this session.
+        */
+       public Object getRawOutput() {
+               return output;
+       }
+
+       /**
+        * Closes the output pipe.
+        */
+       public void close() {
+               try {
+                       IOUtils.flush(writer, outputStream);
+                       if (autoClose)
+                               IOUtils.close(writer, outputStream);
+               } catch (IOException e) {
+                       throw new BeanRuntimeException(e);
+               }
+       }
+}

Reply via email to