http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-core/src/main/java/org/apache/juneau/utils/SearchArgs.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/utils/SearchArgs.java 
b/juneau-core/src/main/java/org/apache/juneau/utils/SearchArgs.java
new file mode 100644
index 0000000..80cbbad
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/utils/SearchArgs.java
@@ -0,0 +1,301 @@
+// 
***************************************************************************************************************************
+// * 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.utils;
+
+import static java.util.Collections.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.util.*;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Encapsulates arguments for basic search/view/sort/position/limit 
functionality.
+ */
+public class SearchArgs {
+       private final Map<String,String> search;
+       private final List<String> view;
+       private final Map<String,Boolean> sort;
+       private final int position, limit;
+       private final boolean ignoreCase;
+
+       private SearchArgs(Builder b) {
+               this.search = unmodifiableMap(new 
LinkedHashMap<String,String>(b.search));
+               this.view = unmodifiableList(new ArrayList<String>(b.view));
+               this.sort = unmodifiableMap(new 
LinkedHashMap<String,Boolean>(b.sort));
+               this.position = b.position;
+               this.limit = b.limit;
+               this.ignoreCase = b.ignoreCase;
+       }
+
+       /**
+        * Builder for {@link SearchArgs} class.
+        */
+       @SuppressWarnings("hiding")
+       public static class Builder {
+               Map<String,String> search = new LinkedHashMap<String,String>();
+               List<String> view = new ArrayList<String>();
+               Map<String,Boolean> sort = new LinkedHashMap<String,Boolean>();
+               int position, limit;
+               boolean ignoreCase;
+
+               /**
+                * Adds search terms to this builder.
+                * <p>
+                * The search terms are a comma-delimited list of key/value 
pairs of column-names and search tokens.
+                * <p>
+                * For example:
+                * <p class='bcode'>
+                *      builder.search(<js>"column1=foo*, column2=bar 
baz"</js>);
+                * </p>
+                * <p>
+                * It's up to implementers to decide the syntax and meaning of 
the search terms.
+                * <p>
+                * Whitespace is trimmed from column names and search tokens.
+                *
+                * @param searchTerms The search terms string.
+                *      Can be <jk>null</jk>.
+                * @return This object (for method chaining).
+                */
+               public Builder search(String searchTerms) {
+                       if (searchTerms != null) {
+                               for (String s : StringUtils.split(searchTerms, 
',')) {
+                                       int i = StringUtils.indexOf(s, '=', 
'>', '<');
+                                       if (i == -1)
+                                               throw new 
RuntimeException("Invalid search terms: '"+searchTerms+"'");
+                                       char c = s.charAt(i);
+                                       search(s.substring(0, i).trim(), 
s.substring(c == '=' ? i+1 : i).trim());
+                               }
+                       }
+                       return this;
+               }
+
+               /**
+                * Adds a search term to this builder.
+                * <p>
+                * It's up to implementers to decide the syntax and meaning of 
the search term.
+                *
+                * @param column The column being searched.
+                * @param searchTerm The search term.
+                * @return This object (for method chaining).
+                */
+               public Builder search(String column, String searchTerm) {
+                       this.search.put(column, searchTerm);
+                       return this;
+               }
+
+               /**
+                * Specifies the list of columns to view.
+                * <p>
+                * The columns argument is a simple comma-delimited list of 
column names.
+                * <p>
+                * For example:
+                * <p class='bcode'>
+                *      builder.view(<js>"column1, column2"</js>);
+                * </p>
+                * <p>
+                * Whitespace is trimmed from column names.
+                * <p>
+                * Empty view columns imply view all columns.
+                *
+                * @param columns The columns being viewed.
+                *      Can be <jk>null</jk>.
+                * @return This object (for method chaining).
+                */
+               public Builder view(String columns) {
+                       if (columns != null)
+                               return 
view(Arrays.asList(StringUtils.split(columns, ',')));
+                       return this;
+               }
+
+               /**
+                * Specifies the list of columns to view.
+                * <p>
+                * Empty view columns imply view all columns.
+                *
+                * @param columns The columns being viewed.
+                * @return This object (for method chaining).
+                */
+               public Builder view(Collection<String> columns) {
+                       this.view.addAll(columns);
+                       return this;
+               }
+
+               /**
+                * Specifies the sort arguments.
+                * <p>
+                * The sort argument is a simple comma-delimited list of column 
names.
+                * <br>Column names can be suffixed with <js>'+'</js> or 
<js>'-'</js> to indicate ascending or descending order.
+                * <br>No suffix implies ascending order.
+                * <p>
+                * For example:
+                * <p class='bcode'>
+                *      <jc>// Order by column1 ascending, then column2 
descending.</jc>
+                *      builder.sort(<js>"column1, column2-"</js>);
+                * </p>
+                * <p>
+                * Note that the order of the order arguments is important.
+                * <p>
+                * Whitespace is trimmed from column names.
+                *
+                * @param sortArgs The columns to sort by.
+                *      Can be <jk>null</jk>.
+                * @return This object (for method chaining).
+                */
+               public Builder sort(String sortArgs) {
+                       if (sortArgs != null)
+                               sort(Arrays.asList(StringUtils.split(sortArgs, 
',')));
+                       return this;
+               }
+
+               /**
+                * Specifies the sort arguments.
+                * <p>
+                * <br>Column names can be suffixed with <js>'+'</js> or 
<js>'-'</js> to indicate ascending or descending order.
+                * <br>No suffix implies ascending order.
+                * <p>
+                * Note that the order of the sort is important.
+                *
+                * @param sortArgs The columns to sort by.
+                *      Can be <jk>null</jk>.
+                * @return This object (for method chaining).
+                */
+               public Builder sort(Collection<String> sortArgs) {
+                       for (String s : sortArgs) {
+                               boolean isDesc = false;
+                               if (endsWith(s, '-', '+')) {
+                                       isDesc = endsWith(s, '-');
+                                       s = s.substring(0, s.length()-1);
+                               }
+                               this.sort.put(s, isDesc);
+                       }
+                       return this;
+               }
+
+               /**
+                * Specifies the starting line number.
+                *
+                * @param position The zero-indexed position.
+                * @return This object (for method chaining).
+                */
+               public Builder position(int position) {
+                       this.position = position;
+                       return this;
+               }
+
+               /**
+                * Specifies the number of rows to return.
+                *
+                * @param limit The number of rows to return.
+                *      If <code>&lt;=0</code>, all rows should be returned.
+                * @return This object (for method chaining).
+                */
+               public Builder limit(int limit) {
+                       this.limit = limit;
+                       return this;
+               }
+
+               /**
+                * Specifies whether case-insensitive search should be used.
+                * <p>
+                * The default is <jk>false</jk>.
+                *
+                * @param value The ignore-case flag value.
+                * @return This object (for method chaining).
+                */
+               public Builder ignoreCase(boolean value) {
+                       this.ignoreCase = value;
+                       return this;
+               }
+
+               /**
+                * Construct the {@link SearchArgs} object.
+                * <p>
+                * This method can be called multiple times to construct new 
objects.
+                *
+                * @return A new {@link SearchArgs} object initialized with 
values in this builder.
+                */
+               public SearchArgs build() {
+                       return new SearchArgs(this);
+               }
+       }
+
+       /**
+        * The query search terms.
+        * <p>
+        * The search terms are key/value pairs consisting of column-names and 
search tokens.
+        * <p>
+        * It's up to implementers to decide the syntax and meaning of the 
search term.
+        *
+        * @return An unmodifiable map of query search terms.
+        */
+       public Map<String,String> getSearch() {
+               return search;
+       }
+
+       /**
+        * The view columns.
+        * <p>
+        * The view columns are the list of columns that should be displayed.
+        * An empty list implies all columns should be displayed.
+        *
+        * @return An unmodifiable list of columns to view.
+        */
+       public List<String> getView() {
+               return view;
+       }
+
+       /**
+        * The sort columns.
+        * <p>
+        * The sort columns are key/value pairs consisting of column-names and 
direction flags
+        *      (<jk>false</jk> = ascending, <jk>true</jk> = descending).
+        *
+        * @return An unmodifiable ordered map of sort columns and directions.
+        */
+       public Map<String,Boolean> getSort() {
+               return sort;
+       }
+
+       /**
+        * The first-row position.
+        *
+        * @return The zero-indexed row number of the first row to display.
+        *      Default is <code>0</code>
+        */
+       public int getPosition() {
+               return position;
+       }
+
+       /**
+        * The number of rows to return.
+        *
+        * @return The number of rows to return in the result.
+        *      Default is <code>0</code> which means return all rows.
+        */
+       public int getLimit() {
+               return limit;
+       }
+
+       /**
+        * The ignore-case flag.
+        * <p>
+        * Used in conjunction with {@link #getSearch()} to specify whether 
case-insensitive searches should be performed.
+        *
+        * @return The number of rows to return in the result.
+        *      Default is <jk>false</jk>.
+        */
+       public boolean isIgnoreCase() {
+               return ignoreCase;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html 
b/juneau-core/src/main/javadoc/overview.html
index a2c57dd..194b659 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -6511,6 +6511,7 @@
                                        <li>{@link 
org.apache.juneau.rest.widget.PoweredByJuneauWidget}
                                        <li>{@link 
org.apache.juneau.rest.widget.ContentTypeLinksColumnWidget}
                                        <li>{@link 
org.apache.juneau.rest.widget.ContentTypeLinksRowWidget}
+                                       <li>{@link 
org.apache.juneau.rest.widget.QueryWidget}
                                </ul>
                        <li><code>devops.css</code> cleaned up.
                        <li>Removed a bunch of URL-related methods from {@link 
org.apache.juneau.rest.RestRequest}.  

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
 
b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
index 381c897..a24e6ee 100644
--- 
a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
+++ 
b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
@@ -23,7 +23,10 @@ import org.apache.juneau.json.*;
 import org.apache.juneau.microservice.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.converters.*;
+import org.apache.juneau.rest.widget.*;
 import org.apache.juneau.serializer.*;
+import org.apache.juneau.transforms.*;
 
 /**
  * Sample REST resource that renders summary and detail views of the same bean.
@@ -32,13 +35,17 @@ import org.apache.juneau.serializer.*;
        title="Pet Store",
        description="An example of a typical REST resource where beans are 
rendered in summary and details views.",
        path="/petstore",
+       widgets={
+               QueryWidget.class
+       },
        htmldoc=@HtmlDoc(
-               
links="{up:'request:/..',options:'servlet:/?method=OPTIONS',source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/PetStoreResource.java'}",
+               
links="{up:'request:/..',options:'servlet:/?method=OPTIONS',query:'$W{query}',source:'$C{Source/gitHub}/org/apache/juneau/examples/rest/PetStoreResource.java'}",
                aside=""
                        + "<div style='max-width:400px' class='text'>"
                        + "     <p>This page shows a standard REST resource 
that renders bean summaries and details.</p>"
                        + "     <p>It shows how different properties can be 
rendered on the same bean in different views.</p>"
                        + "     <p>It also shows examples of HtmlRender classes 
and @BeanProperty(format) annotations.</p>"
+                       + "     <p>It also shows how the Queryable converter 
and query widget can be used to create searchable interfaces.</p>"
                        + "</div>"
        )
 )
@@ -56,7 +63,13 @@ public class PetStoreResource extends Resource {
        }
        
        // Exclude the 'breed' and 'getsAlongWith' properties from the beans.
-       @RestMethod(name="GET", path="/", summary="The complete list of pets in 
the store", bpExcludes="{Pet:'breed,getsAlongWith'}")
+       @RestMethod(
+               name="GET", 
+               path="/", 
+               summary="The complete list of pets in the store", 
+               bpExcludes="{Pet:'breed,getsAlongWith'}",
+               converters=Queryable.class
+       )
        public Collection<Pet> getPets() {
                return petDB.values();
        }
@@ -81,6 +94,15 @@ public class PetStoreResource extends Resource {
                
                @BeanProperty(format="$%.2f")  // Renders price in dollars.
                public float price;
+               
+               @BeanProperty(swap=DateSwap.RFC2822D.class)  // Renders dates 
in RFC2822 format.
+               public Date birthDate;
+       
+               public int getAge() {
+                       Calendar c = new GregorianCalendar();
+                       c.setTime(birthDate);
+                       return new GregorianCalendar().get(Calendar.YEAR) - 
c.get(Calendar.YEAR); 
+               }
        }
        
        @Html(render=KindRender.class)  // Render as an icon in HTML.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/PetStore.json
----------------------------------------------------------------------
diff --git 
a/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/PetStore.json
 
b/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/PetStore.json
index e8d3800..09abfbd 100644
--- 
a/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/PetStore.json
+++ 
b/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/PetStore.json
@@ -11,7 +11,7 @@
 // * specific language governing permissions and limitations under the 
License.                                              *
 // 
***************************************************************************************************************************
 {
-       1: {kind:'CAT',name:'Mr. 
Frisky',price:39.99,breed:'Persian',getsAlongWith:['CAT','FISH','RABBIT']},
-       2: 
{kind:'DOG',name:'Kibbles',price:99.99,breed:'Husky',getsAlongWith:['DOG','BIRD','FISH','MOUSE','RABBIT']},
-       3: 
{kind:'RABBIT',name:'Hoppy',price:49.99,breed:'Angora',getsAlongWith:['DOG','BIRD','FISH','MOUSE']}
+       1: {kind:'CAT',name:'Mr. 
Frisky',price:39.99,breed:'Persian',getsAlongWith:['CAT','FISH','RABBIT'],birthDate:'04
 Jul 2012'},
+       2: 
{kind:'DOG',name:'Kibbles',price:99.99,breed:'Husky',getsAlongWith:['DOG','BIRD','FISH','MOUSE','RABBIT'],birthDate:'01
 Sep 2014'},
+       3: 
{kind:'RABBIT',name:'Hoppy',price:49.99,breed:'Angora',getsAlongWith:['DOG','BIRD','FISH','MOUSE'],birthDate:'16
 Apr 2017'}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/AddressBookResourceTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/AddressBookResourceTest.java
 
b/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/AddressBookResourceTest.java
index b37a4ed..a9093cf 100644
--- 
a/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/AddressBookResourceTest.java
+++ 
b/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/AddressBookResourceTest.java
@@ -190,40 +190,40 @@ public class AddressBookResourceTest extends RestTestcase 
{
                        int rc = client.doGet("/addressBook?method=init").run();
                        assertEquals(200, rc);
 
-                       r = client.doGet("/addressBook/people?q=(name=B*)");
+                       r = client.doGet("/addressBook/people?s=name=B*");
                        people = r.getResponse(PersonList.class);
                        assertEquals(1, people.size());
                        assertEquals("Barack Obama", people.get(0).name);
 
-                       r = 
client.doGet("/addressBook/people?q=(name='~'Barack+Obama~'')");
+                       r = 
client.doGet("/addressBook/people?s=name='Barack+Obama'");
                        people = r.getResponse(PersonList.class);
                        assertEquals(1, people.size());
                        assertEquals("Barack Obama", people.get(0).name);
 
-                       r = 
client.doGet("/addressBook/people?q=(name='~'Barack%20Obama~'')");
+                       r = 
client.doGet("/addressBook/people?s=name='Barack%20Obama'");
                        people = r.getResponse(PersonList.class);
                        assertEquals(1, people.size());
                        assertEquals("Barack Obama", people.get(0).name);
 
-                       r = 
client.doGet("/addressBook/people?v=@(name,birthDate)");
+                       r = 
client.doGet("/addressBook/people?v=name,birthDate");
                        people = r.getResponse(PersonList.class);
                        assertEquals("Barack Obama", people.get(0).name);
                        assertTrue(people.get(0).getAge() > 10);
                        assertEquals(0, people.get(0).addresses.size());
 
-                       r = 
client.doGet("/addressBook/people?v=@(addresses,birthDate)");
+                       r = 
client.doGet("/addressBook/people?v=addresses,birthDate");
                        people = r.getResponse(PersonList.class);
                        assertNull(people.get(0).name);
                        assertTrue(people.get(0).getAge() > 10);
                        assertEquals(2, people.get(0).addresses.size());
 
-                       r = client.doGet("/addressBook/people?s=@((age=d))");
+                       r = client.doGet("/addressBook/people?o=age-");
                        people = r.getResponse(PersonList.class);
                        assertTrue(people.get(0).getAge() > 10);
-                       r = client.doGet("/addressBook/people?s=@(age)");
+                       r = client.doGet("/addressBook/people?o=age");
                        people = r.getResponse(PersonList.class);
                        assertTrue(people.get(0).getAge() > 10);
-                       r = client.doGet("/addressBook/people?s=@((age=a))");
+                       r = client.doGet("/addressBook/people?o=age+");
                        people = r.getResponse(PersonList.class);
                        assertTrue(people.get(0).getAge() > 10);
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
----------------------------------------------------------------------
diff --git 
a/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html 
b/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
index 77e23dc..36d8e9f 100755
--- 
a/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
+++ 
b/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
@@ -653,14 +653,14 @@
                                        <ul>
                                                <li><l>$R{attribute.X}</l> - 
Value returned by {@link 
org.apache.juneau.rest.RestRequest#getAttribute(String)} converted to a string.
                                                <li><l>$R{contextPath}</l> - 
Value returned by {@link org.apache.juneau.rest.RestRequest#getContextPath()}.
-                                               <li><l>$R{formData.X}</l> - 
Value returned by {@link 
org.apache.juneau.rest.RequestFormData#getFirst(String)}.
-                                               <li><l>$R{header.X}</l> - Value 
returned by {@link org.apache.juneau.rest.RequestHeaders#getFirst(String)}.
+                                               <li><l>$R{formData.X}</l> - 
Value returned by {@link 
org.apache.juneau.rest.RequestFormData#getString(String)}.
+                                               <li><l>$R{header.X}</l> - Value 
returned by {@link org.apache.juneau.rest.RequestHeaders#getString(String)}.
                                                <li><l>$R{method}</l> - Value 
returned by {@link org.apache.juneau.rest.RestRequest#getMethod()}.
                                                <li><l>$R{methodSummary}</l> - 
Value returned by {@link org.apache.juneau.rest.RestRequest#getMethodSummary()}.
                                                
<li><l>$R{methodDescription}</l> - Value returned by {@link 
org.apache.juneau.rest.RestRequest#getMethodDescription()}.
                                                <li><l>$R{path.X}</l> - Value 
returned by {@link org.apache.juneau.rest.RequestPathMatch#get(Object)}.
                                                <li><l>$R{pathInfo}</l> - Value 
returned by {@link org.apache.juneau.rest.RestRequest#getPathInfo()}.
-                                               <li><l>$R{query.X}</l> - Value 
returned by {@link org.apache.juneau.rest.RequestQuery#getFirst(String)}.
+                                               <li><l>$R{query.X}</l> - Value 
returned by {@link org.apache.juneau.rest.RequestQuery#getString(String)}.
                                                <li><l>$R{requestParentURI}</l> 
- Value returned by {@link 
org.apache.juneau.UriContext#getRootRelativePathInfoParent()}.
                                                <li><l>$R{requestURI}</l> - 
Value returned by {@link org.apache.juneau.rest.RestRequest#getRequestURI()}.
                                                
<li><l>$R{servletDescription}</l> - Value returned by {@link 
org.apache.juneau.rest.RestRequest#getServletDescription()}.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
index d83b821..34936b7 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
@@ -44,9 +44,9 @@ public class FormDataResource extends RestServletDefault {
        @RestMethod(name="POST", path="/defaultFormData", 
defaultFormData={"f1:1","f2=2"," f3 : 3 "})
        public ObjectMap defaultFormData(RequestFormData formData) {
                return new ObjectMap()
-                       .append("f1", formData.getFirst("f1"))
-                       .append("f2", formData.getFirst("f2"))
-                       .append("f3", formData.getFirst("f3"));
+                       .append("f1", formData.getString("f1"))
+                       .append("f2", formData.getString("f2"))
+                       .append("f3", formData.getString("f3"));
        }
 
        @RestMethod(name="POST", path="/annotatedFormData")

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HeadersResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HeadersResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HeadersResource.java
index edb58f2..1949a96 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HeadersResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HeadersResource.java
@@ -199,17 +199,17 @@ public class HeadersResource extends RestServlet {
        @RestMethod(name="GET", path="/defaultRequestHeaders", 
defaultRequestHeaders={"H1:1","H2=2"," H3 : 3 "})
        public ObjectMap defaultRequestHeaders(RequestHeaders headers) {
                return new ObjectMap()
-                       .append("h1", headers.getFirst("H1"))
-                       .append("h2", headers.getFirst("H2"))
-                       .append("h3", headers.getFirst("H3"));
+                       .append("h1", headers.getString("H1"))
+                       .append("h2", headers.getString("H2"))
+                       .append("h3", headers.getString("H3"));
        }
 
        @RestMethod(name="GET", path="/defaultRequestHeadersCaseInsensitive", 
defaultRequestHeaders={"H1:1","H2=2"," H3 : 3 "})
        public ObjectMap defaultRequestHeadersCaseInsensitive(RequestHeaders 
headers) {
                return new ObjectMap()
-                       .append("h1", headers.getFirst("h1"))
-                       .append("h2", headers.getFirst("h2"))
-                       .append("h3", headers.getFirst("h3"));
+                       .append("h1", headers.getString("h1"))
+                       .append("h2", headers.getString("h2"))
+                       .append("h3", headers.getString("h3"));
        }
 
        @RestMethod(name="GET", path="/annotatedHeaders")

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HtmlPropertiesResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HtmlPropertiesResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HtmlPropertiesResource.java
index e75ea5a..8cfc1a1 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HtmlPropertiesResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/HtmlPropertiesResource.java
@@ -41,8 +41,7 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                path="/Normal",
                htmldoc=@HtmlDoc(
                        title="Normal-title",
-                       description="Normal-text",
-                       links="{link:'Normal-links'}"
+                       description="Normal-text"
                )
        )
        public static class Normal extends RestServletDefault {
@@ -62,8 +61,7 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                        path="/test2",
                        htmldoc=@HtmlDoc(
                                title="Normal.test2-title",
-                               description="Normal.test2-text",
-                               links="{link:'Normal.test2-links'}"
+                               description="Normal.test2-text"
                        )
                )
                public String test2() {
@@ -77,7 +75,6 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                public String test3(RestResponse res) {
                        res.setHtmlTitle("Normal.test3-title");
                        res.setHtmlDescription("Normal.test3-text");
-                       res.setHtmlLinks("{link:'Normal.test3-links'}");
                        return "OK";
                }
 
@@ -88,7 +85,6 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                public String test4(RestResponse res) {
                        res.setProperty(HtmlDocSerializerContext.HTMLDOC_title, 
"Normal.test4-title");
                        
res.setProperty(HtmlDocSerializerContext.HTMLDOC_description, 
"Normal.test4-text");
-                       res.setProperty(HtmlDocSerializerContext.HTMLDOC_links, 
"{link:'Normal.test4-links'}");
                        return "OK";
                }
        }
@@ -102,7 +98,6 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                public void init(RestConfig config) throws Exception {
                        config.setHtmlTitle("NormalInit-title");
                        config.setHtmlDescription("NormalInit-text");
-                       config.setHtmlLinks("{link:'NormalInit-links'}");
                        super.init(config);
                }
 
@@ -121,8 +116,7 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                        path="/test2",
                        htmldoc=@HtmlDoc(
                                title="NormalInit.test2-title",
-                               description="NormalInit.test2-text",
-                               links="{link:'NormalInit.test2-links'}"
+                               description="NormalInit.test2-text"
                        )
                )
                public String test2() {
@@ -136,7 +130,6 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                public String test3(RestResponse res) {
                        res.setHtmlTitle("NormalInit.test3-title");
                        res.setHtmlDescription("NormalInit.test3-text");
-                       res.setHtmlLinks("{link:'NormalInit.test3-links'}");
                        return "OK";
                }
 
@@ -147,7 +140,6 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                public String test4(RestResponse res) {
                        res.setProperty(HtmlDocSerializerContext.HTMLDOC_title, 
"NormalInit.test4-title");
                        
res.setProperty(HtmlDocSerializerContext.HTMLDOC_description, 
"NormalInit.test4-text");
-                       res.setProperty(HtmlDocSerializerContext.HTMLDOC_links, 
"{link:'NormalInit.test4-links'}");
                        return "OK";
                }
        }
@@ -219,8 +211,7 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                        path="/test2",
                        htmldoc=@HtmlDoc(
                                title="NormalSubclassed1.test2-title",
-                               description="NormalSubclassed1.test2-text",
-                               links="{link:'NormalSubclassed1.test2-links'}"
+                               description="NormalSubclassed1.test2-text"
                        )
                )
                public String test2() {
@@ -232,8 +223,7 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                path="/NormalSubclassed2",
                htmldoc=@HtmlDoc(
                        title="NormalSubclassed2-title",
-                       description="NormalSubclassed2-text",
-                       links="{link:'NormalSubclassed2-links'}"
+                       description="NormalSubclassed2-text"
                )
        )
        public static class NormalSubclassed2 extends Normal {
@@ -255,8 +245,7 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                        path="/test2",
                        htmldoc=@HtmlDoc(
                                title="NormalSubclassed2.test2-title",
-                               description="NormalSubclassed2.test2-text",
-                               links="{link:'NormalSubclassed2.test2-links'}"
+                               description="NormalSubclassed2.test2-text"
                        )
                )
                public String test2() {
@@ -269,8 +258,7 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                messages="HtmlPropertiesResource",
                htmldoc=@HtmlDoc(
                        title="$L{pageTitle}",
-                       description="$L{pageText}",
-                       links="$L{pageLinks}"
+                       description="$L{pageText}"
                )
        )
        public static class LocalizedExplicit extends RestServletDefault {
@@ -289,7 +277,7 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                @RestMethod(
                        path="/test2",
                        htmldoc=@HtmlDoc(
-                               title="$L{test2.pageTitle}", 
description="$L{test2.pageText}", links="$L{test2.pageLinks}"
+                               title="$L{test2.pageTitle}", 
description="$L{test2.pageText}"
                        )
                )
                public String test2() {
@@ -303,7 +291,6 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                public String test3(RestResponse res) {
                        res.setHtmlTitle("$L{test3.pageTitle}");
                        res.setHtmlDescription("$L{test3.pageText}");
-                       res.setHtmlLinks("$L{test3.pageLinks}");
                        return "OK";
                }
 
@@ -314,7 +301,6 @@ public class HtmlPropertiesResource extends 
RestServletGroupDefault {
                public String test4(RestResponse res) {
                        res.setProperty(HtmlDocSerializerContext.HTMLDOC_title, 
"$L{test4.pageTitle}");
                        
res.setProperty(HtmlDocSerializerContext.HTMLDOC_description, 
"$L{test4.pageText}");
-                       res.setProperty(HtmlDocSerializerContext.HTMLDOC_links, 
"$L{test4.pageLinks}");
                        return "OK";
                }
        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
index 0e943d4..c769089 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
@@ -45,14 +45,14 @@ public class OverlappingMethodsResource extends 
RestServletDefault {
        public static class Test1Guard extends RestGuard {
                @Override /* RestGuard */
                public boolean isRequestAllowed(RestRequest req) {
-                       return req.getQuery().getFirst("t1","").equals("1");
+                       return req.getQuery().getString("t1","").equals("1");
                }
        }
 
        public static class Test2Guard extends RestGuard {
                @Override /* RestGuard */
                public boolean isRequestAllowed(RestRequest req) {
-                       return req.getQuery().getFirst("t2","").equals("2");
+                       return req.getQuery().getString("t2","").equals("2");
                }
        }
 
@@ -77,14 +77,14 @@ public class OverlappingMethodsResource extends 
RestServletDefault {
        public static class Test3aMatcher extends RestMatcher {
                @Override /* RestMatcher */
                public boolean matches(RestRequest req) {
-                       return req.getQuery().getFirst("t1","").equals("1");
+                       return req.getQuery().getString("t1","").equals("1");
                }
        }
 
        public static class Test3bMatcher extends RestMatcher {
                @Override /* RestMatcher */
                public boolean matches(RestRequest req) {
-                       return req.getQuery().getFirst("t2","").equals("2");
+                       return req.getQuery().getString("t2","").equals("2");
                }
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
index 3d49776..2ebcdaf 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
@@ -124,7 +124,7 @@ public class ParamsResource extends RestServletDefault {
        @RestMethod(name="GET", path="/testParamGet/*")
        public String testParamGet(RestRequest req, @Query("p1") String p1, 
@Query("p2") int p2) throws Exception {
                RequestQuery q = req.getQuery();
-               return 
"p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", 
String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
+               return 
"p1=["+p1+","+req.getQuery().getString("p1")+","+q.get("p1", 
String.class)+"],p2=["+p2+","+q.getString("p2")+","+q.get("p2", int.class)+"]";
        }
 
        
//====================================================================================================
@@ -133,7 +133,7 @@ public class ParamsResource extends RestServletDefault {
        @RestMethod(name="POST", path="/testParamPost/*")
        public String testParamPost(RestRequest req, @FormData("p1") String p1, 
@FormData("p2") int p2) throws Exception {
                RequestFormData f = req.getFormData();
-               return 
"p1=["+p1+","+req.getFormData().getFirst("p1")+","+f.get("p1", 
String.class)+"],p2=["+p2+","+req.getFormData().getFirst("p2")+","+f.get("p2", 
int.class)+"]";
+               return 
"p1=["+p1+","+req.getFormData().getString("p1")+","+f.get("p1", 
String.class)+"],p2=["+p2+","+req.getFormData().getString("p2")+","+f.get("p2", 
int.class)+"]";
        }
 
        
//====================================================================================================
@@ -142,7 +142,7 @@ public class ParamsResource extends RestServletDefault {
        @RestMethod(name="GET", path="/testQParamGet/*")
        public String testQParamGet(RestRequest req, @Query("p1") String p1, 
@Query("p2") int p2) throws Exception {
                RequestQuery q = req.getQuery();
-               return 
"p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", 
String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
+               return 
"p1=["+p1+","+req.getQuery().getString("p1")+","+q.get("p1", 
String.class)+"],p2=["+p2+","+q.getString("p2")+","+q.get("p2", int.class)+"]";
        }
 
        
//====================================================================================================
@@ -151,7 +151,7 @@ public class ParamsResource extends RestServletDefault {
        @RestMethod(name="POST", path="/testQParamPost/*")
        public String testQParamPost(RestRequest req, @Query("p1") String p1, 
@Query("p2") int p2) throws Exception {
                RequestQuery q = req.getQuery();
-               return 
"p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", 
String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
+               return 
"p1=["+p1+","+req.getQuery().getString("p1")+","+q.get("p1", 
String.class)+"],p2=["+p2+","+q.getString("p2")+","+q.get("p2", int.class)+"]";
        }
 
        
//====================================================================================================
@@ -160,7 +160,7 @@ public class ParamsResource extends RestServletDefault {
        @RestMethod(name="GET", path="/testPlainParamGet/*")
        public String testPlainParamGet(RestRequest req, 
@Query(value="p1",format="PLAIN") String p1) throws Exception {
                RequestQuery q = req.getQuery();
-               return 
"p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"]";
+               return 
"p1=["+p1+","+req.getQuery().getString("p1")+","+q.get("p1", String.class)+"]";
        }
 
        
//====================================================================================================
@@ -169,7 +169,7 @@ public class ParamsResource extends RestServletDefault {
        @RestMethod(name="POST", path="/testPlainParamPost/*")
        public String testPlainParamPost(RestRequest req, 
@FormData(value="p1",format="PLAIN") String p1) throws Exception {
                RequestFormData f = req.getFormData();
-               return 
"p1=["+p1+","+req.getFormData().getFirst("p1")+","+f.get("p1", 
String.class)+"]";
+               return 
"p1=["+p1+","+req.getFormData().getString("p1")+","+f.get("p1", 
String.class)+"]";
        }
 
        
//====================================================================================================
@@ -178,7 +178,7 @@ public class ParamsResource extends RestServletDefault {
        @RestMethod(name="GET", path="/testPlainQParamGet/*")
        public String testPlainQParamGet(RestRequest req, 
@Query(value="p1",format="PLAIN") String p1) throws Exception {
                RequestQuery q = req.getQuery();
-               return 
"p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"]";
+               return 
"p1=["+p1+","+req.getQuery().getString("p1")+","+q.get("p1", String.class)+"]";
        }
 
        
//====================================================================================================
@@ -187,7 +187,7 @@ public class ParamsResource extends RestServletDefault {
        @RestMethod(name="POST", path="/testPlainQParamPost/*")
        public String testPlainQParamPost(RestRequest req, 
@Query(value="p1",format="PLAIN") String p1) throws Exception {
                RequestQuery q = req.getQuery();
-               return 
"p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"]";
+               return 
"p1=["+p1+","+req.getQuery().getString("p1")+","+q.get("p1", String.class)+"]";
        }
 
        
//====================================================================================================

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/QueryResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/QueryResource.java 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/QueryResource.java
index 4c33538..db470ac 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/QueryResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/QueryResource.java
@@ -32,9 +32,9 @@ public class QueryResource extends RestServletDefault {
        @RestMethod(name="GET", path="/defaultQuery", 
defaultQuery={"f1:1","f2=2"," f3 : 3 "})
        public ObjectMap defaultQuery(RequestQuery query) {
                return new ObjectMap()
-                       .append("f1", query.getFirst("f1"))
-                       .append("f2", query.getFirst("f2"))
-                       .append("f3", query.getFirst("f3"));
+                       .append("f1", query.getString("f1"))
+                       .append("f2", query.getString("f2"))
+                       .append("f3", query.getString("f3"));
        }
 
        @RestMethod(name="GET", path="/annotatedQuery")

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/main/resources/org/apache/juneau/rest/test/HtmlPropertiesResource.properties
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/resources/org/apache/juneau/rest/test/HtmlPropertiesResource.properties
 
b/juneau-rest-test/src/main/resources/org/apache/juneau/rest/test/HtmlPropertiesResource.properties
index a216564..a35bfe7 100644
--- 
a/juneau-rest-test/src/main/resources/org/apache/juneau/rest/test/HtmlPropertiesResource.properties
+++ 
b/juneau-rest-test/src/main/resources/org/apache/juneau/rest/test/HtmlPropertiesResource.properties
@@ -14,24 +14,18 @@
 
 LocalizedExplicit.pageTitle = LocalizedExplicit.nls.pageTitle
 LocalizedExplicit.pageText = LocalizedExplicit.nls.pageText
-LocalizedExplicit.pageLinks = {link:'LocalizedExplicit.nls.pageLinks'}
 
 LocalizedExplicit.test2.pageTitle = LocalizedExplicit.test2.nls.pageTitle
 LocalizedExplicit.test2.pageText = LocalizedExplicit.test2.nls.pageText
-LocalizedExplicit.test2.pageLinks = 
{link:'LocalizedExplicit.test2.nls.pageLinks'}
 
 LocalizedExplicit.test3.pageTitle = LocalizedExplicit.test3.nls.pageTitle
 LocalizedExplicit.test3.pageText = LocalizedExplicit.test3.nls.pageText
-LocalizedExplicit.test3.pageLinks = 
{link:'LocalizedExplicit.test3.nls.pageLinks'}
 
 LocalizedExplicit.test4.pageTitle = LocalizedExplicit.test4.nls.pageTitle
 LocalizedExplicit.test4.pageText = LocalizedExplicit.test4.nls.pageText
-LocalizedExplicit.test4.pageLinks = 
{link:'LocalizedExplicit.test4.nls.pageLinks'}
 
 LocalizedImplicit.pageTitle = LocalizedImplicit.nls.pageTitle
 LocalizedImplicit.pageText = LocalizedImplicit.nls.pageText
-LocalizedImplicit.pageLinks = {link:'LocalizedImplicit.nls.pageLinks'}
 
 LocalizedImplicit.test2.pageTitle = LocalizedImplicit.test2.nls.pageTitle
 LocalizedImplicit.test2.pageText = LocalizedImplicit.test2.nls.pageText
-LocalizedImplicit.test2.pageLinks = 
{link:'LocalizedImplicit.test2.nls.pageLinks'}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/HtmlPropertiesTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/HtmlPropertiesTest.java
 
b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/HtmlPropertiesTest.java
index acf2b8c..a9df966 100644
--- 
a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/HtmlPropertiesTest.java
+++ 
b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/HtmlPropertiesTest.java
@@ -31,7 +31,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/Normal/test1").accept("text/html").getResponseAsString();
                assertTrue(s.contains("Normal-title"));
                assertTrue(s.contains("Normal-text"));
-               assertTrue(s.contains("Normal-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -43,7 +42,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/Normal/test2").accept("text/html").getResponseAsString();
                assertTrue(s.contains("Normal.test2-title"));
                assertTrue(s.contains("Normal.test2-text"));
-               assertTrue(s.contains("Normal.test2-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -55,7 +53,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/Normal/test3").accept("text/html").getResponseAsString();
                assertTrue(s.contains("Normal.test3-title"));
                assertTrue(s.contains("Normal.test3-text"));
-               assertTrue(s.contains("Normal.test3-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -67,7 +64,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/Normal/test4").accept("text/html").getResponseAsString();
                assertTrue(s.contains("Normal.test4-title"));
                assertTrue(s.contains("Normal.test4-text"));
-               assertTrue(s.contains("Normal.test4-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -79,7 +75,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/NormalInit/test1").accept("text/html").getResponseAsString();
                assertTrue(s.contains("NormalInit-title"));
                assertTrue(s.contains("NormalInit-text"));
-               assertTrue(s.contains("NormalInit-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -91,7 +86,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/NormalInit/test2").accept("text/html").getResponseAsString();
                assertTrue(s.contains("NormalInit.test2-title"));
                assertTrue(s.contains("NormalInit.test2-text"));
-               assertTrue(s.contains("NormalInit.test2-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -103,7 +97,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/NormalInit/test3").accept("text/html").getResponseAsString();
                assertTrue(s.contains("NormalInit.test3-title"));
                assertTrue(s.contains("NormalInit.test3-text"));
-               assertTrue(s.contains("NormalInit.test3-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -115,7 +108,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/NormalInit/test4").accept("text/html").getResponseAsString();
                assertTrue(s.contains("NormalInit.test4-title"));
                assertTrue(s.contains("NormalInit.test4-text"));
-               assertTrue(s.contains("NormalInit.test4-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -171,7 +163,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/NormalSubclassed1/test1").accept("text/html").getResponseAsString();
                assertTrue(s.contains("Normal-title"));
                assertTrue(s.contains("Normal-text"));
-               assertTrue(s.contains("Normal-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -183,7 +174,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/NormalSubclassed1/test2").accept("text/html").getResponseAsString();
                assertTrue(s.contains("NormalSubclassed1.test2-title"));
                assertTrue(s.contains("NormalSubclassed1.test2-text"));
-               assertTrue(s.contains("NormalSubclassed1.test2-links"));
 
        }
 
@@ -196,7 +186,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/NormalSubclassed2/test1").accept("text/html").getResponseAsString();
                assertTrue(s.contains("NormalSubclassed2-title"));
                assertTrue(s.contains("NormalSubclassed2-text"));
-               assertTrue(s.contains("NormalSubclassed2-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -208,7 +197,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/NormalSubclassed2/test2").accept("text/html").getResponseAsString();
                assertTrue(s.contains("NormalSubclassed2.test2-title"));
                assertTrue(s.contains("NormalSubclassed2.test2-text"));
-               assertTrue(s.contains("NormalSubclassed2.test2-links"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -220,7 +208,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/LocalizedExplicit/test1").accept("text/html").getResponseAsString();
                assertTrue(s.contains("LocalizedExplicit.nls.pageTitle"));
                assertTrue(s.contains("LocalizedExplicit.nls.pageText"));
-               assertTrue(s.contains("LocalizedExplicit.nls.pageLinks"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -232,7 +219,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/LocalizedExplicit/test2").accept("text/html").getResponseAsString();
                assertTrue(s.contains("LocalizedExplicit.test2.nls.pageTitle"));
                assertTrue(s.contains("LocalizedExplicit.test2.nls.pageText"));
-               assertTrue(s.contains("LocalizedExplicit.test2.nls.pageLinks"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -244,7 +230,6 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/LocalizedExplicit/test3").accept("text/html").getResponseAsString();
                assertTrue(s.contains("LocalizedExplicit.test3.nls.pageTitle"));
                assertTrue(s.contains("LocalizedExplicit.test3.nls.pageText"));
-               assertTrue(s.contains("LocalizedExplicit.test3.nls.pageLinks"));
        }
 
        
//----------------------------------------------------------------------------------------------------
@@ -256,6 +241,5 @@ public class HtmlPropertiesTest extends RestTestcase {
                String s = 
client.doGet("/testHtmlProperties/LocalizedExplicit/test4").accept("text/html").getResponseAsString();
                assertTrue(s.contains("LocalizedExplicit.test4.nls.pageTitle"));
                assertTrue(s.contains("LocalizedExplicit.test4.nls.pageText"));
-               assertTrue(s.contains("LocalizedExplicit.test4.nls.pageLinks"));
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestMicroservice.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestMicroservice.java
 
b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestMicroservice.java
index a2f0777..725abce 100644
--- 
a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestMicroservice.java
+++ 
b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestMicroservice.java
@@ -36,7 +36,7 @@ public class TestMicroservice {
        static URI microserviceURI;
 
        // Reusable HTTP clients that get created and shut down with the 
microservice.
-       public static RestClient DEFAULT_CLIENT;
+       public static RestClient DEFAULT_CLIENT, DEFAULT_CLIENT_DEBUG;
        public static RestClient DEFAULT_CLIENT_PLAINTEXT;
 
        /**
@@ -56,6 +56,7 @@ public class TestMicroservice {
                                );
                        microserviceURI = microservice.start().getURI();
                        DEFAULT_CLIENT = client().build();
+                       DEFAULT_CLIENT_DEBUG = client().debug().build();
                        DEFAULT_CLIENT_PLAINTEXT = 
client(PlainTextSerializer.class, PlainTextParser.class).build();
                        return true;
                } catch (Throwable e) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
index bcb00db..2563442 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
@@ -67,6 +67,7 @@ class CallMethod implements Comparable<CallMethod>  {
        private final org.apache.juneau.rest.annotation.Parameter[] parameters;
        private final Response[] responses;
        private final RestContext context;
+       private final BeanContext beanContext;
        private final String htmlTitle, htmlDescription, htmlBranding, 
htmlHeader, htmlLinks, htmlNav, htmlAside,
                htmlFooter, htmlCss, htmlCssUrl, htmlNoResultsMessage;
        private final boolean htmlNoWrap;
@@ -89,6 +90,7 @@ class CallMethod implements Comparable<CallMethod>  {
                this.encoders = b.encoders;
                this.urlEncodingParser = b.urlEncodingParser;
                this.urlEncodingSerializer = b.urlEncodingSerializer;
+               this.beanContext = b.beanContext;
                this.properties = b.properties;
                this.defaultRequestHeaders = b.defaultRequestHeaders;
                this.defaultQuery = b.defaultQuery;
@@ -133,6 +135,7 @@ class CallMethod implements Comparable<CallMethod>  {
                private EncoderGroup encoders;
                private UrlEncodingParser urlEncodingParser;
                private UrlEncodingSerializer urlEncodingSerializer;
+               private BeanContext beanContext;
                private ObjectMap properties;
                private Map<String,String> defaultRequestHeaders, defaultQuery, 
defaultFormData;
                private boolean plainParams, deprecated;
@@ -165,6 +168,7 @@ class CallMethod implements Comparable<CallMethod>  {
                                parsers = context.getParsers();
                                urlEncodingSerializer = 
context.getUrlEncodingSerializer();
                                urlEncodingParser = 
context.getUrlEncodingParser();
+                               beanContext = context.getBeanContext();
                                encoders = context.getEncoders();
                                properties = context.getProperties();
                                widgets = new 
HashMap<String,Widget>(context.getWidgets());
@@ -358,8 +362,10 @@ class CallMethod implements Comparable<CallMethod>  {
 
                                params = context.findParams(method, 
plainParams, pathPattern);
 
-                               if (sgb != null)
+                               if (sgb != null) {
                                        serializers = sgb.build();
+                                       beanContext = 
serializers.getBeanContext();
+                               }
                                if (pgb != null)
                                        parsers = pgb.build();
                                if (uepb != null)
@@ -746,7 +752,7 @@ class CallMethod implements Comparable<CallMethod>  {
 
                ObjectMap requestProperties = 
createRequestProperties(properties, req);
                req.init(method, requestProperties, defaultRequestHeaders, 
defaultQuery, defaultFormData, defaultCharset,
-                       serializers, parsers, urlEncodingParser, encoders, 
widgets);
+                       serializers, parsers, urlEncodingParser, beanContext, 
encoders, widgets);
                res.init(requestProperties, defaultCharset, serializers, 
urlEncodingSerializer, encoders);
 
                // Class-level guards
@@ -798,7 +804,7 @@ class CallMethod implements Comparable<CallMethod>  {
                        if (res.hasOutput()) {
                                output = res.getOutput();
                                for (RestConverter converter : converters)
-                                       output = converter.convert(req, output, 
context.getBeanContext().getClassMetaForObject(output));
+                                       output = converter.convert(req, output, 
beanContext.getClassMetaForObject(output));
                                res.setOutput(output);
                        }
                } catch (IllegalArgumentException e) {
@@ -883,8 +889,18 @@ class CallMethod implements Comparable<CallMethod>  {
                                                return htmlHeader == null ? 
null : req.resolveVars(htmlHeader);
                                        if (k.equals(HTMLDOC_branding))
                                                return htmlBranding == null ? 
null : req.resolveVars(htmlBranding);
-                                       if (k.equals(HTMLDOC_links))
-                                               return htmlLinks == null ? null 
: req.resolveVars(htmlLinks);
+                                       if (k.equals(HTMLDOC_links)) {
+                                               if (htmlLinks == null)
+                                                       return null;
+                                               try {
+                                                       ObjectMap m = new 
ObjectMap(htmlLinks), m2 = new ObjectMap();
+                                                       for 
(Map.Entry<String,Object> e : m.entrySet())
+                                                               
m2.put(req.resolveVars(e.getKey()), 
req.resolveVars(StringUtils.toString(e.getValue())));
+                                                       return m2;
+                                               } catch (ParseException e) {
+                                                       throw new 
RuntimeException(e);
+                                               }
+                                       }
                                        if (k.equals(HTMLDOC_nav))
                                                return htmlNav == null ? null : 
req.resolveVars(htmlNav);
                                        if (k.equals(HTMLDOC_aside))

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java
index 5a2ed4d..7de7cdb 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java
@@ -94,7 +94,7 @@ public class RequestFormData extends 
LinkedHashMap<String,String[]> {
         * @param name The form data parameter name.
         * @return The parameter value, or <jk>null</jk> if parameter does not 
exist.
         */
-       public String getFirst(String name) {
+       public String getString(String name) {
                String[] v = get(name);
                if (v == null || v.length == 0)
                        return null;
@@ -109,18 +109,62 @@ public class RequestFormData extends 
LinkedHashMap<String,String[]> {
        }
 
        /**
-        * Same as {@link #getFirst(String)} except returns a default value if 
<jk>null</jk> or empty.
+        * Same as {@link #getString(String)} except returns a default value if 
<jk>null</jk> or empty.
         *
         * @param name The form data parameter name.
         * @param def The default value.
-        * @return The parameter value, or the default value if <jk>null</jk> 
or empty.
+        * @return The parameter value, or the default value if parameter does 
not exist or is <jk>null</jk> or empty.
         */
-       public String getFirst(String name, String def) {
-               String s = getFirst(name);
+       public String getString(String name, String def) {
+               String s = getString(name);
                return StringUtils.isEmpty(s) ? def : s;
        }
 
        /**
+        * Same as {@link #getString(String)} but converts the value to an 
integer.
+        *
+        * @param name The form data parameter name.
+        * @return The parameter value, or <code>0</code> if parameter does not 
exist or is <jk>null</jk> or empty.
+        */
+       public int getInt(String name) {
+               return getInt(name, 0);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
an integer.
+        *
+        * @param name The form data parameter name.
+        * @param def The default value.
+        * @return The parameter value, or the default value if parameter does 
not exist or is <jk>null</jk> or empty.
+        */
+       public int getInt(String name, int def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to a 
boolean.
+        *
+        * @param name The form data parameter name.
+        * @return The parameter value, or <jk>false</jk> if parameter does not 
exist or is <jk>null</jk> or empty.
+        */
+       public boolean getBoolean(String name) {
+               return getBoolean(name, false);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
a boolean.
+        *
+        * @param name The form data parameter name.
+        * @param def The default value.
+        * @return The parameter value, or the default value if parameter does 
not exist or is <jk>null</jk> or empty.
+        */
+       public boolean getBoolean(String name, boolean def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
+       }
+
+       /**
         * Returns the specified form data parameter value converted to a POJO 
using the
         *      {@link UrlEncodingParser} registered with this servlet.
         * <p>
@@ -245,7 +289,7 @@ public class RequestFormData extends 
LinkedHashMap<String,String[]> {
 
        /* Workhorse method */
        <T> T parse(String name, T def, ClassMeta<T> cm) throws ParseException {
-               String val = getFirst(name);
+               String val = getString(name);
                if (val == null)
                        return def;
                return parseValue(val, cm);
@@ -253,7 +297,7 @@ public class RequestFormData extends 
LinkedHashMap<String,String[]> {
 
        /* Workhorse method */
        <T> T parse(String name, ClassMeta<T> cm) throws ParseException {
-               String val = getFirst(name);
+               String val = getString(name);
                if (cm.isPrimitive() && (val == null || val.isEmpty()))
                        return cm.getPrimitiveDefault();
                return parseValue(val, cm);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java
index 87fbb23..ea2d9de 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java
@@ -104,7 +104,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @param name The header name.
         * @return The header value, or <jk>null</jk> if it doesn't exist.
         */
-       public String getFirst(String name) {
+       public String getString(String name) {
                String[] v = null;
                if (queryParams != null)
                        v = queryParams.get(name);
@@ -124,12 +124,56 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @param def The default value to return if the header value isn't 
found.
         * @return The header value, or the default value if the header isn't 
present.
         */
-       public String getFirst(String name, String def) {
-               String s = getFirst(name);
+       public String getString(String name, String def) {
+               String s = getString(name);
                return StringUtils.isEmpty(s) ? def : s;
        }
 
        /**
+        * Same as {@link #getString(String)} but converts the value to an 
integer.
+        *
+        * @param name The HTTP header name.
+        * @return The header value, or the default value if the header isn't 
present.
+        */
+       public int getInt(String name) {
+               return getInt(name, 0);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
an integer.
+        *
+        * @param name The HTTP header name.
+        * @param def The default value to return if the header value isn't 
found.
+        * @return The header value, or the default value if the header isn't 
present.
+        */
+       public int getInt(String name, int def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to a 
boolean.
+        *
+        * @param name The HTTP header name.
+        * @return The header value, or the default value if the header isn't 
present.
+        */
+       public boolean getBoolean(String name) {
+               return getBoolean(name, false);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
a boolean.
+        *
+        * @param name The HTTP header name.
+        * @param def The default value to return if the header value isn't 
found.
+        * @return The header value, or the default value if the header isn't 
present.
+        */
+       public boolean getBoolean(String name, boolean def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
+       }
+
+       /**
         * Sets a request header value.
         *
         * @param name The header name.
@@ -160,7 +204,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parameter value converted to the specified class type.
         */
        public <T> T get(String name, Class<T> type) {
-               String h = getFirst(name);
+               String h = getString(name);
                return beanSession.convertToType(h, type);
        }
 
@@ -174,7 +218,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parameter value converted to the specified class type.
         */
        public <T> T get(String name, T def, Class<T> type) {
-               String h = getFirst(name);
+               String h = getString(name);
                if (h == null)
                        return def;
                return beanSession.convertToType(h, type);
@@ -204,7 +248,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         */
        @SuppressWarnings("unchecked")
        public <T> T get(String name, Type type, Type...args) throws 
ParseException {
-               String h = getFirst(name);
+               String h = getString(name);
                return (T)parser.parse(PartType.HEADER, h, 
beanSession.getClassMeta(type, args));
        }
 
@@ -244,7 +288,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         *
         */
        public Accept getAccept() {
-               return Accept.forString(getFirst("Accept"));
+               return Accept.forString(getString("Accept"));
        }
 
        /**
@@ -260,7 +304,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Accept-Charset</code> header on the 
request, or <jk>null</jk> if not found.
         */
        public AcceptCharset getAcceptCharset() {
-               return AcceptCharset.forString(getFirst("Accept-Charset"));
+               return AcceptCharset.forString(getString("Accept-Charset"));
        }
 
        /**
@@ -276,7 +320,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Accept-Encoding</code> header on the 
request, or <jk>null</jk> if not found.
         */
        public AcceptEncoding getAcceptEncoding() {
-               return AcceptEncoding.forString(getFirst("Accept-Encoding"));
+               return AcceptEncoding.forString(getString("Accept-Encoding"));
        }
 
        /**
@@ -292,7 +336,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Accept-Language</code> header on the 
request, or <jk>null</jk> if not found.
         */
        public AcceptLanguage getAcceptLanguage() {
-               return AcceptLanguage.forString(getFirst("Accept-Language"));
+               return AcceptLanguage.forString(getString("Accept-Language"));
        }
 
        /**
@@ -308,7 +352,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Authorization</code> header on the request, 
or <jk>null</jk> if not found.
         */
        public Authorization getAuthorization() {
-               return Authorization.forString(getFirst("Authorization"));
+               return Authorization.forString(getString("Authorization"));
        }
 
        /**
@@ -324,7 +368,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Cache-Control</code> header on the request, 
or <jk>null</jk> if not found.
         */
        public CacheControl getCacheControl() {
-               return CacheControl.forString(getFirst("Cache-Control"));
+               return CacheControl.forString(getString("Cache-Control"));
        }
 
        /**
@@ -341,7 +385,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code></code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Connection getConnection() {
-               return Connection.forString(getFirst("Connection"));
+               return Connection.forString(getString("Connection"));
        }
 
        /**
@@ -357,7 +401,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Content-Length</code> header on the 
request, or <jk>null</jk> if not found.
         */
        public ContentLength getContentLength() {
-               return ContentLength.forString(getFirst("Content-Length"));
+               return ContentLength.forString(getString("Content-Length"));
        }
 
        /**
@@ -373,7 +417,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Content-Type</code> header on the request, 
or <jk>null</jk> if not found.
         */
        public ContentType getContentType() {
-               return ContentType.forString(getFirst("Content-Type"));
+               return ContentType.forString(getString("Content-Type"));
        }
 
        /**
@@ -389,7 +433,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Date</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Date getDate() {
-               return Date.forString(getFirst("Date"));
+               return Date.forString(getString("Date"));
        }
 
        /**
@@ -405,7 +449,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Expect</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Expect getExpect() {
-               return Expect.forString(getFirst("Expect"));
+               return Expect.forString(getString("Expect"));
        }
 
        /**
@@ -421,7 +465,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>From</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public From getFrom() {
-               return From.forString(getFirst("From"));
+               return From.forString(getString("From"));
        }
 
        /**
@@ -439,7 +483,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Host</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Host getHost() {
-               return Host.forString(getFirst("Host"));
+               return Host.forString(getString("Host"));
        }
 
        /**
@@ -456,7 +500,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>If-Match</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public IfMatch getIfMatch() {
-               return IfMatch.forString(getFirst("If-Match"));
+               return IfMatch.forString(getString("If-Match"));
        }
 
        /**
@@ -472,7 +516,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>If-Modified-Since</code> header on the 
request, or <jk>null</jk> if not found.
         */
        public IfModifiedSince getIfModifiedSince() {
-               return IfModifiedSince.forString(getFirst("If-Modified-Since"));
+               return 
IfModifiedSince.forString(getString("If-Modified-Since"));
        }
 
        /**
@@ -488,7 +532,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>If-None-Match</code> header on the request, 
or <jk>null</jk> if not found.
         */
        public IfNoneMatch getIfNoneMatch() {
-               return IfNoneMatch.forString(getFirst("If-None-Match"));
+               return IfNoneMatch.forString(getString("If-None-Match"));
        }
 
        /**
@@ -504,7 +548,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>If-Range</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public IfRange getIfRange() {
-               return IfRange.forString(getFirst("If-Range"));
+               return IfRange.forString(getString("If-Range"));
        }
 
        /**
@@ -520,7 +564,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>If-Unmodified-Since</code> header on the 
request, or <jk>null</jk> if not found.
         */
        public IfUnmodifiedSince getIfUnmodifiedSince() {
-               return 
IfUnmodifiedSince.forString(getFirst("If-Unmodified-Since"));
+               return 
IfUnmodifiedSince.forString(getString("If-Unmodified-Since"));
        }
 
        /**
@@ -536,7 +580,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Max-Forwards</code> header on the request, 
or <jk>null</jk> if not found.
         */
        public MaxForwards getMaxForwards() {
-               return MaxForwards.forString(getFirst("Max-Forwards"));
+               return MaxForwards.forString(getString("Max-Forwards"));
        }
 
        /**
@@ -552,7 +596,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Pragma</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Pragma getPragma() {
-               return Pragma.forString(getFirst("Pragma"));
+               return Pragma.forString(getString("Pragma"));
        }
 
        /**
@@ -568,7 +612,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Proxy-Authorization</code> header on the 
request, or <jk>null</jk> if not found.
         */
        public ProxyAuthorization getProxyAuthorization() {
-               return 
ProxyAuthorization.forString(getFirst("Proxy-Authorization"));
+               return 
ProxyAuthorization.forString(getString("Proxy-Authorization"));
        }
 
        /**
@@ -584,7 +628,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Range</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Range getRange() {
-               return Range.forString(getFirst("Range"));
+               return Range.forString(getString("Range"));
        }
 
        /**
@@ -600,7 +644,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Referer</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Referer getReferer() {
-               return Referer.forString(getFirst("Referer"));
+               return Referer.forString(getString("Referer"));
        }
 
        /**
@@ -618,7 +662,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>TE</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public TE getTE() {
-               return TE.forString(getFirst("TE"));
+               return TE.forString(getString("TE"));
        }
 
        /**
@@ -629,7 +673,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The <code>Time-Zone</code> header value on the request, or 
<jk>null</jk> if not present.
         */
        public TimeZone getTimeZone() {
-               String tz = getFirst("Time-Zone");
+               String tz = getString("Time-Zone");
                if (tz != null)
                        return TimeZone.getTimeZone(tz);
                return null;
@@ -648,7 +692,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>User-Agent</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public UserAgent getUserAgent() {
-               return UserAgent.forString(getFirst("User-Agent"));
+               return UserAgent.forString(getString("User-Agent"));
        }
 
        /**
@@ -664,7 +708,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Upgrade</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Upgrade getUpgrade() {
-               return Upgrade.forString(getFirst("Upgrade"));
+               return Upgrade.forString(getString("Upgrade"));
        }
 
        /**
@@ -680,7 +724,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Via</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Via getVia() {
-               return Via.forString(getFirst("Via"));
+               return Via.forString(getString("Via"));
        }
 
        /**
@@ -696,7 +740,7 @@ public class RequestHeaders extends 
TreeMap<String,String[]> {
         * @return The parsed <code>Warning</code> header on the request, or 
<jk>null</jk> if not found.
         */
        public Warning getWarning() {
-               return Warning.forString(getFirst("Warning"));
+               return Warning.forString(getString("Warning"));
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java
index e97169e..d7d6cfe 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java
@@ -24,6 +24,7 @@ import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.urlencoding.*;
+import org.apache.juneau.utils.*;
 
 /**
  * Represents the query parameters in an HTTP request.
@@ -81,11 +82,13 @@ public final class RequestQuery extends 
LinkedHashMap<String,String[]> {
         * Same as {@link HttpServletRequest#getParameter(String)} except only 
looks in the URL string, not parameters from URL-Encoded FORM posts.
         * <p>
         * This method can be used to retrieve a parameter without triggering 
the underlying servlet API to load and parse the request body.
+        * <p>
+        * If multiple query parameters have the same name, this returns only 
the first instance.
         *
         * @param name The URL parameter name.
         * @return The parameter value, or <jk>null</jk> if parameter not 
specified or has no value (e.g. <js>"&amp;foo"</js>.
         */
-       public String getFirst(String name) {
+       public String getString(String name) {
                String[] v = get(name);
                if (v == null || v.length == 0)
                        return null;
@@ -100,18 +103,62 @@ public final class RequestQuery extends 
LinkedHashMap<String,String[]> {
        }
 
        /**
-        * Same as {@link #getFirst(String)} but returns the specified default 
value if the query parameter was not specified.
+        * Same as {@link #getString(String)} but returns the specified default 
value if the query parameter was not specified.
         *
         * @param name The URL parameter name.
         * @param def The default value.
         * @return The parameter value, or the default value if parameter not 
specified or has no value (e.g. <js>"&amp;foo"</js>.
         */
-       public String getFirst(String name, String def) {
-               String s = getFirst(name);
+       public String getString(String name, String def) {
+               String s = getString(name);
                return StringUtils.isEmpty(s) ? def : s;
        }
 
        /**
+        * Same as {@link #getString(String)} but converts the value to an 
integer.
+        *
+        * @param name The URL parameter name.
+        * @return The parameter value, or <code>0</code> if parameter not 
specified or has no value (e.g. <js>"&amp;foo"</js>.
+        */
+       public int getInt(String name) {
+               return getInt(name, 0);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
an integer.
+        *
+        * @param name The URL parameter name.
+        * @param def The default value.
+        * @return The parameter value, or the default value if parameter not 
specified or has no value (e.g. <js>"&amp;foo"</js>.
+        */
+       public int getInt(String name, int def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to a 
boolean.
+        *
+        * @param name The URL parameter name.
+        * @return The parameter value, or <jk>false</jk> if parameter not 
specified or has no value (e.g. <js>"&amp;foo"</js>.
+        */
+       public boolean getBoolean(String name) {
+               return getBoolean(name, false);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
a boolean.
+        *
+        * @param name The URL parameter name.
+        * @param def The default value.
+        * @return The parameter value, or the default value if parameter not 
specified or has no value (e.g. <js>"&amp;foo"</js>.
+        */
+       public boolean getBoolean(String name, boolean def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
+       }
+
+       /**
         * Returns the specified query parameter value converted to a POJO.
         * <p>
         * This method can be used to retrieve a parameter without triggering 
the underlying servlet API to load and parse the request body.
@@ -261,9 +308,62 @@ public final class RequestQuery extends 
LinkedHashMap<String,String[]> {
                return false;
        }
 
+       /**
+        * Locates the special search query arguments in the query and returns 
them as a {@link SearchArgs} object.
+        * <p>
+        * The query arguments are as follows:
+        * <ul>
+        *      <li><js>"&s="</js> - A comma-delimited list of 
column-name/search-token pairs.
+        *              <br>Example: <js>"&s=column1=foo*,column2=*bar"</js>
+        *      <li><js>"&v="</js> - A comma-delimited list column names to 
view.
+        *              <br>Example: <js>"&v=column1,column2"</js>
+        *      <li><js>"&o="</js> - A comma-delimited list column names to 
sort by.
+        *              <br>Column names can be suffixed with <js>'-'</js> to 
indicate descending order.
+        *              <br>Example: <js>"&o=column1,column2-"</js>
+        *      <li><js>"&p="</js> - The zero-index row number of the first row 
to display.
+        *              <br>Example: <js>"&p=100"</js>
+        *      <li><js>"&l="</js> - The number of rows to return.
+        *              <br><code>0</code> implies return all rows.
+        *              <br>Example: <js>"&l=100"</js>
+        *      <li><js>"&i="</js> - The case-insensitive search flag.
+        *              <br>Example: <js>"&i=true"</js>
+        * </ul>
+        * <p>
+        * Whitespace is trimmed in the parameters.
+        *
+        * @return A new {@link SearchArgs} object initialized with the special 
search query arguments.
+        *      <jk>null</jk> if no search arguments were found.
+        */
+       public SearchArgs getSearchArgs() {
+               if (hasAny("s","v","o","p","l","i")) {
+                       return new SearchArgs.Builder()
+                               .search(getString("s"))
+                               .view(getString("v"))
+                               .sort(getString("o"))
+                               .position(getInt("p"))
+                               .limit(getInt("l"))
+                               .ignoreCase(getBoolean("i"))
+                               .build();
+               }
+               return null;
+       }
+
+       /**
+        * Returns <jk>true</jk> if the query parameters contains any of the 
specified names.
+        *
+        * @param paramNames The parameter names to check for.
+        * @return <jk>true</jk> if the query parameters contains any of the 
specified names.
+        */
+       public boolean hasAny(String...paramNames) {
+               for (String p : paramNames)
+                       if (containsKey(p))
+                               return true;
+               return false;
+       }
+
        /* Workhorse method */
        private <T> T parse(String name, T def, ClassMeta<T> cm) throws 
ParseException {
-               String val = getFirst(name);
+               String val = getString(name);
                if (val == null)
                        return def;
                return parseValue(val, cm);
@@ -271,7 +371,7 @@ public final class RequestQuery extends 
LinkedHashMap<String,String[]> {
 
        /* Workhorse method */
        private <T> T parse(String name, ClassMeta<T> cm) throws ParseException 
{
-               String val = getFirst(name);
+               String val = getString(name);
                if (cm.isPrimitive() && (val == null || val.isEmpty()))
                        return cm.getPrimitiveDefault();
                return parseValue(val, cm);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index 03f4239..cc0c5b3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -600,7 +600,7 @@ class RestParamDefaults {
                        if (multiPart)
                                return req.getFormData().getAll(name, type);
                        if (plainParams)
-                               return 
bs.convertToType(req.getFormData().getFirst(name), bs.getClassMeta(type));
+                               return 
bs.convertToType(req.getFormData().getString(name), bs.getClassMeta(type));
                        return req.getFormData().get(name, type);
                }
        }
@@ -622,7 +622,7 @@ class RestParamDefaults {
                        if (multiPart)
                                return req.getQuery().getAll(name, type);
                        if (plainParams)
-                               return 
bs.convertToType(req.getQuery().getFirst(name), bs.getClassMeta(type));
+                               return 
bs.convertToType(req.getQuery().getString(name), bs.getClassMeta(type));
                        return req.getQuery().get(name, type);
                }
        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
index 755109f..faf062d 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -110,7 +110,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                        // Can be overridden through a "method" GET attribute.
                        String _method = super.getMethod();
 
-                       String m = getQuery().getFirst("method");
+                       String m = getQuery().getString("method");
                        if (context.allowMethodParam(m))
                                _method = m;
 
@@ -125,7 +125,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                        body = new RequestBody(this);
 
                        if (context.isAllowBodyParam()) {
-                               String b = getQuery().getFirst("body");
+                               String b = getQuery().getString("body");
                                if (b != null) {
                                        headers.put("Content-Type", 
UonSerializer.DEFAULT.getResponseContentType());
                                        body.load(b.getBytes(UTF8));
@@ -135,7 +135,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                        if (context.isAllowHeaderParams())
                                headers.setQueryParams(queryParams);
 
-                       debug = "true".equals(getQuery().getFirst("debug", 
"false")) || "true".equals(getHeaders().getFirst("Debug", "false"));
+                       debug = "true".equals(getQuery().getString("debug", 
"false")) || "true".equals(getHeaders().getString("Debug", "false"));
 
                        this.pathParams = new RequestPathMatch();
 
@@ -153,11 +153,11 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        final void init(Method javaMethod, ObjectMap properties, 
Map<String,String> defHeader,
                        Map<String,String> defQuery, Map<String,String> 
defFormData, String defaultCharset,
                        SerializerGroup mSerializers, ParserGroup mParsers, 
UrlEncodingParser mUrlEncodingParser,
-                       EncoderGroup encoders, Map<String,Widget> widgets) {
+                       BeanContext beanContext, EncoderGroup encoders, 
Map<String,Widget> widgets) {
                this.javaMethod = javaMethod;
                this.properties = properties;
                this.urlEncodingParser = mUrlEncodingParser;
-               this.beanSession = 
urlEncodingParser.getBeanContext().createSession();
+               this.beanSession = beanContext.createSession();
                this.pathParams
                        .setParser(urlEncodingParser)
                        .setBeanSession(beanSession);
@@ -253,7 +253,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
 
        @Override /* ServletRequest */
        public String getHeader(String name) {
-               return getHeaders().getFirst(name);
+               return getHeaders().getString(name);
        }
 
        @Override /* ServletRequest */
@@ -306,7 +306,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
 
        @Override /* ServletRequest */
        public Locale getLocale() {
-               String h = headers.getFirst("Accept-Language");
+               String h = headers.getString("Accept-Language");
                if (h != null) {
                        MediaTypeRange[] mr = MediaTypeRange.parse(h);
                        if (mr.length > 0)
@@ -317,7 +317,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
 
        @Override /* ServletRequest */
        public Enumeration<Locale> getLocales() {
-               String h = headers.getFirst("Accept-Language");
+               String h = headers.getString("Accept-Language");
                if (h != null) {
                        MediaTypeRange[] mr = MediaTypeRange.parse(h);
                        if (mr.length > 0) {
@@ -349,12 +349,12 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        }
 
        /**
-        * Shortcut for calling <code>getQuery().getFirst(name)</code>.
+        * Shortcut for calling <code>getQuery().getString(name)</code>.
         * @param name The query parameter name.
         * @return The query parameter value, or <jk>null<jk> if not found.
         */
        public String getQuery(String name) {
-               return getQuery().getFirst(name);
+               return getQuery().getString(name);
        }
 
 
@@ -389,12 +389,12 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        }
 
        /**
-        * Shortcut for calling <code>getFormData().getFirst(name)</code>.
+        * Shortcut for calling <code>getFormData().getString(name)</code>.
         * @param name The form data parameter name.
         * @return The form data parameter value, or <jk>null<jk> if not found.
         */
        public String getFormData(String name) {
-               return getFormData().getFirst(name);
+               return getFormData().getString(name);
        }
 
 
@@ -613,7 +613,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return <jk>true</jk> if {@code &amp;plainText=true} was specified 
as a URL parameter
         */
        public boolean isPlainText() {
-               return "true".equals(getQuery().getFirst("plainText", "false"));
+               return "true".equals(getQuery().getString("plainText", 
"false"));
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/09590092/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
 
b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
index f84368e..6414db0 100644
--- 
a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
+++ 
b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
@@ -40,8 +40,8 @@ public final class Introspectable implements RestConverter {
        @Override /* RestConverter */
        @SuppressWarnings({"unchecked", "rawtypes"})
        public Object convert(RestRequest req, Object o, ClassMeta cm) throws 
RestException {
-               String method = req.getQuery().getFirst("invokeMethod");
-               String args = req.getQuery().getFirst("invokeArgs");
+               String method = req.getQuery().getString("invokeMethod");
+               String args = req.getQuery().getString("invokeArgs");
                if (method == null)
                        return o;
                try {

Reply via email to