You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 17:29:58 UTC

[09/53] [partial] incubator-juneau git commit: Merge changes from GitHub repo.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/dto/jsonschema/package.html
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/dto/jsonschema/package.html b/org.apache.juneau/src/main/java/org/apache/juneau/dto/jsonschema/package.html
new file mode 100644
index 0000000..51977bf
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/dto/jsonschema/package.html
@@ -0,0 +1,518 @@
+<!DOCTYPE HTML>
+<!--
+/***************************************************************************************************************************
+ * 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.
+ *
+ ***************************************************************************************************************************/
+-->
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+		/* For viewing in Page Designer */
+		@IMPORT url("../../../../../../../javadoc.css");
+
+		/* For viewing in REST interface */
+		@IMPORT url("../htdocs/javadoc.css");
+		body { 
+			margin: 20px; 
+		}	
+	</style>
+	<script>
+		/* Replace all @code and @link tags. */	
+		window.onload = function() {
+			document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+			document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>');
+		}
+	</script>
+</head>
+<body>
+<p>JSON-Schema Data Transfer Objects</p>
+<script>
+	function toggle(x) {
+		var div = x.nextSibling;
+		while (div != null && div.nodeType != 1)
+			div = div.nextSibling;
+		if (div != null) {
+			var d = div.style.display;
+			if (d == 'block' || d == '') {
+				div.style.display = 'none';
+				x.className += " closed";
+			} else {
+				div.style.display = 'block';
+				x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' );
+			}
+		}
+	}
+</script>
+<a id='TOC'></a><h5 class='toc'>Table of Contents</h5>
+<ol class='toc'>
+	<li><p><a class='doclink' href='#Overview'>Overview</a></p>
+	<ol>
+		<li><p><a class='doclink' href='#SchemaDefinition'>JSON-Schema schema definition</a></p>
+		<li><p><a class='doclink' href='#Serialize'>Creating JSON-Schema documents</a></p>
+		<ol>
+			<li><p><a class='doclink' href='#SerializeToOther'>Serializing to other data types</a></p>
+		</ol>
+		<li><p><a class='doclink' href='#Parse'>Parsing JSON-Schema documents</a></p>
+	</ol>
+</ol>
+<!-- ======================================================================================================== -->
+<a id="Overview"></a>
+<h2 class='topic' onclick='toggle(this)'>1 - Overview</h2>
+<div class='topic'>
+	<p>
+		Juneau supports serializing and parsing of JSON-Schema documents through the use of beans defined in the <code>org.apache.juneau.dto.jsonschema</code> package.<br>
+		These beans are used with the existing {@link org.apache.juneau.json.JsonSerializer} and {@link org.apache.juneau.json.JsonParser} classes to produce and consume JSON-Schema documents. 
+	</p>
+	<p>
+		<b>NOTE:</b>  JSON-Schema is currently in draft form.  This API may change as the JSON-Schema specification changes.
+	</p>
+	
+	<!-- ======================================================================================================== -->
+	<a id="SchemaDefinition"></a>
+	<h3 class='topic' onclick='toggle(this)'>1.1 - JSON-Schema schema definition</h3>
+	<div class='topic'>
+		<p>
+			The draft JSON-Schema specification that the JSON-Schema beans are modeled after is as follows:
+		</p>
+		<p class='bcode'>
+	{
+	    <js>"id"</js>: <js>"http://json-schema.org/draft-04/schema#"</js>,
+	    <js>"$schema"</js>: <js>"http://json-schema.org/draft-04/schema#"</js>,
+	    <js>"description"</js>: <js>"Core schema meta-schema"</js>,
+	    <js>"definitions"</js>: {
+	        <js>"schemaArray"</js>: {
+	            <js>"type"</js>: <js>"array"</js>,
+	            <js>"minItems"</js>: 1,
+	            <js>"items"</js>: { <js>"$ref"</js>: <js>"#"</js> }
+	        },
+	        <js>"positiveInteger"</js>: {
+	            <js>"type"</js>: <js>"integer"</js>,
+	            <js>"minimum"</js>: 0
+	        },
+	        <js>"positiveIntegerDefault0"</js>: {
+	            <js>"allOf"</js>: [ { <js>"$ref"</js>: <js>"#/definitions/positiveInteger"</js> }, { <js>"default"</js>: 0 } ]
+	        },
+	        <js>"simpleTypes"</js>: {
+	            <js>"enum"</js>: [ <js>"array"</js>, <js>"boolean"</js>, <js>"integer"</js>, <js>"null"</js>, <js>"number"</js>, <js>"object"</js>, <js>"string"</js> ]
+	        },
+	        <js>"stringArray"</js>: {
+	            <js>"type"</js>: <js>"array"</js>,
+	            <js>"items"</js>: { <js>"type"</js>: <js>"string"</js> },
+	            <js>"minItems"</js>: 1,
+	            <js>"uniqueItems"</js>: <jk>true</jk>
+	        }
+	    },
+	    <js>"type"</js>: <js>"object"</js>,
+	    <js>"properties"</js>: {
+	        <js>"id"</js>: {
+	            <js>"type"</js>: <js>"string"</js>,
+	            <js>"format"</js>: <js>"uri"</js>
+	        },
+	        <js>"$schema"</js>: {
+	            <js>"type"</js>: <js>"string"</js>,
+	            <js>"format"</js>: <js>"uri"</js>
+	        },
+	        <js>"title"</js>: {
+	            <js>"type"</js>: <js>"string"</js>
+	        },
+	        <js>"description"</js>: {
+	            <js>"type"</js>: <js>"string"</js>
+	        },
+	        <js>"default"</js>: {},
+	        <js>"multipleOf"</js>: {
+	            <js>"type"</js>: <js>"number"</js>,
+	            <js>"minimum"</js>: 0,
+	            <js>"exclusiveMinimum"</js>: <jk>true</jk>
+	        },
+	        <js>"maximum"</js>: {
+	            <js>"type"</js>: <js>"number"</js>
+	        },
+	        <js>"exclusiveMaximum"</js>: {
+	            <js>"type"</js>: <js>"boolean"</js>,
+	            <js>"default"</js>: <jk>false</jk>
+	        },
+	        <js>"minimum"</js>: {
+	            <js>"type"</js>: <js>"number"</js>
+	        },
+	        <js>"exclusiveMinimum"</js>: {
+	            <js>"type"</js>: <js>"boolean"</js>,
+	            <js>"default"</js>: <jk>false</jk>
+	        },
+	        <js>"maxLength"</js>: { <js>"$ref"</js>: <js>"#/definitions/positiveInteger"</js> },
+	        <js>"minLength"</js>: { <js>"$ref"</js>: <js>"#/definitions/positiveIntegerDefault0"</js> },
+	        <js>"pattern"</js>: {
+	            <js>"type"</js>: <js>"string"</js>,
+	            <js>"format"</js>: <js>"regex"</js>
+	        },
+	        <js>"additionalItems"</js>: {
+	            <js>"anyOf"</js>: [
+	                { <js>"type"</js>: <js>"boolean"</js> },
+	                { <js>"$ref"</js>: <js>"#"</js> }
+	            ],
+	            <js>"default"</js>: {}
+	        },
+	        <js>"items"</js>: {
+	            <js>"anyOf"</js>: [
+	                { <js>"$ref"</js>: <js>"#"</js> },
+	                { <js>"$ref"</js>: <js>"#/definitions/schemaArray"</js> }
+	            ],
+	            <js>"default"</js>: {}
+	        },
+	        <js>"maxItems"</js>: { <js>"$ref"</js>: <js>"#/definitions/positiveInteger"</js> },
+	        <js>"minItems"</js>: { <js>"$ref"</js>: <js>"#/definitions/positiveIntegerDefault0"</js> },
+	        <js>"uniqueItems"</js>: {
+	            <js>"type"</js>: <js>"boolean"</js>,
+	            <js>"default"</js>: <jk>false</jk>
+	        },
+	        <js>"maxProperties"</js>: { <js>"$ref"</js>: <js>"#/definitions/positiveInteger"</js> },
+	        <js>"minProperties"</js>: { <js>"$ref"</js>: <js>"#/definitions/positiveIntegerDefault0"</js> },
+	        <js>"required"</js>: { <js>"$ref"</js>: <js>"#/definitions/stringArray"</js> },
+	        <js>"additionalProperties"</js>: {
+	            <js>"anyOf"</js>: [
+	                { <js>"type"</js>: <js>"boolean"</js> },
+	                { <js>"$ref"</js>: <js>"#"</js> }
+	            ],
+	            <js>"default"</js>: {}
+	        },
+	        <js>"definitions"</js>: {
+	            <js>"type"</js>: <js>"object"</js>,
+	            <js>"additionalProperties"</js>: { <js>"$ref"</js>: <js>"#"</js> },
+	            <js>"default"</js>: {}
+	        },
+	        <js>"properties"</js>: {
+	            <js>"type"</js>: <js>"object"</js>,
+	            <js>"additionalProperties"</js>: { <js>"$ref"</js>: <js>"#"</js> },
+	            <js>"default"</js>: {}
+	        },
+	        <js>"patternProperties"</js>: {
+	            <js>"type"</js>: <js>"object"</js>,
+	            <js>"additionalProperties"</js>: { <js>"$ref"</js>: <js>"#"</js> },
+	            <js>"default"</js>: {}
+	        },
+	        <js>"dependencies"</js>: {
+	            <js>"type"</js>: <js>"object"</js>,
+	            <js>"additionalProperties"</js>: {
+	                <js>"anyOf"</js>: [
+	                    { <js>"$ref"</js>: <js>"#"</js> },
+	                    { <js>"$ref"</js>: <js>"#/definitions/stringArray"</js> }
+	                ]
+	            }
+	        },
+	        <js>"enum"</js>: {
+	            <js>"type"</js>: <js>"array"</js>,
+	            <js>"minItems"</js>: 1,
+	            <js>"uniqueItems"</js>: <jk>true</jk>
+	        },
+	        <js>"type"</js>: {
+	            <js>"anyOf"</js>: [
+	                { <js>"$ref"</js>: <js>"#/definitions/simpleTypes"</js> },
+	                {
+	                    <js>"type"</js>: <js>"array"</js>,
+	                    <js>"items"</js>: { <js>"$ref"</js>: <js>"#/definitions/simpleTypes"</js> },
+	                    <js>"minItems"</js>: 1,
+	                    <js>"uniqueItems"</js>: <jk>true</jk>
+	                }
+	            ]
+	        },
+	        <js>"allOf"</js>: { <js>"$ref"</js>: <js>"#/definitions/schemaArray"</js> },
+	        <js>"anyOf"</js>: { <js>"$ref"</js>: <js>"#/definitions/schemaArray"</js> },
+	        <js>"oneOf"</js>: { <js>"$ref"</js>: <js>"#/definitions/schemaArray"</js> },
+	        <js>"not"</js>: { <js>"$ref"</js>: <js>"#"</js> }
+	    },
+	    <js>"dependencies"</js>: {
+	        <js>"exclusiveMaximum"</js>: [ <js>"maximum"</js> ],
+	        <js>"exclusiveMinimum"</js>: [ <js>"minimum"</js> ]
+	    },
+	    <js>"default"</js>: {}
+	}
+		</p>
+		<p>
+			The bean classes that make up the model are as follows:
+		</p>
+		<ul class='spaced-list'>
+			<li>{@link org.apache.juneau.dto.jsonschema.Schema} - Top level schema object.
+			<li>{@link org.apache.juneau.dto.jsonschema.SchemaProperty} - A subclass of <code>Schema</code> for representing properties.
+			<li>{@link org.apache.juneau.dto.jsonschema.SchemaPropertySimpleArray} - A convenience subclass of <code>SchemaProperty</code> for representing properties of simple array types.
+			<li>{@link org.apache.juneau.dto.jsonschema.SchemaRef} - Represents a URI reference to another schema.
+			<li>{@link org.apache.juneau.dto.jsonschema.SchemaArray} - An array of <code>Schema</code> objects.
+			<li>{@link org.apache.juneau.dto.jsonschema.JsonType} - An enum of possible JSON data types.
+			<li>{@link org.apache.juneau.dto.jsonschema.JsonTypeArray} - An array of <code>JsonType</code> objects.
+		</ul>
+	</div>	
+
+
+	<!-- ======================================================================================================== -->
+	<a id="Serialize"></a>
+	<h3 class='topic' onclick='toggle(this)'>1.2 - Creating JSON-Schema documents</h3>
+	<div class='topic'>
+		<p>
+			JSON-Schema documents can be constructed using the Juneau JSON-Schema beans as a document model object.
+			These beans are defined with fluent-style setters to make constructing documents as easy as possible.
+		</p>
+		<p>
+			The following is an example JSON-Schema document:
+		</p>
+		<p class='bcode'>
+	{
+		<js>"title"</js>: <js>"Example Schema"</js>,
+		<js>"type"</js>: <js>"object"</js>,
+		<js>"properties"</js>: {
+			<js>"firstName"</js>: {
+				<js>"type"</js>: <js>"string"</js>
+			},
+			<js>"lastName"</js>: {
+				<js>"type"</js>: <js>"string"</js>
+			},
+			<js>"age"</js>: {
+				<js>"description"</js>: <js>"Age in years"</js>,
+				<js>"type"</js>: <js>"integer"</js>,
+				<js>"minimum"</js>: 0
+			}
+		},
+		<js>"required"</js>: [<js>"firstName"</js>, <js>"lastName"</js>]
+	}		
+		</p>
+		<p>
+			This document can be constructing using the following code:
+		</p>
+		<p class='bcode'>
+	<jc>// Create the document object model</jc>
+	Schema s = <jk>new</jk> Schema()
+		.setTitle(<js>"Example Schema"</js>)
+		.setType(JsonType.<jsf>OBJECT</jsf>)
+		.addProperties(
+			<jk>new</jk> SchemaProperty(<js>"firstName"</js>, JsonType.<jsf>STRING</jsf>),
+			<jk>new</jk> SchemaProperty(<js>"lastName"</js>, JsonType.<jsf>STRING</jsf>),
+			<jk>new</jk> SchemaProperty(<js>"age"</js>, JsonType.<jsf>INTEGER</jsf>)
+				.setDescription(<js>"Age in years"</js>)
+				.setMinimum(0)
+		)
+		.addRequired(<js>"firstName"</js>, <js>"lastName"</js>);
+		
+	<jc>// Serialize to JSON</jc>
+	String json = JsonSerializer.<jsf>DEFAULT_READABLE</jsf>.serialize(s);
+		</p>	
+		<p>
+			The following is a more-complex example showing various kinds of constraints.
+		</p>		
+		<p class='bcode'>
+	{
+	    <js>"id"</js>: <js>"http://some.site.somewhere/entry-schema#"</js>,
+	    <js>"$schema"</js>: <js>"http://json-schema.org/draft-04/schema#"</js>,
+	    <js>"description"</js>: <js>"schema for an fstab entry"</js>,
+	    <js>"type"</js>: <js>"object"</js>,
+	    <js>"required"</js>: [ <js>"storage"</js> ],
+	    <js>"properties"</js>: {
+	        <js>"storage"</js>: {
+	            <js>"type"</js>: <js>"object"</js>,
+	            <js>"oneOf"</js>: [
+	                { <js>"$ref"</js>: <js>"#/definitions/diskDevice"</js> },
+	                { <js>"$ref"</js>: <js>"#/definitions/diskUUID"</js> },
+	                { <js>"$ref"</js>: <js>"#/definitions/nfs"</js> },
+	                { <js>"$ref"</js>: <js>"#/definitions/tmpfs"</js> }
+	            ]
+	        },
+	        <js>"fstype"</js>: {
+	            <js>"enum"</js>: [ <js>"ext3"</js>, <js>"ext4"</js>, <js>"btrfs"</js> ]
+	        },
+	        <js>"options"</js>: {
+	            <js>"type"</js>: <js>"array"</js>,
+	            <js>"minItems"</js>: 1,
+	            <js>"items"</js>: { <js>"type"</js>: <js>"string"</js> },
+	            <js>"uniqueItems"</js>: <jk>true</jk>
+	        },
+	        <js>"readonly"</js>: { <js>"type"</js>: <js>"boolean"</js> }
+	    },
+	    <js>"definitions"</js>: {
+	        <js>"diskDevice"</js>: {},
+	        <js>"diskUUID"</js>: {},
+	        <js>"nfs"</js>: {},
+	        <js>"tmpfs"</js>: {}
+	    }
+	}
+		</p>
+		<p>
+			This document can be constructing using the following code:
+		</p>
+		<p class='bcode'>
+	Schema s = <jk>new</jk> Schema()
+		.setId(<js>"http://some.site.somewhere/entry-schema#"</js>)
+		.setSchemaVersionId(<js>"http://json-schema.org/draft-04/schema#"</js>)
+		.setDescription(<js>"schema for an fstab entry"</js>)
+		.setType(JsonType.<jsf>OBJECT</jsf>)
+		.addRequired(<js>"storage"</js>)
+		.addProperties(
+			<jk>new</jk> SchemaProperty(<js>"storage"</js>)
+				.setType(JsonType.<jsf>OBJECT</jsf>)
+				.addOneOf(
+					<jk>new</jk> SchemaRef(<js>"#/definitions/diskDevice"</js>),
+					<jk>new</jk> SchemaRef(<js>"#/definitions/diskUUID"</js>),
+					<jk>new</jk> SchemaRef(<js>"#/definitions/nsf"</js>),
+					<jk>new</jk> SchemaRef(<js>"#/definitions/tmpfs"</js>)
+				),
+			<jk>new</jk> SchemaProperty(<js>"fstype"</js>)
+				.addEnum(<js>"ext3"</js>, <js>"ext4"</js>, <js>"btrfs"</js>),
+			<jk>new</jk> SchemaPropertySimpleArray(<js>"options"</js>, JsonType.<jsf>STRING</jsf>)
+				.setMinItems(1)
+				.setUniqueItems(<jk>true</jk>),
+			<jk>new</jk> SchemaProperty(<js>"readonly"</js>)
+				.setType(JsonType.<jsf>BOOLEAN</jsf>)
+		)
+		.addDefinition(<js>"diskDevice"</js>,
+			<jk>new</jk> Schema()
+		)
+		.addDefinition(<js>"diskUUID"</js>,
+			<jk>new</jk> Schema()
+		)
+		.addDefinition(<js>"nfs"</js>,
+			<jk>new</jk> Schema()
+		)
+		.addDefinition(<js>"tmpfs"</js>,
+			<jk>new</jk> Schema()
+		);
+
+	<jc>// Serialize to JSON</jc>
+	String json = JsonSerializer.<jsf>DEFAULT_READABLE</jsf>.serialize(s);
+		</p>
+	
+	
+		<!-- ======================================================================================================== -->
+		<a id="SerializeToOther"></a>
+		<h4 class='topic' onclick='toggle(this)'>1.2.1 - Serializing to other data types</h4>
+		<div class='topic'>
+			<p>
+				Since the JSON-Schema DTOs are simple beans, they can be used to serialize to a variety of other language types as well as JSON.
+				This also allows JSON-Schema documents to be easily served up using the Juneau REST API.
+			</p>
+			<p>
+				The sample web application includes a REST resource that generates a JSON-Schema document.  
+				We'll use this resource to show what the JSON-Schema document looks like in other languages.
+			</p>
+			<p class='bcode'>
+	<jd>/**
+	 * Sample resource that shows how to serialize JSON-Schema documents.
+	 */</jd>
+	<ja>@RestResource</ja>(
+		path=<js>"/jsonSchema"</js>,
+		messages=<js>"nls/JsonSchemaResource"</js>,
+		properties={
+			<ja>@Property</ja>(name=HtmlDocSerializerContext.<jsf>HTMLDOC_title</jsf>, value=<js>"Sample JSON-Schema document"</js>),
+			<ja>@Property</ja>(name=HtmlDocSerializerContext.<jsf>HTMLDOC_links</jsf>, value=<js>"{options:'?method=OPTIONS'}"</js>)
+		}
+	)
+	<jk>public class</jk> JsonSchemaResource <jk>extends</jk> RestServletJenaDefault {
+	
+		<jk>private</jk> Schema <jf>schema</jf>;     <jc>// The schema document</jc>
+		
+		<jd>/** Servlet initialization */</jd> 
+		<ja>@Override</ja>
+		<jk>public void</jk> init() {
+	
+			<jk>try</jk> {
+				<jf>schema</jf> = <jk>new</jk> Schema()
+					.setId(<js>"http://example.com/sample-schema#"</js>)
+					.setSchemaVersionUri(<js>"http://json-schema.org/draft-04/schema#"</js>)
+					.setTitle(<js>"Example Schema"</js>)
+					.setType(JsonType.<jsf>OBJECT</jsf>)
+					.addProperties(
+						<jk>new</jk> SchemaProperty(<js>"firstName"</js>, JsonType.<jsf>STRING</jsf>),
+						<jk>new</jk> SchemaProperty(<js>"lastName"</js>, JsonType.<jsf>STRING</jsf>),
+						<jk>new</jk> SchemaProperty(<js>"age"</js>, JsonType.<jsf>INTEGER</jsf>)
+							.setDescription(<js>"Age in years"</js>)
+							.setMinimum(0)
+					)
+					.addRequired(<js>"firstName"</js>, <js>"lastName"</js>);
+			} <jk>catch</jk> (Exception e) {
+				<jk>throw new</jk> RuntimeException(e);
+			}
+		}
+		
+		<jd>/** GET request handler */</jd>
+		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/"</js>)
+		<jk>public</jk> Schema getSchema() <jk>throws</jk> Exception {
+			<jk>return</jk> <jf>schema</jf>;
+		}
+		
+		<jd>/** 
+		 * PUT request handler.
+		 * Replaces the schema document with the specified content, and then mirrors it as the response. 
+		 */</jd>
+		<ja>@RestMethod</ja>(name=<js>"PUT"</js>, path=<js>"/"</js>)
+		<jk>public</jk> Schema setSchema(<ja>@Content</ja> Schema schema) <jk>throws</jk> Exception {
+			<jk>this</jk>.<jf>schema</jf> = schema;
+			<jk>return</jk> <jk>this</jk>.<jf>schema</jf>;
+		}
+	
+		<jd>/** OPTIONS request handler */</jd>
+	 	<ja>@RestMethod</ja>(name=<js>"OPTIONS"</js>, path=<js>"/*"</js>)
+		<jk>public</jk> ResourceOptions doOptions(RestRequest req) {
+			<jk>return new</jk> ResourceOptions(<jk>this</jk>, req);
+		}
+	}
+			</p>
+			<p>
+				When you point your browser to this resource, the default content type is HTML (since that's what the browser asks for
+				by default).
+			</p>
+			<h6 class='figure'>HTML</h6>
+			<img class='bordered' src="doc-files/Example_Html.png">
+			<p>
+				The REST API allows you to specify the <code>Accept</code> header as a GET parameter, and the <code>plainText=true</code>
+					parameter forces the returned <code>Content-Type</code> to be <code>text/plain</code>.
+				We'll use this to view the JSON-Schema document in other languages.
+			</p>			
+			
+			<h6 class='figure'>Normal JSON</h6>
+			<img class='bordered' src="doc-files/Example_Json.png">
+			
+			<h6 class='figure'>XML</h6>
+			<img class='bordered' src="doc-files/Example_Xml.png">
+
+			<h6 class='figure'>URL-Encoded</h6>
+			<img class='bordered' src="doc-files/Example_UrlEncoded.png">
+
+			<h6 class='figure'>Abbreviated RDF/XML</h6>
+			<img class='bordered' src="doc-files/Example_XmlRdfAbbrev.png">
+
+			<h6 class='figure'>Turtle</h6>
+			<img class='bordered' src="doc-files/Example_Turtle.png">
+			
+			<p>
+				The full list of options for this resource can be accessed by the <code>options</code> link on the HTML page.
+			</p>
+			
+			<h6 class='figure'>Resource Options</h6>
+			<img class='bordered' src="doc-files/Example_Options.png">
+		</div>	
+		
+	<!-- ======================================================================================================== -->
+	<a id="Parse"></a>
+	<h3 class='topic' onclick='toggle(this)'>1.3 - Parsing JSON-Schema documents</h3>
+	<div class='topic'>
+		<p>
+			Use the {@link org.apache.juneau.json.JsonParser} to parse JSON-Schema documents into DTOs:
+		</p>
+		<p class='bcode'>		
+	<jc>// Use parser to load JSON-Schema document into JSON-Schema DTOs</jc>
+	Schema schema = JsonParser.<jsf>DEFAULT</jsf>.parse(json, Schema.<jk>class</jk>);
+		</p>
+		<p>
+			Schema objects can also be constructed from the other media types using the appropriate parsers.
+		</p>
+	</div>
+
+</div>
+<p align="center"><i><b>*** f�n ***</b></i></p>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/dto/package.html
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/dto/package.html b/org.apache.juneau/src/main/java/org/apache/juneau/dto/package.html
new file mode 100644
index 0000000..4574810
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/dto/package.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<!--
+/***************************************************************************************************************************
+ * 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.
+ *
+ ***************************************************************************************************************************/
+ -->
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+		/* For viewing in Page Designer */
+		@IMPORT url("../../../../../../javadoc.css");
+
+		/* For viewing in REST interface */
+		@IMPORT url("../htdocs/javadoc.css");
+		body { 
+			margin: 20px; 
+		}	
+	</style>
+	<script>
+		/* Replace all @code and @link tags. */	
+		window.onload = function() {
+			document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+			document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>');
+		}
+	</script>
+</head>
+<body>
+<p>Data transfer objects</p>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/encoders/Encoder.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/encoders/Encoder.java b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/Encoder.java
new file mode 100644
index 0000000..128148b
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/Encoder.java
@@ -0,0 +1,57 @@
+/***************************************************************************************************************************
+ * 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.encoders;
+
+import java.io.*;
+
+/**
+ * Used for enabling decompression on requests and compression on responses, such as support for GZIP compression.
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ * 	Used to wrap input and output streams withing compression/decompression streams.
+ * <p>
+ * 	Encoders are registered with <code>RestServlets</code> through the <ja>@RestResource.encoders()</ja> annotation.
+ *
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public abstract class Encoder {
+
+	/**
+	 * Converts the specified compressed input stream into an uncompressed stream.
+	 *
+	 * @param is The compressed stream.
+	 * @return The uncompressed stream.
+	 * @throws IOException If any errors occur, such as on a stream that's not a valid GZIP input stream.
+	 */
+	public abstract InputStream getInputStream(InputStream is) throws IOException;
+
+	/**
+	 * Converts the specified uncompressed output stream into an uncompressed stream.
+	 *
+	 * @param os The uncompressed stream.
+	 * @return The compressed stream stream.
+	 * @throws IOException If any errors occur.
+	 */
+	public abstract OutputStream getOutputStream(OutputStream os) throws IOException;
+
+	/**
+	 * Returns the codings in <code>Content-Encoding</code> and <code>Accept-Encoding</code> headers
+	 * 	that this encoder handles (e.g. <js>"gzip"</js>).
+	 *
+	 * @return The codings that this encoder handles.
+	 */
+	public abstract String[] getCodings();
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/encoders/EncoderGroup.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/encoders/EncoderGroup.java b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/EncoderGroup.java
new file mode 100644
index 0000000..710f16a
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/EncoderGroup.java
@@ -0,0 +1,199 @@
+/***************************************************************************************************************************
+ * 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.encoders;
+
+import static org.apache.juneau.internal.ArrayUtils.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Represents the group of {@link Encoder encoders} keyed by codings.
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ * 	Maintains a set of encoders and the codings that they can handle.
+ * <p>
+ * 	The {@link #findMatch(String)} and {@link #getEncoder(String)} methods are then
+ * 		used to find appropriate encoders for specific <code>Accept-Encoding</code>
+ * 		and <code>Content-Encoding</code> header values.
+ *
+ *
+ * <h6 class='topic'>Match ordering</h6>
+ * <p>
+ * 	Encoders are matched against <code>Accept-Encoding</code> strings in the order they exist in this group.
+ * <p>
+ * 	Adding new entries will cause the entries to be prepended to the group.
+ *  	This allows for previous encoders to be overridden through subsequent calls.
+ * <p>
+ * 	For example, calling <code>g.append(E1.<jk>class</jk>,E2.<jk>class</jk>).append(E3.<jk>class</jk>,E4.<jk>class</jk>)</code>
+ * 	will result in the order <code>E3, E4, E1, E2</code>.
+ *
+ *
+ * <h6 class='topic'>Example</h6>
+ * <p class='bcode'>
+ * 	<jc>// Create an encoder group with support for gzip compression.</jc>
+ * 	EncoderGroup g = <jk>new</jk> EncoderGroup().append(GzipEncoder.<jk>class</jk>);
+ *
+ * 	<jc>// Should return "gzip"</jc>
+ * 	String matchedCoding = g.findMatch(<js>"compress;q=1.0, gzip;q=0.8, identity;q=0.5, *;q=0"</js>);
+ *
+ * 	<jc>// Get the encoder</jc>
+ * 	IEncoder encoder = g.getEncoder(matchedCoding);
+ * </p>
+ *
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public final class EncoderGroup {
+
+	private Map<String,EncoderEntry> entryMap = new TreeMap<String,EncoderEntry>(String.CASE_INSENSITIVE_ORDER);
+	private LinkedList<EncoderEntry> tempEntries = new LinkedList<EncoderEntry>();
+	private EncoderEntry[] entries;
+
+	/**
+	 * Returns the coding string for the matching encoder that can handle the specified <code>Accept-Encoding</code>
+	 * 	or <code>Content-Encoding</code> header value.
+	 * <p>
+	 * 	Returns <jk>null</jk> if no encoders can handle it.
+	 * <p>
+	 * 	This method is fully compliant with the RFC2616/14.3 and 14.11 specifications.
+	 *
+	 * @param acceptEncoding The <code>Accept-Encoding</code> or <code>Content-Encoding</code> value.
+	 * @return The coding value (e.g. <js>"gzip"</js>).
+	 */
+	public String findMatch(String acceptEncoding) {
+		if (getEntries().length == 0)
+			return null;
+
+		MediaRange[] ae = MediaRange.parse(acceptEncoding);
+
+		if (ae.length == 0)
+			ae = MediaRange.parse("*");
+
+		for (MediaRange a : ae)
+			for (EncoderEntry e : getEntries())
+				for (MediaRange a2 : e.encodingRanges)
+					if (a.matches(a2))
+						return a2.getType();
+
+		return null;
+	}
+
+	/**
+	 * Adds the specified encoders to this group.
+	 *
+	 * @param e The encoders to instantiate and add to this group.
+	 * @return This object (for method chaining).
+	 * @throws Exception If an instantiation error occurred.
+	 */
+	public EncoderGroup append(Class<? extends Encoder>...e) throws Exception {
+		for (Class<? extends Encoder> r : reverse(e))
+			append(r.newInstance());
+		return this;
+	}
+
+	/**
+	 * Adds the specified encoders to this group.
+	 *
+	 * @param e The encoder to instantiate and add to this group.
+	 * @return This object (for method chaining).
+	 * @throws Exception If an instantiation error occurred.
+	 */
+	public EncoderGroup append(Class<? extends Encoder> e) throws Exception {
+		append(e.newInstance());
+		return this;
+	}
+
+	/**
+	 * Adds the specified encoders to this group.
+	 *
+	 * @param e The encoders to add to this group.
+	 * @return This object (for method chaining).
+	 */
+	public EncoderGroup append(Encoder...e) {
+		entries = null;
+		for (Encoder r : reverse(e)) {
+			EncoderEntry ee = new EncoderEntry(r);
+			tempEntries.addFirst(ee);
+			for (String s : ee.encodings)
+				this.entryMap.put(s, ee);
+		}
+		return this;
+	}
+
+	/**
+	 * Adds the encoders in the specified group to this group.
+	 *
+	 * @param g The group containing the encoders to add to this group.
+	 * @return This object (for method chaining).
+	 */
+	public EncoderGroup append(EncoderGroup g) {
+		for (EncoderEntry e : reverse(g.getEntries()))
+			append(e.encoder);
+		return this;
+	}
+
+	/**
+	 * Returns the encoder registered with the specified coding (e.g. <js>"gzip"</js>).
+	 *
+	 * @param coding The coding string.
+	 * @return The encoder, or <jk>null</jk> if encoder isn't registered with that coding.
+	 */
+	public Encoder getEncoder(String coding) {
+		EncoderEntry e = entryMap.get(coding);
+		return (e == null ? null : e.encoder);
+	}
+
+	/**
+	 * Returns the set of codings supported by all encoders in this group.
+	 *
+	 * @return The set of codings supported by all encoders in this group.  Never <jk>null</jk>.
+	 */
+	public List<String> getSupportedEncodings() {
+		List<String> l = new ArrayList<String>();
+		for (EncoderEntry e : getEntries())
+			for (String enc : e.encodings)
+				if (! l.contains(enc))
+					l.add(enc);
+		return l;
+	}
+
+	private EncoderEntry[] getEntries() {
+		if (entries == null)
+			entries = tempEntries.toArray(new EncoderEntry[tempEntries.size()]);
+		return entries;
+	}
+
+	static class EncoderEntry {
+		Encoder encoder;
+		MediaRange[] encodingRanges;
+		String[] encodings;
+
+		EncoderEntry(Encoder e) {
+			encoder = e;
+
+			encodings = new String[e.getCodings().length];
+			int i = 0;
+			for (String enc : e.getCodings())
+				encodings[i++] = enc;
+
+			List<MediaRange> l = new LinkedList<MediaRange>();
+			for (i = 0; i < encodings.length; i++)
+				l.addAll(Arrays.asList(MediaRange.parse(encodings[i])));
+			encodingRanges = l.toArray(new MediaRange[l.size()]);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/encoders/GzipEncoder.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/encoders/GzipEncoder.java b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/GzipEncoder.java
new file mode 100644
index 0000000..a08ffb7
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/GzipEncoder.java
@@ -0,0 +1,48 @@
+/***************************************************************************************************************************
+ * 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.encoders;
+
+import java.io.*;
+import java.util.zip.*;
+
+/**
+ * Encoder for handling <js>"gzip"</js> encoding and decoding.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public class GzipEncoder extends Encoder {
+
+	@Override /* Encoder */
+	public OutputStream getOutputStream(OutputStream os) throws IOException {
+		return new GZIPOutputStream(os) {
+			@Override /* OutputStream */
+			public final void close() throws IOException {
+				finish();
+				super.close();
+			}
+		};
+	}
+
+	@Override /* Encoder */
+	public InputStream getInputStream(InputStream is) throws IOException {
+		return new GZIPInputStream(is);
+	}
+
+	/**
+	 * Returns <code>[<js>"gzip"</js>]</code>.
+	 */
+	@Override /* Encoder */
+	public String[] getCodings() {
+		return new String[]{"gzip"};
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/encoders/IdentityEncoder.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/encoders/IdentityEncoder.java b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/IdentityEncoder.java
new file mode 100644
index 0000000..0bbb7e5
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/IdentityEncoder.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.encoders;
+
+import java.io.*;
+
+/**
+ * Encoder for handling <js>"identity"</js> encoding and decoding (e.g. no encoding at all).
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public class IdentityEncoder extends Encoder {
+
+	/** Singleton */
+	public static final IdentityEncoder INSTANCE = new IdentityEncoder();
+
+	/** Constructor. */
+	protected IdentityEncoder() {}
+
+	@Override /* Encoder */
+	public InputStream getInputStream(InputStream is) throws IOException {
+		return is;
+	}
+
+	@Override /* Encoder */
+	public OutputStream getOutputStream(OutputStream os) throws IOException {
+		return os;
+	}
+
+	/**
+	 * Returns <code>[<js>"identity"</js>]</code>.
+	 */
+	@Override /* Encoder */
+	public String[] getCodings() {
+		return new String[]{"identity"};
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/encoders/package.html
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/encoders/package.html b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/package.html
new file mode 100644
index 0000000..41c1ab1
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/encoders/package.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<!--
+/***************************************************************************************************************************
+ * 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.
+ *
+ ***************************************************************************************************************************/
+ -->
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+		/* For viewing in Page Designer */
+		@IMPORT url("../../../../../../javadoc.css");
+
+		/* For viewing in REST interface */
+		@IMPORT url("../htdocs/javadoc.css");
+		body { 
+			margin: 20px; 
+		}	
+	</style>
+	<script>
+		/* Replace all @code and @link tags. */	
+		window.onload = function() {
+			document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+			document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>');
+		}
+	</script>
+</head>
+<body>
+<p>Encoder API</p>
+
+<script>
+	function toggle(x) {
+		var div = x.nextSibling;
+		while (div != null && div.nodeType != 1)
+			div = div.nextSibling;
+		if (div != null) {
+			var d = div.style.display;
+			if (d == 'block' || d == '') {
+				div.style.display = 'none';
+				x.className += " closed";
+			} else {
+				div.style.display = 'block';
+				x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' );
+			}
+		}
+	}
+</script>
+
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
new file mode 100644
index 0000000..f33a880
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlBeanPropertyMeta.java
@@ -0,0 +1,90 @@
+/***************************************************************************************************************************
+ * 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.html;
+
+import org.apache.juneau.*;
+import org.apache.juneau.html.annotation.*;
+
+/**
+ * Metadata on bean properties specific to the HTML serializers and parsers pulled from the {@link Html @Html} annotation on the bean property.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ * @param <T> The bean class.
+ */
+public class HtmlBeanPropertyMeta<T> {
+
+	private boolean asXml, noTables, noTableHeaders, asPlainText;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param beanPropertyMeta The metadata of the bean property of this additional metadata.
+	 */
+	public HtmlBeanPropertyMeta(BeanPropertyMeta<T> beanPropertyMeta) {
+		if (beanPropertyMeta.getField() != null)
+			findHtmlInfo(beanPropertyMeta.getField().getAnnotation(Html.class));
+		if (beanPropertyMeta.getGetter() != null)
+			findHtmlInfo(beanPropertyMeta.getGetter().getAnnotation(Html.class));
+		if (beanPropertyMeta.getSetter() != null)
+			findHtmlInfo(beanPropertyMeta.getSetter().getAnnotation(Html.class));
+	}
+
+	private void findHtmlInfo(Html html) {
+		if (html == null)
+			return;
+		if (html.asXml())
+			asXml = html.asXml();
+		if (html.noTables())
+			noTables = html.noTables();
+		if (html.noTableHeaders())
+			noTableHeaders = html.noTableHeaders();
+		if (html.asPlainText())
+			asPlainText = html.asPlainText();
+	}
+
+	/**
+	 * Returns whether this bean property should be serialized as XML instead of HTML.
+	 *
+	 * @return <jk>true</jk> if the the {@link Html} annotation is specified, and {@link Html#asXml()} is <jk>true</jk>.
+	 */
+	protected boolean isAsXml() {
+		return asXml;
+	}
+
+	/**
+	 * Returns whether this bean property should be serialized as plain text instead of HTML.
+	 *
+	 * @return <jk>true</jk> if the the {@link Html} annotation is specified, and {@link Html#asPlainText()} is <jk>true</jk>.
+	 */
+	protected boolean isAsPlainText() {
+		return asPlainText;
+	}
+
+	/**
+	 * Returns whether this bean property should not be serialized as an HTML table.
+	 *
+	 * @return <jk>true</jk> if the the {@link Html} annotation is specified, and {@link Html#noTables()} is <jk>true</jk>.
+	 */
+	protected boolean isNoTables() {
+		return noTables;
+	}
+
+	/**
+	 * Returns whether this bean property should not include table headers when serialized as an HTML table.
+	 *
+	 * @return <jk>true</jk> if the the {@link Html} annotation is specified, and {@link Html#noTableHeaders()} is <jk>true</jk>.
+	 */
+	public boolean isNoTableHeaders() {
+		return noTableHeaders;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlClassMeta.java b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
new file mode 100644
index 0000000..02dc609
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlClassMeta.java
@@ -0,0 +1,92 @@
+/***************************************************************************************************************************
+ * 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.html;
+
+import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Metadata on classes specific to the HTML serializers and parsers pulled from the {@link Html @Html} annotation on the class.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public class HtmlClassMeta {
+
+	private final Html html;
+	private final boolean asXml, noTables, noTableHeaders, asPlainText;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param c The class that this annotation is defined on.
+	 */
+	public HtmlClassMeta(Class<?> c) {
+		this.html = ReflectionUtils.getAnnotation(Html.class, c);
+		if (html != null) {
+			asXml = html.asXml();
+			noTables = html.noTables();
+			noTableHeaders = html.noTableHeaders();
+			asPlainText = html.asPlainText();
+		} else {
+			asXml = false;
+			noTables = false;
+			noTableHeaders = false;
+			asPlainText = false;
+		}
+	}
+
+	/**
+	 * Returns the {@link Html} annotation defined on the class.
+	 *
+	 * @return The value of the {@link Html} annotation, or <jk>null</jk> if not specified.
+	 */
+	protected Html getAnnotation() {
+		return html;
+	}
+
+	/**
+	 * Returns the {@link Html#asXml()} annotation defined on the class.
+	 *
+	 * @return The value of the {@link Html#asXml()} annotation.
+	 */
+	protected boolean isAsXml() {
+		return asXml;
+	}
+
+	/**
+	 * Returns the {@link Html#asPlainText()} annotation defined on the class.
+	 *
+	 * @return The value of the {@link Html#asPlainText()} annotation.
+	 */
+	protected boolean isAsPlainText() {
+		return asPlainText;
+	}
+
+	/**
+	 * Returns the {@link Html#noTables()} annotation defined on the class.
+	 *
+	 * @return The value of the {@link Html#noTables()} annotation.
+	 */
+	protected boolean isNoTables() {
+		return noTables;
+	}
+
+	/**
+	 * Returns the {@link Html#noTableHeaders()} annotation defined on the class.
+	 *
+	 * @return The value of the {@link Html#noTableHeaders()} annotation.
+	 */
+	public boolean isNoTableHeaders() {
+		return noTableHeaders;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java
new file mode 100644
index 0000000..2c1a0fe
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java
@@ -0,0 +1,168 @@
+/***************************************************************************************************************************
+ * 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.html;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.dto.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.serializer.*;
+
+/**
+ * Serializes POJOs to HTTP responses as HTML documents.
+ *
+ *
+ * <h6 class='topic'>Media types</h6>
+ * <p>
+ * 	Handles <code>Accept</code> types: <code>text/html</code>
+ * <p>
+ * 	Produces <code>Content-Type</code> types: <code>text/html</code>
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ * 	Same as {@link HtmlSerializer}, except wraps the response in <code><xt>&lt;html&gt;</code>, <code><xt>&lt;head&gt;</code>,
+ * 	and <code><xt>&lt;body&gt;</code> tags so that it can be rendered in a browser.
+ *
+ *
+ * <h6 class='topic'>Configurable properties</h6>
+ * <p>
+ * 	This class has the following properties associated with it:
+ * <ul>
+ * 	<li>{@link HtmlDocSerializerContext}
+ * 	<li>{@link BeanContext}
+ * </ul>
+ *
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+@Produces("text/html")
+public class HtmlDocSerializer extends HtmlStrippedDocSerializer {
+
+	// Properties defined in RestServletProperties
+	private static final String
+		REST_method = "RestServlet.method",
+		REST_relativeServletURI = "RestServlet.relativeServletURI";
+
+
+	//--------------------------------------------------------------------------------
+	// Overridden methods
+	//--------------------------------------------------------------------------------
+
+	@Override /* Serializer */
+	public HtmlDocSerializerSession createSession(Object output, ObjectMap properties, Method javaMethod) {
+		return new HtmlDocSerializerSession(getContext(HtmlDocSerializerContext.class), getBeanContext(), output, properties, javaMethod);
+	}
+
+	@Override /* Serializer */
+	protected void doSerialize(SerializerSession session, Object o) throws Exception {
+
+		HtmlDocSerializerSession s = (HtmlDocSerializerSession)session;
+		HtmlWriter w = s.getWriter();
+
+		ObjectMap properties = s.getProperties();
+
+		boolean isOptionsPage = properties.containsKey(REST_method) && properties.getString(REST_method).equalsIgnoreCase("OPTIONS");
+
+		// Render the header.
+		w.sTag("html").nl();
+		w.sTag("head").nl();
+
+		String cssUrl = s.getCssUrl();
+		if (cssUrl == null)
+			cssUrl = properties.getString(REST_relativeServletURI) + "/style.css";
+
+		w.oTag(1, "style")
+			.attr("type", "text/css")
+			.appendln(">")
+			.append(2, "@import ").q().append(cssUrl).q().appendln(";");
+		if (s.isNoWrap())
+			w.appendln("\n* {white-space:nowrap;}");
+		if (s.getCssImports() != null)
+			for (String cssImport : s.getCssImports())
+				w.append(2, "@import ").q().append(cssImport).q().appendln(";");
+		w.eTag(1, "style").nl();
+		w.eTag("head").nl();
+		w.sTag("body").nl();
+		// Write the title of the page.
+		String title = s.getTitle();
+		if (title == null && isOptionsPage)
+			title = "Options";
+		String description = s.getDescription();
+		if (title != null)
+			w.oTag(1, "h3").attr("class", "title").append('>').encodeText(title).eTag("h3").nl();
+		if (description != null)
+			w.oTag(1, "h5").attr("class", "description").append('>').encodeText(description).eTag("h5").nl();
+
+		// Write the action links that render above the results.
+		List<Link> actions = new LinkedList<Link>();
+
+		// If this is an OPTIONS request, provide a 'back' link to return to the GET request page.
+		if (! isOptionsPage) {
+			Map<String,String> htmlLinks = s.getLinks();
+			if (htmlLinks != null) {
+				for (Map.Entry<String,String> e : htmlLinks.entrySet()) {
+					String uri = e.getValue();
+					if (uri.indexOf("://") == -1 && ! StringUtils.startsWith(uri, '/')) {
+						StringBuilder sb = new StringBuilder(properties.getString(REST_relativeServletURI));
+						if (! (uri.isEmpty() || uri.charAt(0) == '?' || uri.charAt(0) == '/'))
+							sb.append('/');
+						sb.append(uri);
+						uri = sb.toString();
+					}
+
+					actions.add(new Link(e.getKey(), uri));
+				}
+			}
+		}
+
+		if (actions.size() > 0) {
+			w.oTag(1, "p").attr("class", "links").append('>').nl();
+			for (Iterator<Link> i = actions.iterator(); i.hasNext();) {
+				Link h = i.next();
+				w.oTag(2, "a").attr("class", "link").attr("href", h.getHref(), true).append('>').append(h.getName()).eTag("a").nl();
+				if (i.hasNext())
+					w.append(3, " - ").nl();
+			}
+			w.eTag(1, "p").nl();
+		}
+
+		s.indent = 3;
+
+		// To allow for page formatting using CSS, we encapsulate the data inside two div tags:
+		// <div class='outerdata'><div class='data' id='data'>...</div></div>
+		w.oTag(1, "div").attr("class","outerdata").append('>').nl();
+		w.oTag(2, "div").attr("class","data").attr("id", "data").append('>').nl();
+		if (isEmptyList(o))
+			w.oTag(3, "p").append('>').append("no results").eTag("p");
+		else
+			super.doSerialize(s, o);
+		w.eTag(2, "div").nl();
+		w.eTag(1, "div").nl();
+
+		w.eTag("body").nl().eTag("html").nl();
+	}
+
+	private boolean isEmptyList(Object o) {
+		if (o == null)
+			return false;
+		if (o instanceof Collection && ((Collection<?>)o).size() == 0)
+			return true;
+		if (o.getClass().isArray() && Array.getLength(o) == 0)
+			return true;
+		return false;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java
new file mode 100644
index 0000000..53c0742
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializerContext.java
@@ -0,0 +1,199 @@
+/***************************************************************************************************************************
+ * 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.html;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Properties associated with the {@link HtmlDocSerializer} class.
+ * <p>
+ * 	These are typically specified via <ja>@RestResource.properties()</ja> and <ja>@RestMethod.properties()</ja> annotations,
+ * 		although they can also be set programmatically via the <code>RestResponse.setProperty()</code> method.
+ *
+ * <h6 class='topic'>Example</h6>
+ * <p class='bcode'>
+ * 	<ja>@RestResource</ja>(
+ * 		messages=<js>"nls/AddressBookResource"</js>,
+ * 		properties={
+ * 			<ja>@Property</ja>(name=HtmlDocSerializerContext.<jsf>HTMLDOC_title</jsf>, value=<js>"$L{title}"</js>),
+ * 			<ja>@Property</ja>(name=HtmlDocSerializerContext.<jsf>HTMLDOC_description</jsf>, value=<js>"$L{description}"</js>),
+ * 			<ja>@Property</ja>(name=HtmlDocSerializerContext.<jsf>HTMLDOC_links</jsf>, value=<js>"{options:'?method=OPTIONS',doc:'doc'}"</js>)
+ * 		}
+ * 	)
+ * 	<jk>public class</jk> AddressBookResource <jk>extends</jk> RestServletJenaDefault {
+ * </p>
+ * <p>
+ * 	The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
+ * 	These variables are replaced at runtime based on the HTTP request locale.
+ * 	Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
+ * </p>
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public final class HtmlDocSerializerContext extends HtmlSerializerContext {
+
+	/**
+	 * Adds a title at the top of a page.
+	 *
+	 * <dl>
+	 * 	<dt>Example:</dt>
+	 * 	<dd>
+	 * <p>
+	 * 	The <code>AddressBookResource</code> sample class uses this property...
+	 * </p>
+	 * <p class='bcode'>
+	 * 	<ja>@RestResource</ja>(
+	 * 		messages=<js>"nls/AddressBookResource"</js>,
+	 * 		properties={
+	 * 			<ja>@Property</ja>(name=HtmlDocSerializerContext.<jsf>HTMLDOC_title</jsf>, value=<js>"$L{title}"</js>)
+	 * 		}
+	 * 	)
+	 * 	<jk>public class</jk> AddressBookResource <jk>extends</jk> RestServletJenaDefault {
+	 * </p>
+	 * <p>
+	 * 	...with this property in <code>AddressBookResource.properties</code>...
+	 * </p>
+	 * <p class='bcode'>
+	 * 	title = <js>AddressBook sample resource</js>
+	 * </p>
+	 * <p>
+	 * 	...to produce this title on the HTML page...
+	 * </p>
+	 * 		<img class='bordered' src='doc-files/HTML_TITLE.png'>
+	 * 	</dd>
+	 * </dl>
+	 */
+	public static final String HTMLDOC_title = "HtmlSerializer.title";
+
+	/**
+	 * Adds a description right below the title of a page.
+	 *
+	 * <dl>
+	 * 	<dt>Example:</dt>
+	 * 	<dd>
+	 * <p>
+	 * 	The <code>AddressBookResource</code> sample class uses this property...
+	 * </p>
+	 * <p class='bcode'>
+	 * 	<ja>@RestResource</ja>(
+	 * 		messages=<js>"nls/AddressBookResource"</js>,
+	 * 		properties={
+	 * 			<ja>@Property</ja>(name=HtmlDocSerializerContext.<jsf>HTMLDOC_description</jsf>, value=<js>"description"</js>, type=<jsf>NLS</jsf>)
+	 * 		}
+	 * 	)
+	 * 	<jk>public class</jk> AddressBookResource <jk>extends</jk> RestServletJenaDefault {
+	 * </p>
+	 * <p>
+	 * 	...with this property in <code>AddressBookResource.properties</code>...
+	 * </p>
+	 * <p class='bcode'>
+	 * 	description = <js>Simple address book POJO sample resource</js>
+	 * </p>
+	 * <p>
+	 * 	...to produce this description on the HTML page...
+	 * </p>
+	 * 		<img class='bordered' src='doc-files/HTML_DESCRIPTION.png'>
+	 * 	</dd>
+	 * </dl>
+	 */
+	public static final String HTMLDOC_description = "HtmlSerializer.description";
+
+	/**
+	 * Adds a list of hyperlinks immediately under the title and description but above the content of the page.
+	 * <p>
+	 * 	This can be used to provide convenient hyperlinks when viewing the REST interface from a browser.
+	 * <p>
+	 * 	The value is a JSON object string where the keys are anchor text and the values are URLs.
+	 * <p>
+	 * 	Relative URLs are considered relative to the servlet path.
+	 * 	For example, if the servlet path is <js>"http://localhost/myContext/myServlet"</js>, and the
+	 * 		URL is <js>"foo"</js>, the link becomes <js>"http://localhost/myContext/myServlet/foo"</js>.
+	 * 	Absolute (<js>"/myOtherContext/foo"</js>) and fully-qualified (<js>"http://localhost2/foo"</js>) URLs
+	 * 		can also be used.
+	 *
+	 * <dl>
+	 * 	<dt>Example:</dt>
+	 * 	<dd>
+	 * <p>
+	 * 	The <code>AddressBookResource</code> sample class uses this property...
+	 * </p>
+	 * <p class='bcode'>
+	 * 	<ja>@RestResource</ja>(
+	 * 		messages=<js>"nls/AddressBookResource"</js>,
+	 * 		properties={
+	 * 			<ja>@Property</ja>(name=HtmlDocSerializerContext.<jsf>HTMLDOC_links</jsf>, value=<js>"{options:'?method=OPTIONS',doc:'doc'}"</js>)
+	 * 		}
+	 * 	)
+	 * 	<jk>public class</jk> AddressBookResource <jk>extends</jk> RestServletJenaDefault {
+	 * </p>
+	 * <p>
+	 * 	...to produce this list of links on the HTML page...
+	 * </p>
+	 * 		<img class='bordered' src='doc-files/HTML_LINKS.png'>
+	 * 	</dd>
+	 * </dl>
+	 */
+	public static final String HTMLDOC_links = "HtmlDocSerializer.links";
+
+	/**
+	 * Similar to {@link #HTMLDOC_links} except appends on to the existing list of links.
+	 */
+	public static final String HTMLDOC_links_add = "HtmlDocSerializer.links.add";
+
+	/**
+	 * Adds a link to the specified stylesheet URL (<l>String</l>, default=<jk>null</jk>).
+	 * <p>
+	 * 	If not specified, defaults to the built-in stylesheet located at <js>"/servletPath/style.css"</js>.
+	 * 	Note that this stylesheet is controlled by the <code><ja>@RestResource</ja>.style()</code> annotation.
+	 */
+	public static final String HTMLDOC_cssUrl = "HtmlDocSerializer.cssUrl";
+
+	/**
+	 * Imports the specified CSS page URLs into the page (<l>String[]</l>, default=<code>[]</code>).
+	 */
+	public static final String HTMLDOC_cssImports = "HtmlDocSerializer.cssImports";
+
+	/**
+	 * Append to the {@link #HTMLDOC_cssImports} property.
+	 */
+	public static final String HTMLDOC_cssImports_add = "HtmlDocSerializer.cssImports.add";
+
+	/**
+	 * Adds <js>"* {white-space:nowrap}"</js> to the style header to prevent word wrapping.
+	 */
+	public static final String HTMLDOC_nowrap = "HtmlDocSerializer.nowrap";
+
+	final String[] cssImports;
+	final Map<String,String> links;
+	final String title, description, cssUrl;
+	final boolean nowrap;
+
+	/**
+	 * Constructor.
+	 * <p>
+	 * Typically only called from {@link ContextFactory#getContext(Class)}.
+	 *
+	 * @param cf The factory that created this context.
+	 */
+	public HtmlDocSerializerContext(ContextFactory cf) {
+		super(cf);
+		cssImports = cf.getProperty(HTMLDOC_cssImports, String[].class, new String[0]);
+		title = cf.getProperty(HTMLDOC_title, String.class, null);
+		description = cf.getProperty(HTMLDOC_description, String.class, null);
+		cssUrl = cf.getProperty(HTMLDOC_cssUrl, String.class, null);
+		nowrap = cf.getProperty(HTMLDOC_nowrap, boolean.class, false);
+		links = cf.getMap(HTMLDOC_links, String.class, String.class, Collections.<String,String>emptyMap());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java
new file mode 100644
index 0000000..476f6c5
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java
@@ -0,0 +1,135 @@
+/***************************************************************************************************************************
+ * 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.html;
+
+import static org.apache.juneau.html.HtmlDocSerializerContext.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.serializer.*;
+
+/**
+ * Context object that lives for the duration of a single serialization of {@link HtmlSerializer} and its subclasses.
+ * <p>
+ * 	See {@link SerializerContext} for details.
+ * </p>
+ * <p>
+ * 	This class is NOT thread safe.  It is meant to be discarded after one-time use.
+ * </p>
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public final class HtmlDocSerializerSession extends HtmlSerializerSession {
+
+	private final String title, description, cssUrl;
+	private final String[] cssImports;
+	private final Map<String,String> links;
+	private final boolean nowrap;
+
+	/**
+	 * 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 beanContext The bean context being used.
+	 * @param output The output object.  See {@link JsonSerializerSession#getWriter()} for valid class types.
+	 * @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.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	protected HtmlDocSerializerSession(HtmlDocSerializerContext ctx, BeanContext beanContext, Object output, ObjectMap op, Method javaMethod) {
+		super(ctx, beanContext, output, op, javaMethod);
+		if (op == null || op.isEmpty()) {
+			title = ctx.title;
+			description = ctx.description;
+			links = ctx.links;
+			cssUrl = ctx.cssUrl;
+			cssImports = ctx.cssImports;
+			nowrap = ctx.nowrap;
+		} else {
+			title = op.getString(HTMLDOC_title, ctx.title);
+			description = op.getString(HTMLDOC_description, ctx.description);
+			links = new LinkedHashMap(op.getMap(HTMLDOC_links, ctx.links));
+			cssUrl = op.getString(HTMLDOC_cssUrl, ctx.cssUrl);
+			cssImports = StringUtils.split(op.getString(HTMLDOC_cssImports, null), ',');
+			nowrap = op.getBoolean(HTMLDOC_cssUrl, ctx.nowrap);
+		}
+	}
+
+	/**
+	 * Returns the {@link HtmlDocSerializerContext#HTMLDOC_title} setting value in this context.
+	 *
+	 * @return The {@link HtmlDocSerializerContext#HTMLDOC_title} setting value in this context.
+	 */
+	public final String getTitle() {
+		return title;
+	}
+
+	/**
+	 * Returns the {@link HtmlDocSerializerContext#HTMLDOC_description} setting value in this context.
+	 *
+	 * @return The {@link HtmlDocSerializerContext#HTMLDOC_description} setting value in this context.
+	 */
+	public final String getDescription() {
+		return description;
+	}
+
+	/**
+	 * Returns the {@link HtmlDocSerializerContext#HTMLDOC_links} setting value in this context.
+	 *
+	 * @return The {@link HtmlDocSerializerContext#HTMLDOC_links} setting value in this context.
+	 */
+	public final Map<String,String> getLinks() {
+		return links;
+	}
+
+	/**
+	 * Returns the {@link HtmlDocSerializerContext#HTMLDOC_cssUrl} setting value in this context.
+	 *
+	 * @return The {@link HtmlDocSerializerContext#HTMLDOC_cssUrl} setting value in this context.
+	 */
+	public final String getCssUrl() {
+		return cssUrl;
+	}
+
+	/**
+	 * Returns the {@link HtmlDocSerializerContext#HTMLDOC_cssImports} setting value in this context.
+	 *
+	 * @return The {@link HtmlDocSerializerContext#HTMLDOC_cssImports} setting value in this context.
+	 */
+	public final String[] getCssImports() {
+		return cssImports;
+	}
+
+	/**
+	 * Returns the {@link HtmlDocSerializerContext#HTMLDOC_nowrap} setting value in this context.
+	 *
+	 * @return The {@link HtmlDocSerializerContext#HTMLDOC_nowrap} setting value in this context.
+	 */
+	public final boolean isNoWrap() {
+		return nowrap;
+	}
+
+	@Override /* XmlSerializerSession */
+	public HtmlWriter getWriter() throws Exception {
+		Object output = getOutput();
+		if (output instanceof HtmlWriter)
+			return (HtmlWriter)output;
+		return new HtmlWriter(super.getWriter(), isUseIndentation(), isTrimStrings(), getQuoteChar(), getRelativeUriBase(), getAbsolutePathUriBase());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlLink.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlLink.java b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlLink.java
new file mode 100644
index 0000000..87f2954
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/html/HtmlLink.java
@@ -0,0 +1,49 @@
+/***************************************************************************************************************************
+ * 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.html;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Used in conjunction with the {@link HtmlSerializer} class to define hyperlinks.
+ * <p>
+ * 	This annotation is applied to classes.
+ * <p>
+ * 	Annotation that can be used to specify that a class has a URL associated with it.
+ * <p>
+ * 	When rendered using the {@link org.apache.juneau.html.HtmlSerializer HtmlSerializer} class, this class will get rendered as a hyperlink like so...
+ * <p class='code'>
+ * 	<xt>&lt;a</xt> <xa>href</xa>=<xs>'hrefProperty'</xs><xt>&gt;</xt>nameProperty<xt>&lt;/a&gt;</xt>
+ * </p>
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+@Documented
+@Target(TYPE)
+@Retention(RUNTIME)
+@Inherited
+public @interface HtmlLink {
+
+	/**
+	 * The bean property whose value becomes the name in the hyperlink.
+	 */
+	String nameProperty() default "";
+
+	/**
+	 * The bean property whose value becomes the url in the hyperlink.
+	 */
+	String hrefProperty() default "";
+}