Support for 3rd-party proxy interfaces.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/7139635d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/7139635d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/7139635d

Branch: refs/heads/master
Commit: 7139635d3cc0c2304f83bed070c62f301c0a7b1c
Parents: 7f0e681
Author: JamesBognar <[email protected]>
Authored: Thu Apr 13 16:37:39 2017 -0400
Committer: JamesBognar <[email protected]>
Committed: Thu Apr 13 16:37:39 2017 -0400

----------------------------------------------------------------------
 .../urlencoding/UrlEncodingParserTest.java      |  112 +-
 .../urlencoding/UrlEncodingSerializerTest.java  |   47 +-
 .../main/java/org/apache/juneau/ClassMeta.java  |   44 +-
 .../apache/juneau/annotation/Remoteable.java    |   27 -
 .../main/java/org/apache/juneau/dto/Link.java   |    2 +-
 .../org/apache/juneau/internal/StringUtils.java |  123 ++
 .../java/org/apache/juneau/parser/Parser.java   |    2 +
 .../java/org/apache/juneau/remoteable/Body.java |   49 +
 .../org/apache/juneau/remoteable/FormData.java  |   60 +
 .../apache/juneau/remoteable/FormDataIfNE.java  |   34 +
 .../org/apache/juneau/remoteable/Header.java    |   56 +
 .../apache/juneau/remoteable/HeaderIfNE.java    |   34 +
 .../org/apache/juneau/remoteable/Query.java     |   60 +
 .../org/apache/juneau/remoteable/QueryIfNE.java |   34 +
 .../apache/juneau/remoteable/RemoteMethod.java  |   63 +
 .../juneau/remoteable/RemoteMethodArg.java      |   41 +
 .../apache/juneau/remoteable/Remoteable.java    |   65 +
 .../juneau/remoteable/RemoteableMeta.java       |   67 +
 .../remoteable/RemoteableMetadataException.java |   56 +
 .../juneau/remoteable/RemoteableMethodMeta.java |  177 ++
 .../org/apache/juneau/remoteable/package.html   |   72 +
 .../org/apache/juneau/uon/UonSerializer.java    |   23 +-
 .../apache/juneau/uon/UonSerializerSession.java |    7 +-
 .../java/org/apache/juneau/uon/UonUtils.java    |   52 +
 .../java/org/apache/juneau/uon/UonWriter.java   |   30 +-
 .../juneau/urlencoding/UrlEncodingParser.java   |   46 +-
 .../urlencoding/UrlEncodingSerializer.java      |   43 +-
 .../UrlEncodingSerializerBuilder.java           |   42 +
 .../UrlEncodingSerializerContext.java           |   32 +-
 .../UrlEncodingSerializerSession.java           |   17 +-
 .../java/org/apache/juneau/utils/AList.java     |   11 +
 .../main/java/org/apache/juneau/utils/ASet.java |   11 +
 juneau-core/src/main/javadoc/overview.html      |  321 +++-
 .../examples/addressbook/IAddressBook.java      |    3 +
 .../SampleRemoteableServicesResourceTest.java   |   21 +-
 .../org/apache/juneau/microservice/package.html |    2 +-
 .../juneau/rest/client/NameValuePairs.java      |   42 +-
 .../org/apache/juneau/rest/client/RestCall.java |  285 ++-
 .../apache/juneau/rest/client/RestClient.java   |  148 +-
 .../juneau/rest/client/RestClientBuilder.java   |   40 +-
 .../juneau/rest/client/RestRequestEntity.java   |    2 +
 .../org/apache/juneau/rest/client/RetryOn.java  |   24 +-
 .../rest/client/SerializedNameValuePair.java    |   31 +-
 .../org/apache/juneau/rest/client/package.html  |    4 +-
 .../juneau/rest/test/FormDataResource.java      |   37 +
 .../apache/juneau/rest/test/InterfaceProxy.java |  108 +-
 .../rest/test/InterfaceProxyResource.java       |  221 ++-
 .../java/org/apache/juneau/rest/test/Root.java  |    2 +
 .../org/apache/juneau/rest/test/TestUtils.java  |   79 +
 .../rest/test/ThirdPartyProxyResource.java      | 1218 +++++++++++++
 .../apache/juneau/rest/test/pojos/ABean.java    |   24 +
 .../juneau/rest/test/pojos/Constants.java       |   25 +
 .../rest/test/pojos/ImplicitSwappedPojo.java    |   35 +
 .../juneau/rest/test/pojos/SwappedPojo.java     |   20 +
 .../juneau/rest/test/pojos/SwappedPojoSwap.java |   35 +
 .../apache/juneau/rest/test/pojos/TestEnum.java |   17 +
 .../juneau/rest/test/pojos/TypedBean.java       |   17 +
 .../juneau/rest/test/pojos/TypedBeanImpl.java   |   25 +
 .../org/apache/juneau/rest/test/ConfigTest.java |    4 +-
 .../apache/juneau/rest/test/ContentTest.java    |   10 +-
 .../apache/juneau/rest/test/FormDataTest.java   |  100 ++
 .../juneau/rest/test/InterfaceProxyTest.java    |  548 +++---
 .../apache/juneau/rest/test/RestTestcase.java   |   38 +-
 .../apache/juneau/rest/test/RestUtilsTest.java  |   63 +-
 .../org/apache/juneau/rest/test/TestUtils.java  |   79 -
 .../juneau/rest/test/ThirdPartyProxyTest.java   | 1627 ++++++++++++++++++
 .../org/apache/juneau/rest/test/_TestSuite.java |    2 +
 .../java/org/apache/juneau/rest/Redirect.java   |    4 +-
 .../org/apache/juneau/rest/RestCallHandler.java |   10 +-
 .../org/apache/juneau/rest/RestContext.java     |   39 +-
 .../org/apache/juneau/rest/RestException.java   |   19 +
 .../org/apache/juneau/rest/RestRequest.java     |   87 +-
 .../org/apache/juneau/rest/RestResponse.java    |    3 +-
 .../org/apache/juneau/rest/RestServlet.java     |    7 +-
 .../java/org/apache/juneau/rest/RestUtils.java  |   91 -
 .../org/apache/juneau/rest/UrlPathPattern.java  |    7 +-
 .../juneau/rest/annotation/RestMethod.java      |    2 +-
 .../juneau/rest/labels/ResourceDescription.java |    6 +-
 .../apache/juneau/rest/labels/ResourceLink.java |    4 +-
 .../java/org/apache/juneau/rest/package.html    |    2 +-
 .../remoteable/RemoteableServiceProperties.java |    2 +-
 .../apache/juneau/rest/remoteable/package.html  |    6 +-
 .../apache/juneau/rest/vars/UrlEncodeVar.java   |    8 +-
 83 files changed, 5984 insertions(+), 1109 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
