You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2020/07/01 14:41:26 UTC
[juneau] branch master updated: Add header support to @Remote
annotation.
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 1dff96f Add header support to @Remote annotation.
1dff96f is described below
commit 1dff96f7fd454e2c77fd9278ec617779bdb13454
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Wed Jul 1 10:41:20 2020 -0400
Add header support to @Remote annotation.
---
.../org/apache/juneau/http/BasicHeaderTest.java} | 89 ++++++++--------
.../juneau/http/BasicNameValuePairTest.java} | 88 ++++++++--------
.../org/apache/juneau/http/HeaderSupplierTest.java | 51 +++++++++
.../org/apache/juneau/http/BasicNameValuePair.java | 15 +++
.../{remote/Remote.java => HeaderSupplier.java} | 115 +++++++++++++--------
.../Remote.java => NameValuePairSupplier.java} | 115 +++++++++++++--------
.../org/apache/juneau/http/header/BasicHeader.java | 15 +++
.../java/org/apache/juneau/http/remote/Remote.java | 63 +++++++++++
.../apache/juneau/internal/CollectionUtils.java | 36 +++++++
juneau-doc/docs/ReleaseNotes/8.1.4.html | 8 +-
.../juneau/rest/client/remote/RemoteMeta.java | 27 ++++-
.../org/apache/juneau/rest/client2/RestClient.java | 84 +++++++--------
12 files changed, 480 insertions(+), 226 deletions(-)
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/BasicHeaderTest.java
similarity index 64%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
copy to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/BasicHeaderTest.java
index c2e5217..78f54d3 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/BasicHeaderTest.java
@@ -1,45 +1,44 @@
-// ***************************************************************************************************************************
-// * 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.http.remote;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-import java.lang.annotation.*;
-
-/**
- * Identifies a proxy against a REST interface.
- *
- * <ul class='seealso'>
- * <li class='link'>{@doc juneau-rest-client.RestProxies}
- * </ul>
- */
-@Documented
-@Target({TYPE})
-@Retention(RUNTIME)
-@Inherited
-public @interface Remote {
-
- /**
- * REST service path.
- *
- * <p>
- * The possible values are:
- * <ul class='spaced-list'>
- * <li>An absolute URL.
- * <li>A relative URL interpreted as relative to the root URL defined on the <c>RestClient</c>
- * <li>No path interpreted as the class name (e.g. <js>"http://localhost/root-url/org.foo.MyInterface"</js>)
- * </ul>
- */
- String path() default "";
-}
+// ***************************************************************************************************************************
+// * 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.http;
+
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.http.header.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class BasicHeaderTest {
+
+ @Test
+ public void a01_of_pairString() {
+ BasicHeader h = BasicHeader.ofPair("Foo:bar");
+ assertEquals("Foo", h.getName());
+ assertEquals("bar", h.getValue());
+
+ h = BasicHeader.ofPair(" Foo : bar ");
+ assertEquals("Foo", h.getName());
+ assertEquals("bar", h.getValue());
+
+ h = BasicHeader.ofPair(" Foo : bar : baz ");
+ assertEquals("Foo", h.getName());
+ assertEquals("bar : baz", h.getValue());
+
+ h = BasicHeader.ofPair("Foo");
+ assertEquals("Foo", h.getName());
+ assertEquals("", h.getValue());
+
+ assertNull(BasicHeader.ofPair((String)null));
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/BasicNameValuePairTest.java
similarity index 64%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
copy to juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/BasicNameValuePairTest.java
index c2e5217..e8d6b73 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/BasicNameValuePairTest.java
@@ -1,45 +1,43 @@
-// ***************************************************************************************************************************
-// * 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.http.remote;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-import java.lang.annotation.*;
-
-/**
- * Identifies a proxy against a REST interface.
- *
- * <ul class='seealso'>
- * <li class='link'>{@doc juneau-rest-client.RestProxies}
- * </ul>
- */
-@Documented
-@Target({TYPE})
-@Retention(RUNTIME)
-@Inherited
-public @interface Remote {
-
- /**
- * REST service path.
- *
- * <p>
- * The possible values are:
- * <ul class='spaced-list'>
- * <li>An absolute URL.
- * <li>A relative URL interpreted as relative to the root URL defined on the <c>RestClient</c>
- * <li>No path interpreted as the class name (e.g. <js>"http://localhost/root-url/org.foo.MyInterface"</js>)
- * </ul>
- */
- String path() default "";
-}
+// ***************************************************************************************************************************
+// * 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.http;
+
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class BasicNameValuePairTest {
+
+ @Test
+ public void a01_of_pairString() {
+ BasicNameValuePair h = BasicNameValuePair.ofPair("Foo:bar");
+ assertEquals("Foo", h.getName());
+ assertEquals("bar", h.getValue());
+
+ h = BasicNameValuePair.ofPair(" Foo : bar ");
+ assertEquals("Foo", h.getName());
+ assertEquals("bar", h.getValue());
+
+ h = BasicNameValuePair.ofPair(" Foo : bar : baz ");
+ assertEquals("Foo", h.getName());
+ assertEquals("bar : baz", h.getValue());
+
+ h = BasicNameValuePair.ofPair("Foo");
+ assertEquals("Foo", h.getName());
+ assertEquals("", h.getValue());
+
+ assertNull(BasicNameValuePair.ofPair((String)null));
+ }
+}
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/HeaderSupplierTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/HeaderSupplierTest.java
new file mode 100644
index 0000000..4900fc9
--- /dev/null
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/http/HeaderSupplierTest.java
@@ -0,0 +1,51 @@
+// ***************************************************************************************************************************
+// * 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.http;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.http.*;
+import org.apache.juneau.http.header.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class HeaderSupplierTest {
+
+ @Test
+ public void a01_basic() {
+ HeaderSupplier h = HeaderSupplier.create();
+ assertObject(h.iterator()).json().is("[]");
+ h.add(header("Foo","bar"));
+ assertObject(h.iterator()).json().is("['Foo: bar']");
+ h.add(header("Foo","baz"));
+ assertObject(h.iterator()).json().is("['Foo: bar','Foo: baz']");
+ h.add(HeaderSupplier.create());
+ assertObject(h.iterator()).json().is("['Foo: bar','Foo: baz']");
+ h.add(HeaderSupplier.create().add(header("Foo","qux")));
+ assertObject(h.iterator()).json().is("['Foo: bar','Foo: baz','Foo: qux']");
+ h.add(HeaderSupplier.create().add(header("Foo","quux")).add(header("Foo","quuux")));
+ assertObject(h.iterator()).json().is("['Foo: bar','Foo: baz','Foo: qux','Foo: quux','Foo: quuux']");
+ h.add(HeaderSupplier.create().add(HeaderSupplier.create().add(header("Foo","ruux")).add(header("Foo","ruuux"))));
+ assertObject(h.iterator()).json().is("['Foo: bar','Foo: baz','Foo: qux','Foo: quux','Foo: quuux','Foo: ruux','Foo: ruuux']");
+ h.add((Header)null);
+ assertObject(h.iterator()).json().is("['Foo: bar','Foo: baz','Foo: qux','Foo: quux','Foo: quuux','Foo: ruux','Foo: ruuux']");
+ h.add((HeaderSupplier)null);
+ assertObject(h.iterator()).json().is("['Foo: bar','Foo: baz','Foo: qux','Foo: quux','Foo: quuux','Foo: ruux','Foo: ruuux']");
+ }
+
+ private static Header header(String name, Object val) {
+ return BasicHeader.of(name, val);
+ }
+
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicNameValuePair.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicNameValuePair.java
index be5601b..5ebdf31 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicNameValuePair.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/BasicNameValuePair.java
@@ -45,6 +45,21 @@ public class BasicNameValuePair implements NameValuePair, Headerable {
}
/**
+ * Creates a {@link NameValuePair} from a name/value pair string (e.g. <js>"Foo: bar"</js>)
+ *
+ * @param pair The pair string.
+ * @return A new {@link NameValuePair} object.
+ */
+ public static BasicNameValuePair ofPair(String pair) {
+ if (pair == null)
+ return null;
+ int i = pair.indexOf(':');
+ if (i == -1)
+ return of(pair, "");
+ return of(pair.substring(0,i).trim(), pair.substring(i+1).trim());
+ }
+
+ /**
* Convenience creator using supplier.
*
* <p>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HeaderSupplier.java
similarity index 55%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HeaderSupplier.java
index c2e5217..01b6b34 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HeaderSupplier.java
@@ -1,45 +1,70 @@
-// ***************************************************************************************************************************
-// * 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.http.remote;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-import java.lang.annotation.*;
-
-/**
- * Identifies a proxy against a REST interface.
- *
- * <ul class='seealso'>
- * <li class='link'>{@doc juneau-rest-client.RestProxies}
- * </ul>
- */
-@Documented
-@Target({TYPE})
-@Retention(RUNTIME)
-@Inherited
-public @interface Remote {
-
- /**
- * REST service path.
- *
- * <p>
- * The possible values are:
- * <ul class='spaced-list'>
- * <li>An absolute URL.
- * <li>A relative URL interpreted as relative to the root URL defined on the <c>RestClient</c>
- * <li>No path interpreted as the class name (e.g. <js>"http://localhost/root-url/org.foo.MyInterface"</js>)
- * </ul>
- */
- String path() default "";
-}
+// ***************************************************************************************************************************
+// * 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.http;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.http.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Specifies a dynamic supplier of {@link Header} objects.
+ *
+ * This class is thread safe.
+ */
+public class HeaderSupplier implements Iterable<Header> {
+
+ /** Represents no header supplier */
+ public final class Null extends HeaderSupplier {}
+
+ /**
+ * Convenience creator.
+ *
+ * @return A new {@link HeaderSupplier} object.
+ */
+ public static HeaderSupplier create() {
+ return new HeaderSupplier();
+ }
+
+ private final List<Iterable<Header>> headers = new CopyOnWriteArrayList<>();
+
+ /**
+ * Add a header to this supplier.
+ *
+ * @param h The header to add. <jk>null</jk> values are ignored.
+ * @return This object (for method chaining).
+ */
+ public HeaderSupplier add(Header h) {
+ if (h != null)
+ headers.add(Collections.singleton(h));
+ return this;
+ }
+
+ /**
+ * Add a supplier to this supplier.
+ *
+ * @param h The supplier to add. <jk>null</jk> values are ignored.
+ * @return This object (for method chaining).
+ */
+ public HeaderSupplier add(HeaderSupplier h) {
+ if (h != null)
+ headers.add(h);
+ return this;
+ }
+
+ @Override
+ public Iterator<Header> iterator() {
+ return CollectionUtils.iterator(headers);
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/NameValuePairSupplier.java
similarity index 53%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/NameValuePairSupplier.java
index c2e5217..4646614 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/NameValuePairSupplier.java
@@ -1,45 +1,70 @@
-// ***************************************************************************************************************************
-// * 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.http.remote;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-import java.lang.annotation.*;
-
-/**
- * Identifies a proxy against a REST interface.
- *
- * <ul class='seealso'>
- * <li class='link'>{@doc juneau-rest-client.RestProxies}
- * </ul>
- */
-@Documented
-@Target({TYPE})
-@Retention(RUNTIME)
-@Inherited
-public @interface Remote {
-
- /**
- * REST service path.
- *
- * <p>
- * The possible values are:
- * <ul class='spaced-list'>
- * <li>An absolute URL.
- * <li>A relative URL interpreted as relative to the root URL defined on the <c>RestClient</c>
- * <li>No path interpreted as the class name (e.g. <js>"http://localhost/root-url/org.foo.MyInterface"</js>)
- * </ul>
- */
- String path() default "";
-}
+// ***************************************************************************************************************************
+// * 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.http;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.http.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Specifies a dynamic supplier of {@link NameValuePair} objects.
+ *
+ * This class is thread safe.
+ */
+public class NameValuePairSupplier implements Iterable<NameValuePair> {
+
+ /** Represents no header supplier */
+ public final class Null extends NameValuePairSupplier {}
+
+ /**
+ * Convenience creator.
+ *
+ * @return A new {@link NameValuePairSupplier} object.
+ */
+ public static NameValuePairSupplier create() {
+ return new NameValuePairSupplier();
+ }
+
+ private final List<Iterable<NameValuePair>> pairs = new CopyOnWriteArrayList<>();
+
+ /**
+ * Add a name-value pair to this supplier.
+ *
+ * @param h The name-value pair to add. <jk>null</jk> values are ignored.
+ * @return This object (for method chaining).
+ */
+ public NameValuePairSupplier add(NameValuePair h) {
+ if (h != null)
+ pairs.add(Collections.singleton(h));
+ return this;
+ }
+
+ /**
+ * Add a supplier to this supplier.
+ *
+ * @param h The supplier to add. <jk>null</jk> values are ignored.
+ * @return This object (for method chaining).
+ */
+ public NameValuePairSupplier add(NameValuePairSupplier h) {
+ if (h != null)
+ pairs.add(h);
+ return this;
+ }
+
+ @Override
+ public Iterator<NameValuePair> iterator() {
+ return CollectionUtils.iterator(pairs);
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/BasicHeader.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/BasicHeader.java
index c3e1425..4649653 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/BasicHeader.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/BasicHeader.java
@@ -53,6 +53,21 @@ public class BasicHeader implements Header, Cloneable, Serializable {
}
/**
+ * Creates a {@link Header} from a name/value pair string (e.g. <js>"Foo: bar"</js>)
+ *
+ * @param pair The pair string.
+ * @return A new {@link Header} object.
+ */
+ public static BasicHeader ofPair(String pair) {
+ if (pair == null)
+ return null;
+ int i = pair.indexOf(':');
+ if (i == -1)
+ return of(pair, "");
+ return of(pair.substring(0,i).trim(), pair.substring(i+1).trim());
+ }
+
+ /**
* Convenience creator.
*
* @param o The name value pair that makes up the header name and value.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
index c2e5217..2df756c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/remote/Remote.java
@@ -17,6 +17,8 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.http.*;
+
/**
* Identifies a proxy against a REST interface.
*
@@ -42,4 +44,65 @@ public @interface Remote {
* </ul>
*/
String path() default "";
+
+ /**
+ * Default request headers.
+ *
+ * <p>
+ * Specifies headers to set on all requests.
+ *
+ * <ul class='notes'>
+ * <li>
+ * Supports {@doc DefaultSvlVariables}
+ * (e.g. <js>"$P{mySystemProperty}"</js>).
+ * </ul>
+ */
+ String[] headers() default {};
+
+ /**
+ * Default request header supplier.
+ *
+ * <p>
+ * Specifies a dynamic supplier of headers to set on all requests.
+ *
+ * <ul class='notes'>
+ * <li>
+ * Supplier class must provide a public no-arg constructor.
+ * </ul>
+ */
+ Class<? extends HeaderSupplier> headerSupplier() default HeaderSupplier.Null.class;
+
+ /**
+ * Specifies the client version of this interface.
+ *
+ * <p>
+ * Used to populate the <js>"X-Client-Version"</js> header that identifies what version of client this is
+ * so that the server side can handle older versions accordingly.
+ *
+ * <p>
+ * The format of this is a string of the format <c>#[.#[.#[...]]</c> (e.g. <js>"1.2.3"</js>).
+ *
+ * <p>
+ * The server side then uses an OSGi-version matching pattern to identify which methods to call:
+ * <p class='bcode w800'>
+ * <jc>// Call this method if X-Client-Version is at least 2.0.
+ * // Note that this also matches 2.0.1.</jc>
+ * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"2.0"</js>)
+ * <jk>public</jk> Object method1() {...}
+ *
+ * <jc>// Call this method if X-Client-Version is at least 1.1, but less than 2.0.</jc>
+ * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[1.1,2.0)"</js>)
+ * <jk>public</jk> Object method2() {...}
+ *
+ * <jc>// Call this method if X-Client-Version is less than 1.1.</jc>
+ * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[0,1.1)"</js>)
+ * <jk>public</jk> Object method3() {...}
+ * </p>
+ */
+ String version() default "";
+
+ /**
+ * Specifies the client version header name.
+ */
+ String versionHeader() default "X-Client-Version";
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
index 81a8123..55965ad 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
@@ -71,6 +71,42 @@ public final class CollectionUtils {
}
/**
+ * Creates an iterator over a list of iterable objects.
+ *
+ * @param <E> The element type.
+ * @param l The iterables to iterate over.
+ * @return A new iterator.
+ */
+ public static <E> Iterator<E> iterator(final List<Iterable<E>> l) {
+ return new Iterator<E>() {
+ Iterator<Iterable<E>> i1 = l.iterator();
+ Iterator<E> i2 = i1.hasNext() ? i1.next().iterator() : null;
+
+ @Override /* Iterator */
+ public boolean hasNext() {
+ while (i2 != null && ! i2.hasNext())
+ i2 = (i1.hasNext() ? i1.next().iterator() : null);
+ return (i2 != null);
+ }
+
+ @Override /* Iterator */
+ public E next() {
+ hasNext();
+ if (i2 == null)
+ throw new NoSuchElementException();
+ return i2.next();
+ }
+
+ @Override /* Iterator */
+ public void remove() {
+ if (i2 == null)
+ throw new NoSuchElementException();
+ i2.remove();
+ }
+ };
+ }
+
+ /**
* Adds a set of values to an existing list.
*
* @param appendTo
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index a114811..1a62c16 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -474,7 +474,13 @@
Future<String> f = i.doGet();
<jc>// Do other stuff.</jc>
String result = f.get();
- </li>p>
+ </p>
+ <li>Additions to {@link oaj.http.remote.Remote} annotation:
+ <ul>
+ <li>{@link oaj.http.remote.Remote#version version} - Adds a client version header to all requests.
+ <li>{@link oaj.http.remote.Remote#headers headers} - Adds a set of headers to all requests.
+ <li>{@link oaj.http.remote.Remote#headerSupplier headerSupplier} - Adds a dynamic supplier of headers to all requests.
+ </ul>
</ul>
<h5 class='topic w800'>juneau-rest-mock</h5>
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMeta.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMeta.java
index d9ad3b5..d555cfb 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMeta.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMeta.java
@@ -16,7 +16,10 @@ import static org.apache.juneau.internal.StringUtils.*;
import java.lang.reflect.*;
import java.util.*;
+import org.apache.http.*;
import org.apache.juneau.collections.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.header.*;
import org.apache.juneau.http.remote.*;
import org.apache.juneau.reflect.*;
@@ -35,6 +38,7 @@ public class RemoteMeta {
private final Map<Method,RemoteMethodMeta> methods;
private final String path;
+ private final HeaderSupplier headerSupplier = HeaderSupplier.create();
/**
* Constructor.
@@ -52,9 +56,21 @@ public class RemoteMeta {
for (org.apache.juneau.http.remote.RemoteResource r : ci.getAnnotations(org.apache.juneau.http.remote.RemoteResource.class))
if (! r.path().isEmpty())
path = trimSlashes(r.path());
- for (Remote r : ci.getAnnotations(Remote.class))
+ for (Remote r : ci.getAnnotations(Remote.class)) {
if (! r.path().isEmpty())
path = trimSlashes(r.path());
+ for (String h : r.headers())
+ headerSupplier.add(BasicHeader.ofPair(h));
+ if (! r.version().isEmpty())
+ headerSupplier.add(ClientVersion.of(r.version()));
+ if (r.headerSupplier() != HeaderSupplier.Null.class) {
+ try {
+ headerSupplier.add(r.headerSupplier().newInstance());
+ } catch (Exception e) {
+ throw new RuntimeException("Could not instantiate HeaderSupplier class.", e);
+ }
+ }
+ }
AMap<Method,RemoteMethodMeta> methods = AMap.of();
for (MethodInfo m : ci.getPublicMethods())
@@ -86,4 +102,13 @@ public class RemoteMeta {
public String getPath() {
return path;
}
+
+ /**
+ * Returns the headers to set on all requests.
+ *
+ * @return The headers to set on all requests.
+ */
+ public Iterable<Header> getHeaders() {
+ return headerSupplier;
+ }
}
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
index 0eac74d..ed2fc24 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
@@ -30,7 +30,6 @@ import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import java.util.regex.*;
-import java.util.stream.*;
import javax.net.ssl.*;
@@ -1872,7 +1871,8 @@ public class RestClient extends BeanContext implements HttpClient, Closeable, Re
private static final Set<String> NO_BODY_METHODS = ASet.unmodifiable("GET","HEAD","DELETE","CONNECT","OPTIONS","TRACE");
- private final List<Object> headers, query, formData;
+ private final HeaderSupplier headers;
+ private final NameValuePairSupplier query, formData;
final CloseableHttpClient httpClient;
private final boolean keepHttpClientOpen, leakDetection;
private final UrlEncodingSerializer urlEncodingSerializer; // Used for form posts only.
@@ -1982,48 +1982,41 @@ public class RestClient extends BeanContext implements HttpClient, Closeable, Re
HttpPartSerializerSession partSerializerSession = partSerializer.createPartSession(null);
- Function<Object,Object> headerFunction = new Function<Object,Object>() {
- @Override
- public Object apply(Object x) {
- if (x instanceof SerializedHeaderBuilder)
- x = ((SerializedHeaderBuilder)x).serializer(partSerializerSession, false).build();
- else if (x instanceof SerializedNameValuePairBuilder)
- x = ((SerializedNameValuePairBuilder)x).serializer(partSerializerSession, false).build();
- return BasicHeader.cast(x);
- }
- };
-
- Function<Object,Object> nameValuePairFunction = new Function<Object,Object>() {
- @Override
- public Object apply(Object x) {
- if (x instanceof SerializedNameValuePairBuilder)
- x = ((SerializedNameValuePairBuilder)x).serializer(partSerializerSession, false).build();
- if (x instanceof SerializedHeaderBuilder)
- x = ((SerializedHeaderBuilder)x).serializer(partSerializerSession, false).build();
- return BasicNameValuePair.cast(x);
- }
- };
-
- this.headers = Collections.unmodifiableList(
- getListProperty(RESTCLIENT_headers, Object.class)
- .stream()
- .map(headerFunction)
- .collect(Collectors.toList())
- );
-
- this.query = Collections.unmodifiableList(
- getListProperty(RESTCLIENT_query, Object.class)
- .stream()
- .map(nameValuePairFunction)
- .collect(Collectors.toList())
- );
-
- this.formData = Collections.unmodifiableList(
- getListProperty(RESTCLIENT_formData, Object.class)
- .stream()
- .map(nameValuePairFunction)
- .collect(Collectors.toList())
- );
+ this.headers = HeaderSupplier.create();
+ for (Object o : getListProperty(RESTCLIENT_headers, Object.class)) {
+ if (o instanceof SerializedHeaderBuilder)
+ o = ((SerializedHeaderBuilder)o).serializer(partSerializerSession, false).build();
+ else if (o instanceof SerializedNameValuePairBuilder)
+ o = ((SerializedNameValuePairBuilder)o).serializer(partSerializerSession, false).build();
+ if (o instanceof HeaderSupplier)
+ headers.add((HeaderSupplier)o);
+ else
+ headers.add(BasicHeader.cast(o));
+ }
+
+ this.query = NameValuePairSupplier.create();
+ for (Object o : getListProperty(RESTCLIENT_query, Object.class)) {
+ if (o instanceof SerializedHeaderBuilder)
+ o = ((SerializedHeaderBuilder)o).serializer(partSerializerSession, false).build();
+ else if (o instanceof SerializedNameValuePairBuilder)
+ o = ((SerializedNameValuePairBuilder)o).serializer(partSerializerSession, false).build();
+ if (o instanceof NameValuePairSupplier)
+ query.add((NameValuePairSupplier)o);
+ else
+ query.add(BasicNameValuePair.cast(o));
+ }
+
+ this.formData = NameValuePairSupplier.create();
+ for (Object o : getListProperty(RESTCLIENT_formData, Object.class)) {
+ if (o instanceof SerializedHeaderBuilder)
+ o = ((SerializedHeaderBuilder)o).serializer(partSerializerSession, false).build();
+ else if (o instanceof SerializedNameValuePairBuilder)
+ o = ((SerializedNameValuePairBuilder)o).serializer(partSerializerSession, false).build();
+ if (o instanceof NameValuePairSupplier)
+ formData.add((NameValuePairSupplier)o);
+ else
+ formData.add(BasicNameValuePair.cast(o));
+ }
this.callHandler = getInstanceProperty(RESTCLIENT_callHandler, RestCallHandler.class, BasicRestCallHandler.class, ResourceResolver.FUZZY, ps, this);
@@ -3039,6 +3032,9 @@ public class RestClient extends BeanContext implements HttpClient, Closeable, Re
rc.serializer(serializer);
rc.parser(parser);
+ for (Header h : rm.getHeaders())
+ rc.header(h);
+
for (RemoteMethodArg a : rmm.getPathArgs())
rc.pathArg(a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer(s));