http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/95e832e1/juneau-core/src/main/java/org/apache/juneau/uon/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/package.html
b/juneau-core/src/main/java/org/apache/juneau/uon/package.html
new file mode 100644
index 0000000..e6b1d10
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/package.html
@@ -0,0 +1,1339 @@
+<!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>UON notation serialization and parsing support</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'>UON support overview</a></p>
+ <ol>
+ <li><p><a class='doclink'
href='#OverviewExample'>Example</a></p>
+ </ol>
+ <li><p><a class='doclink'
href='#UrlEncodingSerializer'>UrlEncodingSerializer and UonSerializer
classes</a></p>
+ <ol>
+ <li><p><a class='doclink' href='#BeanAnnotations'>@Bean and
@BeanProperty annotations</a></p>
+ <li><p><a class='doclink'
href='#Collections'>Collections</a></p>
+ <li><p><a class='doclink' href='#Recursion'> Non-tree models
and recursion detection</a></p>
+ <li><p><a class='doclink'
href='#SerializerConfigurableProperties'>Configurable properties</a></p>
+ <li><p><a class='doclink' href='#SerializerOtherNotes'>Other
notes</a></p>
+ </ol>
+ <li><p><a class='doclink' href='#UrlEncodingParser'>UrlEncodingParser
and UonParser classes</a></p>
+ <ol>
+ <li><p><a class='doclink' href='#GenericParsing'>Parsing into
generic POJO models</a></p>
+ <li><p><a class='doclink'
href='#ParserConfigurableProperties'>Configurable properties</a></p>
+ <li><p><a class='doclink' href='#ParserOtherNotes'>Other
notes</a></p>
+ </ol>
+ <li><p><a class='doclink' href='#RestApiSupport'>REST API
support</a></p>
+ <ol>
+ <li><p><a class='doclink' href='#RestServerSupport'>REST server
support</a></p>
+ <ol>
+ <li><p><a class='doclink' href='#RestServletDefault'>Using
RestServletDefault</a></p>
+ <li><p><a class='doclink' href='#RestServlet'>Using RestServlet
with annotations</a></p>
+ <li><p><a class='doclink' href='#DefaultProvider'>Using JAX-RS
DefaultProvider</a></p>
+ <li><p><a class='doclink' href='#BaseProvider'>Using JAX-RS
BaseProvider with annotations</a></p>
+ </ol>
+ <li><p><a class='doclink' href='#RestClientSupport'>REST client
support</a></p>
+ </ol>
+</ol>
+
+<!--
========================================================================================================
-->
+<a id="Overview"></a>
+<h2 class='topic' onclick='toggle(this)'>1 - URL encoding support overview</h2>
+<div class='topic'>
+ <p>
+ Juneau supports converting arbitrary POJOs to and from
URL-encoded strings using ultra-efficient serializers and parsers.<br>
+ The serializer converts POJOs directly to URL-encoded strings
without the need for intermediate DOM objects using a highly-efficient state
machine.<br>
+ Likewise, the parser creates POJOs directly from URL-encoded
strings without the need for intermediate DOM objects.
+ </p>
+ <p>
+ Juneau uses UON (URL-Encoded Object Notation) for representing
POJOs.
+ The UON specification can be found <a
href='doc-files/rfc_uon.txt'>here</a>.
+ </p>
+ <p>
+ Juneau can serialize and parse instances of any of the
following POJO types:
+ </p>
+ <ul class='spaced-list'>
+ <li>Java primitives and primitive objects (e.g.
<code>String</code>, <code>Integer</code>, <code>Boolean</code>,
<code>Float</code>).
+ <li>Java Collections Framework objects (e.g.
<code>HashSet</code>, <code>TreeMap</code>) containing anything on this list.
+ <li>Multi-dimensional arrays of any type on this list.
+ <li>Java Beans with properties of any type on this list.
+ <li>Classes with standard transformations to and from
<code>Strings</code> (e.g. classes containing <code>toString()</code>,
<code>fromString()</code>, <code>valueOf()</code>,
<code>constructor(String)</code>).
+ <li>Non-serializable classes and properties with associated
<code>PojoSwaps</code> that convert them to serializable forms.
+ </ul>
+ <p>
+ Refer to <a
href='../../../../overview-summary.html#Core.PojoCategories'
class='doclink'>POJO Categories</a> for a complete definition of supported
POJOs.
+ </p>
+ <h6 class='topic'>Prerequisites</h6>
+ <p>
+ The Juneau URL-encoding serialization and parsing support does
not require any external prerequisites.
+ It only requires Java 1.6 or above.
+ </p>
+
+ <!--
========================================================================================================
-->
+ <a id="OverviewExample"></a>
+ <h3 class='topic' onclick='toggle(this)'>1.1 - URL-encoding support
overview - example</h3>
+ <div class='topic'>
+ <p>
+ The example shown here is from the Address Book
resource located in the <code>org.apache.juneau.sample.war</code>
application.<br>
+ The POJO model consists of a <code>List</code> of
<code>Person</code> beans, with each <code>Person</code> containing
+ zero or more <code>Address</code> beans.
+ </p>
+ <p>
+ When you point a browser at
<code>/sample/addressBook/people/1</code>, the POJO is rendered as HTML:
+ </p>
+ <img class='bordered' src="doc-files/Example_HTML.png">
+ <p>
+ By appending
<code>?Accept=application/x-www-form-urlencoded&plainText=true</code> to
the URL, you can view the data as a URL-encoded string:
+ </p>
+ <p class='bcode'>
+ <un>0</un>=(
+
<ua>uri</ua>=<us>http://localhost:10000/addressBook/people/1</us>,
+
<ua>addressBookUri</ua>=<us>http://localhost:10000/addressBook/</us>,
+ <ua>id</ua>=<un>1</un>,
+ <ua>name</ua>=<us>'Barack+Obama'</us>,
+ <ua>birthDate</ua>=<us>'Aug+4,+1961'</us>,
+ <ua>addresses</ua>=@(
+ (
+
<ua>uri</ua>=<us>http://localhost:10000/addressBook/addresses/1</us>,
+
<ua>personUri</ua>=<us>http://localhost:10000/addressBook/people/1</us>,
+ <ua>id</ua>=<un>1</un>,
+
<ua>street</ua>=<us>'1600+Pennsylvania+Ave'</us>,
+ <ua>city</ua>=<us>Washington</us>,
+ <ua>state</ua>=<us>DC</us>,
+ <ua>zip</ua>=<un>20500</un>,
+ <ua>isCurrent</ua>=<uk>true</uk>
+ ),
+ (
+
<ua>uri</ua>=<us>http://localhost:10000/addressBook/addresses/2</us>,
+
<ua>personUri</ua>=<us>http://localhost:10000/addressBook/people/1</us>,
+ <ua>id</ua>=<un>2</un>,
+ <ua>street</ua>=<us>'5046+S+Greenwood+Ave'</us>,
+ <ua>city</ua>=<us>Chicago</us>,
+ <ua>state</ua>=<us>IL</us>,
+ <ua>zip</ua>=<un>60615</un>,
+ <ua>isCurrent</ua>=<uk>false</uk>
+ )
+ ),
+ <ua>age</ua>=<un>56</un>
+ )
+ &<un>1</un>=(
+
<ua>uri</ua>=<us>http://localhost:10000/addressBook/people/2</us>,
+
<ua>addressBookUri</ua>=<us>http://localhost:10000/addressBook/</us>,
+ <ua>id</ua>=<un>2</un>,
+ <ua>name</ua>=<us>'George+Walker+Bush'</us>,
+ <ua>birthDate</ua>=<us>'Jul+6,+1946'</us>,
+ <ua>addresses</ua>=@(
+ (
+
<ua>uri</ua>=<us>http://localhost:10000/addressBook/addresses/3</us>,
+
<ua>personUri</ua>=<us>http://localhost:10000/addressBook/people/2</us>,
+ <ua>id</ua>=<un>3</un>,
+ <ua>street</ua>=<us>'43+Prairie+Chapel+Rd'</us>,
+ <ua>city</ua>=<us>Crawford</us>,
+ <ua>state</ua>=<us>TX</us>,
+ <ua>zip</ua>=<un>76638</un>,
+ <ua>isCurrent</ua>=<uk>true</uk>
+ ),
+ (
+
<ua>uri</ua>=<us>http://localhost:10000/addressBook/addresses/4</us>,
+
<ua>personUri</ua>=<us>http://localhost:10000/addressBook/people/2</us>,
+ <ua>id</ua>=<un>4</un>,
+
<ua>street</ua>=<us>'1600+Pennsylvania+Ave'</us>,
+ <ua>city</ua>=<us>Washington</us>,
+ <ua>state</ua>=<us>DC</us>,
+ <ua>zip</ua>=<un><us>20500</un>,
+ <ua>isCurrent</ua>=<uk>false</uk>
+ )
+ ),
+ <ua>age</ua>=<un>71</un>
+ )
+ </p>
+
+ <p>
+ Juneau supports two kinds of serialization:
+ </p>
+ <ul class='spaced-list'>
+ <li>Construction of full URL query parameter strings
(e.g. <code>&key=value</code> pairs) from beans and maps.
+ <li>Construction of URL query parameter value strings
(e.g. just the <code>value</code> portion of <code>&key=value</code> pairs)
from any POJO.
+ </ul>
+ <p>
+ Top-level beans and maps can serialized as key/value
pairs as shown below:
+ </p>
+ <h6 class='figure'>Example: A bean with 2 string properties,
'foo' and 'baz', serialized to a query string</h6>
+ <p class='bcode'>
+
http://localhost/sample?<ua>foo</ua>=<us>bar</us>&<ua>baz</ua>=<us>bing</us>
+ </p>
+ <p>
+ Lower-level beans and maps are also serialized as
key/value pairs, but are surrounded with a <js>"(...)"</js> construct to denote
an object mapping,
+ and uses a comma as the parameter delimiter
instead of <js>"&"</js>.<br>
+ </p>
+ <h6 class='figure'>Example: A bean serialized as a query
parameter value.</h6>
+ <p class='bcode'>
+
http://localhost/sample?<ua>a1</ua>=(<ua>foo</ua>=<us>bar</us>,<ua>baz</ua>=<us>bing</us>)
+ </p>
+
+ <h6 class='figure'>General methodology:</h6>
+ <table class='styled' style='border-collapse: collapse;'>
+ <tr><th>Java type</th><th>JSON
equivalent</th><th>UON</th></tr>
+ <tr>
+ <td>Maps/beans</td>
+ <td>OBJECT</td>
+ <td class='code'>
+ <ua>a1</ua>=(<ua>b1</ua>=<us>x1</us>,<ua>b2</ua>=<us>x2</us>)
+
<ua>a1</ua>=(<ua>b1</ua>=(<ua>c1</ua>=<us>x1</us>,<ua>c2</ua>=<us>x2</us>))
+ </td>
+ </tr>
+ <tr>
+ <td>Collections/arrays</td>
+ <td>ARRAY</td>
+ <td class='code'>
+ <ua>a1</ua>=@(<us>x1</us>,<us>x2</us>)
+ <ua>a1</ua>=@(@(<us>x1</us>,<us>x2</us>),@(<us>x3</us>,<us>x4</us>))
+
<ua>a1</ua>=@((<ua>b1</ua>=<us>x1</us>,<ua>b2</ua>=<us>x2</us>),(<ua>c1</ua>=<us>x1</us>,<ua>c2</ua>=<us>x2</us>))
+ </td>
+ </tr>
+ <tr>
+ <td>Booleans</td>
+ <td>BOOLEAN</td>
+ <td class='code'>
+ <ua>a1</ua>=<uk>true</uk>&<ua>a2</ua>=<uk>false</uk>
+ </td>
+ </tr>
+ <tr>
+ <td>int/float/double/...</td>
+ <td>NUMBER</td>
+ <td class='code'>
+ <ua>a1</ua>=<un>123</un>&<ua>a2</ua>=<un>1.23e1</un>
+ </td>
+ </tr>
+ <tr>
+ <td>null</td>
+ <td>NULL</td>
+ <td class='code'>
+ <ua>a1</ua>=<uk>null</uk>
+ </td>
+ </tr>
+ <tr>
+ <td>String</td>
+ <td>STRING</td>
+ <td class='code'>
+ <ua>a1</ua>=<us>foobar</us>
+ <ua>a1</ua>=<us>'true'</us>
+ <ua>a1</ua>=<us>'null'</us>
+ <ua>a1</ua>=<us>'123'</us>
+ <ua>a1</ua>=<us>' string with whitespace '</us>
+ <ua>a1</ua>=<us>'string with ~'escaped~' quotes'</us>
+ </td>
+ </tr>
+ </table>
+ <p>
+ Refer to the <a href='doc-files/rfc_uon.txt'>UON
specification</a> for a complete set of syntax rules.
+ <p>
+ <code>PojoSwaps</code> can be used to convert
non-serializable POJOs into serializable forms, such as converting
+ <code>Calendar</code> object to ISO8601
strings, or <code><uk>byte</uk>[]</code> arrays to Base-64 encoded strings.<br>
+ These transforms can be associated at various levels:
+ </p>
+ <ul class='spaced-list'>
+ <li>On serializer and parser instances to handle all
objects of the class type globally.
+ <li>On classes through the <code><ja>@Bean</ja></code>
annotation.
+ <li>On bean properties through the
<code><ja>@BeanProperty</ja></code> annotations.
+ </ul>
+ <h6 class='figure'>Example: A serialized Calendar object using
<code>CalendarSwap.RFC2822DTZ</code> transform.</h6>
+ <p class='bcode'>
+ http://localhost/sample?<ua>a1=<us>'Sun,+03+Mar+1901+09:05:06+GMT'</us>
+ </p>
+ <p>
+ For more information about transforms, refer to <a
class='doclink'
href='../transform/package-summary.html#TOC'>org.apache.juneau.transform</a>.
+ </p>
+ </div>
+
+</div>
+
+<!--
========================================================================================================
-->
+<a id="UrlEncodingSerializer"></a>
+<h2 class='topic' onclick='toggle(this)'>2 - UrlEncodingSerializer and
UonSerializer classes</h2>
+<div class='topic'>
+ <p>
+ {@link org.apache.juneau.urlencoding.UrlEncodingSerializer} and
{@link org.apache.juneau.uon.UonSerializer} classes are used to convert POJOs
to URL-encoded strings.<br>
+ The <code>UonSerializer</code> class converts parameter values
to UON notation.
+ The <code>UrlEncodingSerializer</code> class converts a POJO to
key/value URL-Encoded pairs using <code>UonSerializer</code> to serialize the
values.
+ If you're trying to construct complete URL-Encoded entities,
use <code>UrlEncodingSerializer</code>.
+ If you're constructing your own key/value pairs, use
<code>UonSerializer</code>.
+ </p>
+ <p>
+ The serializers include several configurable settings.<br>
+ Static reusable instances of serializers are provided with
commonly-used settings:
+ </p>
+ <ul class='spaced-list'>
+ <li>{@link
org.apache.juneau.urlencoding.UrlEncodingSerializer#DEFAULT} - All default
settings, strict mode.
+ <li>{@link
org.apache.juneau.urlencoding.UrlEncodingSerializer#DEFAULT_READABLE} - Use
whitespace and indentation for readability.
+ <li>{@link org.apache.juneau.uon.UonSerializer#DEFAULT} - All
default settings, strict mode.
+ <li>{@link
org.apache.juneau.uon.UonSerializer#DEFAULT_READABLE} - Use whitespace and
indentation for readability.
+ <li>{@link
org.apache.juneau.uon.UonSerializer#DEFAULT_ENCODING} - Same as DEFAULT, but
use URL-Encoding on special characters.
+ </ul>
+ <p>
+ The general guidelines on which serializer to use is:
+ </p>
+ <ul class='spaced-list'>
+ <li>Use encoding serializers when you're using the results to
construct a URI yourself, and therefore
+ need invalid URI characters to be encoded.
+ <li>Use unencoding serializers when you're creating parameter
values and passing them off to some other
+ utility class that will itself encode invalid URI
characters.
+ <li>Use the readable serializer for debugging purposes.
+ </ul>
+
+ <h6 class='topic'>Notes about examples</h6>
+ <p>
+ The examples shown in this document will use default strict
settings.<br>
+ For brevity, the examples will use public fields instead of
getters/setters to reduce the size of the examples.<br>
+ In the real world, you'll typically want to use standard bean
getters and setters.
+ </p>
+ <p>
+ To start off simple, we'll begin with the following simplified
bean and build upon it.
+ </p>
+ <p class='bcode'>
+ <jk>public class</jk> Person {
+ <jc>// Bean properties</jc>
+ <jk>public int</jk> <jf>id</jf>;
+ <jk>public</jk> String <jf>name</jf>;
+
+ <jc>// Bean constructor (needed by parser)</jc>
+ <jk>public</jk> Person() {}
+
+ <jc>// Normal constructor</jc>
+ <jk>public</jk> Person(<jk>int</jk> id, String name) {
+ <jk>this</jk>.<jf>id</jf> = id;
+ <jk>this</jk>.<jf>name</jf> = name;
+ }
+ }
+ </p>
+ <p>
+ The following code shows how to convert this to a URL-encoded
value:
+ </p>
+ <p class='bcode'>
+ <jc>// Use serializer with readable output, simple mode.</jc>
+ UonSerializer s = UonSerializer.<jsf>DEFAULT</jsf>;
+
+ <jc>// Create our bean.</jc>
+ Person p = <uk>new</uk> Person(1, <js>"John Smith"</js>);
+
+ <jc>// Serialize the bean to URL-encoded parameter value.</jc>
+ String urlencoded = s.serialize(p);
+ </p>
+ <p>
+ The code above produces the following output:
+ </p>
+ <p class='bcode'>
+ (<ua>id</ua>=<un>1</un>,<ua>name</ua>=<us>'John+Smith'</us>)
+ </p>
+ <p>
+ The {@link org.apache.juneau.urlencoding.UrlEncodingSerializer}
class converts
+ maps and beans into top-level query parameter strings.
+ </p>
+ <p class='bcode'>
+ <jc>// Use serializer with readable output, simple mode.</jc>
+ UrlEncodingSerializer s = UrlEncodingSerializer.<jsf>DEFAULT</jsf>;
+
+ <jc>// Serialize the bean to URL-encoded query string.</jc>
+ String urlencoded = s.serialize(p);
+ </p>
+ <p>
+ The code above produces the following output:
+ </p>
+ <p class='bcode'>
+ <ua>id</ua>=<un>1</un>&<ua>name</ua>=<us>'John+Smith'</us>
+ </p>
+ <p>
+ By default, the <code>UrlEncodingSerializer</code> class will
URL-Encode special characters, and the <code>UonSerializer</code> will NOT
URL-encode special characters.
+ </p>
+
+
+ <!--
========================================================================================================
-->
+ <a id="BeanAnnotations"></a>
+ <h3 class='topic' onclick='toggle(this)'>2.1 - @Bean and @BeanProperty
annotations</h3>
+ <div class='topic'>
+ <p>
+ The {@link org.apache.juneau.annotation.Bean @Bean} and
{@link org.apache.juneau.annotation.BeanProperty @BeanProperty} annotations
+ are used to customize the behavior of beans
across the entire framework.<br>
+ They have various uses:
+ </p>
+ <ul class='spaced-list'>
+ <li>Hiding bean properties.
+ <li>Specifying the ordering of bean properties.
+ <li>Overriding the names of bean properties.
+ <li>Associating transforms at both the class and
property level (to convert non-serializable POJOs to serializable forms).
+ </ul>
+ <p>
+ For example, we now add a <code>birthDate</code>
property, and associate a transform with it to transform
+ it to an ISO8601 date-time string in GMT
time.<br>
+ We'll also add a couple of <code>URI</code>
properties.<br>
+ By default, <code>Calendars</code> are treated as beans
by the framework, which is usually not how you want them serialized.<br>
+ Using transforms, we can convert them to standardized
string forms.
+ </p>
+ <p class='bcode'>
+ <jk>public class</jk> Person {
+ <jc>// Bean properties</jc>
+ <jk>public int</jk> <jf>id</jf>;
+ <jk>public</jk> String <jf>name</jf>;
+ <jk>public</jk> URI <jf>uri</jf>;
+ <jk>public</jk> URI <jf>addressBookUri</jf>;
+
+
<ja>@BeanProperty</ja>(swap=CalendarSwap.ISO8601DTZ.<jk>class</jk>)
<jk>public</jk> Calendar <jf>birthDate</jf>;
+
+ <jc>// Bean constructor (needed by parser)</jc>
+ <jk>public</jk> Person() {}
+
+ <jc>// Normal constructor</jc>
+ <jk>public</jk> Person(<jk>int</jk> id, String name, String
uri, String addressBookUri, String birthDate) <jk>throws</jk> Exception {
+ <jk>this</jk>.<jf>id</jf> = id;
+ <jk>this</jk>.<jf>name</jf> = name;
+ <jk>this</jk>.<jf>uri</jf> = <jk>new</jk> URI(uri);
+ <jk>this</jk>.<jf>addressBookUri</jf> = <jk>new</jk>
URI(addressBookUri);
+ <jk>this</jk>.<jf>birthDate</jf> = <jk>new</jk>
GregorianCalendar();
+
<jk>this</jk>.<jf>birthDate</jf>.setTime(DateFormat.<jsm>getDateInstance</jsm>(DateFormat.<jsf>MEDIUM</jsf>).parse(birthDate));
+ }
+ }
+ </p>
+ <p>
+ Next, we alter our code to pass in the birthdate:
+ </p>
+ <p class='bcode'>
+ <jc>// Create our bean.</jc>
+ Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>,
<js>"http://sample/addressBook/person/1"</js>,
<js>"http://sample/addressBook"</js>, <js>"Aug 12, 1946"</js>);
+ </p>
+ <p>
+ Now when we rerun the sample code, we'll get the
following:
+ </p>
+ <p class='bcode'>
+
(<ua>id</ua>=<un>1</un>,<ua>name</ua>=<us>'John+Smith'</us>,<ua>uri</ua>=<us>http://sample/addressBook/person/1</us>,<ua>addressBookUri</ua>=<us>http://sample/addressBook</us>,<ua>birthDate</ua>=<us>1946-08-12T00:00:00Z</us>)
+ </p>
+ <p>
+ Using <code>UrlEncodingSerializer</code> instead would
create the following:
+ </p>
+ <p class='bcode'>
+
<ua>id</ua>=<un>1</un>&<ua>name</ua>=<us>'John+Smith'</us>&<ua>uri</ua>=<us>http://sample/addressBook/person/1</us>&<ua>addressBookUri</ua>=<us>http://sample/addressBook</us>&<ua>birthDate</ua>=<us>1946-08-12T00:00:00Z</us>
+ </p>
+ <p>
+ Another useful feature is the {@link
org.apache.juneau.annotation.Bean#propertyNamer()} annotation that allows you
to plug in your own
+ logic for determining bean property names.<br>
+ The {@link org.apache.juneau.PropertyNamerDashedLC} is
an example of an alternate property namer.
+ It converts bean property names to lowercase-dashed
format.
+ </p>
+ <h6 class='figure'>Example:</h6>
+ <p class='bcode'>
+ <ja>@Bean</ja>(propertyNamer=PropertyNamerDashedLC.<jk>class</jk>)
+ <jk>public class</jk> Person {
+ ...
+ </p>
+ <h6 class='figure'>Results</h6>
+ <p class='bcode'>
+
(<ua>id</ua>=<un>1</us>,<ua>name</ua>=<us>'John+Smith'</us>,<ua>uri</ua>=<us>http://sample/addressBook/person/1</us>,<ua>address-book-uri</ua>=<us>http://sample/addressBook</us>,<ua>birth-date</ua>=<us>1946-08-12T00:00:00Z</us>)
+ </p>
+ </div>
+
+
+ <!--
========================================================================================================
-->
+ <a id="Collections"></a>
+ <h3 class='topic' onclick='toggle(this)'>2.2 - Collections</h3>
+ <div class='topic'>
+ <p>
+ In our example, let's add a list-of-beans property to
our sample class:
+ </p>
+ <p class='bcode'>
+ <jk>public class</jk> Person {
+
+ <jc>// Bean properties</jc>
+ <jk>public</jk> LinkedList<Address> <jf>addresses</jf> =
<jk>new</jk> LinkedList<Address>();
+ ...
+ }
+ </p>
+ <p>
+ The <code>Address</code> class has the following
properties defined:
+ </p>
+ <p class='bcode'>
+ <jk>public class</jk> Address {
+
+ <jc>// Bean properties</jc>
+ <jk>public</jk> URI <jf>uri</jf>;
+ <jk>public</jk> URI <jf>personUri</jf>;
+ <jk>public int</jk> <jf>id</jf>;
+ <jk>public</jk> String <jf>street</jf>, <jf>city</jf>,
<jf>state</jf>;
+ <jk>public int</jk> <jf>zip</jf>;
+ <jk>public boolean</jk> <jf>isCurrent</jf>;
+ }
+ </p>
+ <p>
+ Next, add some quick-and-dirty code to add an address
to our person bean:
+ </p>
+ <p class='bcode'>
+ <jc>// Use serializer with readable output, simple mode.</jc>
+ UonSerializer s = UonSerializer.<jsf>DEFAULT_READABLE</jsf>;
+
+ <jc>// Create our bean.</jc>
+ Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>,
<js>"http://sample/addressBook/person/1"</js>,
<js>"http://sample/addressBook"</js>, <js>"Aug 12, 1946"</js>);
+ Address a = <jk>new</jk> Address();
+ a.<jf>uri</jf> = <jk>new</jk>
URI(<js>"http://sample/addressBook/address/1"</js>);
+ a.<jf>personUri</jf> = <jk>new</jk>
URI(<js>"http://sample/addressBook/person/1"</js>);
+ a.<jf>id</jf> = 1;
+ a.<jf>street</jf> = <js>"100 Main Street"</js>;
+ a.<jf>city</jf> = <js>"Anywhereville"</js>;
+ a.<jf>state</jf> = <js>"NY"</js>;
+ a.<jf>zip</jf> = 12345;
+ a.<jf>isCurrent</jf> = <jk>true</jk>;
+ p.<jf>addresses</jf>.add(a);
+ </p>
+ <p>
+ Now when we run the sample code, we get the following
(in readable format):
+ </p>
+ <p class='bcode'>
+ (
+ <ua>id</ua>=<un>1</un>,
+ <ua>name</ua>=<us>'John+Smith'</us>,
+ <ua>uri</ua>=<us>http://sample/addressBook/person/1</us>,
+ <ua>addressBookUri</ua>=<us>http://sample/addressBook</us>,
+ <ua>birthDate</ua>=<us>1946-08-12T00:00:00Z</us>,
+ <ua>addresses</ua>=@(
+ (
+
<ua>uri</ua>=<us>http://sample/addressBook/address/1</us>,
+
<ua>personUri</ua>=<us>http://sample/addressBook/person/1</us>,
+ <ua>id</ua>=<un>1</un>,
+ <ua>street</ua>=<us>'100+Main+Street'</us>,
+ <ua>city</ua>=<us>Anywhereville</us>,
+ <ua>state</ua>=<us>NY</us>,
+ <ua>zip</ua>=<un>12345</un>,
+ <ua>isCurrent</ua>=<uk>true</uk>
+ )
+ )
+ )
+ </p>
+ <p>
+ If we were to use <code>UrlEncodingSerializer</code>
instead, we would get the following:
+ </p>
+ <p class='bcode'>
+ <ua>id</ua>=<un>1</un>&
+ <ua>name</ua>=<us>'John+Smith'</us>&
+ <ua>uri</ua>=<us>http://sample/addressBook/person/1</us>&
+ <ua>addressBookUri</ua>=<us>http://sample/addressBook</us>&
+ <ua>birthDate</ua>=<us>1946-08-12T00:00:00Z</us>&
+ <ua>addresses</ua>=@(
+ (
+
<ua>uri</ua>=<us>http://sample/addressBook/address/1</us>,
+
<ua>personUri</ua>=<us>http://sample/addressBook/person/1</us>,
+ <ua>id</ua>=<un>1</un>,
+ <ua>street</ua>=<us>'100+Main+Street'</us>,
+ <ua>city</ua>=<us>Anywhereville</us>,
+ <ua>state</ua>=<us>NY</us>,
+ <ua>zip</ua>=<un>12345</un>,
+ <ua>isCurrent</ua>=<uk>true</uk>
+ )
+ )
+ </p>
+ </div>
+ <p>
+ Note how the top level <code>Person</code> bean is serialized
using the standard <js>'&'</js> delimiter, whereas the lower-level
<code>Address</code>
+ bean is serialized using the <js>','</js> character to
prevent the <code>addresses</code> field from being incompletely parsed.
+ </p>
+
+
+ <!--
========================================================================================================
-->
+ <a id="Recursion"></a>
+ <h3 class='topic' onclick='toggle(this)'>2.3 - Non-tree models and
recursion detection</h3>
+ <div class='topic'>
+ <p>
+ The URL-encoding serializer is designed to be used
against POJO tree structures. <br>
+ It expects that there not be loops in the POJO model
(e.g. children with references to parents, etc...).<br>
+ If you try to serialize models with loops, you will
usually cause a <code>StackOverflowError</code> to
+ be thrown (if {@link
org.apache.juneau.serializer.SerializerContext#SERIALIZER_maxDepth} is not
reached first).
+ </p>
+ <p>
+ If you still want to use the URL-encoding serializer on
such models, Juneau provides the
+ {@link
org.apache.juneau.serializer.SerializerContext#SERIALIZER_detectRecursions}
setting.<br>
+ It tells the serializer to look for instances of an
object in the current branch of the tree and
+ skip serialization when a duplicate is
encountered.
+ </p>
+ <p>
+ For example, let's make a POJO model out of the
following classes:
+ </p>
+ <p class='bcode'>
+ <jk>public class</jk> A {
+ <jk>public</jk> B b;
+ }
+
+ <jk>public class</jk> B {
+ <jk>public</jk> C c;
+ }
+
+ <jk>public class</jk> C {
+ <jk>public</jk> A a;
+ }
+ </p>
+ <p>
+ Now we create a model with a loop and serialize the
results.
+ </p>
+ <p class='bcode'>
+ <jc>// Clone an existing serializer and set property for detecting
recursions.</jc>
+ UrlEncodingSerializer s =
UrlEncodingSerializer.<jsf>DEFAULT_READABLE</jsf>.builder().detectRecursions(<jk>true</jk>).build();
+
+ <jc>// Create a recursive loop.</jc>
+ A a = <jk>new</jk> A();
+ a.<jf>b</jf> = <jk>new</jk> B();
+ a.<jf>b</jf>.<jf>c</jf> = <jk>new</jk> C();
+ a.<jf>b</jf>.<jf>c</jf>.<jf>a</jf> = a;
+
+ <jc>// Serialize.</jc>
+ String json = s.serialize(a);
+ </p>
+ <p>
+ What we end up with is the following, which does not
serialize the contents of the <code>c</code> field:
+ </p>
+ <p class='bcode'>
+ (
+ <ua>b</ua>=(
+ <ua>c</ua>=()
+ )
+ )
+ </p>
+ <p>
+ Without recursion detection enabled, this would cause a
stack-overflow error.
+ </p>
+ <p>
+ Recursion detection introduces a performance penalty of
around 20%.<br>
+ For this reason the setting is disabled by default.
+ </p>
+ </div>
+
+
+ <!--
========================================================================================================
-->
+ <a id="SerializerConfigurableProperties"></a>
+ <h3 class='topic' onclick='toggle(this)'>2.4 - Configurable
properties</h3>
+ <div class='topic'>
+ <p>
+ See the following classes for all configurable
properties that can be used on this serializer:
+ </p>
+ <ul class='spaced-list'>
+ <li>{@link org.apache.juneau.BeanContext} - Bean
context properties.
+ <li>{@link org.apache.juneau.uon.UonSerializerContext}
- UON serializer context properties.
+ <li>{@link
org.apache.juneau.urlencoding.UrlEncodingSerializerContext} - URL-Encoding
serializer context properties.
+ </ul>
+ </div>
+
+
+ <!--
========================================================================================================
-->
+ <a id="SerializerOtherNotes"></a>
+ <h3 class='topic' onclick='toggle(this)'>2.5 - Other notes</h3>
+ <div class='topic'>
+ <ul class='spaced-list'>
+ <li>Like all other Juneau serializers, the URL-encoding
serializers are thread safe and maintain an internal cache of bean classes
encountered.<br>
+ For performance reasons, it's recommended that
serializers be reused whenever possible instead of always creating new
instances.
+ </ul>
+ </div>
+</div>
+
+
+<!--
========================================================================================================
-->
+<a id="UrlEncodingParser"></a>
+<h2 class='topic' onclick='toggle(this)'>3 - UrlEncodingParser and UonParser
classes</h2>
+<div class='topic'>
+ <p>
+ {@link org.apache.juneau.urlencoding.UrlEncodingParser} and
{@link org.apache.juneau.uon.UonParser} classes are used to convert URL-encoded
strings back into POJOs.<br>
+ The <code>UonParser</code> class converts UON-encoded parameter
values to POJOs.
+ The <code>UrlEncodingParser</code> class converts entire
URL-Encoded strings to POJOs using <code>UonSerializer</code> to serialize
indivisual values.
+ If you're trying to parse an entire URL-Encoded string, use
<code>UrlEncodingParser</code>.
+ If you're trying to parse an individual value (such as that
returned by <code>RestServlet.getQueryParameter(name)</code>), use
<code>UonParser</code>.
+ </p>
+ <p>
+ The following static reusable instances of
<code>UrlEncodingParser</code> are provided for convenience:
+ </p>
+ <ul class='spaced-list'>
+ <li>{@link
org.apache.juneau.urlencoding.UrlEncodingParser#DEFAULT} - Default parser for
entire URL-encoded strings, decode <code>%xx</code> sequences.
+ <li>{@link org.apache.juneau.uon.UonParser#DEFAULT} - Default
parser for URL-encoded parameter values, don't decode <code>%xx</code>
sequences.
+ <li>{@link org.apache.juneau.uon.UonParser#DEFAULT_DECODING} -
Default parser for URL-encoded parameter values, decode <code>%xx</code>
sequences.
+ </ul>
+ <p>
+ The general guildlines on which parser to use is:
+ </p>
+ <ul class='spaced-list'>
+ <li>Use the <code>DEFAULT</code> parser for parameter values
that have already had <code>%xx</code> sequences decoded,
+ such as when using
<code>HttpServletRequest.getQueryParameter(name)</code>.
+ <li>Use the <code>DEFAULT_ENCODED</code> parser if the input
has not already had <code>%xx</code> sequences decoded.
+ </ul>
+ <p>
+ Let's build upon the previous example and parse the generated
URL-encoded string back into the original bean.<br>
+ We start with the URL-encoded string that was generated.
+ </p>
+ <p class='bcode'>
+ <jc>// Use serializer with readable output.</jc>
+ UonSerializer s = UonSerializer.<jsf>DEFAULT_READABLE</jsf>;
+
+ <jc>// Create our bean.</jc>
+ Person p = <jk>new</jk> Person(1, <js>"John Smith"</js>,
<js>"http://sample/addressBook/person/1"</js>,
<js>"http://sample/addressBook"</js>, <js>"Aug 12, 1946"</js>);
+ Address a = <jk>new</jk> Address();
+ a.<jf>uri</jf> = <jk>new</jk>
URI(<js>"http://sample/addressBook/address/1"</js>);
+ a.<jf>personUri</jf> = <jk>new</jk>
URI(<js>"http://sample/addressBook/person/1"</js>);
+ a.<jf>id</jf> = 1;
+ a.<jf>street</jf> = <js>"100 Main Street"</js>;
+ a.<jf>city</jf> = <js>"Anywhereville"</js>;
+ a.<jf>state</jf> = <js>"NY"</js>;
+ a.<jf>zip</jf> = 12345;
+ a.<jf>isCurrent</jf> = <jk>true</jk>;
+ p.<jf>addresses</jf>.add(a);
+
+ <jc>// Serialize the bean.</jc>
+ String urlencoded = s.serialize(p);
+ </p>
+ <p>
+ This code produced the following:
+ </p>
+ <p class='bcode'>
+ (
+ <ua>id</ua>=<un>1</un>,
+ <ua>name</ua>=<us>'John+Smith'</us>,
+ <ua>uri</ua>=<us>http://sample/addressBook/person/1</us>,
+ <ua>addressBookUri</ua>=<us>http://sample/addressBook</us>,
+ <ua>birthDate</ua>=<us>1946-08-12T00:00:00Z</us>,
+ <ua>addresses</ua>=@(
+ (
+
<ua>uri</ua>=<us>http://sample/addressBook/address/1</us>,
+
<ua>personUri</ua>=<us>http://sample/addressBook/person/1</us>,
+ <ua>id</ua>=<un>1</un>,
+ <ua>street</ua>=<us>'100+Main+Street'</us>,
+ <ua>city</ua>=<us>Anywhereville</us>,
+ <ua>state</ua>=<us>NY</us>,
+ <ua>zip</ua>=<un>12345</un>,
+ <ua>isCurrent</ua>=<uk>true</uk>
+ )
+ )
+ )
+ </p>
+ <p>
+ The code to convert this back into a bean is:
+ </p>
+ <p class='bcode'>
+ <jc>// Parse it back into a bean using the reusable JSON parser.</jc>
+ Person p = UonParser.<jsf>DEFAULT</jsf>.parse(urlencoded,
Person.<jk>class</jk>);
+
+ <jc>// Render it back as JSON.</jc>
+ json = JsonSerializer.<jsf>DEFAULT_LAX_READABLE</jsf>.serialize(p);
+ </p>
+ <p>
+ We print it back out to JSON to show that all the data has been
preserved:
+ </p>
+ <p class='bcode'>
+ {
+ id: <un>1</un>,
+ name: <js>'John Smith'</js>,
+ uri: <js>'http://sample/addressBook/person/1'</js>,
+ addressBookUri: <js>'http://sample/addressBook'</js>,
+ birthDate: <js>'1946-08-12T00:00:00Z'</js>,
+ addresses: [
+ {
+ uri:
<js>'http://sample/addressBook/address/1'</js>,
+ personUri:
<js>'http://sample/addressBook/person/1'</js>,
+ id: <un>1</un>,
+ street: <js>'100 Main Street'</js>,
+ city: <js>'Anywhereville'</js>,
+ state: <js>'NY'</js>,
+ zip: <un>12345</un>,
+ isCurrent: <jk>true</jk>
+ }
+ ]
+ }
+ </p>
+
+
+ <!--
========================================================================================================
-->
+ <a id="GenericParsing"></a>
+ <h3 class='topic' onclick='toggle(this)'>3.1 - Parsing into generic
POJO models</h3>
+ <div class='topic'>
+ <p>
+ The URL-encoding parser is not limited to parsing back
into the original bean classes.<br>
+ If the bean classes are not available on the parsing
side, the parser can also be used to
+ parse into a generic model consisting of
<code>Maps</code>, <code>Collections</code>, and primitive
+ objects.
+ </p>
+ <p>
+ You can parse into any <code>Map</code> type (e.g.
<code>HashMap</code>, <code>TreeMap</code>), but
+ using {@link org.apache.juneau.ObjectMap} is
recommended since it has many convenience methods
+ for converting values to various types.<br>
+ The same is true when parsing collections. You can use
any Collection (e.g. <code>HashSet</code>, <code>LinkedList</code>)
+ or array (e.g. <code>Object[]</code>,
<code>String[]</code>, <code>String[][]</code>), but using
+ {@link org.apache.juneau.ObjectList} is
recommended.
+ </p>
+ <p>
+ When the map or list type is not specified, or is the
abstract <code>Map</code>, <code>Collection</code>, or <code>List</code> types,
+ the parser will use <code>ObjectMap</code> and
<code>ObjectList</code> by default.
+ </p>
+ <p>
+ Starting back with our original URL-encoded string:
+ </p>
+ <p class='bcode'>
+ (
+ <ua>id</ua>=<un>1</un>,
+ <ua>name</ua>=<us>'John+Smith'</us>,
+ <ua>uri</ua>=<us>http://sample/addressBook/person/1</us>,
+ <ua>addressBookUri</ua>=<us>http://sample/addressBook</us>,
+ <ua>birthDate</ua>=<us>1946-08-12T00:00:00Z</us>,
+ <ua>addresses</ua>=@(
+ (
+
<ua>uri</ua>=<us>http://sample/addressBook/address/1</us>,
+
<ua>personUri</ua>=<us>http://sample/addressBook/person/1</us>,
+ <ua>id</ua>=<un>1</un>,
+ <ua>street</ua>=<us>'100+Main+Street'</us>,
+ <ua>city</ua>=<us>Anywhereville</us>,
+ <ua>state</ua>=<us>NY</us>,
+ <ua>zip</ua>=<un>12345</un>,
+ <ua>isCurrent</ua>=<uk>true</uk>
+ )
+ )
+ )
+ </p>
+ <p>
+ We can parse this into a generic <code>ObjectMap</code>:
+ </p>
+ <p class='bcode'>
+ <jc>// Parse URL-encoded string into a generic POJO model.</jc>
+ ObjectMap m = UonParser.<jsf>DEFAULT</jsf>.parse(urlencoded,
ObjectMap.<jk>class</jk>);
+
+ <jc>// Convert it back to JSON.</jc>
+ String json =
JsonSerializer.<jsf>DEFAULT_LAX_READABLE</jsf>.serialize(m);
+ </p>
+ <p>
+ What we end up with is the exact same output.<br>
+ Even the numbers and booleans are preserved because
they are parsed into <code>Number</code> and <code>Boolean</code> objects
+ when parsing into generic models.
+ </p>
+ <p class='bcode'>
+ {
+ id: <un>1</un>,
+ name: <js>'John Smith'</js>,
+ uri: <js>'http://sample/addressBook/person/1'</js>,
+ addressBookUri: <js>'http://sample/addressBook'</js>,
+ birthDate: <js>'1946-08-12T00:00:00Z'</js>,
+ addresses: [
+ {
+ uri:
<js>'http://sample/addressBook/address/1'</js>,
+ personUri:
<js>'http://sample/addressBook/person/1'</js>,
+ id: <un>1</un>,
+ street: <js>'100 Main Street'</js>,
+ city: <js>'Anywhereville'</js>,
+ state: <js>'NY'</js>,
+ zip: <un>12345</un>,
+ isCurrent: <jk>true</jk>
+ }
+ ]
+ }
+ </p>
+ <p>
+ Once parsed into a generic model, various convenience
methods are provided on the <code>ObjectMap</code>
+ and <code>ObjectList</code> classes to retrieve
values:
+ </p>
+ <p class='bcode'>
+ <jc>// Parse URL-encoded string into a generic POJO model.</jc>
+ ObjectMap m = UonParser.<jsf>DEFAULT</jsf>.parse(urlencoded,
ObjectMap.<jk>class</jk>);
+
+ <jc>// Get some simple values.</jc>
+ String name = m.getString(<js>"name"</js>);
+ <jk>int</jk> id = m.getInt(<js>"id"</js>);
+
+ <jc>// Get a value convertable from a String.</jc>
+ URI uri = m.get(URI.<jk>class</jk>, <js>"uri"</js>);
+
+ <jc>// Get a value using a transform.</jc>
+ CalendarSwap transform = <jk>new</jk> CalendarSwap.ISO8601DTZ();
+ Calendar birthDate = m.get(transform, <js>"birthDate"</js>);
+
+ <jc>// Get the addresses.</jc>
+ ObjectList addresses = m.getObjectList(<js>"addresses"</js>);
+
+ <jc>// Get the first address and convert it to a bean.</jc>
+ Address address = addresses.get(Address.<jk>class</jk>, 0);
+ </p>
+
+ <p>
+ As a general rule, parsing into beans is often more
efficient than parsing into generic models.<br>
+ And working with beans is often less error prone than
working with generic models.
+ </p>
+ </div>
+
+
+ <!--
========================================================================================================
-->
+ <a id="ParserConfigurableProperties"></a>
+ <h3 class='topic' onclick='toggle(this)'>3.2 - Configurable
properties</h3>
+ <div class='topic'>
+ <p>
+ See the following classes for all configurable
properties that can be used on this parser:
+ </p>
+ <ul class='spaced-list'>
+ <li>{@link org.apache.juneau.BeanContext} - Bean
context properties.
+ <li>{@link org.apache.juneau.uon.UonParserContext} -
UON parser context properties.
+ <li>{@link
org.apache.juneau.urlencoding.UrlEncodingParserContext} - URL-Encoding parser
context properties.
+ </ul>
+ </div>
+
+
+ <!--
========================================================================================================
-->
+ <a id="ParserOtherNotes"></a>
+ <h3 class='topic' onclick='toggle(this)'>3.3 - Other notes</h3>
+ <div class='topic'>
+ <ul class='spaced-list'>
+ <li>Like all other Juneau parsers, the URL-encoding
parsers are thread safe and maintain an internal cache of bean classes
encountered.<br>
+ For performance reasons, it's recommended that
parser be reused whenever possible instead of always creating new instances.
+ </ul>
+ </div>
+
+</div>
+
+
+<!--
========================================================================================================
-->
+<a id="RestApiSupport"></a>
+<h2 class='topic' onclick='toggle(this)'>4 - REST API support</h2>
+<div class='topic'>
+ <p>
+ Juneau provides fully-integrated support for URL-encoding
serialization/parsing in the REST server and client APIs.<br>
+ The next two sections describe these in detail.
+ </p>
+
+ <!--
========================================================================================================
-->
+ <a id="RestServerSupport"></a>
+ <h3 class='topic' onclick='toggle(this)'>4.1 - REST server support</h3>
+ <div class='topic'>
+ <p>
+ There are four general ways of defining REST interfaces
with support for JSON.
+ Two using the built-in Juneau Server API, and two using
the JAX-RS integration component.
+ </p>
+ <ul class='spaced-list'>
+ <li>Create a servlet that subclasses from {@link
org.apache.juneau.rest.RestServletDefault}.<br>
+ This includes URL-encoding
serialization/parsing support by default, in addition to several other media
types.
+ <li>Create a servlet that subclasses from {@link
org.apache.juneau.rest.RestServlet} and specify the
+ URL-encoding serializer and/or parser
using the {@link org.apache.juneau.rest.annotation.RestResource#serializers()}
and
+ {@link
org.apache.juneau.rest.annotation.RestResource#parsers()} on the entire servlet
class, or
+ the {@link
org.apache.juneau.rest.annotation.RestMethod#serializers()} and {@link
org.apache.juneau.rest.annotation.RestMethod#parsers()}
+ annotations on individual methods
within the class.
+ <li>Register {@link
org.apache.juneau.rest.jaxrs.DefaultProvider} with JAX-RS.<br>
+ This includes URL-encoding
serialization/parsing support by default, in addition to several other media
types.
+ <li>Create and register a subclass of {@link
org.apache.juneau.rest.jaxrs.BaseProvider} and specify the serializers and
parsers to use on JAX-RS resources.
+ </ul>
+ <p>
+ In general, the Juneau REST server API is much more
configurable and easier to use than JAX-RS, but beware that the author may be
slightly biased in this statement.
+ </p>
+
+ <!--
========================================================================================================
-->
+ <a id="RestServletDefault"></a>
+ <h4 class='topic' onclick='toggle(this)'>4.1.1 - Using
RestServletDefault</h4>
+ <div class='topic'>
+ <p>
+ The quickest way to implement a REST resource
with URL-encoding support is to create a subclass of {@link
org.apache.juneau.rest.RestServletDefault}.<br>
+ This class provides support for JSON, XML,
HTML, URL-Encoding, and others.
+ </p>
+ <p>
+ The <code>AddressBookResource</code> example
shown in the first chapter uses the <code>RestServletJenaDefault</code> class
+ which is a subclass of
<code>RestServletDefault</code> with additional support for RDF languages.<br>
+ The start of the class definition is shown
below:
+ </p>
+ <p class='bcode'>
+ <jc>// Proof-of-concept resource that shows off the capabilities of
working with POJO resources.
+ // Consists of an in-memory address book repository.</jc>
+ <ja>@RestResource</ja>(
+ messages=<js>"nls/AddressBookResource"</js>,
+ properties={
+
<ja>@Property</ja>(name=SerializerContext.<jsf>SERIALIZER_useWhitespace</jsf>,
value=<js>"true"</js>),
+
<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>)
+ },
+ encoders=GzipEncoder.<jk>class</jk>
+ )
+ <jk>public class</jk> AddressBookResource <jk>extends</jk>
RestServletJenaDefault {
+ </p>
+ <p>
+ Notice how serializer and parser properties can
be specified using the <code>@RestResource.properties()</code> annotation.<br>
+ In this case, we're overriding the
<jsf>SERIALIZER_useWhitespace</jsf> property to add whitespace to the output.
+ The remaining properties are specific to the
HTML serializer.
+ </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.
+ See {@link
org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+ </p>
+ <p>
+ This document won't go into all the details of
the Juneau <code>RestServlet</code> class.<br>
+ Refer to the <a class='doclink'
href='../rest/package-summary.html#TOC'>org.apache.juneau.rest</a>
documentation for more information on the REST servlet class in general.
+ </p>
+ <p>
+ The rest of the code in the resource class
consists of REST methods that simply accept and return POJOs.<br>
+ The framework takes care of all content
negotiation, serialization/parsing, and error handling.<br>
+ Below are 3 of those methods to give you a
general idea of the concept:
+ </p>
+ <p class='bcode'>
+ <jc>// GET person request handler</jc>
+ <ja>@RestMethod</ja>(name=<js>"GET"</js>,
path=<js>"/people/{id}/*"</js>, rc={200,404})
+ <jk>public</jk> Person getPerson(RestRequest req, <ja>@Path</ja>
<jk>int</jk> id) throws Exception {
+
properties.put(HtmlDocSerializerContext.<jsf>HTMLDOC_title</jsf>,
req.getPathInfo());
+ <jk>return</jk> findPerson(id);
+ }
+
+ <jc>// POST person handler</jc>
+ <ja>@RestMethod</ja>(name=<js>"POST"</js>, path=<js>"/people"</js>,
guards=AdminGuard.<jk>class</jk>, rc={307,404})
+ <jk>public void</jk> createPerson(RestResponse res, <ja>@Body</ja>
CreatePerson cp) <jk>throws</jk> Exception {
+ Person p = addressBook.createPerson(cp);
+ res.sendRedirect(p.<jf>uri</jf>);
+ }
+
+ <jc>// DELETE person handler</jc>
+ <ja>@RestMethod</ja>(name=<js>"DELETE"</js>,
path=<js>"/people/{id}"</js>, guards=AdminGuard.<jk>class</jk>, rc={200,404})
+ <jk>public</jk> String deletePerson(RestResponse res, <ja>@Path</ja>
<jk>int</jk> id) <jk>throws</jk> Exception {
+ Person p = findPerson(id);
+ addressBook.remove(p);
+ <jk>return</jk> <js>"DELETE successful"</js>;
+ }
+ </p>
+ <p>
+ The resource class can be registered with the
web application like any other servlet, or can be
+ defined as a child of another resource
through the {@link org.apache.juneau.rest.annotation.RestResource#children()}
annotation.
+ </div>
+
+ <!--
========================================================================================================
-->
+ <a id="RestServlet"></a>
+ <h4 class='topic' onclick='toggle(this)'>4.1.2 - Using
RestServlet with annotations</h4>
+ <div class='topic'>
+ <p>
+ For fine-tuned control of media types, the
{@link org.apache.juneau.rest.RestServlet} class
+ can be subclassed directly.<br>
+ The serializers/parsers can be specified
through annotations at the class and/or method levels.
+ </p>
+ <p>
+ An equivalent <code>AddressBookResource</code>
class could be defined to only support URL-encoding using
+ the following definition:
+ </p>
+ <p class='bcode'>
+ <ja>@RestResource</ja>(
+ serializers={UrlEncodingSerializer.<jk>class</jk>},
+ parsers={UrlEncodingParser.<jk>class</jk>},
+ properties={
+
<ja>@Property</ja>(name=SerializerContext.<jsf>SERIALIZER_useWhitespace</jsf>,
value=<js>"true"</js>)
+ }
+ )
+ <jk>public class</jk> AddressBookResource <jk>extends</jk> RestServlet {
+ </p>
+ <p>
+ Likewise, serializers and parsers can be
specified/augmented/overridden at the method level like so:
+ </p>
+ <p class='bcode'>
+ <jc>// GET person request handler</jc>
+ <ja>@RestMethod</ja>(name=<js>"GET"</js>,
path=<js>"/people/{id}/*"</js>, rc={200,404},
+ serializers={UrlEncodingSerializer.<jk>class</jk>},
+ parsers={UrlEncodingParser.<jk>class</jk>},
+ properties={
+
<ja>@Property</ja>(name=SerializerContext.<jsf>SERIALIZER_useWhitespace</jsf>,
value=<js>"true"</js>)
+ }
+ )
+ <jk>public</jk> Person getPerson(RestRequest req, <ja>@Path</ja>
<jk>int</jk> id) throws Exception {
+
properties.put(HtmlDocSerializerContext.<jsf>HTMLDOC_title</jsf>,
req.getPathInfo());
+ <jk>return</jk> findPerson(id);
+ }
+ </p>
+ <p>
+ The {@link
org.apache.juneau.rest.annotation.RestMethod#serializersInherit()} and
+ {@link
org.apache.juneau.rest.annotation.RestMethod#parsersInherit()} control how
various artifacts
+ are inherited from the parent class.<br>
+ Refer to <a class='doclink'
href='../rest/package-summary.html#TOC'>org.apache.juneau.rest</a> for
additional information on using these annotations.
+ </p>
+ </div>
+
+ <!--
========================================================================================================
-->
+ <a id="DefaultProvider"></a>
+ <h4 class='topic' onclick='toggle(this)'>4.1.3 - Using JAX-RS
DefaultProvider</h4>
+ <div class='topic'>
+ <p>
+ URL-encoding media type support in JAX-RS can
be achieved by using the {@link org.apache.juneau.rest.jaxrs.DefaultProvider}
class.<br>
+ It implements the JAX-RS
<code>MessageBodyReader</code> and <code>MessageBodyWriter</code> interfaces
for all Juneau supported media types.
+ </p>
+ <p>
+ The <code>DefaultProvider</code> class
definition is shown below:
+ </p>
+ <p class='bcode'>
+ <ja>@Provider</ja>
+ <ja>@Produces</ja>(
+ <js>"application/json,text/json,"</js>+
<jc>// JsonSerializer</jc>
+ <js>"application/json+simple,text/json+simple,"</js>+
<jc>// JsonSerializer.Simple</jc>
+ <js>"application/json+schema,text/json+schema,"</js>+
<jc>// JsonSchemaSerializer</jc>
+ <js>"text/xml,"</js>+
<jc>// XmlDocSerializer</jc>
+ <js>"text/xml+simple,"</js>+
<jc>// XmlDocSerializer.Simple</jc>
+ <js>"text/xml+schema,"</js>+
<jc>// XmlSchemaDocSerializer</jc>
+ <js>"text/html,"</js>+
<jc>// HtmlDocSerializer</jc>
+ <js>"text/uon,"</js>+
<jc>// UonSerializer</jc>
+ <js>"application/x-www-form-urlencoded,"</js>+
<jc>// UrlEncodingSerializer</jc>
+ <js>"text/xml+soap,"</js>+
<jc>// SoapXmlSerializer</jc>
+ <js>"application/x-java-serialized-object"</js>
<jc>// JavaSerializedObjectSerializer</jc>
+ )
+ <ja>@Consumes</ja>(
+ <js>"application/json,text/json,"</js>+
<jc>// JsonParser</jc>
+ <js>"text/xml,"</js>+
<jc>// XmlParser</jc>
+ <js>"text/html,"</js>+
<jc>// HtmlParser</jc>
+ <js>"text/uon,"</js>+
<jc>// UonParser</jc>
+ <js>"application/x-www-form-urlencoded,"</js>+
<jc>// UrlEncodingParser</jc>
+ <js>"application/x-java-serialized-object"</js>
<jc>// JavaSerializedObjectParser</jc>
+ )
+ <ja>@JuneauProvider</ja>(
+ serializers={
+ JsonSerializer.<jk>class</jk>,
+ JsonSerializer.Simple.<jk>class</jk>,
+ JsonSchemaSerializer.<jk>class</jk>,
+ XmlDocSerializer.<jk>class</jk>,
+ XmlDocSerializer.Simple.<jk>class</jk>,
+ XmlSchemaDocSerializer.<jk>class</jk>,
+ HtmlDocSerializer.<jk>class</jk>,
+ UonSerializer.<jk>class</jk>,
+ UrlEncodingSerializer.<jk>class</jk>,
+ SoapXmlSerializer.<jk>class</jk>,
+ JavaSerializedObjectSerializer.<jk>class</jk>
+ },
+ parsers={
+ JsonParser.<jk>class</jk>,
+ XmlParser.<jk>class</jk>,
+ HtmlParser.<jk>class</jk>,
+ UonParser.<jk>class</jk>,
+ UrlEncodingParser.<jk>class</jk>,
+ JavaSerializedObjectParser.<jk>class</jk>,
+ }
+ )
+ <jk>public final class</jk> DefaultProvider <jk>extends</jk>
BaseProvider {}
+ </p>
+ <p>
+ That's the entire class. It consists of only
annotations to hook up media types to Juneau serializers and parsers.
+ The <ja>@Provider</ja>, <ja>@Produces</ja>, and
<ja>@Consumes</ja> annotations are standard JAX-RS annotations, and the
<ja>@JuneauProvider</ja> annotation is from Juneau.
+ </p>
+ <p>
+ To enable the provider, you need to make the
JAX-RS environment aware of it.
+ In Wink, this is accomplished by adding an
entry to a config file.
+ </p>
+ <p class='bcode'>
+ <xt><web-app</xt> <ua>version</ua>=<us>"2.3"</us><xt>></xt>
+ <xt><servlet></xt>
+
<xt><servlet-name></xt>WinkService<xt></servlet-name></xt>
+
<xt><servlet-class></xt>org.apache.wink.server.internal.servlet.RestServlet<xt></servlet-class></xt>
+ <xt><init-param></xt>
+
<xt><param-name></xt>applicationConfigLocation<xt></param-name></xt>
+
<xt><param-value></xt>/WEB-INF/wink.cfg<xt></param-value></xt>
+ <xt></init-param></xt>
+ <xt></servlet></xt>
+ </p>
+ <p>
+ Simply include a reference to the provider in
the configuration file.
+ <p class='bcode'>
+ org.apache.juneau.rest.jaxrs.DefaultProvider
+ </p>
+ <p>
+ Properties can be specified on providers
through the {@link org.apache.juneau.rest.jaxrs.JuneauProvider#properties()}
annotation.<br>
+ Properties can also be specified at the method
level by using the {@link
org.apache.juneau.rest.annotation.RestMethod#properties} annotation, like so:
+ </p>
+ <p class='bcode'>
+ <ja>@GET</ja>
+ <ja>@Produces</ja>(<js>"*/*"</js>)
+ <ja>@RestMethod</ja>( <jc>/* Override some properties */</jc>
+ properties={
+
<ja>@Property</ja>(name=SerializerContext.<jsf>SERIALIZER_useWhitespace</jsf>,
value=<js>"true"</js>)
+ }
+ )
+ <jk>public</jk> Message getMessage() {
+ <jk>return</jk> message;
+ }
+ </p>
+ <h6 class='topic'>Limitations</h6>
+ <p>
+ In general, the Juneau REST API is considerably
more flexible than the JAX-RS API, since you can specify and override
+ serializers, parsers, properties,
transforms, converters, guards, etc... at both the class and method levels.<br>
+ Therefore, the JAX-RS API has the following
limitations that the Juneau Server API does not:
+ </p>
+ <ul class='spaced-list'>
+ <li>The ability to specify different media type
providers at the class and method levels.<br>
+ For example, you may want to use
<code>JsonSerializer</code> with one set of properties on
+ one class, and another instance
with different properties on another class.<br>
+ There is currently no way to define
this at the class level.<br>
+ You can override properties at the
method level, but this can be cumbersome since it would have to be
+ done for all methods in the
resource.
+ <li>The Juneau Server API allows you to
manipulate properties programatically through the {@link
org.apache.juneau.rest.RestResponse#setProperty(String,Object)}
+ method, and through the {@link
org.apache.juneau.rest.annotation.Properties} annotation.<br>
+ There is no equivalent in JAX-RS.
+ </ul>
+ </div>
+
+ <!--
========================================================================================================
-->
+ <a id="BaseProvider"></a>
+ <h4 class='topic' onclick='toggle(this)'>4.1.4 - Using JAX-RS
BaseProvider with annotations</h4>
+ <div class='topic'>
+ <p>
+ To provide support for only JSON media types,
you can define your own provider class, like so:
+ </p>
+ <p class='bcode'>
+ <ja>@Provider</ja>
+ <ja>@Produces</ja>(
+ <js>"application/x-www-form-urlencoded"</js>
<jc>// UrlEncodingSerializer</jc>
+ )
+ <ja>@Consumes</ja>(
+ <js>"application/x-www-form-urlencoded"</js>
<jc>// UrlEncodingParser</jc>
+ )
+ <ja>@JuneauProvider</ja>(
+ serializers={
+ UrlEncodingSerializer.<jk>class</jk>
+ },
+ parsers={
+ UrlEncodingParser.<jk>class</jk>,
+ }
+ properties={
+
<ja>@Property</ja>(name=SerializerContext.<jsf>SERIALIZER_useWhitespace</jsf>,
value=<js>"true"</js>)
+ }
+ )
+ <jk>public final class</jk> MyUrlEncodingProvider <jk>extends</jk>
BaseProvider {}
+ </p>
+ <p>
+ Then register it with Wink the same way as
<code>DefaultProvider</code>.
+ </p>
+ </div>
+
+ </div>
+
+ <!--
========================================================================================================
-->
+ <a id="RestClientSupport"></a>
+ <h3 class='topic' onclick='toggle(this)'>4.2 - REST client support</h3>
+ <div class='topic'>
+ <p>
+ The {@link org.apache.juneau.rest.client.RestClient}
class provides an easy-to-use REST client interface with
+ pluggable media type handling using any of the
Juneau serializers and parsers.<br>
+ Defining a client to support the URL-encoding media
type on HTTP requests and responses can be done in one line of code:
+ </p>
+ <p class='bcode'>
+ <jc>// Create a client to handle URL-encoded requests and
responses.</jc>
+ RestClient client = <uk>new</uk>
RestClient(UrlEncodingSerializer.<uk>class</uk>,
UrlEncodingParser.<uk>class</uk>);
+ </p>
+ <p>
+ The client handles all content negotiation based on the
registered serializers and parsers.
+ </p>
+ <p>
+ The following code is pulled from the main method of
the <code>ClientTest</code> class in the sample web application, and
+ is run against the
<code>AddressBookResource</code> class running within the sample app.<br>
+ It shows how the client can be used to interact with
the REST API while completely hiding the negotiated content type and working
with nothing more than beans.
+ </p>
+ <h6 class='figure'>Example:</h6>
+ <p class='bcode'>
+ String root = <js>"http://localhost:9080/sample/addressBook"</js>;
+
+ <jc>// Get the current contents of the address book</jc>
+ AddressBook ab =
client.doGet(root).getResponse(AddressBook.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"Number of entries = "</js> +
ab.size());
+
+ <jc>// Delete the existing entries</jc>
+ <jk>for</jk> (Person p : ab) {
+ String r =
client.doDelete(p.<jf>uri</jf>).getResponse(String.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"Deleted person "</js> +
p.<jf>name</jf> + <js>", response = "</js> + r);
+ }
+
+ <jc>// Make sure they're gone</jc>
+ ab = client.doGet(root).getResponse(AddressBook.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"Number of entries = "</js> +
ab.size());
+
+ <jc>// Add 1st person again</jc>
+ CreatePerson cp = <jk>new</jk> CreatePerson(
+ <js>"Barack Obama"</js>,
+ <jsm>toCalendar</jsm>(<js>"Aug 4, 1961"</js>),
+ <jk>new</jk> CreateAddress(<js>"1600 Pennsylvania Ave"</js>,
<js>"Washington"</js>, <js>"DC"</js>, 20500, <jk>true</jk>),
+ <jk>new</jk> CreateAddress(<js>"5046 S Greenwood Ave"</js>,
<js>"Chicago"</js>, <js>"IL"</js>, 60615, <jk>false</jk>)
+ );
+ Person p = client.doPost(root + <js>"/people"</js>,
cp).getResponse(Person.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"Created person "</js> +
p.<jf>name</jf> + <js>", uri = "</js> + p.<jf>uri</jf>);
+
+ <jc>// Add 2nd person again, but add addresses separately</jc>
+ cp = <jk>new</jk> CreatePerson(
+ <js>"George Walker Bush"</js>,
+ toCalendar(<js>"Jul 6, 1946"</js>)
+ );
+ p = client.doPost(root + <js>"/people"</js>,
cp).getResponse(Person.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"Created person "</js> +
p.<jf>name</jf> + <js>", uri = "</js> + p.<jf>uri</jf>);
+
+ <jc>// Add addresses to 2nd person</jc>
+ CreateAddress ca = <jk>new</jk> CreateAddress(<js>"43 Prairie Chapel
Rd"</js>, <js>"Crawford"</js>, <js>"TX"</js>, 76638, <jk>true</jk>);
+ Address a = client.doPost(p.<jf>uri</jf> + <js>"/addresses"</js>,
ca).getResponse(Address.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"Created address "</js> +
a.<jf>uri</jf>);
+
+ ca = <jk>new</jk> CreateAddress(<js>"1600 Pennsylvania Ave"</js>,
<js>"Washington"</js>, <js>"DC"</js>, 20500, <jk>false</jk>);
+ a = client.doPost(p.<jf>uri</jf> + "/addresses"</js>,
ca).getResponse(Address.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"Created address "</js> +
a.<jf>uri</jf>);
+
+ <jc>// Find 1st person, and change name</jc>
+ Person[] pp = client.doGet(root +
<js>"?q={name:\"'Barack+Obama'\"}"</js>).getResponse(Person[].<jk>class</jk>);
+ String r = client.doPut(pp[0].<jf>uri</jf> + <js>"/name"</js>,
<js>"Barack Hussein Obama"</js>).getResponse(String.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"Changed name, response = "</js> + r);
+ p = client.doGet(pp[0].<jf>uri</jf>).getResponse(Person.<jk>class</jk>);
+ System.<jsm>out</jsm>.println(<js>"New name = "</js> + p.<jf>name</jf>);
+ </p>
+ <h6 class='figure'>Results</h6>
+ <p class='bcode'>
+ Number of entries = 2
+ Deleted person Barack Obama, response = DELETE successful
+ Deleted person George Walker Bush, response = DELETE successful
+ Number of entries = 0
+ Created person Barack Obama, uri =
http://localhost:9080/sample/addressBook/people/3
+ Created person George Walker Bush, uri =
http://localhost:9080/sample/addressBook/people/4
+ Created address http://localhost:9080/sample/addressBook/addresses/7
+ Created address http://localhost:9080/sample/addressBook/addresses/8
+ Changed name, response = PUT successful
+ New name = Barack Hussein Obama
+ </p>
+ </div>
+</div>
+<p align="center"><i><b>*** fÃn ***</b></i></p>
+
+</body>
+</html>
\ No newline at end of file