This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 6cfc7a1  Improvements to RemoteableServlet.
6cfc7a1 is described below

commit 6cfc7a14788888015f81beef0fac998c0ff866e6
Author: JamesBognar <[email protected]>
AuthorDate: Sat Mar 3 16:49:44 2018 -0500

    Improvements to RemoteableServlet.
---
 .../apache/juneau/config/internal/ConfigEntry.java |   4 +-
 .../apache/juneau/config/internal/ConfigMap.java   |   2 +-
 .../java/org/apache/juneau/utils/CharSetTest.java  |   2 +-
 .../java/org/apache/juneau/internal/AsciiSet.java  |  89 ++++-
 .../org/apache/juneau/internal/StringUtils.java    |  83 ++++-
 .../org/apache/juneau/json/JsonParserSession.java  |   2 +-
 .../java/org/apache/juneau/json/JsonWriter.java    |   8 +-
 .../org/apache/juneau/uon/UonParserSession.java    |   4 +-
 .../main/java/org/apache/juneau/uon/UonUtils.java  |   4 +-
 .../main/java/org/apache/juneau/uon/UonWriter.java |   8 +-
 .../juneau/yaml/proto/YamlParserSession.java       |   2 +-
 .../org/apache/juneau/yaml/proto/YamlWriter.java   |   6 +-
 ...u-rest-server.RemoteableProxiesServerSide.1.png | Bin 15845 -> 238253 bytes
 ...u-rest-server.RemoteableProxiesServerSide.2.png | Bin 20379 -> 154803 bytes
 ...u-rest-server.RemoteableProxiesServerSide.3.png | Bin 33919 -> 193060 bytes
 ...u-rest-server.RemoteableProxiesServerSide.4.png | Bin 21108 -> 96014 bytes
 ...u-rest-server.RemoteableProxiesServerSide.5.png | Bin 10553 -> 62998 bytes
 ...u-rest-server.RemoteableProxiesServerSide.6.png | Bin 24934 -> 191932 bytes
 ...u-rest-server.RemoteableProxiesServerSide.7.png | Bin 0 -> 138344 bytes
 ...u-rest-server.RemoteableProxiesServerSide.8.png | Bin 0 -> 145941 bytes
 ...u-rest-server.RemoteableProxiesServerSide.9.png | Bin 0 -> 143557 bytes
 juneau-doc/src/main/javadoc/overview.html          | 406 ++++++++-------------
 .../juneau/examples/addressbook/AddressBook.java   |  38 +-
 .../examples/rest/SampleRemoteableServlet.java     |  83 ++++-
 .../java/org/apache/juneau/rest/RestRequest.java   |   3 +-
 .../juneau/rest/remoteable/RemoteableServlet.java  |  62 +++-
 26 files changed, 495 insertions(+), 311 deletions(-)

diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
index bee83cb..45c3e06 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigEntry.java
@@ -33,7 +33,7 @@ public class ConfigEntry {
        
        static final ConfigEntry NULL = new ConfigEntry(null, null, null, null, 
null);
        
-       private final static AsciiSet MOD_CHARS = new AsciiSet("#$%&*+^@~");
+       private final static AsciiSet MOD_CHARS = AsciiSet.create("#$%&*+^@~");
 
        ConfigEntry(String line, List<String> preLines) {
                this.rawLine = line;
@@ -164,5 +164,5 @@ public class ConfigEntry {
                return w;
        }
        
-       private static final AsciiSet REPLACE_CHARS = new AsciiSet("\\#");
+       private static final AsciiSet REPLACE_CHARS = AsciiSet.create("\\#");
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
index dd7c69c..b2b3e72 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
@@ -34,7 +34,7 @@ public class ConfigMap implements ConfigStoreListener {
        private volatile String contents;        // The original contents of 
this object.
        private final String name;               // The name  of this object.
 
-       private final static AsciiSet MOD_CHARS = new AsciiSet("#$%&*+^@~");
+       private final static AsciiSet MOD_CHARS = AsciiSet.create("#$%&*+^@~");
        
        // Changes that have been applied since the last load.
        private final List<ConfigEvent> changes = 
Collections.synchronizedList(new ArrayList<ConfigEvent>());
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/CharSetTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/CharSetTest.java
index 41910a8..61abab2 100755
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/CharSetTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/CharSetTest.java
@@ -24,7 +24,7 @@ public class CharSetTest {
        
//====================================================================================================
        @Test
        public void test() throws Exception {
-               AsciiSet cs = new AsciiSet("abc\u1234");
+               AsciiSet cs = AsciiSet.create("abc\u1234");
                assertTrue(cs.contains('a'));
                assertFalse(cs.contains('d'));
                assertFalse(cs.contains('\u1234'));
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
index f5a4717..14484fe 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/AsciiSet.java
@@ -12,26 +12,99 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.internal;
 
+import java.util.Arrays;
+
 /**
  * Stores a set of ASCII characters for quick lookup.
  */
 public final class AsciiSet {
-       final boolean[] store = new boolean[128];
+       private final boolean[] store;
+
+       AsciiSet(boolean[] store) {
+               this.store = Arrays.copyOf(store, store.length);
+       }
 
        /**
-        * Constructor.
+        * Creates an ASCII set with the specified characters.
         * 
         * @param chars The characters to keep in this store.
+        * @return A new object.
         */
-       public AsciiSet(String chars) {
-               for (int i = 0; i < chars.length(); i++) {
-                       char c = chars.charAt(i);
-                       if (c < 128)
-                               store[c] = true;
-               }
+       public static AsciiSet create(String chars) {
+               return new Builder().chars(chars).build();
+       }
+       
+       /**
+        * Creates a builder for an ASCII set.
+        * 
+        * @return A new builder.
+        */
+       public static AsciiSet.Builder create() {
+               return new Builder();
        }
 
        /**
+        * Builder class for {@link AsciiSet} objects.
+        */
+       public static class Builder {
+               final boolean[] store = new boolean[128];
+               
+               /**
+                * Adds a range of characters to this set.
+                * 
+                * @param start The start character.
+                * @param end The end character.
+                * @return This object (for method chaining).
+                */
+               public AsciiSet.Builder range(char start, char end) {
+                       for (char c = start; c <= end; c++)
+                               if (c < 128)
+                                       store[c] = true;
+                       return this;
+               }
+               
+               /**
+                * Shortcut for calling multiple ranges.
+                * 
+                * @param s Strings of the form "A-Z" where A and Z represent 
the first and last characters in the range.
+                * @return This object (for method chaining).
+                */
+               public AsciiSet.Builder ranges(String...s) {
+                       for (String ss : s) {
+                               if (ss.length() != 3 || ss.charAt(1) != '-')
+                                       throw new RuntimeException("Value 
passed to ranges() must be 3 characters");
+                               range(ss.charAt(0), ss.charAt(2));
+                       }
+                       return this;
+               }
+               
+               /**
+                * Adds a set of characters to this set.
+                * 
+                * @param chars The characters to keep in this store.
+                * @return This object (for method chaining).
+                */
+               public AsciiSet.Builder chars(String chars) {
+                       for (int i = 0; i < chars.length(); i++) {
+                               char c = chars.charAt(i);
+                               if (c < 128)
+                                       store[c] = true;
+                       }
+                       return this;
+               }
+               
+               /**
+                * Create a new {@link AsciiSet} object with the contents of 
this builder.
+                * 
+                * @return A new {link AsciiSet} object.
+                */
+               public AsciiSet build() {
+                       return new AsciiSet(store);
+               }
+       }
+       
+       
+       /**
         * Returns <jk>true</jk> if the specified character is in this store.
         * 
         * @param c The character to check.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index 9f2d0bc..4cc55fc 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -37,17 +37,17 @@ import org.apache.juneau.parser.ParseException;
  */
 public final class StringUtils {
 
-       private static final AsciiSet numberChars = new 
AsciiSet("-xX.+-#pP0123456789abcdefABCDEF");
-       private static final AsciiSet firstNumberChars = new 
AsciiSet("+-.#0123456789");
-       private static final AsciiSet octChars = new AsciiSet("01234567");
-       private static final AsciiSet decChars = new AsciiSet("0123456789");
-       private static final AsciiSet hexChars = new 
AsciiSet("0123456789abcdefABCDEF");
+       private static final AsciiSet numberChars = 
AsciiSet.create("-xX.+-#pP0123456789abcdefABCDEF");
+       private static final AsciiSet firstNumberChars 
=AsciiSet.create("+-.#0123456789");
+       private static final AsciiSet octChars = AsciiSet.create("01234567");
+       private static final AsciiSet decChars = AsciiSet.create("0123456789");
+       private static final AsciiSet hexChars = 
AsciiSet.create("0123456789abcdefABCDEF");
 
        // Maps 6-bit nibbles to BASE64 characters.
        private static final char[] base64m1 = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
 
        // Characters that do not need to be URL-encoded
-       private static final AsciiSet unencodedChars = new 
AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()\\");
+       private static final AsciiSet unencodedChars = 
AsciiSet.create().ranges("a-z","A-Z","0-9").chars("-_.!~*'()\\").build();
 
        // Maps BASE64 characters to 6-bit nibbles.
        private static final byte[] base64m2 = new byte[128];
@@ -1540,6 +1540,77 @@ public final class StringUtils {
                return null;
        }
 
+       private static final AsciiSet URL_ENCODE_PATHINFO_VALIDCHARS = 
+               
AsciiSet.create().ranges("a-z","A-Z","0-9").chars("-_.*/()").build();
+       
+       /**
+        * Similar to {@link #urlEncode(Object)} but doesn't encode 
<js>"/"</js> characters.
+        * 
+        * @param o The object to encode.
+        * @return The URL encoded string, or <jk>null</jk> if the object was 
null.
+        */
+       public static String urlEncodePath(Object o) {
+               if (o == null)
+                       return null;
+               String s = asString(o);
+
+               boolean needsEncode = false;
+               for (int i = 0; i < s.length() && ! needsEncode; i++) 
+                       needsEncode = 
URL_ENCODE_PATHINFO_VALIDCHARS.contains(s.charAt(i));
+               if (! needsEncode)
+                       return s;
+               
+               StringBuilder sb = new StringBuilder();
+               CharArrayWriter caw = new CharArrayWriter();
+               int caseDiff = ('a' - 'A');
+               
+               for (int i = 0; i < s.length();) {
+                       char c = s.charAt(i);
+                       if (URL_ENCODE_PATHINFO_VALIDCHARS.contains(c)) {
+                               sb.append(c);
+                               i++;
+                       } else {
+                               if (c == ' ') {
+                                       sb.append('+');
+                                       i++;
+                               } else {
+                                       do {
+                                               caw.write(c);
+                                               if (c >= 0xD800 && c <= 0xDBFF) 
{
+                                                       if ( (i+1) < 
s.length()) {
+                                                               int d = 
s.charAt(i+1);
+                                                               if (d >= 0xDC00 
&& d <= 0xDFFF) {
+                                                                       
caw.write(d);
+                                                                       i++;
+                                                               }
+                                                       }
+                                               }
+                                               i++;
+                                       } while (i < s.length() && 
!URL_ENCODE_PATHINFO_VALIDCHARS.contains((c = s.charAt(i))));
+                                       
+                                       caw.flush();
+                                       String s2 = new 
String(caw.toCharArray());
+                                       byte[] ba = s2.getBytes(IOUtils.UTF8);
+                                       for (int j = 0; j < ba.length; j++) {
+                                               sb.append('%');
+                                               char ch = 
Character.forDigit((ba[j] >> 4) & 0xF, 16);
+                                               if (Character.isLetter(ch)) {
+                                                       ch -= caseDiff;
+                                               }
+                                               sb.append(ch);
+                                               ch = Character.forDigit(ba[j] & 
0xF, 16);
+                                               if (Character.isLetter(ch)) {
+                                                       ch -= caseDiff;
+                                               }
+                                               sb.append(ch);
+                                       }
+                                       caw.reset();
+                               }
+                       }
+               }
+               return sb.toString();
+       }
+       
        /**
         * Decodes a <code>application/x-www-form-urlencoded</code> string 
using <code>UTF-8</code> encoding scheme.
         * 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
index b3fc575..e0d7a86 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
@@ -34,7 +34,7 @@ import org.apache.juneau.transform.*;
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public final class JsonParserSession extends ReaderParserSession {
 
-       private static final AsciiSet decChars = new AsciiSet("0123456789");
+       private static final AsciiSet decChars = 
AsciiSet.create().ranges("0-9").build();
        
        private final boolean validateEnd; 
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonWriter.java
index 3c8a957..40486c0 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonWriter.java
@@ -33,8 +33,8 @@ public final class JsonWriter extends SerializerWriter {
 
        // Characters that trigger special handling of serializing attribute 
values.
        private static final AsciiSet
-               encodedChars = new AsciiSet("\n\t\b\f\r'\"\\"),
-               encodedChars2 = new AsciiSet("\n\t\b\f\r'\"\\/");
+               encodedChars = AsciiSet.create("\n\t\b\f\r'\"\\"),
+               encodedChars2 = AsciiSet.create("\n\t\b\f\r'\"\\/");
 
        private static final KeywordSet reservedWords = new KeywordSet(
                
"arguments","break","case","catch","class","const","continue","debugger","default","delete",
@@ -49,8 +49,8 @@ public final class JsonWriter extends SerializerWriter {
        // These are actually more strict than the actual Javascript 
specification, but
        // can be narrowed in the future if necessary.
        // For example, we quote attributes that start with $ even though we 
don't need to.
-       private static final AsciiSet validAttrChars = new 
AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
-       private static final AsciiSet validFirstAttrChars = new 
AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_");
+       private static final AsciiSet validAttrChars = 
AsciiSet.create().ranges("a-z","A-Z","0-9").chars("_").build();
+       private static final AsciiSet validFirstAttrChars = 
AsciiSet.create().ranges("a-z","A-Z").chars("_").build();
 
        private final AsciiSet ec;
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
index 11c682a..c5a53c6 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonParserSession.java
@@ -35,7 +35,7 @@ import org.apache.juneau.transform.*;
 public class UonParserSession extends ReaderParserSession {
 
        // Characters that need to be preceded with an escape character.
-       private static final AsciiSet escapedChars = new 
AsciiSet("~'\u0001\u0002");
+       private static final AsciiSet escapedChars = 
AsciiSet.create("~'\u0001\u0002");
 
        private static final char AMP='\u0001', EQ='\u0002';  // Flags set in 
reader to denote & and = characters.
 
@@ -692,7 +692,7 @@ public class UonParserSession extends ReaderParserSession {
                return ("null".equals(s) ? null : trim(s));
        }
 
-       private static final AsciiSet endCharsParam = new AsciiSet(""+AMP), 
endCharsNormal = new AsciiSet(",)"+AMP);
+       private static final AsciiSet endCharsParam = AsciiSet.create(""+AMP), 
endCharsNormal = AsciiSet.create(",)"+AMP);
 
 
        /*
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonUtils.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonUtils.java
index 7a77546..02d9ea0 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonUtils.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonUtils.java
@@ -21,8 +21,8 @@ import org.apache.juneau.internal.*;
  */
 public final class UonUtils {
 
-       private static final AsciiSet needsQuoteChars = new 
AsciiSet("),=\n\t\r\b\f ");
-       private static final AsciiSet maybeNeedsQuotesFirstChar = new 
AsciiSet("),=\n\t\r\b\f tfn+-.#0123456789");
+       private static final AsciiSet needsQuoteChars = 
AsciiSet.create("),=\n\t\r\b\f ");
+       private static final AsciiSet maybeNeedsQuotesFirstChar = 
AsciiSet.create("),=\n\t\r\b\f tfn+-.#0123456789");
 
        /**
         * Returns <jk>true</jk> if the specified string needs to be quoted per 
UON notation.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonWriter.java
index 44b367e..6929007 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/uon/UonWriter.java
@@ -33,16 +33,16 @@ public final class UonWriter extends SerializerWriter {
        private final boolean encodeChars, plainTextParams;
 
        // Characters that do not need to be URL-encoded in strings.
-       private static final AsciiSet unencodedChars = new 
AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;/?:@-_.!*'$(),~=");
+       private static final AsciiSet unencodedChars = 
AsciiSet.create().ranges("a-z","A-Z","0-9").chars(";/?:@-_.!*'$(),~=").build();
 
        // Characters that do not need to be URL-encoded in attribute names.
        // Identical to unencodedChars, but excludes '='.
-       private static final AsciiSet unencodedCharsAttrName = new 
AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;/?:@-_.!*'$(),~");
+       private static final AsciiSet unencodedCharsAttrName = 
AsciiSet.create().ranges("a-z","A-Z","0-9").chars(";/?:@-_.!*'$(),~").build();
 
        // Characters that need to be preceded with an escape character.
-       private static final AsciiSet escapedChars = new AsciiSet("~'");
+       private static final AsciiSet escapedChars = AsciiSet.create("~'");
 
-       private static final AsciiSet noChars = new AsciiSet("");
+       private static final AsciiSet noChars = AsciiSet.create("");
 
        private static char[] hexArray = "0123456789ABCDEF".toCharArray();
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
index 2f33098..6cd960d 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserSession.java
@@ -33,7 +33,7 @@ import org.apache.juneau.transform.*;
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public final class YamlParserSession extends ReaderParserSession {
 
-       private static final AsciiSet decChars = new AsciiSet("0123456789");
+       private static final AsciiSet decChars = 
AsciiSet.create().ranges("0-9").build();
 
        /**
         * Create a new session using properties specified in the context.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlWriter.java
index af5475d..2d59fe1 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlWriter.java
@@ -31,7 +31,7 @@ public final class YamlWriter extends SerializerWriter {
 
        // Characters that trigger special handling of serializing attribute 
values.
        private static final AsciiSet
-               encodedChars = new AsciiSet("\n\t\b\f\r'\"\\");
+               encodedChars = AsciiSet.create("\n\t\b\f\r'\"\\");
 
        private static final KeywordSet reservedWords = new KeywordSet(
                "y","Y","yes","Yes","YES","n","N","no","No","NO",
@@ -45,8 +45,8 @@ public final class YamlWriter extends SerializerWriter {
        // These are actually more strict than the actual Javascript 
specification, but
        // can be narrowed in the future if necessary.
        // For example, we quote attributes that start with $ even though we 
don't need to.
-       private static final AsciiSet validAttrChars = new 
AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
-       private static final AsciiSet validFirstAttrChars = new 
AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_");
+       private static final AsciiSet validAttrChars = 
AsciiSet.create().ranges("a-z","A-Z","0-9").chars("_").build();
+       private static final AsciiSet validFirstAttrChars = 
AsciiSet.create().ranges("a-z","A-Z").chars("_").build();
 
        private final AsciiSet ec;
 
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.1.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.1.png
index 9cd4b2f..19d3106 100644
Binary files 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.1.png
 and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.1.png
 differ
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.2.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.2.png
index de2074d..e5d1953 100644
Binary files 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.2.png
 and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.2.png
 differ
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.3.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.3.png
index 8307c13..ecab4ad 100644
Binary files 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.3.png
 and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.3.png
 differ
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.4.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.4.png
index 896f579..c9c0f1c 100644
Binary files 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.4.png
 and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.4.png
 differ
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.5.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.5.png
index 2c255d3..bf38b24 100644
Binary files 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.5.png
 and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.5.png
 differ
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.6.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.6.png
index f45a3d2..0a166d8 100644
Binary files 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.6.png
 and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.6.png
 differ
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.7.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.7.png
new file mode 100644
index 0000000..e454740
Binary files /dev/null and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.7.png
 differ
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.8.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.8.png
new file mode 100644
index 0000000..1f228ca
Binary files /dev/null and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.8.png
 differ
diff --git 
a/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.9.png
 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.9.png
new file mode 100644
index 0000000..0385fe5
Binary files /dev/null and 
b/juneau-doc/src/main/javadoc/doc-files/juneau-rest-server.RemoteableProxiesServerSide.9.png
 differ
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index 0fa9a4e..e159f68 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -288,7 +288,6 @@
                <li><p><a class='doclink' 
href='#juneau-rest-client.Debugging'>Debugging</a></p>
                <li><p><a class='doclink' 
href='#juneau-rest-client.Logging'>Logging</a></p>
                <li><p><a class='doclink' 
href='#juneau-rest-client.Interceptors'>Interceptors</a></p>
-               <li><p><a class='doclink' 
href='#juneau-rest-client.Remoteable'>Remoteable Proxies</a></p>
                <li><p><a class='doclink' 
href='#juneau-rest-client.Other'>Other Useful Methods</a></p>
        </ol>
        <li><p class='toc2'><a class='doclink' 
href='#juneau-microservice-server'><i>juneau-microservice-server</i></a></p>
@@ -10380,7 +10379,7 @@
                <p>
                        Proxy interfaces are retrieved using the {@link 
org.apache.juneau.rest.client.RestClient#getRemoteableProxy(Class)} 
                        method.
-                       The remoteable servlet is a specialized subclass of 
{@link org.apache.juneau.rest.RestServlet} that provides a 
+                       <br>The remoteable servlet is a specialized subclass of 
{@link org.apache.juneau.rest.RestServlet} that provides a 
                        full-blown REST interface for calling remoteable 
services (e.g. POJOs) remotely. 
                </p>
                <p>
@@ -10388,56 +10387,57 @@
                        on a client...
                </p>
                <p class='bcode'>
+       <ja>@Remoteable</ja>
        <jk>public interface</jk> IAddressBook {
+
+               <jd>/** Initialize this address book with preset entries */</jd>
+               <jk>void</jk> init() <jk>throws</jk> Exception;
+       
+               <jd>/** Return all people in the address book */</jd>
+               List&lt;Person&gt; getPeople();
+       
+               <jd>/** Return all addresses in the address book */</jd>
+               List&lt;Address&gt; getAddresses();
+       
+               <jd>/** Create a person in this address book */</jd>
                Person createPerson(CreatePerson cp) <jk>throws</jk> Exception;
+       
+               <jd>/** Find a person by id */</jd>
                Person findPerson(<jk>int</jk> id);
+       
+               <jd>/** Find an address by id */</jd>
                Address findAddress(<jk>int</jk> id);
+       
+               <jd>/** Find a person by address id */</jd>
                Person findPersonWithAddress(<jk>int</jk> id);
+       
+               <jd>/** Remove a person by id */</jd>
+               Person removePerson(<jk>int</jk> id);
        }
                </p>                    
                <p>
-                       The client side code for invoking this method is shown 
below...
-               </p>
-               <p class='bcode'>
-       <jc>// Create a RestClient using JSON for serialization, and point to 
the server-side remoteable servlet.</jc>
-       RestClient client = RestClient.<jsm>create</jsm>()
-               
.rootUrl(<js>"https://localhost:9080/juneau/sample/remoteable";</js>)
-               .build();
-       
-       <jc>// Create a proxy interface.</jc>
-       IAddressBook ab = 
client.getRemoteableProxy(IAddressBook.<jk>class</jk>);
-       
-       <jc>// Invoke a method on the server side and get the returned 
result.</jc>
-       Person p = ab.createPerson(
-               <jk>new</jk> CreatePerson(<js>"Test Person"</js>,
-                       AddressBook.<jsm>toCalendar</jsm>(<js>"Aug 1, 
1999"</js>),
-                       <jk>new</jk> CreateAddress(<js>"Test street"</js>, 
<js>"Test city"</js>, <js>"Test state"</js>, 12345, <jk>true</jk>))
-       );
-               </p>
-               <p>
-                       The requirements for a method to be callable through 
the remoteable service are:
+                       The requirements for an interface method to be callable 
through the remoteable service are:
                </p>
                <ul class='spaced-list'>
                        <li>
                                The method must be public.
                        <li>
                                The parameter and return types must be <a 
class='doclink' href='#juneau-marshall.PojoCategories'>serializable and 
parsable</a>.
+                       <li>
+                               The method can optionally throw any 
<code>Throwable</code> that has a public no-arg or single-arg-string 
constructors.
+                               <br>There are automatically recreated on the 
client side when thrown on the server side.
                </ul>
                <p>
-                       Juneau provides the capability of calling methods on 
POJOs on a server through client-side proxy interfaces.
-                       It offers a number of advantages over other similar 
remote proxy interfaces, such as being much simpler to 
-                               define and use, and allowing much more 
flexibility in the types of objects serialized.
-               </p>
-               <p>
-                       The remote proxy interface API allows you to invoke 
server-side POJO methods on the client side using REST as 
-                       the communications protocol:
+                       The client side code for invoking this method is shown 
below...
                </p>
                <p class='bcode'>
-       <jc>// Create a client with basic JSON support.</jc>
-       RestClient client = 
RestClient.<jsm>create</jsm>().rootUrl(<js>"http://localhost/remoteable";</js>).build();
+       <jc>// Create a RestClient using JSON for serialization, and point to 
the server-side remoteable servlet.</jc>
+       RestClient client = RestClient.<jsm>create</jsm>()
+               .rootUrl(<js>"http://localhost:10000/remoteable";</js>)
+               .build();
        
-       <jc>// Get an interface proxy.</jc>
-       IAddressBook ab = 
client.getRemoteableProxy(IAddressBook.<jk>class</jk>);
+       <jc>// Create a proxy interface.</jc>
+       IAddressBook ab = 
client.getRemoteableProxy(IAddressBook.<jk>class</jk>);
        
        <jc>// Invoke a method on the server side and get the returned 
result.</jc>
        Person p = ab.createPerson(
@@ -10452,7 +10452,7 @@
                        Under the covers, this method call gets converted to a 
REST POST.
                </p>
                <p class='bcode'>
-       HTTP POST 
http://localhost/remoteable/org.apache.juneau.examples.rest.IAddressBook/createPerson
+       HTTP POST 
http://localhost:10000/remoteable/org.apache.juneau.examples.rest.IAddressBook/createPerson
        Accept: application/json
        Content-Type: application/json
        
@@ -10473,8 +10473,9 @@
        ]
                </p>
                <p>
-                       Note that the body of the request is an array.  This 
array contains the serialized arguments of the method.
-                       The object returned by the method is then serialized as 
the body of the response.
+                       Note that the body of the request is an array.  
+                       <br>This array contains the serialized arguments of the 
method.
+                       <br>The object returned by the method is then 
serialized as the body of the response.
                </p>
                <p>
                        To define a remoteable interface, simply add the {@link 
org.apache.juneau.remoteable.Remoteable @Remoteable} 
@@ -10482,18 +10483,14 @@
                </p>
                <p class='bcode'>
        <ja>@Remoteable</ja>
-       <jk>public interface</jk> IAddressBook {...}
+       <jk>public interface</jk> MyInterface {...}
                </p>
                <p>
                        This annotation tells the framework that all methods 
defined on this interface can be executed remotely.
-                       It can be applied to super-interfaces, super-classes, 
etc..., and exposes the methods at whatever level it is 
+                       <br>It can be applied to super-interfaces, 
super-classes, etc..., and exposes the methods at whatever level it is 
                        defined.  
                </p>
                <p>
-                       The {@link org.apache.juneau.remoteable.RemoteMethod 
@RemoteMethod} annotation can also be used on individual 
-                       methods to tailor which methods are exposed or their 
paths.
-               </p>
-               <p>
                        There are two ways to expose remoteable proxies on the 
server side:
                </p>
                <ol class='spaced-list'>
@@ -10508,7 +10505,7 @@
                </p>
                <p class='bcode'>
        <ja>@RestResource</ja>(
-               path=<js>"/remote"</js>
+               path=<js>"/remoteable"</js>
        )
        <jk>public class</jk> SampleRemoteableServlet <jk>extends</jk> 
RemoteableServlet {
        
@@ -10531,7 +10528,7 @@
                <p>
                        The 
<code><ja>@RestMethod</ja>(name=<jsf>PROXY</jsf>)</code> approach is easier if 
you only have a single 
                        interface you want to expose.  
-                       You simply define a Java method whose return type is an 
interface, and return the implementation of that 
+                       <br>You simply define a Java method whose return type 
is an interface, and return the implementation of that 
                        interface:
                </p>
                <p class='bcode'>
@@ -10543,37 +10540,17 @@
                </p>
                <p>
                        In either case, the proxy communications layer is pure 
REST.
-                       Therefore, in cases where the interface classes are not 
available on the client side, the same method calls can 
+                       <br>Therefore, in cases where the interface classes are 
not available on the client side, the same method calls can 
                        be made through pure REST calls.  
-                       This can also aid significantly in debugging, since 
calls to the remoteable service can be made directly from
+                       <br>This can also aid significantly in debugging, since 
calls to the remoteable service can be made directly from
                        a browser with no coding involved.
                </p>
                <p>
                        The parameters and return types of the Java methods can 
be any of the supported <a class='doclink' 
href='#juneau-marshall.PojoCategories'>serializable and parsable types</a>.
-                       This ends up being WAY more flexible than other proxy 
interfaces since Juneau can handle so may POJO types 
+                       <br>This ends up being WAY more flexible than other 
proxy interfaces since Juneau can handle so may POJO types 
                        out-of-the-box.
-                       Most of the time you don't even need to modify your 
existing Java implementation code.
-               </p>
-               <p>
-                       The <code>RemoteableServlet</code> class itself shows 
how sophisticated REST interfaces can be built on the Juneau 
-                       RestServlet API using very little code.  
-                       The class consists of only 53 lines of code, yet is a 
sophisticated discoverable and 
-                       self-documenting REST interface.  
-                       And since the remote proxy API is built on top of REST, 
it can be debugged using just a browser.
+                       <br>Most of the time you don't even need to modify your 
existing Java implementation code.
                </p>
-               <p>
-                       The requirements for a method to be callable through a 
remoteable service are:
-               </p>
-               <ul class='spaced-list'>
-                       <li>
-                               The method must be public.
-                       <li>
-                               The parameter and return types must be <a 
href='#juneau-marshall.PojoCategories'>serializable and parsable</a>.
-                               Parameterized types are supported.
-                       <li>
-                               Methods can throw <code>Throwables</code> with 
public no-arg or single-arg-string constructors which will 
-                               be automatically recreated on the client side.
-               </ul>
                
                <!-- 
========================================================================================================
 -->
                <a id="juneau-rest-server.RemoteableProxiesClientSide"></a>
@@ -10585,9 +10562,9 @@
                        </p>
                        <p>
                                It may seem that the client-side code would 
need to be complex.
-                               In reality, it builds upon existing 
serializing, parsing, and REST capabilities in Juneau resulting in very 
+                               <br>In reality, it builds upon existing 
serializing, parsing, and REST capabilities in Juneau resulting in very 
                                little additional code.
-                               The entire code for the 
<code>RestClient.getRemoteableProxy(Class)</code> method is shown below:
+                               <br>The entire code for the 
<code>RestClient.getRemoteableProxy(Class)</code> method is shown below:
                        </p>
                        <p class='bcode'>
        <jk>public</jk> &lt;T&gt; T getRemoteableProxy(<jk>final</jk> 
Class&lt;T&gt; interfaceClass) {
@@ -10598,7 +10575,7 @@
                                <ja>@Override</ja> <jc>/* InvocationHandler 
*/</jc>
                                <jk>public</jk> Object invoke(Object proxy, 
Method method, Object[] args) {
                                        <jk>try</jk> {
-                                               String uri = 
<jf>remoteableServletUri</jf> + '/' + interfaceClass.getName() + '/' + 
ClassUtils.<jsm>getMethodSignature</jsm>(method);
+                                               String uri = 
<jf>remoteableServletUri</jf> + <js>'/'</js> + interfaceClass.getName() + 
<js>'/'</js> + ClassUtils.<jsm>getMethodSignature</jsm>(method);
                                                <jk>return</jk> doPost(uri, 
args).getResponse(method.getReturnType());
                                        } <jk>catch</jk> (Exception e) {
                                                <jk>throw new</jk> 
RuntimeException(e);
@@ -10609,7 +10586,7 @@
                        </p>
                        <p>
                                Since we build upon the existing 
<code>RestClient</code> API, we inherit all of it's features.
-                               For example, convenience methods for setting 
POJO filters and properties to customize the behavior of the 
+                               <br>For example, convenience methods for 
setting POJO filters and properties to customize the behavior of the 
                                serializers and parsers, and the ability to 
provide your own customized Apache <code>HttpClient</code> for 
                                handling various scenarios involving 
authentication and Internet proxies.
                        </p>
@@ -10625,13 +10602,13 @@
                        <p>
                                The {@link 
org.apache.juneau.rest.remoteable.RemoteableServlet} class is an implementation 
of 
                                {@link org.apache.juneau.rest.RestServlet} that 
provides a REST interface for invoking calls on POJOs.
-                               The <code>RemoteableServlet</code> class is 
abstract and must implement a single method for providing the set 
+                               <br>The <code>RemoteableServlet</code> class is 
abstract and must implement a single method for providing the set 
                                of POJOs to expose as remote interfaces.  
                        </p>
                        <p>
                                The samples bundle includes a sample 
implementation of a remoteable service that can be used to interact with 
                                the address book POJO also included in the 
bundle.  
-                               The method that must be implemented is {@link 
org.apache.juneau.rest.remoteable.RemoteableServlet#getServiceMap()}
+                               <br>The method that must be implemented is 
{@link org.apache.juneau.rest.remoteable.RemoteableServlet#getServiceMap()}
                                that simply returns a mapping of Java 
interfaces (or classes) to POJO instances.
                        </p>
                        <p class='bcode'>
@@ -10658,27 +10635,31 @@
                        </p>
                        <p>
                                Since this class is a servlet, and can be 
deployed as such.  
-                               In the sample code, it's listed as a child 
resource to <code>org.apache.juneau.rest.samples.RootResources</code>
-                                       which makes it available under the URL 
<code>/juneau/sample/remoteable</code>.
+                               <br>In the sample code, it's listed as a child 
resource to <code>org.apache.juneau.rest.samples.RootResources</code>
+                                       which makes it available under the URL 
<code>/sample/remoteable</code>.
                        </p>
                        <p>
                                If you point your browser to that URL, you get 
a list of available interfaces:
                        </p>
-                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.1.png">
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.1.png" 
style='width:800px;'>
                        <p>
                                Clicking the hyperlinks on each shows you the 
list of methods that can be invoked on that service.
-                               Note that the <code>IAddressBook</code> link 
shows that you can only invoke methods defined on that
+                               <br>Note that the <code>IAddressBook</code> 
link shows that you can only invoke methods defined on that
                                interface, whereas the <code>AddressBook</code> 
link shows ALL public methods defined on that class.
+                       </p>
+                       <h5 class='figure'>IAddressBook</h5>
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.2.png" 
style='width:800px;'>
+                       <p>
                                Since <code>AddressBook</code> extends from 
<code>LinkedList</code>, you may notice familiar collections
                                framework methods listed.
                        </p>
-                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.2.png">
-                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.3.png">
+                       <h5 class='figure'>AddressBook</h5>
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.3.png" 
style='width:800px;'>
                        <p>
                                Let's see how we can interact with this 
interface through nothing more than REST calls to get a better idea on 
                                how this works.
-                               We'll use the same method call as in the 
introduction.
-                               First, we need to create the serialized form of 
the arguments:
+                               <br>We'll use the same method call as in the 
introduction.
+                               <br>First, we need to create the serialized 
form of the arguments:
                        </p>
        <p class='bcode'>
        Object[] args = <jk>new</jk> Object[] {
@@ -10711,57 +10692,80 @@
                        </p>
                        <p>
                                Note that in this example we're using JSON.  
-                               However, various other content types can also 
be used such as XML, URL-Encoding, UON, or HTML.  
-                               In practice however, JSON will preferred since 
it is often the most efficient.
+                               <br>However, various other content types can 
also be used such as XML, URL-Encoding, UON, or HTML.  
+                               <br>In practice however, JSON will preferred 
since it is often the most efficient.
                        </p>
                        <p>
                                Next, we can use a tool such as Poster to make 
the REST call.
-                               Methods are invoked by POSTing the serialized 
object array to the URI of the interface method.
-                               In this case, we want to POST our JSON to 
<code>/juneau/sample/remoteable/org.apache.juneau.examples.addressbook.IAddressBook/createPerson(org.apache.juneau.examples.addressbook.CreatePerson)</code>.
-                               Make sure that we specify the 
<code>Content-Type</code> of the body as <code>text/json</code>.
-                               We also want the results to be returned as 
JSON, so we set the <code>Accept</code> header to 
+                               <br>Methods are invoked by POSTing the 
serialized object array to the URI of the interface method.
+                               In this case, we want to POST our JSON to the 
following URL:
+                       </p>
+                       <p class='bcode'>
+       
http://localhost:10000/remoteable/org.apache.juneau.examples.addressbook.IAddressBook/createPerson(org.apache.juneau.examples.addressbook.CreatePerson)
+                       </p>
+                       <p>
+                               <br>Make sure that we specify the 
<code>Content-Type</code> of the body as <code>text/json</code>.
+                               <br>We also want the results to be returned as 
JSON, so we set the <code>Accept</code> header to 
                                <code>text/json</code> as well.
                        </p>
-                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.4.png">
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.4.png" 
style='width:400px;'>
                        <p>
                                When we execute the POST, we should see the 
following successful response whose body contains the returned 
                                <code>Person</code> bean serialized to JSON:
                        </p>
-                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.5.png">
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.5.png" 
style='width:400px;'>
                        <p>
                                From there, we could use the following code 
snippet to reconstruct the response object from JSON:
                        </p>
                        <p class='bcode'>
-                               String response = <js>"<i>output from 
above</i>"</js>;
-                               Person p = 
JsonParser.<jsf>DEFAULT</jsf>.parse(response, Person.<jk>class</jk>);
+       String response = <js>"<i>output from above</i>"</js>;
+       Person p = JsonParser.<jsf>DEFAULT</jsf>.parse(response, 
Person.<jk>class</jk>);
                        </p>
                        <p>
                                If we alter our servlet to allow overloaded GET 
requests, we can invoke methods using nothing more than a 
                                browser...
                        </p>
                        <p class='bcode'>
-                       <ja>@RestResource</ja>(
-                               path=<js>"/remoteable"</js>,
-                               
-                               <jc>// Allow us to use method=POST from a 
browser.</jc>
-                               allowedMethodParams=<js>"*"</js>
-                       )
-                       <jk>public class</jk> SampleRemoteableServlet 
<jk>extends</jk> RemoteableServlet {
+       <ja>@RestResource</ja>(
+               path=<js>"/remoteable"</js>,
+               
+               <jc>// Allow us to use method=POST from a browser.</jc>
+               allowedMethodParams=<js>"*"</js>
+       )
+       <jk>public class</jk> SampleRemoteableServlet <jk>extends</jk> 
RemoteableServlet {
+                       </p>
+                       <p>
+                               For example, to invoke the 
<code>getPeople()</code> method on our bean:
                        </p>
+                       <p class='bcode'>
+       
http://localhost:10000/remoteable/org.apache.juneau.examples.addressbook.IAddressBook/getPeople?method=POST
+                       </p>                    
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.6.png" 
style='width:800px;'>
                        <p>
-                               For example, here we call the 
<code>findPerson(<jk>int</jk>)</code> method to retrieve a person and get the 
+                               Here we call the 
<code>findPerson(<jk>int</jk>)</code> method to retrieve a person and get the 
                                returned POJO (in this case as HTML since 
that's what's in the <code>Accept</code> header when calling from a 
                                browser):
                        </p>
-                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.6.png">
+                       <p class='bcode'>
+       
http://localhost:10000/remoteable/org.apache.juneau.examples.addressbook.IAddressBook/findPerson(int)?method=POST&amp;body=@(3)
+                       </p>                    
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.7.png" 
style='width:800px;'>
                        <p>
-                               When specifying the POST body as a 
<code>&amp;content</code> parameter, the method arguments should be in UON 
+                               When specifying the POST body as a 
<code>&amp;body</code> parameter, the method arguments should be in UON 
                                notation.
-                               See {@link org.apache.juneau.uon.UonSerializer} 
for more information about this encoding.
-                               Usually you can also pass in JSON if you 
specify <code>&amp;Content-Type=text/json</code> in the URL parameters
+                               <br>See {@link 
org.apache.juneau.uon.UonSerializer} for more information about this encoding.
+                               <br>Usually you can also pass in JSON if you 
specify <code>&amp;Content-Type=text/json</code> in the URL parameters
                                but passing in unencoded JSON in a URL may not 
work in all browsers.  
-                               Therefore, UON is preferred.
+                               <br>Therefore, UON is preferred.
                        </p>
+                       <p>
+                               The hyperlinks on the method names above lead 
you to a simple form-entry page where you can test
+                               passing parameters in UON notation as 
URL-encoded form posts.
+                       </p>
+                       <h5 class='figure'>Sample form entry page</h5>
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.8.png" 
style='width:800px;'>
+                       <h5 class='figure'>Sample form entry page results</h5>
+                       <img class='bordered' 
src="doc-files/juneau-rest-server.RemoteableProxiesServerSide.9.png" 
style='width:800px;'>
                </div>
 
                <!-- 
========================================================================================================
 -->
@@ -10772,45 +10776,45 @@
                        <p>
                                What if you want fine-tuned control over which 
methods are exposed in an interface instead of just all public 
                                methods?
-                               For this, the {@link 
org.apache.juneau.remoteable.Remoteable @Remoteable} annotation is provided.
-                               It can be applied to individual interface 
methods to only expose those methods through the remoteable servlet.
+                               <br>For this, the {@link 
org.apache.juneau.remoteable.Remoteable @Remoteable} annotation is provided.
+                               <br>It can be applied to individual interface 
methods to only expose those methods through the remoteable servlet.
                        </p>
                        <p>
                                For example, to expose only the first 2 methods 
in our <code>IAddressBook</code> interface...
                        </p>
                        <p class='bcode'>
-                       <jk>public interface</jk> IAddressBook {
-                               <ja>@Remoteable</ja> Person 
createPerson(CreatePerson cp) <jk>throws</jk> Exception;
-                               <ja>@Remoteable</ja> Person 
findPerson(<jk>int</jk> id);
-                               Address findAddress(<jk>int</jk> id);
-                               Person findPersonWithAddress(<jk>int</jk> id);
-                       }
+       <jk>public interface</jk> IAddressBook {
+               <ja>@Remoteable</ja> Person createPerson(CreatePerson cp) 
<jk>throws</jk> Exception;
+               <ja>@Remoteable</ja> Person findPerson(<jk>int</jk> id);
+               Address findAddress(<jk>int</jk> id);
+               Person findPersonWithAddress(<jk>int</jk> id);
+       }
                        </p>    
                        <p>
                                On the server side, the option to restrict 
access to only annotated methods is defined through a property:
                        </p>
                        <p class='bcode'>
-                       <ja>@RestResource</ja>(
-                               path=<js>"/remoteable"</js>,
-                               properties={
-                                       <jc>// Only expose methods annotated 
with @Remoteable.</jc>
-                                       
<ja>@Property</ja>(name=<jsf>REMOTEABLE_includeOnlyRemotableMethods</jsf>, 
value=<js>"true"</js>)
-                               }
-                       )
-                       <jk>public class</jk> SampleRemoteableServlet 
<jk>extends</jk> RemoteableServlet {
+       <ja>@RestResource</ja>(
+               path=<js>"/remoteable"</js>,
+               properties={
+                       <jc>// Only expose methods annotated with 
@Remoteable.</jc>
+                       
<ja>@Property</ja>(name=<jsf>REMOTEABLE_includeOnlyRemotableMethods</jsf>, 
value=<js>"true"</js>)
+               }
+       )
+       <jk>public class</jk> SampleRemoteableServlet <jk>extends</jk> 
RemoteableServlet {
                        </p>
                        <p>
                                The <ja>@Remoteable</ja> annotation can also be 
applied to the interface class to expose all public methods 
                                defined on that interface.
                        </p>
                        <p class='bcode'>
-                       <ja>@Remoteable</ja>
-                       <jk>public interface</jk> IAddressBook {
-                               Person createPerson(CreatePerson cp) 
<jk>throws</jk> Exception;
-                               Person findPerson(<jk>int</jk> id);
-                               Address findAddress(<jk>int</jk> id);
-                               Person findPersonWithAddress(<jk>int</jk> id);
-                       }
+       <ja>@Remoteable</ja>
+       <jk>public interface</jk> IAddressBook {
+               Person createPerson(CreatePerson cp) <jk>throws</jk> Exception;
+               Person findPerson(<jk>int</jk> id);
+               Address findAddress(<jk>int</jk> id);
+               Person findPersonWithAddress(<jk>int</jk> id);
+       }
                        </p>    
                </div>
        </div>  
@@ -11170,7 +11174,7 @@
                and output to and from POJOs using any of the provided 
serializers and parsers.
        </p>
        <p>
-               Built upon the <l>Apache HttpClient</l> libraries, it extends 
that API and provides specialized APIs for working with 
+               Built upon the Apache HttpClient libraries, it extends that API 
and provides specialized APIs for working with 
                REST interfaces while maintaining all the functionality 
available in the HttpClient API.
        </p>
        <p class='bcode'>
@@ -11178,7 +11182,7 @@
        <jk>try</jk> (RestClient client = 
RestClient.<jsm>create</jsm>().build()) {
        
                <jc>// The address of the root resource.</jc>
-               String url = 
<js>"http://localhost:9080/sample/addressBook";</js>;
+               String url = <js>"http://localhost:10000/addressBook";</js>;
                
                <jc>// Do a REST GET against a remote REST interface and convert
                // the response to an unstructured ObjectMap object.</jc>
@@ -11240,7 +11244,7 @@
        
                <jc>// GET request, ignoring output</jc>
                <jk>try</jk> {
-                       <jk>int</jk> rc = 
client.doGet(<js>"http://localhost:9080/sample/addressBook";</js>).run();
+                       <jk>int</jk> rc = 
client.doGet(<js>"http://localhost:10000/addressBook";</js>).run();
                        <jc>// Succeeded!</jc>
                } <jk>catch</jk> (RestCallException e) {
                        <jc>// Failed!</jc>
@@ -11257,70 +11261,70 @@
                <jc>// GET request, getting output as a String.  No POJO 
parsing is performed.
                // Note that when calling one of the getX() methods, you don't 
need to call connect() or disconnect(), since
                //      it's automatically called for you.</jc>
-               String output = 
client.doGet(<js>"http://localhost:9080/sample/addressBook";</js>)
+               String output = 
client.doGet(<js>"http://localhost:10000/addressBook";</js>)
                        .getResponseAsString();
                                
                <jc>// GET request, getting output as a Reader</jc>
-               Reader r = 
client.doGet(<js>"http://localhost:9080/sample/addressBook";</js>)
+               Reader r = 
client.doGet(<js>"http://localhost:10000/addressBook";</js>)
                        .getReader();
                                
                <jc>// GET request, getting output as an untyped map</jc>
                <jc>// Input must be an object (e.g. "{...}")</jc>
-               ObjectMap m = 
client.doGet(<js>"http://localhost:9080/sample/addressBook/0";</js>)
+               ObjectMap m = 
client.doGet(<js>"http://localhost:10000/addressBook/0";</js>)
                        .getResponse(ObjectMap.<jk>class</jk>);
                                
                <jc>// GET request, getting output as an untyped list</jc>
                <jc>// Input must be an array (e.g. "[...]")</jc>
-               ObjectList l = 
client.doGet(<js>"http://localhost:9080/sample/addressBook";</js>)
+               ObjectList l = 
client.doGet(<js>"http://localhost:10000/addressBook";</js>)
                        .getResponse(ObjectList.<jk>class</jk>);
                                
                <jc>// GET request, getting output as a parsed bean</jc>
                <jc>// Input must be an object (e.g. "{...}")</jc>
                <jc>// Note that you don't have to do any casting!</jc>
-               Person p = 
client.doGet(<js>"http://localhost:9080/sample/addressBook/0";</js>)
+               Person p = 
client.doGet(<js>"http://localhost:10000/addressBook/0";</js>)
                        .getResponse(Person.<jk>class</jk>);
                                
                <jc>// GET request, getting output as a parsed bean</jc>
                <jc>// Input must be an array of objects (e.g. 
"[{...},{...}]")</jc>
-               Person[] pa = 
client.doGet(<js>"http://localhost:9080/sample/addressBook";</js>)
+               Person[] pa = 
client.doGet(<js>"http://localhost:10000/addressBook";</js>)
                        .getResponse(Person[].<jk>class</jk>);
                                
                <jc>// Same as above, except as a List&lt;Person&gt;</jc>
-               List&lt;Person&gt; pl = 
client.doGet(<js>"http://localhost:9080/sample/addressBook";</js>)
+               List&lt;Person&gt; pl = 
client.doGet(<js>"http://localhost:10000/addressBook";</js>)
                        .getResponse(List.<jk>class</jk>, 
Person.<jk>class</jk>);
                                
                <jc>// GET request, getting output as a parsed string</jc>
                <jc>// Input must be a string (e.g. 
"&lt;string&gt;foo&lt;/string&gt;" or "'foo'")</jc>
-               String name = 
client.doGet(<js>"http://localhost:9080/sample/addressBook/0/name";</js>)
+               String name = 
client.doGet(<js>"http://localhost:10000/addressBook/0/name";</js>)
                        .getResponse(String.<jk>class</jk>);
                                
                <jc>// GET request, getting output as a parsed number</jc>
                <jc>// Input must be a number (e.g. 
"&lt;number&gt;123&lt;/number&gt;" or "123")</jc>
-               <jk>int</jk> age = 
client.doGet(<js>"http://localhost:9080/sample/addressBook/0/age";</js>)
+               <jk>int</jk> age = 
client.doGet(<js>"http://localhost:10000/addressBook/0/age";</js>)
                        .getResponse(Integer.<jk>class</jk>);
                                
                <jc>// GET request, getting output as a parsed boolean</jc>
                <jc>// Input must be a boolean (e.g. 
"&lt;boolean&gt;true&lt;/boolean&gt;" or "true")</jc>
-               <jk>boolean</jk> isCurrent = 
client.doGet(<js>"http://localhost:9080/sample/addressBook/0/addresses/0/isCurrent";</js>)
+               <jk>boolean</jk> isCurrent = 
client.doGet(<js>"http://localhost:10000/addressBook/0/addresses/0/isCurrent";</js>)
                        .getResponse(Boolean.<jk>class</jk>);
        }
                                
        <jc>// GET request, getting a filtered object</jc>
        <jk>try</jk> (RestClient client = 
RestClient.<jsm>create</jsm>().pojoSwaps(CalendarSwap.<jsf>ISO8601</jsf>.<jk>class</jk>).build())
 {
-               Calendar birthDate = 
client.doGet(<js>"http://localhost:9080/sample/addressBook/0/birthDate";</js>)
+               Calendar birthDate = 
client.doGet(<js>"http://localhost:10000/addressBook/0/birthDate";</js>)
                        .getResponse(GregorianCalendar.<jk>class</jk>);
        
                <jc>// PUT request on regular field</jc>
                String newName = <js>"John Smith"</js>;
-               <jk>int</jk> rc = 
client.doPut(<js>"http://localhost:9080/addressBook/0/name";</js>, 
newName).run();
+               <jk>int</jk> rc = 
client.doPut(<js>"http://localhost:10000/addressBook/0/name";</js>, 
newName).run();
                
                <jc>// PUT request on filtered field</jc>
                Calendar newBirthDate = <jk>new</jk> GregorianCalendar(1, 2, 3, 
4, 5, 6);
-               rc = 
client.doPut(<js>"http://localhost:9080/sample/addressBook/0/birthDate";</js>, 
newBirthDate).run();
+               rc = 
client.doPut(<js>"http://localhost:10000/addressBook/0/birthDate";</js>, 
newBirthDate).run();
                
                <jc>// POST of a new entry to a list</jc>
                Address newAddress = <jk>new</jk> Address(<js>"101 Main 
St"</js>, <js>"Anywhere"</js>, <js>"NY"</js>, 12121, <jk>false</jk>);
-               rc = 
client.doPost(<js>"http://localhost:9080/addressBook/0/addresses";</js>, 
newAddress).run(); 
+               rc = 
client.doPost(<js>"http://localhost:10000/addressBook/0/addresses";</js>, 
newAddress).run();        
        }
        </p>
        
@@ -12104,69 +12108,8 @@
        </div>
        
        <!-- 
=======================================================================================================
 -->
-       <a id="juneau-rest-client.Remoteable"></a>
-       <h3 class='topic' onclick='toggle(this)'>9.9 - Remoteable Proxies</h3>
-       <div class='topic'>
-               <p>
-                       Juneau provides the capability of calling methods on 
POJOs on a server through client-side proxy interfaces.
-                       <br>It offers a number of advantages over other similar 
remote proxy interfaces, such as being much simpler to 
-                       use and allowing much more flexibility.
-               </p>
-               <p>
-                       Proxy interfaces are retrieved using the {@link 
org.apache.juneau.rest.client.RestClient#getRemoteableProxy(Class)} 
-                       <br>method.
-                       The remoteable servlet is a specialized subclass of 
{@link org.apache.juneau.rest.RestServlet} that 
-                       provides a full-blown REST interface for calling 
interfaces remotely. 
-               </p>
-               <p>
-                       In this example, we have the following interface 
defined that we want to call from the client side against
-                       a POJO on the server side (i.e. a Remoteable Service)...
-               </p>
-               <p class='bcode'>
-       <jk>public interface</jk> IAddressBook {
-               Person createPerson(CreatePerson cp) <jk>throws</jk> Exception;
-       }
-               </p>                    
-               <p>
-                       The client side code for invoking this method is shown 
below...
-               </p>
-               <p class='bcode'>
-       <jc>// Create a RestClient using JSON for serialization, and point to 
the server-side remoteable servlet.</jc>
-       RestClient client = RestClient.<jsm>create</jsm>()
-               
.rootUrl(<js>"https://localhost:9080/juneau/sample/remoteable";</js>)
-               .build();
-       
-       <jc>// Create a proxy interface.</jc>
-       IAddressBook ab = 
client.getRemoteableProxy(IAddressBook.<jk>class</jk>);
-       
-       <jc>// Invoke a method on the server side and get the returned 
result.</jc>
-       Person p = ab.createPerson(
-               <jk>new</jk> CreatePerson(<js>"Test Person"</js>,
-                       AddressBook.<jsm>toCalendar</jsm>(<js>"Aug 1, 
1999"</js>),
-                       <jk>new</jk> CreateAddress(<js>"Test street"</js>, 
<js>"Test city"</js>, <js>"Test state"</js>, 12345, <jk>true</jk>))
-       );
-               </p>
-               <p>
-                       The requirements for a method to be callable through a 
remoteable service are:
-               </p>
-               <ul class='spaced-list'>
-                       <li>
-                               The method must be public.
-                       <li>
-                               The parameter and return types must be <a 
class='doclink' href='#juneau-marshall.PojoCategories'>serializable and 
parsable</a>.
-               </ul>
-               <p>
-                       One significant feature is that the remoteable services 
servlet is a full-blown REST interface.  
-                       <br>Therefore, in cases where the interface classes are 
not available on the client side, the same method calls 
-                       can be made through pure REST calls.  
-                       <br>This can also aid significantly in debugging since 
calls to the remoteable service can be called directly 
-                       from a browser with no code involved.
-               </p>
-       </div>
-       
-       <!-- 
=======================================================================================================
 -->
        <a id="juneau-rest-client.Other"></a>
-       <h3 class='topic' onclick='toggle(this)'>9.10 - Other Useful 
Methods</h3>
+       <h3 class='topic' onclick='toggle(this)'>9.9 - Other Useful Methods</h3>
        <div class='topic'>
                <p>
                        The {@link 
org.apache.juneau.rest.client.RestClientBuilder#rootUrl(Object)} method can be 
used to specify a 
@@ -12537,7 +12480,7 @@
                                Now open your browser and point to 
<l>http://localhost:10000</l>.  
                                You should see the following:
                        </p>
-                       <img class='bordered' 
src="doc-files/MicroserviceServer.Running.6.png" style='width:800px;'>
+                       <img class='bordered' 
src="doc-files/juneau-microservice-server.Running.1.png" style='width:400px;'>
                        <p>
                                You have started a REST interface on port 10000.
                                <br>You can enter the command <code>exit</code> 
to shut it down.
@@ -16540,17 +16483,13 @@
                <p>
                        TBD
                </p>
-
-
-               <h5 class='topic'>juneau-microservice</h5>
+               
+               <h5 class='topic'>juneau-server</h5>
                <ul class='spaced-list'>
                        <li>
-                               <code>Resource</code> and 
<code>ResourceGroup</code> classes removed.
-                               <br>{@link 
org.apache.juneau.rest.BasicRestServlet} and {@link 
org.apache.juneau.rest.BasicRestServletGroup} can be used instead.
+                               Fixed bug in <code>UriResolver</code> when 
request path info had special characters.
                        <li>
-                               <code>ResourceJena</code> and 
<code>ResourceJenaGroup</code> classes rename to
-                               {@link 
org.apache.juneau.microservice.BasicRestServletJena} and {@link 
org.apache.juneau.microservice.BasicRestServletJenaGroup} can be used instead.
-                               
+                               {@link 
org.apache.juneau.rest.remoteable.RemoteableServlet} now provides a form page 
for invoking remoteable methods in a browser.
                </ul>
        </div>
        
@@ -16989,38 +16928,15 @@
                                {@link org.apache.juneau.rest.client.RestCall} 
and {@link org.apache.juneau.rest.client.RestClient}
                                now implement the <code>Closeable</code> 
interface.
                </ul>
-
-               <h5 class='topic'>Documentation</h5>
+               
+               <h5 class='topic'>juneau-microservice</h5>
                <ul class='spaced-list'>
                        <li>
-                               New and updated sections in overview document:
-                               <ul>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.PojoSwaps'>PojoSwaps</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.PerMediaTypePojoSwaps'>Per-media-type PojoSwaps</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.OneWayPojoSwaps'>One-way PojoSwaps</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.SwapAnnotation'>@Swap Annotation</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.SwapMethods'>Swap Methods</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.SurrogateClasses'>Surrogate Classes</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.BeanAnnotation'>@Bean Annotation</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.BeanPropertyAnnotation'>@BeanProperty Annotation</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.BeanConstructorAnnotation'>@BeanConstructor 
Annotation</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.BeanIgnoreAnnotation'>@BeanIgnore Annotation</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.NamePropertyAnnotation'>@NameProperty Annotation</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.ParentPropertyAnnotation'>@ParentProperty 
Annotation</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.URIs'>URIs</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.BeanFilters'>BeanFilters</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.StopClasses'>Stop Classes</a></p>
-                                       <li><p><a class='doclink' 
href='#juneau-marshall.BypassSerialization'>Bypass Serialization</a></p>
-                               </ul>           
+                               <code>Resource</code> and 
<code>ResourceGroup</code> classes removed.
+                               <br>{@link 
org.apache.juneau.rest.BasicRestServlet} and {@link 
org.apache.juneau.rest.BasicRestServletGroup} can be used instead.
                        <li>
-                               Updated javadocs:
-                               <ul>
-                                       <li>{@link 
org.apache.juneau.BeanContext}
-                                       <li>{@link 
org.apache.juneau.BeanContextBuilder}
-                                       <li>{@link 
org.apache.juneau.rest.RestContext}
-                                       <li>{@link 
org.apache.juneau.rest.RestContextBuilder}
-                                       <li>Swagger DTOs.
-                               </ul>           
+                               <code>ResourceJena</code> and 
<code>ResourceJenaGroup</code> classes renamed to
+                               {@link 
org.apache.juneau.microservice.BasicRestServletJena} and {@link 
org.apache.juneau.microservice.BasicRestServletJenaGroup}.
                </ul>
        </div>
 
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/AddressBook.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/AddressBook.java
index dbc27d5..e7aa0e0 100755
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/AddressBook.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/addressbook/AddressBook.java
@@ -37,24 +37,28 @@ public class AddressBook extends LinkedList<Person> 
implements IAddressBook {
        }
 
        @Override /* IAddressBook */
-       public void init() throws Exception {
+       public void init() {
                clear();
-               createPerson(
-                       new CreatePerson(
-                               "Barack Obama",
-                               toCalendar("Aug 4, 1961"),
-                               new CreateAddress("1600 Pennsylvania Ave", 
"Washington", "DC", 20500, true),
-                               new CreateAddress("5046 S Greenwood Ave", 
"Chicago", "IL", 60615, false)
-                       )
-               );
-               createPerson(
-                       new CreatePerson(
-                               "George Walker Bush",
-                               toCalendar("Jul 6, 1946"),
-                               new CreateAddress("43 Prairie Chapel Rd", 
"Crawford", "TX", 76638, true),
-                               new CreateAddress("1600 Pennsylvania Ave", 
"Washington", "DC", 20500, false)
-                       )
-               );
+               try {
+                       createPerson(
+                               new CreatePerson(
+                                       "Barack Obama",
+                                       toCalendar("Aug 4, 1961"),
+                                       new CreateAddress("1600 Pennsylvania 
Ave", "Washington", "DC", 20500, true),
+                                       new CreateAddress("5046 S Greenwood 
Ave", "Chicago", "IL", 60615, false)
+                               )
+                       );
+                       createPerson(
+                               new CreatePerson(
+                                       "George Walker Bush",
+                                       toCalendar("Jul 6, 1946"),
+                                       new CreateAddress("43 Prairie Chapel 
Rd", "Crawford", "TX", 76638, true),
+                                       new CreateAddress("1600 Pennsylvania 
Ave", "Washington", "DC", 20500, false)
+                               )
+                       );
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
        }
 
        @Override /* IAddressBook */
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
index 55cd0f51..80fba93 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
@@ -12,9 +12,15 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.examples.rest;
 
+import static org.apache.juneau.http.HttpMethodName.*;
+
 import java.util.*;
+import java.util.Map;
 
+import org.apache.juneau.dto.*;
+import org.apache.juneau.dto.html5.*;
 import org.apache.juneau.examples.addressbook.*;
+import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.remoteable.*;
 
@@ -32,14 +38,6 @@ import org.apache.juneau.rest.remoteable.*;
                        "up: request:/..",
                        "options: servlet:/?method=OPTIONS",
                        "source: 
$C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java"
-               },
-               aside={
-                       "<div style='max-width:400px;min-width:200px' 
class='text'>",
-                       "       <p>Shows how to use the 
<code>RemoteableServlet</code> class to define RPC-style remoteable interfaces 
using REST as a protocol.</p>",
-                       "       <p>Remoteable proxies are retrieved on the 
client side using <code>RestClient.getInterfaceProxy(Class)</code>.</p>",
-                       "       <p>Methods are invoked using POSTs of 
serialized arrays of objects and the returned value is marshalled back as a 
response.</p>",
-                       "       <p>GET requests (as shown here) show the 
available methods on the interface.</p>",
-                       "</div>"
                }
        ),
        // Allow us to use method=POST from a browser.
@@ -48,7 +46,12 @@ import org.apache.juneau.rest.remoteable.*;
 )
 public class SampleRemoteableServlet extends RemoteableServlet {
 
-       AddressBook addressBook = new AddressBook();
+       private final AddressBook addressBook;
+       
+       public SampleRemoteableServlet() {
+               addressBook = new AddressBook();
+               addressBook.init();
+       }
 
        @Override /* RemoteableServlet */
        protected Map<Class<?>,Object> getServiceMap() throws Exception {
@@ -61,4 +64,66 @@ public class SampleRemoteableServlet extends 
RemoteableServlet {
                m.put(AddressBook.class, addressBook);
                return m;
        }
+       
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Overridden methods to provide aside messages
+       
//-----------------------------------------------------------------------------------------------------------------
+       
+       @Override /* RemoteableServlet */
+       @RestMethod(name=GET, path="/",
+               htmldoc=@HtmlDoc(
+                       aside={
+                               "<div style='max-width:400px;min-width:200px' 
class='text'>",
+                               "       <p>Shows how to use the 
<code>RemoteableServlet</code> class to define RPC-style remoteable interfaces 
using REST as a protocol.</p>",
+                               "       <p>Remoteable proxies are retrieved on 
the client side using <code>RestClient.getInterfaceProxy(Class)</code>.</p>",
+                               "       <p>Methods are invoked using POSTs of 
serialized arrays of objects and the returned value is marshalled back as a 
response.</p>",
+                               "       <p>This page shows the list of keys 
returned by the getServiceMap() method which returns a Map&lt;Class,Object&gt; 
which maps interfaces to objects.</p>",
+                               "       <p>This example shows the differences 
between using interfaces (preferred) and classes as keys.</p>",
+                               "</div>"
+                       }
+               )
+       )
+       public List<LinkString> getInterfaces(RestRequest req) throws Exception 
{
+               return super.getInterfaces(req);
+       }
+
+       @Override /* RemoteableServlet */
+       @RestMethod(name=GET, path="/{javaInterface}", summary="List of 
available methods on $RP{javaInterface}.",
+               htmldoc=@HtmlDoc(
+                       aside={
+                               "<div style='max-width:400px;min-width:200px' 
class='text'>",
+                               "       <p>Shows the list of methods defined on 
this interface.</p>",
+                               "       <p>Links take you to a form-entry page 
for testing.</p>",
+                               "</div>"
+                       }
+               )
+       )
+       public Collection<LinkString> listMethods(RestRequest req, 
@Path("javaInterface") String javaInterface) throws Exception {
+               return super.listMethods(req, javaInterface);
+       }
+
+       /**
+        * [GET /{javaInterface] - Get the list of all remoteable methods on 
the specified interface name.
+        * 
+        * @param req The HTTP servlet request.
+        * @param javaInterface The Java interface name.
+        * @param javaMethod The Java method name or signature.
+        * @return A simple form entry page for invoking a remoteable method.
+        * @throws Exception
+        */
+       @Override /* RemoteableServlet */
+       @RestMethod(name=GET, path="/{javaInterface}/{javaMethod}", 
summary="Form entry for method $RP{javaMethod} on interface $RP{javaInterface}",
+               htmldoc=@HtmlDoc(
+                       aside={
+                               "<div style='max-width:400px;min-width:200px' 
class='text'>",
+                               "       <p>A rudimentary form-entry page.</p>",
+                               "       <p>When using this form, arguments are 
passed as a URL-encoded FORM post.</p>",
+                               "       <p>However, other formats such as JSON 
and XML can also be posted.</p>",
+                               "</div>"
+                       }
+               )
+       )
+       public Div showEntryForm(RestRequest req, @Path("javaInterface") String 
javaInterface, @Path("javaMethod") String javaMethod) throws Exception {
+               return super.showEntryForm(req, javaInterface, javaMethod);
+       } 
 }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 6417820..e6c1aaa 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -34,6 +34,7 @@ import org.apache.juneau.*;
 import org.apache.juneau.config.*;
 import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.http.*;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.widget.*;
@@ -755,7 +756,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                        StringBuilder authority = new 
StringBuilder(getScheme()).append("://").append(getServerName());
                        if (! (port == 80 && "http".equals(scheme) || port == 
443 && "https".equals(scheme)))
                                authority.append(':').append(port);
-                       uriContext = new UriContext(authority.toString(), 
getContextPath(), getServletPath(), super.getPathInfo());
+                       uriContext = new UriContext(authority.toString(), 
getContextPath(), getServletPath(), 
StringUtils.urlEncodePath(super.getPathInfo()));
                }
                return uriContext;
        }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
index fed6c3b..42b26da 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
@@ -13,13 +13,19 @@
 package org.apache.juneau.rest.remoteable;
 
 import static javax.servlet.http.HttpServletResponse.*;
+import static org.apache.juneau.dto.html5.HtmlBuilder.*;
 import static org.apache.juneau.http.HttpMethodName.*;
+import static org.apache.juneau.internal.StringUtils.*;
 
+import java.lang.reflect.*;
 import java.util.*;
+import java.util.Map;
 import java.util.concurrent.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.dto.*;
+import org.apache.juneau.dto.html5.*;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
@@ -76,7 +82,7 @@ public abstract class RemoteableServlet extends 
BasicRestServlet {
                boolean useAll = ! useOnlyAnnotated();
                for (Class<?> c : getServiceMap().keySet()) {
                        if (useAll || 
getContext().getBeanContext().getClassMeta(c).isRemoteable())
-                               l.add(new LinkString(c.getName(), "{0}/{1}", 
req.getRequestURI(), c.getName()));
+                               l.add(new LinkString(c.getName(), "{0}/{1}", 
req.getRequestURI(), urlEncode(c.getName())));
                }
                return l;
        }
@@ -84,16 +90,64 @@ public abstract class RemoteableServlet extends 
BasicRestServlet {
        /**
         * [GET /{javaInterface] - Get the list of all remoteable methods on 
the specified interface name.
         * 
+        * @param req The HTTP servlet request.
         * @param javaInterface The Java interface name.
         * @return The methods defined on the interface.
         * @throws Exception
         */
-       @RestMethod(name=GET, path="/{javaInterface}")
-       public Collection<String> listMethods(@Path String javaInterface) 
throws Exception {
-               return getMethods(javaInterface).keySet();
+       @RestMethod(name=GET, path="/{javaInterface}", summary="List of 
available methods on $RP{javaInterface}.")
+       public Collection<LinkString> listMethods(RestRequest req, 
@Path("javaInterface") String javaInterface) throws Exception {
+               List<LinkString> l = new ArrayList<>();
+               for (String s : getMethods(javaInterface).keySet()) {
+                       l.add(new LinkString(s, "{0}/{1}", req.getRequestURI(), 
urlEncode(s)));
+               }
+               return l;
        }
 
        /**
+        * [GET /{javaInterface] - Get the list of all remoteable methods on 
the specified interface name.
+        * 
+        * @param req The HTTP servlet request.
+        * @param javaInterface The Java interface name.
+        * @param javaMethod The Java method name or signature.
+        * @return A simple form entry page for invoking a remoteable method.
+        * @throws Exception
+        */
+       @RestMethod(name=GET, path="/{javaInterface}/{javaMethod}", 
summary="Form entry for method $RP{javaMethod} on interface $RP{javaInterface}")
+       public Div showEntryForm(RestRequest req, @Path("javaInterface") String 
javaInterface, @Path("javaMethod") String javaMethod) throws Exception {
+               
+               // Find the method.
+               java.lang.reflect.Method m = 
getMethods(javaInterface).get(javaMethod);
+               if (m == null)
+                       throw new RestException(SC_NOT_FOUND, "Method not 
found");
+
+               Table t = table();
+               
+               Type[] types = m.getGenericParameterTypes();
+               if (types.length == 0) {
+                       t.child(tr(td("No 
arguments").colspan(3).style("text-align:center")));
+               } else {
+                       t.child(tr(th("Index"),th("Type"),th("Value")));
+                       for (int i = 0; i < types.length; i++) {
+                               String type = ClassUtils.toString(types[i]);
+                               t.child(tr(td(i), td(type), 
td(input().name(String.valueOf(i)).type("text"))));
+                       }
+               }
+               
+               t.child(
+                       tr(
+                               
td().colspan(3).style("text-align:right").children(
+                                       types.length == 0 ? null : 
button("reset", "Reset"),
+                                       
button("button","Cancel").onclick("window.location.href='/'"),
+                                       button("submit", "Submit")
+                               )
+                       )
+               );
+
+               return 
div(form().id("form").action("request:/").method(POST).children(t));
+       } 
+
+       /**
         * [POST /{javaInterface}/{javaMethod}] - Invoke the specified service 
method.
         * 
         * @param req The HTTP request.

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to