index 3fd318e..d23eca3 100755
--- 
a/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
@@ -48,13 +48,13 @@ public class UrlEncodingParserTest {
                assertEquals("a", p.parse(t, String.class));
 
                t = "a";
-               assertEquals("a", p.parseParameter(t, Object.class));
-               assertEquals("a", p.parseParameter(t, String.class));
+               assertEquals("a", p.parsePart(t, Object.class));
+               assertEquals("a", p.parsePart(t, String.class));
                t = "'a'";
-               assertEquals("a", p.parseParameter(t, String.class));
-               assertEquals("a", p.parseParameter(t, Object.class));
+               assertEquals("a", p.parsePart(t, String.class));
+               assertEquals("a", p.parsePart(t, Object.class));
                t = " 'a' ";
-               assertEquals("a", p.parseParameter(t, String.class));
+               assertEquals("a", p.parsePart(t, String.class));
 
                // 2nd level
                t = "?a=a";
@@ -74,7 +74,7 @@ public class UrlEncodingParserTest {
                assertNull(m.get("f"));
 
                t = "(a=b,c=123,d=false,e=true,f=%00)";
-               m = p.parseParameter(t, Map.class);
+               m = p.parsePart(t, Map.class);
                assertEquals("b", m.get("a"));
                assertTrue(m.get("c") instanceof Number);
                assertEquals(123, m.get("c"));
@@ -85,7 +85,7 @@ public class UrlEncodingParserTest {
                assertEquals("%00", m.get("f"));
 
                t = "(a=b,c=123,d=false,e=true,f=null)";
-               m = p.parseParameter(t, Map.class);
+               m = p.parsePart(t, Map.class);
                assertTrue(m.containsKey("f"));
                assertNull(m.get("f"));
 
@@ -99,7 +99,7 @@ public class UrlEncodingParserTest {
                t = "_value=null";
                assertNull(p.parse(t, Object.class));
                t = "null";
-               assertNull(p.parseParameter(t, Object.class));
+               assertNull(p.parsePart(t, Object.class));
 
                // 2nd level
                t = "?null=null";
@@ -121,10 +121,10 @@ public class UrlEncodingParserTest {
                // Empty array
                // Top level
                t = "@()";
-               l = (List)p.parseParameter(t, Object.class);
+               l = (List)p.parsePart(t, Object.class);
                assertTrue(l.isEmpty());
                t = " @( ) ";
-               l = p.parseParameter(t, List.class);
+               l = p.parsePart(t, List.class);
                assertTrue(l.isEmpty());
 
                // 2nd level in map
@@ -152,12 +152,12 @@ public class UrlEncodingParserTest {
                l = (List)l.get(0);
                assertTrue(l.isEmpty());
                t = "@(@())";
-               l = (List)p.parseParameter(t, Object.class);
+               l = (List)p.parsePart(t, Object.class);
                assertTrue(l.size() == 1);
                l = (List)l.get(0);
                assertTrue(l.isEmpty());
                t = "@(@())";
-               l = (List)p.parseParameter(t, LinkedList.class, List.class);
+               l = (List)p.parsePart(t, LinkedList.class, List.class);
                assertTrue(l.size() == 1);
                l = (List)l.get(0);
                assertTrue(l.isEmpty());
@@ -173,11 +173,11 @@ public class UrlEncodingParserTest {
                assertTrue(l.size() == 1);
                assertEquals("", l.get(0));
                t = "@('')";
-               l = (List)p.parseParameter(t, Object.class);
+               l = (List)p.parsePart(t, Object.class);
                assertTrue(l.size() == 1);
                assertEquals("", l.get(0));
                t = "@('')";
-               l = (List)p.parseParameter(t, List.class, String.class);
+               l = (List)p.parsePart(t, List.class, String.class);
                assertTrue(l.size() == 1);
                assertEquals("", l.get(0));
 
@@ -203,13 +203,13 @@ public class UrlEncodingParserTest {
                assertEquals("", l.get(1));
                assertEquals("", l.get(2));
                t = "@('','','')";
-               l = (List)p.parseParameter(t, Object.class);
+               l = (List)p.parsePart(t, Object.class);
                assertTrue(l.size() == 3);
                assertEquals("", l.get(0));
                assertEquals("", l.get(1));
                assertEquals("", l.get(2));
                t = "@('','','')";
-               l = (List)p.parseParameter(t, List.class, Object.class);
+               l = (List)p.parsePart(t, List.class, Object.class);
                assertTrue(l.size() == 3);
                assertEquals("", l.get(0));
                assertEquals("", l.get(1));
@@ -223,10 +223,10 @@ public class UrlEncodingParserTest {
                assertEquals("\u0000", p.parse(t, String.class));
                assertEquals("\u0000", p.parse(t, Object.class));
                t = "'\u0000'";
-               assertEquals("\u0000", p.parseParameter(t, Object.class));
+               assertEquals("\u0000", p.parsePart(t, Object.class));
                t = "'\u0000'";
-               assertEquals("\u0000", p.parseParameter(t, String.class));
-               assertEquals("\u0000", p.parseParameter(t, Object.class));
+               assertEquals("\u0000", p.parsePart(t, String.class));
+               assertEquals("\u0000", p.parsePart(t, Object.class));
 
                // 2nd level
                t = "?'\u0000'='\u0000'";
@@ -248,12 +248,12 @@ public class UrlEncodingParserTest {
                b = p.parse(t, Boolean.class);
                assertEquals(Boolean.FALSE, b);
                t = "false";
-               b = (Boolean)p.parseParameter(t, Object.class);
+               b = (Boolean)p.parsePart(t, Object.class);
                assertEquals(Boolean.FALSE, b);
-               b = p.parseParameter(t, Boolean.class);
+               b = p.parsePart(t, Boolean.class);
                assertEquals(Boolean.FALSE, b);
                t = "false";
-               b = p.parseParameter(t, Boolean.class);
+               b = p.parsePart(t, Boolean.class);
                assertEquals(Boolean.FALSE, b);
 
                // 2nd level
@@ -279,16 +279,16 @@ public class UrlEncodingParserTest {
                i = p.parse(t, Integer.class);
                assertEquals(123, i.intValue());
                t = "123";
-               i = (Integer)p.parseParameter(t, Object.class);
+               i = (Integer)p.parsePart(t, Object.class);
                assertEquals(123, i.intValue());
-               i = p.parseParameter(t, Integer.class);
+               i = p.parsePart(t, Integer.class);
                assertEquals(123, i.intValue());
-               d = p.parseParameter(t, Double.class);
+               d = p.parsePart(t, Double.class);
                assertEquals(123, d.intValue());
-               f = p.parseParameter(t, Float.class);
+               f = p.parsePart(t, Float.class);
                assertEquals(123, f.intValue());
                t = "123";
-               i = p.parseParameter(t, Integer.class);
+               i = p.parsePart(t, Integer.class);
                assertEquals(123, i.intValue());
 
                // 2nd level
@@ -303,7 +303,7 @@ public class UrlEncodingParserTest {
                t = "_value=x;/?:@-_.!*'";
                assertEquals("x;/?:@-_.!*'", p.parse(t, Object.class));
                t = "x;/?:@-_.!*'";
-               assertEquals("x;/?:@-_.!*'", p.parseParameter(t, Object.class));
+               assertEquals("x;/?:@-_.!*'", p.parsePart(t, Object.class));
 
                // 2nd level
                t = "?x;/?:@-_.!*'=x;/?:@-_.!*'";
@@ -327,10 +327,10 @@ public class UrlEncodingParserTest {
                assertEquals("x{}|\\^[]`<>#%\"&+", p.parse(t, Object.class));
                assertEquals("x{}|\\^[]`<>#%\"&+", p.parse(t, String.class));
                t = "x{}|\\^[]`<>#%\"&+";
-               assertEquals("x{}|\\^[]`<>#%\"&+", p.parseParameter(t, 
Object.class));
+               assertEquals("x{}|\\^[]`<>#%\"&+", p.parsePart(t, 
Object.class));
                t = "x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B";
-               assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
p.parseParameter(t, Object.class));
-               assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
p.parseParameter(t, String.class));
+               assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
p.parsePart(t, Object.class));
+               assertEquals("x%7B%7D%7C%5C%5E%5B%5D%60%3C%3E%23%25%22%26%2B", 
p.parsePart(t, String.class));
 
                // 2nd level
                t = "?x{}|\\^[]`<>#%\"&+=x{}|\\^[]`<>#%\"&+";
@@ -350,11 +350,11 @@ public class UrlEncodingParserTest {
                t = "_value='x$,()'";
                assertEquals("x$,()", p.parse(t, Object.class));
                t = "'x$,()'";
-               assertEquals("x$,()", p.parseParameter(t, Object.class));
+               assertEquals("x$,()", p.parsePart(t, Object.class));
                t = "_value='x~~$~~,~~(~~)'";
                assertEquals("x~$~,~(~)", p.parse(t, Object.class));
                t = "'x~~$~~,~~(~~)'";
-               assertEquals("x~$~,~(~)", p.parseParameter(t, Object.class));
+               assertEquals("x~$~,~(~)", p.parsePart(t, Object.class));
 
                // At secondary levels, these characters are escaped and not 
encoded.
                // 2nd level
@@ -373,9 +373,9 @@ public class UrlEncodingParserTest {
                t = "_value='x%3D'";
                assertEquals("x=", p.parse(t, Object.class));
                t = "'x='";
-               assertEquals("x=", p.parseParameter(t, Object.class));
+               assertEquals("x=", p.parsePart(t, Object.class));
                t = "'x%3D'";
-               assertEquals("x%3D", p.parseParameter(t, Object.class));
+               assertEquals("x%3D", p.parsePart(t, Object.class));
 
                // 2nd level
                t = "?'x%3D'='x%3D'";
@@ -394,8 +394,8 @@ public class UrlEncodingParserTest {
                assertEquals("()", p.parse(t, Object.class));
                assertEquals("()", p.parse(t, String.class));
                t = "'()'";
-               assertEquals("()", p.parseParameter(t, Object.class));
-               assertEquals("()", p.parseParameter(t, String.class));
+               assertEquals("()", p.parsePart(t, Object.class));
+               assertEquals("()", p.parsePart(t, String.class));
 
                // 2nd level
                t = "?'()'='()'";
@@ -412,9 +412,9 @@ public class UrlEncodingParserTest {
                t = "_value=$a";
                assertEquals("$a", p.parse(t, Object.class));
                t = "$a";
-               assertEquals("$a", p.parseParameter(t, Object.class));
+               assertEquals("$a", p.parsePart(t, Object.class));
                t = "$a";
-               assertEquals("$a", p.parseParameter(t, Object.class));
+               assertEquals("$a", p.parsePart(t, Object.class));
 
                // 2nd level
                t = "?$a=$a";
@@ -428,7 +428,7 @@ public class UrlEncodingParserTest {
                t = "_value=";
                assertEquals("", p.parse(t, Object.class));
                t = "";
-               assertEquals("", p.parseParameter(t, Object.class));
+               assertEquals("", p.parsePart(t, Object.class));
 
                // 2nd level
                t = "?=";
@@ -450,9 +450,9 @@ public class UrlEncodingParserTest {
                t = "_value='%0A'";
                assertEquals("\n", p.parse(t, Object.class));
                t = "'%0A'";
-               assertEquals("%0A", p.parseParameter(t, Object.class));
+               assertEquals("%0A", p.parsePart(t, Object.class));
                t = "'\n'";
-               assertEquals("\n", p.parseParameter(t, Object.class));
+               assertEquals("\n", p.parsePart(t, Object.class));
 
                // 2nd level
                t = "?'%0A'='%0A'";
@@ -482,11 +482,11 @@ public class UrlEncodingParserTest {
                assertEquals("¢", p.parse(t, Object.class));
                assertEquals("¢", p.parse(t, String.class));
                t = "¢";
-               assertEquals("¢", p.parseParameter(t, Object.class));
-               assertEquals("¢", p.parseParameter(t, String.class));
+               assertEquals("¢", p.parsePart(t, Object.class));
+               assertEquals("¢", p.parsePart(t, String.class));
                t = "%C2%A2";
-               assertEquals("%C2%A2", p.parseParameter(t, Object.class));
-               assertEquals("%C2%A2", p.parseParameter(t, String.class));
+               assertEquals("%C2%A2", p.parsePart(t, Object.class));
+               assertEquals("%C2%A2", p.parsePart(t, String.class));
 
                // 2nd level
                t = "?%C2%A2=%C2%A2";
@@ -507,11 +507,11 @@ public class UrlEncodingParserTest {
                assertEquals("€", p.parse(t, Object.class));
                assertEquals("€", p.parse(t, String.class));
                t = "€";
-               assertEquals("€", p.parseParameter(t, Object.class));
-               assertEquals("€", p.parseParameter(t, String.class));
+               assertEquals("€", p.parsePart(t, Object.class));
+               assertEquals("€", p.parsePart(t, String.class));
                t = "%E2%82%AC";
-               assertEquals("%E2%82%AC", p.parseParameter(t, Object.class));
-               assertEquals("%E2%82%AC", p.parseParameter(t, String.class));
+               assertEquals("%E2%82%AC", p.parsePart(t, Object.class));
+               assertEquals("%E2%82%AC", p.parsePart(t, String.class));
 
                // 2nd level
                t = "?%E2%82%AC=%E2%82%AC";
@@ -532,11 +532,11 @@ public class UrlEncodingParserTest {
                assertEquals("𤭢", p.parse(t, Object.class));
                assertEquals("𤭢", p.parse(t, String.class));
                t = "𤭢";
-               assertEquals("𤭢", p.parseParameter(t, Object.class));
-               assertEquals("𤭢", p.parseParameter(t, String.class));
+               assertEquals("𤭢", p.parsePart(t, Object.class));
+               assertEquals("𤭢", p.parsePart(t, String.class));
                t = "%F0%A4%AD%A2";
-               assertEquals("%F0%A4%AD%A2", p.parseParameter(t, Object.class));
-               assertEquals("%F0%A4%AD%A2", p.parseParameter(t, String.class));
+               assertEquals("%F0%A4%AD%A2", p.parsePart(t, Object.class));
+               assertEquals("%F0%A4%AD%A2", p.parsePart(t, String.class));
 
                // 2nd level
                t = "?%F0%A4%AD%A2=%F0%A4%AD%A2";
@@ -563,12 +563,12 @@ public class UrlEncodingParserTest {
                assertEquals(123, t.f2);
 
                s = "(f1=foo,f2=123)";
-               t = p.parseParameter(s, A.class);
+               t = p.parsePart(s, A.class);
                assertEquals("foo", t.f1);
                assertEquals(123, t.f2);
 
                s = "('f1'='foo','f2'=123)";
-               t = p.parseParameter(s, A.class);
+               t = p.parsePart(s, A.class);
                assertEquals("foo", t.f1);
                assertEquals(123, t.f2);
        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
index 66e50dc..07f3755 100755
--- 
a/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingSerializerTest.java
@@ -17,7 +17,10 @@ import static org.junit.Assert.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
 import org.apache.juneau.json.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.utils.*;
 import org.junit.*;
 
 @SuppressWarnings("javadoc")
@@ -449,8 +452,50 @@ public class UrlEncodingSerializerTest {
        public void testParseParameterObjectMap() throws Exception {
                String in = "(name='foo bar')";
                
-               ObjectMap r =  UrlEncodingParser.DEFAULT.parseParameter(in, 
ObjectMap.class);
+               ObjectMap r =  UrlEncodingParser.DEFAULT.parsePart(in, 
ObjectMap.class);
        
                assertEquals("{name:'foo bar'}", 
JsonSerializer.DEFAULT_LAX.toString(r));
        }
+       
+       
//====================================================================================================
+       // Test URLENC_paramFormat == PLAINTEXT.
+       
//====================================================================================================
+       @Test
+       public void testPlainTextParams() throws Exception {
+               WriterSerializer s = 
UrlEncodingSerializer.DEFAULT.builder().plainTextParams().build();
+               
+               assertEquals("_value=foo", s.serialize("foo"));
+               assertEquals("_value='foo'", s.serialize("'foo'"));
+               assertEquals("_value=(foo)", s.serialize("(foo)"));
+               assertEquals("_value=@(foo)", s.serialize("@(foo)"));
+               
+               Map<String,Object> m = new AMap<String,Object>()
+                       .append("foo", "foo")
+                       .append("'foo'", "'foo'")
+                       .append("(foo)", "(foo)")
+                       .append("@(foo)", "@(foo)");
+               assertEquals("foo=foo&'foo'='foo'&(foo)=(foo)&@(foo)=@(foo)", 
s.serialize(m));
+               
+               List<String> l = new AList<String>().appendAll("foo", "'foo'", 
"(foo)", "@(foo)");
+               assertEquals("0=foo&1='foo'&2=(foo)&3=@(foo)", s.serialize(l));
+               
+               A a = new A();
+               assertEquals("'foo'='foo'&(foo)=(foo)&@(foo)=@(foo)&foo=foo", 
s.serialize(a));
+       }
+       
+       @Bean(sort=true)
+       public static class A {
+               
+               @BeanProperty(name="foo")
+               public String f1 = "foo";
+               
+               @BeanProperty(name="'foo'")
+               public String f2 = "'foo'";
+
+               @BeanProperty(name="(foo)")
+               public String f3 = "(foo)";
+
+               @BeanProperty(name="@(foo)")
+               public String f4 = "@(foo)";
+       }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java 
b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
index f1513bb..4c1a333 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
@@ -26,6 +26,7 @@ import java.util.concurrent.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
+import org.apache.juneau.remoteable.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.transform.*;
 import org.apache.juneau.utils.*;
@@ -82,8 +83,7 @@ public final class ClassMeta<T> implements Type {
                isMemberClass;                                       // True if 
this is a non-static member class.
        private final Object primitiveDefault;                  // Default 
value for primitive type classes.
        private final Map<String,Method>
-               remoteableMethods,                                   // Methods 
annotated with @Remoteable.  Contains all public methods if class is annotated 
with @Remotable.
-               proxyableMethods,                                    // 
Remoteable methods only if at least one method is marked @Remoteable, otherwise 
all public methods.
+               remoteableMethods,                                   // Methods 
annotated with @RemoteMethod.
                publicMethods;                                       // All 
public methods, including static methods.
        private final PojoSwap<?,?>[] childPojoSwaps;           // Any 
PojoSwaps where the normal type is a subclass of this class.
        private final ConcurrentHashMap<Class<?>,PojoSwap<?,?>>
@@ -161,7 +161,6 @@ public final class ClassMeta<T> implements Type {
                this.primitiveDefault = builder.primitiveDefault;
                this.publicMethods = builder.publicMethods;
                this.remoteableMethods = builder.remoteableMethods;
-               this.proxyableMethods = builder.proxyableMethods;
                this.beanFilter = beanFilter;
                this.pojoSwap = builder.pojoSwap;
                this.extMeta = new MetadataMap();
@@ -212,7 +211,6 @@ public final class ClassMeta<T> implements Type {
                this.isMemberClass = mainType.isMemberClass;
                this.primitiveDefault = mainType.primitiveDefault;
                this.remoteableMethods = mainType.remoteableMethods;
-               this.proxyableMethods = mainType.proxyableMethods;
                this.publicMethods = mainType.publicMethods;
                this.beanContext = mainType.beanContext;
                this.serializedClassMeta = this;
@@ -260,7 +258,6 @@ public final class ClassMeta<T> implements Type {
                this.isMemberClass = false;
                this.primitiveDefault = null;
                this.remoteableMethods = null;
-               this.proxyableMethods = null;
                this.publicMethods = null;
                this.beanContext = null;
                this.serializedClassMeta = this;
@@ -306,8 +303,7 @@ public final class ClassMeta<T> implements Type {
                Object primitiveDefault = null;
                Map<String,Method>
                        publicMethods = new LinkedHashMap<String,Method>(),
-                       remoteableMethods = null,
-                       proxyableMethods = null;
+                       remoteableMethods = new LinkedHashMap<String,Method>();
                ClassMeta<?>
                        keyType = null,
                        valueType = null,
@@ -534,17 +530,22 @@ public final class ClassMeta<T> implements Type {
                                if (isPublic(m) && isNotDeprecated(m))
                                        
publicMethods.put(ClassUtils.getMethodSignature(m), m);
 
-                       if (c.getAnnotation(Remoteable.class) != null) {
-                               remoteableMethods = proxyableMethods = 
publicMethods;
-                       } else {
-                               for (Method m : c.getMethods()) {
-                                       if (m.getAnnotation(Remoteable.class) 
!= null) {
-                                               if (remoteableMethods == null)
-                                                       remoteableMethods = new 
LinkedHashMap<String,Method>();
-                                               
remoteableMethods.put(ClassUtils.getMethodSignature(m), m);
+                       Map<Class<?>,Remoteable> remoteableMap = 
ReflectionUtils.findAnnotationsMap(Remoteable.class, c);
+                       if (! remoteableMap.isEmpty()) {
+                               Map.Entry<Class<?>,Remoteable> e = 
remoteableMap.entrySet().iterator().next();  // Grab the first one.
+                               Class<?> ic = e.getKey();
+                               Remoteable r = e.getValue();
+                               String methodPaths = r.methodPaths();
+                               String expose = r.expose();
+                               for (Method m : "DECLARED".equals(expose) ? 
ic.getDeclaredMethods() : ic.getMethods()) {
+                                       if (isPublic(m)) {
+                                               RemoteMethod rm = 
m.getAnnotation(RemoteMethod.class);
+                                               if (rm != null || ! 
"ANNOTATED".equals(expose)) {
+                                                       String path = 
"NAME".equals(methodPaths) ? m.getName() : ClassUtils.getMethodSignature(m);
+                                                       
remoteableMethods.put(path, m);
+                                               }
                                        }
                                }
-                               proxyableMethods = (remoteableMethods != null ? 
remoteableMethods : publicMethods);
                        }
 
                        if (innerClass != Object.class) {
@@ -1193,17 +1194,6 @@ public final class ClassMeta<T> implements Type {
        }
 
        /**
-        * All methods on this class that can be exposed as a proxy method.
-        * <p>
-        * Same as {@link #getRemoteableMethods()} except returns all public 
methods if class is not annotated with {@link Remoteable @Remotable}.
-        *
-        * @return All proxyable methods on this class.
-        */
-       public Map<String,Method> getProxyableMethods() {
-               return proxyableMethods;
-       }
-
-       /**
         * All public methods on this class including static methods.
         * Keys are method signatures.
         *

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/annotation/Remoteable.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/annotation/Remoteable.java 
b/juneau-core/src/main/java/org/apache/juneau/annotation/Remoteable.java
deleted file mode 100644
index 42a8e46..0000000
--- a/juneau-core/src/main/java/org/apache/juneau/annotation/Remoteable.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// 
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                
                                              *
-// *                                                                           
                                              *
-// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
-// *                                                                           
                                              *
-// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the 
License.                                              *
-// 
***************************************************************************************************************************
-package org.apache.juneau.annotation;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-import java.lang.annotation.*;
-
-/**
- * Identifies services whose Java class or methods can be invoked remotely.
- */
-@Documented
-@Target({TYPE,METHOD})
-@Retention(RUNTIME)
-@Inherited
-public @interface Remoteable {}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/dto/Link.java 
b/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
index e3a3cd3..1b926aa 100644
--- a/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
+++ b/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
@@ -104,7 +104,7 @@ public class Link implements Comparable<Link> {
         */
        public Link setHref(String href, Object...args) {
                for (int i = 0; i < args.length; i++)
-                       args[i] = 
UrlEncodingSerializer.DEFAULT.serializeUrlPart(args[i]);
+                       args[i] = 
UrlEncodingSerializer.DEFAULT.serializePart(args[i], null, null);
                this.href = (args.length > 0 ? MessageFormat.format(href, args) 
: href);
                return this;
        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java 
b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
index 8f69eaf..a5bc00e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -16,6 +16,7 @@ import static org.apache.juneau.internal.ThrowableUtils.*;
 
 import java.io.*;
 import java.math.*;
+import java.net.*;
 import java.nio.*;
 import java.nio.charset.*;
 import java.util.*;
@@ -40,6 +41,9 @@ public final class StringUtils {
        // 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-_.!~*'()\\");
+
        // Maps BASE64 characters to 6-bit nibbles.
        private static final byte[] base64m2 = new byte[128];
        static {
@@ -1114,4 +1118,123 @@ public final class StringUtils {
                                s = s.substring(0, s.length()-1);
                return s;
        }
+
+       /**
+        * Returns <jk>true</jk> if the specified string is one of the 
specified values.
+        *
+        * @param s The string to test.
+        * Can be <jk>null</jk>.
+        * @param values The values to test.
+        * Can contain <jk>null</jk>.
+        * @return <jk>true</jk> if the specified string is one of the 
specified values.
+        */
+       public static boolean isOneOf(String s, String...values) {
+               for (int i = 0; i < values.length; i++)
+                       if (StringUtils.isEquals(s, values[i]))
+                               return true;
+               return false;
+       }
+
+       /**
+        * Trims <js>'/'</js> characters from both the start and end of the 
specified string.
+        *
+        * @param s The string to trim.
+        * @return A new trimmed string, or the same string if no trimming was 
necessary.
+        */
+       public static String trimSlashes(String s) {
+               if (s == null)
+                       return null;
+               while (endsWith(s, '/'))
+                       s = s.substring(0, s.length()-1);
+               while (s.length() > 0 && s.charAt(0) == '/')
+                       s = s.substring(1);
+               return s;
+       }
+
+       /**
+        * Trims <js>'/'</js> characters from the end of the specified string.
+        *
+        * @param s The string to trim.
+        * @return A new trimmed string, or the same string if no trimming was 
necessary.
+        */
+       public static String trimTrailingSlashes(String s) {
+               if (s == null)
+                       return null;
+               while (endsWith(s, '/'))
+                       s = s.substring(0, s.length()-1);
+               return s;
+       }
+
+       /**
+        * Trims <js>'/'</js> characters from the end of the specified string.
+        *
+        * @param s The string to trim.
+        * @return The same string buffer.
+        */
+       public static StringBuffer trimTrailingSlashes(StringBuffer s) {
+               if (s == null)
+                       return null;
+               while (s.length() > 0 && s.charAt(s.length()-1) == '/')
+                       s.setLength(s.length()-1);
+               return s;
+       }
+
+       /**
+        * Decodes a <code>application/x-www-form-urlencoded</code> string 
using <code>UTF-8</code> encoding scheme.
+        *
+        * @param s The string to decode.
+        * @return The decoded string, or <jk>null</jk> if input is 
<jk>null</jk>.
+        */
+       public static String urlDecode(String s) {
+               if (s == null)
+                       return s;
+               boolean needsDecode = false;
+               for (int i = 0; i < s.length() && ! needsDecode; i++) {
+                       char c = s.charAt(i);
+                       if (c == '+' || c == '%')
+                               needsDecode = true;
+               }
+               if (needsDecode)
+               try {
+                               return URLDecoder.decode(s, "UTF-8");
+                       } catch (UnsupportedEncodingException e) {/* Won't 
happen */}
+               return s;
+       }
+
+       /**
+        * Encodes a <code>application/x-www-form-urlencoded</code> string 
using <code>UTF-8</code> encoding scheme.
+        *
+        * @param s The string to encode.
+        * @return The encoded string, or <jk>null</jk> if input is 
<jk>null</jk>.
+        */
+       public static String urlEncode(String s) {
+               if (s == null)
+                       return null;
+               boolean needsEncode = false;
+               for (int i = 0; i < s.length() && ! needsEncode; i++)
+                       needsEncode |= (! unencodedChars.contains(s.charAt(i)));
+               if (needsEncode) {
+                       try {
+                               return URLEncoder.encode(s, "UTF-8");
+                       } catch (UnsupportedEncodingException e) {/* Won't 
happen */}
+               }
+               return s;
+       }
+
+       /**
+        * Returns the first non-whitespace character in the string.
+        *
+        * @param s The string to check.
+        * @return The first non-whitespace character, or <code>0</code> if the 
string is <jk>null</jk>, empty, or composed of only whitespace.
+        */
+       public static char firstNonWhitespaceChar(String s) {
+               if (s != null) {
+                       for (int i = 0; i < s.length(); i++) {
+                               char c = s.charAt(i);
+                               if (! Character.isWhitespace(c))
+                                       return c;
+                       }
+               }
+               return 0;
+       }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java 
b/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java
index 25b3840..f64b271 100644
--- a/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java
@@ -198,6 +198,8 @@ public abstract class Parser extends CoreObject {
         */
        public final <T> T parseSession(ParserSession session, ClassMeta<T> 
type) throws ParseException {
                try {
+                       if (type.isVoid())
+                               return null;
                        return doParse(session, type);
                } catch (ParseException e) {
                        throw e;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java
new file mode 100644
index 0000000..a73b694
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java
@@ -0,0 +1,49 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.io.*;
+import java.lang.annotation.*;
+
+import org.apache.juneau.serializer.*;
+
+/**
+ * Annotation applied to Java method arguments of interface proxies to denote 
that they are the HTTP body of the request.
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ *     <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ *     <jk>public interface</jk> MyProxy {
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod"</js>)
+ *             String myProxyMethod(<ja>@Body</ja> MyPojo pojo);
+ *     }
+ * </p>
+ * <p>
+ * The argument can be any of the following types:
+ * <ul class='spaced-list'>
+ *     <li>Any serializable POJO - Converted to text using the {@link 
Serializer} registered with the <code>RestClient</code>.
+ *     <li>{@link Reader} - Raw contents of {@code Reader} will be serialized 
to remote resource.
+ *     <li>{@link InputStream} - Raw contents of {@code InputStream} will be 
serialized to remote resource.
+ *     <li><code>HttpEntity</code> - Bypass Juneau serialization and pass 
HttpEntity directly to HttpClient.
+ *     <li><code>NameValuePairs</code> - Converted to a URL-encoded FORM post.
+ * </ul>
+ */
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface Body {}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
new file mode 100644
index 0000000..5d0ecf2
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
@@ -0,0 +1,60 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Annotation applied to Java method arguments of interface proxies to denote 
that they are FORM post parameters on the request.
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ *     <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ *     <jk>public interface</jk> MyProxy {
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod1"</js>)
+ *             String myProxyMethod1(<ja>@FormData</ja>(<js>"foo"</js>)</ja> 
String foo, <ja>@FormData</ja>(<js>"bar"</js>)</ja> MyPojo pojo);
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod2"</js>)
+ *             String myProxyMethod2(<ja>@FormData</ja> NameValuePairs form);
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod3"</js>)
+ *             String myProxyMethod2(<ja>@FormData</ja> 
Map&lt;String,Object&gt; form);
+ *     }
+ * </p>
+ * <p>
+ * The argument can be any of the following types:
+ * <ul class='spaced-list'>
+ *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *     <li><code>NameValuePairs</code> - Individual name-value pairs.
+ *     <li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * </ul>
+ */
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface FormData {
+
+       /**
+        * The form post parameter name.
+        * Can be blank if the value is an instance of 
<code>NameValuePairs</code> or <code>Map&lt;String,Object&gt;</code>.
+        */
+       String value() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
new file mode 100644
index 0000000..a156928
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
@@ -0,0 +1,34 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Identical to {@link FormData @FormData} except skips values if they're 
null/blank.
+ */
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface FormDataIfNE {
+
+       /**
+        * The form post parameter name.
+        * Can be blank if the value is an instance of 
<code>NameValuePairs</code> or <code>Map&lt;String,Object&gt;</code>.
+        */
+       String value() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
new file mode 100644
index 0000000..31eab82
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
@@ -0,0 +1,56 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Annotation applied to Java method arguments of interface proxies to denote 
that they are serialized as an HTTP header value.
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ *     <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ *     <jk>public interface</jk> MyProxy {
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod1"</js>)
+ *             String myProxyMethod1(<ja>@Header</ja>(<js>"Foo"</js>)</ja> 
String foo, <ja>@Header</ja>(<js>"Bar"</js>)</ja> MyPojo pojo);
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod2"</js>)
+ *             String myProxyMethod2(<ja>@Header</ja> Map&lt;String,Object&gt; 
headers);
+ *     }
+ * </p>
+ * <p>
+ * The argument can be any of the following types:
+ * <ul class='spaced-list'>
+ *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *     <li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * </ul>
+ */
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface Header {
+
+       /**
+        * The HTTP header name.
+        * Can be blank if the value is an instance of 
<code>Map&lt;String,Object&gt;</code>.
+        */
+       String value() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
new file mode 100644
index 0000000..e99915e
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
@@ -0,0 +1,34 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Identical to {@link Header @Header} except skips values if they're 
null/blank.
+ */
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface HeaderIfNE {
+
+       /**
+        * The HTTP header name.
+        * Can be blank if the value is an instance of 
<code>Map&lt;String,Object&gt;</code>.
+        */
+       String value();
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
new file mode 100644
index 0000000..0a82529
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
@@ -0,0 +1,60 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Annotation applied to Java method arguments of interface proxies to denote 
that they are QUERY parameters on the request.
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ *     <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ *     <jk>public interface</jk> MyProxy {
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod1"</js>)
+ *             String myProxyMethod1(<ja>@Query</ja>(<js>"foo"</js>)</ja> 
String foo, <ja>@Query</ja>(<js>"bar"</js>)</ja> MyPojo pojo);
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod2"</js>)
+ *             String myProxyMethod2(<ja>@Query</ja> Map&lt;String,Object&gt; 
query);
+ *
+ *             <ja>@RemoteMethod</ja>(path=<js>"/mymethod3"</js>)
+ *             String myProxyMethod2(<ja>@Query</ja> String queryString);
+ *     }
+ * </p>
+ * <p>
+ * The argument can be any of the following types:
+ * <ul class='spaced-list'>
+ *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *     <li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *     <li>{@link String} - Treated as a query string.
+ * </ul>
+*/
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface Query {
+
+       /**
+        * The query parameter name.
+        * Can be blank if the value is an instance of 
<code>Map&lt;String,Object&gt;</code> or <code>String</code>.
+        */
+       String value() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
new file mode 100644
index 0000000..10ae263
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
@@ -0,0 +1,34 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Identical to {@link Query @Query} except skips values if they're null/blank.
+ */
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface QueryIfNE {
+
+       /**
+        * The query parameter name.
+        * Can be blank if the value is an instance of 
<code>Map&lt;String,Object&gt;</code> or <code>String</code>.
+        */
+       String value();
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java
new file mode 100644
index 0000000..335c306
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java
@@ -0,0 +1,63 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.io.*;
+import java.lang.annotation.*;
+
+/**
+ * Annotation applied to Java methods on interface proxy classes.
+ * <p>
+ * TODO <i>(sorry)</i>
+ * <p>
+ * The return type on the Java method can be any of the following:
+ * <ul>
+ *     <li><jk>void</jk> - Don't parse any response.  Note that the method 
will still throw an exception if an error HTTP status is returned.
+ *     <li>Any parsable POJO - The body of the response will be converted to 
the POJO using the parser defined on the <code>RestClient</code>.
+ *     <li><code>HttpResponse</code> - Returns the raw 
<code>HttpResponse</code> returned by the inner <code>HttpClient</code>.
+ *     <li>{@link Reader} - Returns access to the raw reader of the response.
+ *     <li>{@link InputStream} - Returns access to the raw input stream of the 
response.
+ * </ul>
+ */
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+@Inherited
+public @interface RemoteMethod {
+
+       /**
+        * The path to the REST service for this Java method relative to the 
parent proxy interface URL.
+        * <p>
+        * The default value is the Java method name (e.g. 
<js>"http://localhost/root-url/org.foo.MyInterface/myMethod";</js>) if
+        *      {@link Remoteable#methodPaths() @Remoteable.methodPaths()} is 
<js>"NAME"</js>, or the Java method signature
+        *      (e.g. 
<js>"http://localhost/root-url/org.foo.MyInterface/myMethod(int,boolean,java.lang.String)"</js>)
 if
+        *      it's <js>"SIGNATURE"</js>.
+        */
+       String path() default "";
+
+       /**
+        * Defines whether to use <code>GET</code> or <code>POST</code> for 
REST calls.
+        * <p>
+        * Possible values:
+        * <ul>
+        *      <li><js>"POST"</js> (default) - Parameters are serialized using 
the serializer registered with the RestClient.
+        *      <li><js>"GET"</js> - Parameters are serialized using the 
UrlEncodingSerializer registered with the RestClient.
+        * </ul>
+        * <p>
+        * The default value is <js>"POST"</js>.
+        */
+       String httpMethod() default "POST";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
new file mode 100644
index 0000000..86d2eac
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
@@ -0,0 +1,41 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+/**
+ * Represents the metadata about an annotated argument of a method on a remote 
proxy interface.
+ */
+public class RemoteMethodArg {
+
+       /** The argument name.  Can be blank. */
+       public final String name;
+
+       /** The zero-based index of the argument on the Java method. */
+       public final int index;
+
+       /** The value is skipped if it's null/empty. */
+       public final boolean skipIfNE;
+
+       /**
+        * Constructor.
+        *
+        * @param name The argument name.  Can be blank.
+        * @param index The zero-based index of the argument on the Java method.
+        * @param skipIfNE The value is skipped if it's null/empty.
+        */
+       protected RemoteMethodArg(String name, int index, boolean skipIfNE) {
+               this.name = name;
+               this.index = index;
+               this.skipIfNE = skipIfNE;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/Remoteable.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/Remoteable.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/Remoteable.java
new file mode 100644
index 0000000..d2e33b2
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Remoteable.java
@@ -0,0 +1,65 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Identifies a remote proxy interface against a REST interface.
+ * <p>
+ * TODO <i>(sorry)</i>
+ */
+@Documented
+@Target({TYPE,METHOD})
+@Retention(RUNTIME)
+@Inherited
+public @interface Remoteable {
+
+       /**
+        * The absolute or relative path of the REST service.
+        * <p>
+        * When a relative path is specified, it's relative to the root-url 
defined on the <code>RestClient</code> used
+        * to instantiate the interface.
+        * <p>
+        * When no path is specified, the path is assumed to be the class name 
(e.g. <js>"http://localhost/root-url/org.foo.MyInterface";</js>)
+        */
+       String path() default "";
+
+       /**
+        * Identifies which methods on the interface should be exposed through 
the proxy.
+        * <p>
+        * The options are:
+        * <ul>
+        *      <li><js>"DECLARED"</js> (default) - Only methods declared on 
the immediate interface/class are exposed.  Methods on parent 
interfaces/classes are ignored.
+        *      <li><js>"ANNOTATED"</js> - Only methods annotated with {@link 
RemoteMethod} are exposed.
+        *      <li><js>"ALL"</js> - All methods defined on the interface or 
class are exposed.
+        * </ul>
+        */
+       String expose() default "DECLARED";
+
+       /**
+        * Defines the methodology to use for the path names of the methods 
when not explicitly defined via {@link RemoteMethod#path() 
@RemoteMethod.path()}.
+        * <p>
+        * The options are:
+        * <ul>
+        *      <li><js>"NAME"</js> (default) - Use the method name (e.g. 
"myMethod").
+        *      <li><js>"SIGNATURE"</js> - Use the method signature (e.g. 
"myMethod(int,boolean,java.lang.String,int[][][])").
+        * </ul>
+        * <p>
+        * Note that if you use <js>"NAME"</js>, method names must be unique in 
the interface.
+        */
+       String methodPaths() default "NAME";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMeta.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMeta.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMeta.java
new file mode 100644
index 0000000..a6ee0b2
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMeta.java
@@ -0,0 +1,67 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.ClassUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Contains the meta-data about a remoteable interface.
+ * <p>
+ * Captures the information in {@link Remoteable @Remoteable} and {@link 
RemoteMethod @RemoteMethod}
+ * annotations for caching and reuse.
+ */
+public class RemoteableMeta {
+
+       private final Map<Method,RemoteableMethodMeta> methods;
+
+       /**
+        * Constructor.
+        *
+        * @param c The interface class annotated with a {@link Remoteable 
@Remoteable} annotation (optional).
+        * @param restUrl The absolute URL of the remote REST interface that 
implements this proxy interface.
+        */
+       public RemoteableMeta(Class<?> c, String restUrl) {
+               Remoteable r = ReflectionUtils.getAnnotation(Remoteable.class, 
c);
+
+               String expose = r == null ? "DECLARED" : r.expose();
+               if (! isOneOf(expose, "ALL", "DECLARED", "ANNOTATED"))
+                       throw new RemoteableMetadataException(c, "Invalid value 
specified for ''expose'' annotation.  Valid values are 
[ALL,ANNOTATED,DECLARED].");
+
+               Map<Method,RemoteableMethodMeta> _methods = new 
LinkedHashMap<Method,RemoteableMethodMeta>();
+               for (Method m : expose.equals("DECLARED") ? 
c.getDeclaredMethods() : c.getMethods()) {
+                       if (isPublic(m)) {
+                               RemoteMethod rm = 
c.getAnnotation(RemoteMethod.class);
+                               if (rm != null || ! expose.equals("ANNOTATED"))
+                                       _methods.put(m, new 
RemoteableMethodMeta(restUrl, m));
+                       }
+               }
+
+               this.methods = Collections.unmodifiableMap(_methods);
+       }
+
+       /**
+        * Returns the metadata about the specified method on this interface 
proxy.
+        *
+        * @param m The method to look up.
+        * @return Metadata about the method, or <jk>null</jk> if no metadata 
was found.
+        */
+       public RemoteableMethodMeta getMethodMeta(Method m) {
+               return methods.get(m);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMetadataException.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMetadataException.java
 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMetadataException.java
new file mode 100644
index 0000000..384ef4f
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMetadataException.java
@@ -0,0 +1,56 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import java.lang.reflect.*;
+import java.text.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Exceptions caused by invalid Remoteable classes.
+ */
+public class RemoteableMetadataException extends FormattedRuntimeException {
+
+       private static final long serialVersionUID = 1L;
+
+       /**
+        * Constructor.
+        *
+        * @param m The interface method that has an invalid definition.
+        * @param message The {@link MessageFormat}-style message.
+        * @param args Optional {@link MessageFormat}-style arguments.
+        */
+       public RemoteableMetadataException(Method m, String message, 
Object...args) {
+               super(getMessage(m.getDeclaringClass(), m, message), args);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param c The interface class that has an invalid definition.
+        * @param message The {@link MessageFormat}-style message.
+        * @param args Optional {@link MessageFormat}-style arguments.
+        */
+       public RemoteableMetadataException(Class<?> c, String message, 
Object...args) {
+               super(getMessage(c, null, message), args);
+       }
+
+       private static final String getMessage(Class<?> c, Method m, String 
msg) {
+               StringBuilder sb = new StringBuilder("Invalid remoteable 
definition found on class ").append(c.getName());
+               if (m != null)
+                       sb.append(" on method ").append(m.getName());
+               sb.append(". ").append(msg);
+               return sb.toString();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
new file mode 100644
index 0000000..12b0d6b
--- /dev/null
+++ 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
@@ -0,0 +1,177 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.remoteable;
+
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Contains the meta-data about a Java method on a remoteable interface.
+ * <p>
+ * Captures the information in {@link RemoteMethod @RemoteMethod} annotations 
for caching and reuse.
+ */
+public class RemoteableMethodMeta {
+
+       private final String httpMethod;
+       private final String url;
+       private final RemoteMethodArg[] queryArgs, headerArgs, formDataArgs;
+       private final Integer[] otherArgs;
+       private final Integer bodyArg;
+
+       /**
+        * Constructor.
+        *
+        * @param restUrl The absolute URL of the REST interface backing the 
interface proxy.
+        * @param m The Java method.
+        */
+       public RemoteableMethodMeta(final String restUrl, Method m) {
+               Builder b = new Builder(restUrl, m);
+               this.httpMethod = b.httpMethod;
+               this.url = b.url;
+               this.queryArgs = b.queryArgs.toArray(new 
RemoteMethodArg[b.queryArgs.size()]);
+               this.formDataArgs = b.formDataArgs.toArray(new 
RemoteMethodArg[b.formDataArgs.size()]);
+               this.headerArgs = b.headerArgs.toArray(new 
RemoteMethodArg[b.headerArgs.size()]);
+               this.otherArgs = b.otherArgs.toArray(new 
Integer[b.otherArgs.size()]);
+               this.bodyArg = b.bodyArg;
+       }
+
+       private static class Builder {
+               private String httpMethod, url;
+               private List<RemoteMethodArg>
+                       queryArgs = new LinkedList<RemoteMethodArg>(),
+                       headerArgs = new LinkedList<RemoteMethodArg>(),
+                       formDataArgs = new LinkedList<RemoteMethodArg>();
+               private List<Integer> otherArgs = new LinkedList<Integer>();
+               private Integer bodyArg;
+
+               private Builder(String restUrl, Method m) {
+                       Remoteable r = 
m.getDeclaringClass().getAnnotation(Remoteable.class);
+                       RemoteMethod rm = m.getAnnotation(RemoteMethod.class);
+
+                       httpMethod = rm == null ? "POST" : rm.httpMethod();
+                       if (! isOneOf(httpMethod, "GET", "POST"))
+                               throw new RemoteableMetadataException(m, 
"Invalid value specified for @RemoteMethod.httpMethod() annotation.  Valid 
values are [GET,POST].");
+
+                       String path = rm == null || rm.path().isEmpty() ? null 
: rm.path();
+                       String methodPaths = r == null ? "NAME" : 
r.methodPaths();
+
+                       if (! isOneOf(methodPaths, "NAME", "SIGNATURE"))
+                               throw new RemoteableMetadataException(m, 
"Invalid value specified for @Remoteable.methodPaths() annotation.  Valid 
values are [NAME,SIGNATURE].");
+
+                       url =
+                               trimSlashes(restUrl)
+                               + '/'
+                               + (path != null ? trimSlashes(path) : 
urlEncode("NAME".equals(methodPaths) ? m.getName() : 
ClassUtils.getMethodSignature(m)));
+
+                       int index = 0;
+                       for (Annotation[] aa : m.getParameterAnnotations()) {
+                               boolean annotated = false;
+                               for (Annotation a : aa) {
+                                       Class<?> ca = a.annotationType();
+                                       if (ca == Query.class) {
+                                               Query q = (Query)a;
+                                               annotated = queryArgs.add(new 
RemoteMethodArg(q.value(), index, false));
+                                       } else if (ca == QueryIfNE.class) {
+                                               Query q = (Query)a;
+                                               annotated = queryArgs.add(new 
RemoteMethodArg(q.value(), index, true));
+                                       } else if (ca == FormData.class) {
+                                               FormData f = (FormData)a;
+                                               annotated = 
formDataArgs.add(new RemoteMethodArg(f.value(), index, false));
+                                       } else if (ca == FormDataIfNE.class) {
+                                               FormData f = (FormData)a;
+                                               annotated = 
formDataArgs.add(new RemoteMethodArg(f.value(), index, true));
+                                       } else if (ca == Header.class) {
+                                               Header h = (Header)a;
+                                               annotated = headerArgs.add(new 
RemoteMethodArg(h.value(), index, false));
+                                       } else if (ca == HeaderIfNE.class) {
+                                               Header h = (Header)a;
+                                               annotated = headerArgs.add(new 
RemoteMethodArg(h.value(), index, true));
+                                       } else if (ca == Body.class) {
+                                               annotated = true;
+                                               if (bodyArg == null)
+                                                       bodyArg = index;
+                                               else
+                                                       throw new 
RemoteableMetadataException(m, "Multiple @Body parameters found.  Only one can 
be specified per Java method.");
+                                       }
+                               }
+                               if (! annotated)
+                                       otherArgs.add(index);
+                               index++;
+                       }
+
+                       if (bodyArg != null && otherArgs.size() > 0)
+                               throw new RemoteableMetadataException(m, "@Body 
and non-annotated parameters found together.  Non-annotated parameters cannot 
be used when @Body is used.");
+               }
+       }
+
+       /**
+        * Returns the value of the {@link RemoteMethod#httpMethod()} 
annotation on this Java method.
+        * @return The value of the {@link RemoteMethod#httpMethod()} 
annotation, never <jk>null</jk>.
+        */
+       public String getHttpMethod() {
+               return httpMethod;
+       }
+
+       /**
+        * Returns the absolute URL of the REST interface invoked by this Java 
method.
+        * @return The absolute URL of the REST interface, never <jk>null</jk>.
+        */
+       public String getUrl() {
+               return url;
+       }
+
+       /**
+        * Returns the {@link Query @Query} annotated arguments on this Java 
method.
+        * @return A map of {@link Query#value() @Query.value()} names to 
zero-indexed argument indices.
+        */
+       public RemoteMethodArg[] getQueryArgs() {
+               return queryArgs;
+       }
+
+       /**
+        * Returns the {@link FormData @FormData} annotated arguments on this 
Java method.
+        * @return A map of {@link FormData#value() @FormData.value()} names to 
zero-indexed argument indices.
+        */
+       public RemoteMethodArg[] getFormDataArgs() {
+               return formDataArgs;
+       }
+
+       /**
+        * Returns the {@link Header @Header} annotated arguments on this Java 
method.
+        * @return A map of {@link Header#value() @Header.value()} names to 
zero-indexed argument indices.
+        */
+       public RemoteMethodArg[] getHeaderArgs() {
+               return headerArgs;
+       }
+
+       /**
+        * Returns the remaining non-annotated arguments on this Java method.
+        * @return A list of zero-indexed argument indices.
+        */
+       public Integer[] getOtherArgs() {
+               return otherArgs;
+       }
+
+       /**
+        * Returns the argument annotated with {@link Body @Body}.
+        * @return A index of the argument with the {@link Body @Body} 
annotation, or <jk>null</jk> if no argument exists.
+        */
+       public Integer getBodyArg() {
+               return bodyArg;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-core/src/main/java/org/apache/juneau/remoteable/package.html
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/package.html 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/package.html
new file mode 100644
index 0000000..45bc0e9
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/package.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<!--
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *  
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ 
***************************************************************************************************************************/
+ -->
+<html>
+<head>
+       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+       <style type="text/css">
+               /* For viewing in Page Designer */
+               @IMPORT url("../../../../../../javadoc.css");
+
+               /* For viewing in REST interface */
+               @IMPORT url("../htdocs/javadoc.css");
+               body { 
+                       margin: 20px; 
+               }       
+       </style>
+       <script>
+               /* Replace all @code and @link tags. */ 
+               window.onload = function() {
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, 
'<code>$3</code>');
+               }
+       </script>
+</head>
+<body>
+<p>Remoteable interface proxies</p>
+<script>
+       function toggle(x) {
+               var div = x.nextSibling;
+               while (div != null && div.nodeType != 1)
+                       div = div.nextSibling;
+               if (div != null) {
+                       var d = div.style.display;
+                       if (d == 'block' || d == '') {
+                               div.style.display = 'none';
+                               x.className += " closed";
+                       } else {
+                               div.style.display = 'block';
+                               x.className = 
x.className.replace(/(?:^|\s)closed(?!\S)/g , '' );
+                       }
+               }
+       }
+</script>
+
+<a id='TOC'></a><h5 class='toc'>Table of Contents</h5>
+<ol class='toc'>
+       <li><p><a class='doclink' href='#Overview'>Remoteable support 
overview</a></p> 
+</ol>
+
+<!-- 
========================================================================================================
 -->
+<a id="Overview"></a>
+<h2 class='topic' onclick='toggle(this)'>1 - Remoteable support overview</h2>
+<div class='topic'>
+       TODO <i>(sorry)</i>
+</div>
+<p align="center"><i><b>*** fín ***</b></i></p>
+
+</body>
+</html>
\ No newline at end of file

Reply via email to