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