From d29f4161ff07e66dd996df2947932141233d81fc Mon Sep 17 00:00:00 2001 From: Jan Supol Date: Wed, 13 Nov 2019 21:04:18 +0100 Subject: [PATCH 1/4] new InvocationBuilderListener SPI Signed-off-by: Jan Supol --- .../InvocationBuilderListenerStage.java | 126 +++++++++++++ .../jersey/client/JerseyWebTarget.java | 12 +- .../client/spi/InvocationBuilderListener.java | 170 ++++++++++++++++++ .../spi/InvocationBuilderListenerTest.java | 103 +++++++++++ 4 files changed, 408 insertions(+), 3 deletions(-) create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/spi/InvocationBuilderListener.java create mode 100644 core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java diff --git a/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java b/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java new file mode 100644 index 0000000000..7b447ccac7 --- /dev/null +++ b/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011, 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.client; + +import org.glassfish.jersey.client.spi.InvocationBuilderListener; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Providers; +import org.glassfish.jersey.model.internal.RankedComparator; + +import javax.ws.rs.client.Invocation; +import javax.ws.rs.core.CacheControl; +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import java.util.Iterator; +import java.util.Locale; + +/** + * Client request processing stage. During a request creation, when the {@link Invocation.Builder} + * would be created, this class is utilized. + */ +/* package */ class InvocationBuilderListenerStage { + final Iterator invocationBuilderListenerIterator; + + /* package */ InvocationBuilderListenerStage(InjectionManager injectionManager) { + final RankedComparator comparator = + new RankedComparator<>(RankedComparator.Order.ASCENDING); + invocationBuilderListenerIterator = Providers + .getAllProviders(injectionManager, InvocationBuilderListener.class, comparator).iterator(); + } + + /* package */ void invokeListener(Invocation.Builder builder) { + while (invocationBuilderListenerIterator.hasNext()) { + invocationBuilderListenerIterator.next().onNewBuilder(new InvocationBuilderContextImpl(builder)); + } + } + + private static class InvocationBuilderContextImpl implements InvocationBuilderListener.InvocationBuilderContext { + private final Invocation.Builder builder; + + private InvocationBuilderContextImpl(Invocation.Builder builder) { + this.builder = builder; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext accept(String... mediaTypes) { + builder.accept(mediaTypes); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext accept(MediaType... mediaTypes) { + builder.accept(mediaTypes); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext acceptLanguage(Locale... locales) { + builder.acceptLanguage(locales); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext acceptLanguage(String... locales) { + builder.acceptLanguage(locales); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext acceptEncoding(String... encodings) { + builder.acceptEncoding(encodings); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext cookie(Cookie cookie) { + builder.cookie(cookie); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext cookie(String name, String value) { + builder.cookie(name, value); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext cacheControl(CacheControl cacheControl) { + builder.cacheControl(cacheControl); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext header(String name, Object value) { + builder.header(name, value); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext headers(MultivaluedMap headers) { + builder.headers(headers); + return this; + } + + @Override + public InvocationBuilderListener.InvocationBuilderContext property(String name, Object value) { + builder.property(name, value); + return this; + } + } +} + diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyWebTarget.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyWebTarget.java index 6c4f029fdc..ed986d74d9 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyWebTarget.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyWebTarget.java @@ -188,14 +188,15 @@ private static void checkForNullValues(String name, Object[] values) { @Override public JerseyInvocation.Builder request() { checkNotClosed(); - return new JerseyInvocation.Builder(getUri(), config.snapshot()); + JerseyInvocation.Builder b = new JerseyInvocation.Builder(getUri(), config.snapshot()); + return onBuilder(b); } @Override public JerseyInvocation.Builder request(String... acceptedResponseTypes) { checkNotClosed(); JerseyInvocation.Builder b = new JerseyInvocation.Builder(getUri(), config.snapshot()); - b.request().accept(acceptedResponseTypes); + onBuilder(b).request().accept(acceptedResponseTypes); return b; } @@ -203,7 +204,7 @@ public JerseyInvocation.Builder request(String... acceptedResponseTypes) { public JerseyInvocation.Builder request(MediaType... acceptedResponseTypes) { checkNotClosed(); JerseyInvocation.Builder b = new JerseyInvocation.Builder(getUri(), config.snapshot()); - b.request().accept(acceptedResponseTypes); + onBuilder(b).request().accept(acceptedResponseTypes); return b; } @@ -358,4 +359,9 @@ public JerseyWebTarget preInitialize() { public String toString() { return "JerseyWebTarget { " + targetUri.toTemplate() + " }"; } + + private static JerseyInvocation.Builder onBuilder(JerseyInvocation.Builder builder) { + new InvocationBuilderListenerStage(builder.request().getInjectionManager()).invokeListener(builder); + return builder; + } } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/spi/InvocationBuilderListener.java b/core-client/src/main/java/org/glassfish/jersey/client/spi/InvocationBuilderListener.java new file mode 100644 index 0000000000..302455d0ae --- /dev/null +++ b/core-client/src/main/java/org/glassfish/jersey/client/spi/InvocationBuilderListener.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.client.spi; + +import org.glassfish.jersey.Beta; +import org.glassfish.jersey.spi.Contract; + +import javax.ws.rs.ConstrainedTo; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.CacheControl; +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import java.util.Locale; + +/** + * Implementations of this interface will be notified when a new Invocation.Builder + * is created. This will allow implementations to access the invocation builders, + * and is intended for global providers. For example, the Invocation.Builder properties can be + * accessed to set properties that are available on the {@link javax.ws.rs.client.ClientRequestContext}. + * + * In order for the InvocationBuilderListener to be called, the implementation of the interface needs + * to be registered on the {@code Client} the same way the {@code ClientRequestFilter} is registered, for instance. + * + * @since 2.30 + */ +@Beta +@Contract +@ConstrainedTo(RuntimeType.CLIENT) +public interface InvocationBuilderListener { + + /** + * An {@link javax.ws.rs.client.Invocation.Builder} subset of setter methods. + */ + public interface InvocationBuilderContext { + /** + * Add the accepted response media types. + * + * @param mediaTypes accepted response media types. + * @return the updated context. + */ + InvocationBuilderContext accept(String... mediaTypes); + + /** + * Add the accepted response media types. + * + * @param mediaTypes accepted response media types. + * @return the updated context. + */ + InvocationBuilderContext accept(MediaType... mediaTypes); + + /** + * Add acceptable languages. + * + * @param locales an array of the acceptable languages. + * @return the updated context. + */ + InvocationBuilderContext acceptLanguage(Locale... locales); + + /** + * Add acceptable languages. + * + * @param locales an array of the acceptable languages. + * @return the updated context. + */ + InvocationBuilderContext acceptLanguage(String... locales); + + /** + * Add acceptable encodings. + * + * @param encodings an array of the acceptable encodings. + * @return the updated context. + */ + InvocationBuilderContext acceptEncoding(String... encodings); + + /** + * Add a cookie to be set. + * + * @param cookie to be set. + * @return the updated context. + */ + InvocationBuilderContext cookie(Cookie cookie); + + /** + * Add a cookie to be set. + * + * @param name the name of the cookie. + * @param value the value of the cookie. + * @return the updated context. + */ + InvocationBuilderContext cookie(String name, String value); + + /** + * Set the cache control data of the message. + * + * @param cacheControl the cache control directives, if {@code null} + * any existing cache control directives will be removed. + * @return the updated context. + */ + InvocationBuilderContext cacheControl(CacheControl cacheControl); + + /** + * Add an arbitrary header. + * + * @param name the name of the header + * @param value the value of the header, the header will be serialized + * using a {@link javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate} if + * one is available via {@link javax.ws.rs.ext.RuntimeDelegate#createHeaderDelegate(java.lang.Class)} + * for the class of {@code value} or using its {@code toString} method + * if a header delegate is not available. If {@code value} is {@code null} + * then all current headers of the same name will be removed. + * @return the updated context. + */ + InvocationBuilderContext header(String name, Object value); + + /** + * Replaces all existing headers with the newly supplied headers. + * + * @param headers new headers to be set, if {@code null} all existing + * headers will be removed. + * @return the updated context. + */ + InvocationBuilderContext headers(MultivaluedMap headers); + + /** + * Set a new property in the context of a request represented by this invocation builder. + *

+ * The property is available for a later retrieval via {@link ClientRequestContext#getProperty(String)} + * or {@link javax.ws.rs.ext.InterceptorContext#getProperty(String)}. + * If a property with a given name is already set in the request context, + * the existing value of the property will be updated. + * Setting a {@code null} value into a property effectively removes the property + * from the request property bag. + *

+ * + * @param name property name. + * @param value (new) property value. {@code null} value removes the property + * with the given name. + * @return the updated context. + * @see Invocation#property(String, Object) + */ + InvocationBuilderContext property(String name, Object value); + } + + /** + * Whenever an {@link Invocation.Builder} is created, (i.e. when + * {@link WebTarget#request()}, {@link WebTarget#request(String...)}, + * {@link WebTarget#request(MediaType...)} is called), this method would be invoked. + * @param context the updated {@link InvocationBuilderContext}. + */ + void onNewBuilder(InvocationBuilderContext context); + +} diff --git a/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java b/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java new file mode 100644 index 0000000000..56e52422c4 --- /dev/null +++ b/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.client.spi; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; + +public class InvocationBuilderListenerTest { + + private static final String PROPERTY_NAME = "test_property"; + private static final String ONE = "one"; + + private WebTarget target; + + @Before + public void setUp() { + target = ClientBuilder.newClient().target("http://localhost:8080").register(AbortRequestFilter.class) + .register(new PropertySetterInvocationBuilderListener(a -> a.property(key(ONE), ONE))); + } + + @Test + public void testRequest() throws ExecutionException, InterruptedException { + try (Response r = target.request().async().get().get()) { + assertDefault(r); + } + } + + @Test + public void testRequestString() { + try (Response r = target.request(MediaType.TEXT_HTML).build("GET").invoke()) { + assertDefault(r); + } + } + + @Test + public void testRequestMediaType() throws ExecutionException, InterruptedException { + try (Response r = target.request(MediaType.TEXT_PLAIN_TYPE).rx().get().toCompletableFuture().get()) { + assertDefault(r); + } + } + + private void assertDefault(Response response) { + Assert.assertEquals(key(ONE) + "=" + ONE, response.readEntity(String.class)); + } + + private static String key(String keySuffix) { + return new StringBuilder().append(PROPERTY_NAME).append('_').append(keySuffix).toString(); + } + + public static class PropertySetterInvocationBuilderListener implements InvocationBuilderListener { + + private final Consumer builderConsumer; + + public PropertySetterInvocationBuilderListener(Consumer builderConsumer) { + this.builderConsumer = builderConsumer; + } + + @Override + public void onNewBuilder(InvocationBuilderContext context) { + builderConsumer.accept(context); + } + } + + public static class AbortRequestFilter implements ClientRequestFilter { + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + StringBuilder sb = new StringBuilder(); + for (String propertyName : requestContext.getPropertyNames()) { + if (propertyName.startsWith(PROPERTY_NAME)) { + sb.append(propertyName).append("=").append(requestContext.getProperty(propertyName)); + } + } + requestContext.abortWith(Response.ok().entity(sb.toString()).build()); + } + } +} From 03ba2fc7391fb4f3df2eab4ca49ab774e97d6eff Mon Sep 17 00:00:00 2001 From: Jan Supol Date: Tue, 19 Nov 2019 13:37:50 +0100 Subject: [PATCH 2/4] Fix copyright year Signed-off-by: Jan Supol --- .../glassfish/jersey/client/InvocationBuilderListenerStage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java b/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java index 7b447ccac7..6c640d0534 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at From cee49373d9d466a1931affcf5ba496c9fc099830 Mon Sep 17 00:00:00 2001 From: Jan Supol Date: Tue, 19 Nov 2019 20:47:16 +0100 Subject: [PATCH 3/4] Make previously set headers, configuration, properties, and URI accessible in the listener Signed-off-by: Jan Supol --- .../InvocationBuilderListenerStage.java | 73 ++++++++++- .../client/spi/InvocationBuilderListener.java | 116 +++++++++++++++++- .../spi/InvocationBuilderListenerTest.java | 92 ++++++++++++++ 3 files changed, 277 insertions(+), 4 deletions(-) diff --git a/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java b/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java index 6c640d0534..dc947c6611 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/InvocationBuilderListenerStage.java @@ -23,11 +23,17 @@ import javax.ws.rs.client.Invocation; import javax.ws.rs.core.CacheControl; +import javax.ws.rs.core.Configuration; import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import java.net.URI; +import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Locale; +import java.util.Map; /** * Client request processing stage. During a request creation, when the {@link Invocation.Builder} @@ -43,16 +49,16 @@ .getAllProviders(injectionManager, InvocationBuilderListener.class, comparator).iterator(); } - /* package */ void invokeListener(Invocation.Builder builder) { + /* package */ void invokeListener(JerseyInvocation.Builder builder) { while (invocationBuilderListenerIterator.hasNext()) { invocationBuilderListenerIterator.next().onNewBuilder(new InvocationBuilderContextImpl(builder)); } } private static class InvocationBuilderContextImpl implements InvocationBuilderListener.InvocationBuilderContext { - private final Invocation.Builder builder; + private final JerseyInvocation.Builder builder; - private InvocationBuilderContextImpl(Invocation.Builder builder) { + private InvocationBuilderContextImpl(JerseyInvocation.Builder builder) { this.builder = builder; } @@ -104,6 +110,62 @@ public InvocationBuilderListener.InvocationBuilderContext cacheControl(CacheCont return this; } + @Override + public List getAccepted() { + return getHeader(HttpHeaders.ACCEPT); + } + + @Override + public List getAcceptedLanguages() { + return getHeader(HttpHeaders.ACCEPT_LANGUAGE); + } + + @Override + public List getCacheControls() { + return (List) (List) builder.request().getHeaders().get(HttpHeaders.CACHE_CONTROL); + } + + @Override + public Configuration getConfiguration() { + return builder.request().getConfiguration(); + } + + @Override + public Map getCookies() { + return builder.request().getCookies(); + } + + @Override + public List getEncodings() { + return getHeader(HttpHeaders.ACCEPT_ENCODING); + } + + @Override + public List getHeader(String name) { + return builder.request().getRequestHeader(name); + } + + @Override + public MultivaluedMap getHeaders() { + return builder.request().getHeaders(); + } + + @Override + public Object getProperty(String name) { + return builder.request().getProperty(name); + } + + @Override + public Collection getPropertyNames() { + return builder.request().getPropertyNames(); + } + + @Override + public URI getUri() { + return builder.request().getUri(); + } + + @Override public InvocationBuilderListener.InvocationBuilderContext header(String name, Object value) { builder.header(name, value); @@ -121,6 +183,11 @@ public InvocationBuilderListener.InvocationBuilderContext property(String name, builder.property(name, value); return this; } + + @Override + public void removeProperty(String name) { + builder.request().removeProperty(name); + } } } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/spi/InvocationBuilderListener.java b/core-client/src/main/java/org/glassfish/jersey/client/spi/InvocationBuilderListener.java index 302455d0ae..d0cd24c311 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/spi/InvocationBuilderListener.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/spi/InvocationBuilderListener.java @@ -25,20 +25,28 @@ import javax.ws.rs.client.Invocation; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.CacheControl; +import javax.ws.rs.core.Configuration; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; +import java.net.URI; +import java.util.Collection; +import java.util.List; import java.util.Locale; +import java.util.Map; /** * Implementations of this interface will be notified when a new Invocation.Builder * is created. This will allow implementations to access the invocation builders, * and is intended for global providers. For example, the Invocation.Builder properties can be * accessed to set properties that are available on the {@link javax.ws.rs.client.ClientRequestContext}. - * + *

* In order for the InvocationBuilderListener to be called, the implementation of the interface needs * to be registered on the {@code Client} the same way the {@code ClientRequestFilter} is registered, for instance. * + * If multiple {@code InvocationBuilderListeners} are to be utilized, the order of execution is driven by the {@code Priority}, + * the lower the priority value, the higher the priority, the sooner the execution. + * * @since 2.30 */ @Beta @@ -116,6 +124,102 @@ public interface InvocationBuilderContext { */ InvocationBuilderContext cacheControl(CacheControl cacheControl); + /** + * Get the accepted response media types. + * + * @return accepted response media types. + */ + List getAccepted(); + + /** + * Get acceptable languages. + * + * @return acceptable languages. + */ + List getAcceptedLanguages(); + + /** + * Get the cache control data of the message. + * + * @return the cache control data of the message. + */ + List getCacheControls(); + + /** + * Get runtime configuration. + * + * @return runtime configuration. + */ + Configuration getConfiguration(); + + /** + * Get any cookies that accompanied the request. + * + * @return a read-only map of cookie name (String) to {@link javax.ws.rs.core.Cookie}. + */ + Map getCookies(); + + /** + * Get acceptable encodings. + * + * @return acceptable encodings. + */ + List getEncodings(); + + /** + * Get the values of a HTTP request header. The returned List is read-only. + * + * @param name the header name, case insensitive. + * @return a read-only list of header values. + */ + List getHeader(String name); + + /** + * Get the mutable message headers multivalued map. + * + * @return mutable multivalued map of message headers. + */ + MultivaluedMap getHeaders(); + + /** + * Returns the property with the given name registered in the current request/response + * exchange context, or {@code null} if there is no property by that name. + *

+ * A property allows filters and interceptors to exchange + * additional custom information not already provided by this interface. + *

+ *

+ * A list of supported properties can be retrieved using {@link #getPropertyNames()}. + * Custom property names should follow the same convention as package names. + *

+ * + * @param name a {@code String} specifying the name of the property. + * @return an {@code Object} containing the value of the property, or + * {@code null} if no property exists matching the given name. + * @see #getPropertyNames() + */ + Object getProperty(String name); + + /** + * Returns an immutable {@link Collection collection} containing the property names + * available within the context of the current request/response exchange context. + *

+ * Use the {@link #getProperty} method with a property name to get the value of + * a property. + *

+ * + * @return an immutable {@link Collection collection} of property names. + * @see #getProperty + */ + Collection getPropertyNames(); + + /** + * Get the request URI. + * + * @return request URI. + */ + URI getUri(); + /** * Add an arbitrary header. * @@ -157,12 +261,22 @@ public interface InvocationBuilderContext { * @see Invocation#property(String, Object) */ InvocationBuilderContext property(String name, Object value); + + /** + * Removes a property with the given name from the current request/response + * exchange context. After removal, subsequent calls to {@link #getProperty} + * to retrieve the property value will return {@code null}. + * + * @param name a {@code String} specifying the name of the property to be removed. + */ + void removeProperty(String name); } /** * Whenever an {@link Invocation.Builder} is created, (i.e. when * {@link WebTarget#request()}, {@link WebTarget#request(String...)}, * {@link WebTarget#request(MediaType...)} is called), this method would be invoked. + * * @param context the updated {@link InvocationBuilderContext}. */ void onNewBuilder(InvocationBuilderContext context); diff --git a/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java b/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java index 56e52422c4..8184233f10 100644 --- a/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java +++ b/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java @@ -16,18 +16,28 @@ package org.glassfish.jersey.client.spi; +import org.glassfish.jersey.internal.PropertiesDelegate; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import javax.annotation.Priority; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.ClientRequestContext; import javax.ws.rs.client.ClientRequestFilter; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.CacheControl; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.ext.RuntimeDelegate; import java.io.IOException; +import java.util.Date; +import java.util.Locale; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; @@ -65,6 +75,25 @@ public void testRequestMediaType() throws ExecutionException, InterruptedExcepti } } + @Test + public void testConfigurationProperties() { + String value = "OTHER_VALUE"; + try (Response r = target.property(key(ConfigurationInvocationBuilderListener.OTHER_PROPERTY), value) + .register(ConfigurationInvocationBuilderListener.class).request().get()) { + Assert.assertTrue( + r.readEntity(String.class).contains(key(ConfigurationInvocationBuilderListener.OTHER_PROPERTY) + "=" + value) + ); + } + } + + @Test + public void testGetters() { + try (Response r = target.register(SetterInvocationBuilderListener.class, 100) + .register(GetterInvocationBuilderListener.class, 200).request().get()) { + assertDefault(r); + } + } + private void assertDefault(Response response) { Assert.assertEquals(key(ONE) + "=" + ONE, response.readEntity(String.class)); } @@ -100,4 +129,67 @@ public void filter(ClientRequestContext requestContext) throws IOException { requestContext.abortWith(Response.ok().entity(sb.toString()).build()); } } + + public static class ConfigurationInvocationBuilderListener implements InvocationBuilderListener { + static final String OTHER_PROPERTY = "OTHER_PROPERTY"; + + @Override + public void onNewBuilder(InvocationBuilderContext context) { + context.property(key(OTHER_PROPERTY), context.getConfiguration().getProperty(key(OTHER_PROPERTY))); + } + } + + public static class SetterInvocationBuilderListener implements InvocationBuilderListener { + + @Override + public void onNewBuilder(InvocationBuilderContext context) { + context.accept(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON_PATCH_JSON_TYPE) + .acceptEncoding("GZIP") + .acceptLanguage(Locale.GERMAN) + .acceptLanguage(new Locale.Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build()) + .property(PROPERTY_NAME, PROPERTY_NAME) + .cacheControl(CacheControl.valueOf(PROPERTY_NAME)) + .cookie("Cookie","CookieValue") + .header(HttpHeaders.CONTENT_ID, PROPERTY_NAME); + } + } + + public static class GetterInvocationBuilderListener implements InvocationBuilderListener { + + @Override + public void onNewBuilder(InvocationBuilderContext context) { + Date date = new Date(); + RuntimeDelegate.HeaderDelegate localeDelegate = RuntimeDelegate.getInstance().createHeaderDelegate(Locale.class); + Assert.assertThat(context.getAccepted(), + Matchers.containsInAnyOrder(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_PATCH_JSON)); + Assert.assertThat(context.getEncodings(), Matchers.contains("GZIP")); + Assert.assertThat(context.getAcceptedLanguages(), + Matchers.containsInAnyOrder(localeDelegate.toString(Locale.GERMAN), + localeDelegate.toString( + new Locale.Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build() + ) + ) + ); + + Assert.assertThat(context.getHeader(HttpHeaders.CONTENT_ID), Matchers.contains(PROPERTY_NAME)); + context.getHeaders().add(HttpHeaders.DATE, date); + Assert.assertThat(context.getHeader(HttpHeaders.DATE), Matchers.notNullValue()); + Assert.assertThat(context.getHeaders().getFirst(HttpHeaders.DATE), Matchers.is(date)); + + Assert.assertNotNull(context.getUri()); + Assert.assertTrue(context.getUri().toASCIIString().startsWith("http://")); + + Assert.assertThat(context.getPropertyNames(), Matchers.contains(PROPERTY_NAME)); + Assert.assertThat(context.getProperty(PROPERTY_NAME), Matchers.is(PROPERTY_NAME)); + context.removeProperty(PROPERTY_NAME); + Assert.assertTrue(context.getPropertyNames().isEmpty()); + + Assert.assertThat(context.getCacheControls().get(0).toString(), + Matchers.is(CacheControl.valueOf(PROPERTY_NAME).toString()) + ); + Assert.assertThat(context.getCookies().size(), Matchers.is(1)); + Assert.assertThat(context.getCookies().get("Cookie"), Matchers.notNullValue()); + } + } } From a96b0c3f90940b486ee9a5c91823feb141f24f5e Mon Sep 17 00:00:00 2001 From: Jan Supol Date: Tue, 19 Nov 2019 21:19:17 +0100 Subject: [PATCH 4/4] fix checkstyle Signed-off-by: Jan Supol --- .../jersey/client/spi/InvocationBuilderListenerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java b/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java index 8184233f10..ddb6e76fae 100644 --- a/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java +++ b/core-client/src/test/java/org/glassfish/jersey/client/spi/InvocationBuilderListenerTest.java @@ -150,7 +150,7 @@ public void onNewBuilder(InvocationBuilderContext context) { .acceptLanguage(new Locale.Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build()) .property(PROPERTY_NAME, PROPERTY_NAME) .cacheControl(CacheControl.valueOf(PROPERTY_NAME)) - .cookie("Cookie","CookieValue") + .cookie("Cookie", "CookieValue") .header(HttpHeaders.CONTENT_ID, PROPERTY_NAME); } } @@ -159,7 +159,7 @@ public static class GetterInvocationBuilderListener implements InvocationBuilder @Override public void onNewBuilder(InvocationBuilderContext context) { - Date date = new Date(); + Date date = new Date(); RuntimeDelegate.HeaderDelegate localeDelegate = RuntimeDelegate.getInstance().createHeaderDelegate(Locale.class); Assert.assertThat(context.getAccepted(), Matchers.containsInAnyOrder(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_PATCH_JSON));