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<String,Object> 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<String,Object></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<String,Object></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<String,Object></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<String,Object> 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<String,Object></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<String,Object></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<String,Object></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<String,Object> 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<String,Object></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<String,Object></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<String,Object></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
