Skip to content

Commit 21e8faf

Browse files
committed
Enable CompletionStage unwrap in MBW
Signed-off-by: Jan Supol <[email protected]>
1 parent fad4061 commit 21e8faf

File tree

6 files changed

+241
-74
lines changed

6 files changed

+241
-74
lines changed

core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java

Lines changed: 14 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@
4848
import org.glassfish.jersey.internal.inject.InjectionManager;
4949
import org.glassfish.jersey.internal.inject.InjectionManagerSupplier;
5050
import org.glassfish.jersey.internal.util.ExceptionUtils;
51-
import org.glassfish.jersey.internal.util.PropertiesHelper;
51+
import org.glassfish.jersey.internal.PropertiesResolver;
52+
import org.glassfish.jersey.internal.util.collection.LazyValue;
53+
import org.glassfish.jersey.internal.util.collection.Value;
54+
import org.glassfish.jersey.internal.util.collection.Values;
5255
import org.glassfish.jersey.message.MessageBodyWorkers;
5356
import org.glassfish.jersey.message.internal.HeaderUtils;
5457
import org.glassfish.jersey.message.internal.OutboundMessageContext;
@@ -58,7 +61,8 @@
5861
*
5962
* @author Marek Potociar
6063
*/
61-
public class ClientRequest extends OutboundMessageContext implements ClientRequestContext, HttpHeaders, InjectionManagerSupplier {
64+
public class ClientRequest extends OutboundMessageContext
65+
implements ClientRequestContext, HttpHeaders, InjectionManagerSupplier, PropertiesResolver {
6266

6367
// Request-scoped configuration instance
6468
private final ClientConfig clientConfig;
@@ -82,6 +86,10 @@ public class ClientRequest extends OutboundMessageContext implements ClientReque
8286
private Iterable<ReaderInterceptor> readerInterceptors;
8387
// do not add user-agent header (if not directly set) to the request.
8488
private boolean ignoreUserAgent;
89+
// lazy PropertiesResolver
90+
private LazyValue<PropertiesResolver> propertiesResolver = Values.lazy(
91+
(Value<PropertiesResolver>) () -> PropertiesResolver.create(getConfiguration(), getPropertiesDelegate())
92+
);
8593

8694
private static final Logger LOGGER = Logger.getLogger(ClientRequest.class.getName());
8795

@@ -120,65 +128,14 @@ public ClientRequest(final ClientRequest original) {
120128
this.ignoreUserAgent = original.ignoreUserAgent;
121129
}
122130

123-
/**
124-
* Resolve a property value for the specified property {@code name}.
125-
*
126-
* <p>
127-
* The method returns the value of the property registered in the request-specific
128-
* property bag, if available. If no property for the given property name is found
129-
* in the request-specific property bag, the method looks at the properties stored
130-
* in the {@link #getConfiguration() global client-runtime configuration} this request
131-
* belongs to. If there is a value defined in the client-runtime configuration,
132-
* it is returned, otherwise the method returns {@code null} if no such property is
133-
* registered neither in the client runtime nor in the request-specific property bag.
134-
* </p>
135-
*
136-
* @param name property name.
137-
* @param type expected property class type.
138-
* @param <T> property Java type.
139-
* @return resolved property value or {@code null} if no such property is registered.
140-
*/
131+
@Override
141132
public <T> T resolveProperty(final String name, final Class<T> type) {
142-
return resolveProperty(name, null, type);
133+
return propertiesResolver.get().resolveProperty(name, type);
143134
}
144135

145-
/**
146-
* Resolve a property value for the specified property {@code name}.
147-
*
148-
* <p>
149-
* The method returns the value of the property registered in the request-specific
150-
* property bag, if available. If no property for the given property name is found
151-
* in the request-specific property bag, the method looks at the properties stored
152-
* in the {@link #getConfiguration() global client-runtime configuration} this request
153-
* belongs to. If there is a value defined in the client-runtime configuration,
154-
* it is returned, otherwise the method returns {@code defaultValue} if no such property is
155-
* registered neither in the client runtime nor in the request-specific property bag.
156-
* </p>
157-
*
158-
* @param name property name.
159-
* @param defaultValue default value to return if the property is not registered.
160-
* @param <T> property Java type.
161-
* @return resolved property value or {@code defaultValue} if no such property is registered.
162-
*/
163-
@SuppressWarnings("unchecked")
136+
@Override
164137
public <T> T resolveProperty(final String name, final T defaultValue) {
165-
return resolveProperty(name, defaultValue, (Class<T>) defaultValue.getClass());
166-
}
167-
168-
private <T> T resolveProperty(final String name, Object defaultValue, final Class<T> type) {
169-
// Check runtime configuration first
170-
Object result = getConfiguration().getProperty(name);
171-
if (result != null) {
172-
defaultValue = result;
173-
}
174-
175-
// Check request properties next
176-
result = propertiesDelegate.getProperty(name);
177-
if (result == null) {
178-
result = defaultValue;
179-
}
180-
181-
return (result == null) ? null : PropertiesHelper.convertValue(result, type);
138+
return propertiesResolver.get().resolveProperty(name, defaultValue);
182139
}
183140

184141
@Override
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
package org.glassfish.jersey.internal;
17+
18+
import org.glassfish.jersey.internal.util.PropertiesHelper;
19+
20+
import javax.ws.rs.core.Configuration;
21+
22+
/**
23+
* Resolver of a property value for the specified property {@code name} from the
24+
* request-specific property bag, or the {@link Configuration global runtime configuration}.
25+
*/
26+
public interface PropertiesResolver {
27+
/**
28+
* Resolve a property value for the specified property {@code name}.
29+
*
30+
* <p>
31+
* The method returns the value of the property registered in the request-specific
32+
* property bag, if available. If no property for the given property name is found
33+
* in the request-specific property bag, the method looks at the properties stored
34+
* in the {@link Configuration global runtime configuration} this request
35+
* belongs to. If there is a value defined in the runtime configuration,
36+
* it is returned, otherwise the method returns {@code null} if no such property is
37+
* registered neither in the runtime nor in the request-specific property bag.
38+
* </p>
39+
*
40+
* @param name property name.
41+
* @param type expected property class type.
42+
* @param <T> property Java type.
43+
* @return resolved property value or {@code null} if no such property is registered.
44+
*/
45+
public <T> T resolveProperty(final String name, final Class<T> type);
46+
47+
/**
48+
* Resolve a property value for the specified property {@code name}.
49+
*
50+
* <p>
51+
* The method returns the value of the property registered in the request-specific
52+
* property bag, if available. If no property for the given property name is found
53+
* in the request-specific property bag, the method looks at the properties stored
54+
* in the {@link Configuration global runtime configuration} this request
55+
* belongs to. If there is a value defined in the runtime configuration,
56+
* it is returned, otherwise the method returns {@code defaultValue} if no such property is
57+
* registered neither in the runtime nor in the request-specific property bag.
58+
* </p>
59+
*
60+
* @param name property name.
61+
* @param defaultValue default value to return if the property is not registered.
62+
* @param <T> property Java type.
63+
* @return resolved property value or {@code defaultValue} if no such property is registered.
64+
*/
65+
public <T> T resolveProperty(final String name, final T defaultValue);
66+
67+
/**
68+
* Return new instance of {@link PropertiesResolver}.
69+
* @param configuration Runtime {@link Configuration}.
70+
* @param delegate Request scoped {@link PropertiesDelegate properties delegate}.
71+
* @return A new instance of {@link PropertiesResolver}.
72+
*/
73+
public static PropertiesResolver create(Configuration configuration, PropertiesDelegate delegate) {
74+
return new PropertiesResolver() {
75+
@Override
76+
public <T> T resolveProperty(String name, Class<T> type) {
77+
return resolveProperty(name, null, type);
78+
}
79+
80+
@Override
81+
@SuppressWarnings("unchecked")
82+
public <T> T resolveProperty(String name, T defaultValue) {
83+
return resolveProperty(name, defaultValue, (Class<T>) defaultValue.getClass());
84+
}
85+
86+
private <T> T resolveProperty(final String name, Object defaultValue, final Class<T> type) {
87+
// Check runtime configuration first
88+
Object result = configuration.getProperty(name);
89+
if (result != null) {
90+
defaultValue = result;
91+
}
92+
93+
// Check request properties next
94+
result = delegate.getProperty(name);
95+
if (result == null) {
96+
result = defaultValue;
97+
}
98+
99+
return (result == null) ? null : PropertiesHelper.convertValue(result, type);
100+
}
101+
};
102+
}
103+
}

core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -49,8 +49,12 @@
4949

5050
import org.glassfish.jersey.internal.PropertiesDelegate;
5151
import org.glassfish.jersey.internal.guava.Preconditions;
52+
import org.glassfish.jersey.internal.PropertiesResolver;
53+
import org.glassfish.jersey.internal.util.collection.LazyValue;
5254
import org.glassfish.jersey.internal.util.collection.Ref;
5355
import org.glassfish.jersey.internal.util.collection.Refs;
56+
import org.glassfish.jersey.internal.util.collection.Value;
57+
import org.glassfish.jersey.internal.util.collection.Values;
5458
import org.glassfish.jersey.message.internal.AcceptableMediaType;
5559
import org.glassfish.jersey.message.internal.HttpHeaderReader;
5660
import org.glassfish.jersey.message.internal.InboundMessageContext;
@@ -80,7 +84,7 @@
8084
* @author Marek Potociar
8185
*/
8286
public class ContainerRequest extends InboundMessageContext
83-
implements ContainerRequestContext, Request, HttpHeaders, PropertiesDelegate {
87+
implements ContainerRequestContext, Request, HttpHeaders, PropertiesDelegate, PropertiesResolver {
8488

8589
private static final URI DEFAULT_BASE_URI = URI.create("/");
8690

@@ -114,6 +118,10 @@ public class ContainerRequest extends InboundMessageContext
114118
private ContainerResponseWriter responseWriter;
115119
// True if the request is used in the response processing phase (for example in ContainerResponseFilter)
116120
private boolean inResponseProcessingPhase;
121+
// lazy PropertiesResolver
122+
private LazyValue<PropertiesResolver> propertiesResolver = Values.lazy(
123+
(Value<PropertiesResolver>) () -> PropertiesResolver.create(getConfiguration(), getPropertiesDelegate())
124+
);
117125

118126
private static final String ERROR_REQUEST_SET_ENTITY_STREAM_IN_RESPONSE_PHASE =
119127
LocalizationMessages.ERROR_REQUEST_SET_ENTITY_STREAM_IN_RESPONSE_PHASE();
@@ -274,6 +282,16 @@ public <T> T readEntity(final Class<T> rawType, final Type type, final Annotatio
274282
return super.readEntity(rawType, type, annotations, propertiesDelegate);
275283
}
276284

285+
@Override
286+
public <T> T resolveProperty(final String name, final Class<T> type) {
287+
return propertiesResolver.get().resolveProperty(name, type);
288+
}
289+
290+
@Override
291+
public <T> T resolveProperty(final String name, final T defaultValue) {
292+
return propertiesResolver.get().resolveProperty(name, defaultValue);
293+
}
294+
277295
@Override
278296
public Object getProperty(final String name) {
279297
return propertiesDelegate.getProperty(name);

core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -724,6 +724,23 @@ public final class ServerProperties {
724724
public static final String LOCATION_HEADER_RELATIVE_URI_RESOLUTION_DISABLED =
725725
"jersey.config.server.headers.location.relative.resolution.disabled";
726726

727+
728+
/**
729+
* If {@code true} message body writer will not use {@code CompletionStage} as a generic type.
730+
* The {@code CompletionStage} value will be unwrapped and the message body writer will be invoked with the unwrapped type.
731+
*
732+
* <p>
733+
* The default value is {@code false}.
734+
* </p>
735+
* <p>
736+
* The name of the configuration property is <tt>{@value}</tt>.
737+
* </p>
738+
*
739+
* @since 2.33
740+
*/
741+
public static final String UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE =
742+
"jersey.config.server.unwrap.completion.stage.writer.enable";
743+
727744
private ServerProperties() {
728745
// prevents instantiation
729746
}

core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -59,6 +59,7 @@
5959
import org.glassfish.jersey.process.Inflector;
6060
import org.glassfish.jersey.server.ContainerRequest;
6161
import org.glassfish.jersey.server.ContainerResponse;
62+
import org.glassfish.jersey.server.ServerProperties;
6263
import org.glassfish.jersey.server.internal.LocalizationMessages;
6364
import org.glassfish.jersey.server.internal.ProcessingProviders;
6465
import org.glassfish.jersey.server.internal.inject.ConfiguredValidator;
@@ -83,6 +84,8 @@ public class ResourceMethodInvoker implements Endpoint, ResourceInfo {
8384
private final Annotation[] methodAnnotations;
8485
private final Type invocableResponseType;
8586
private final boolean canUseInvocableResponseType;
87+
private final boolean isCompletionStageResponseType;
88+
private final Type completionStageResponseType;
8689
private final ResourceMethodDispatcher dispatcher;
8790
private final Method resourceMethod;
8891
private final Class<?> resourceClass;
@@ -306,7 +309,10 @@ protected void configure() {
306309
&& Void.class != invocableResponseType
307310
&& // Do NOT change the entity type for Response or it's subclasses.
308311
!((invocableResponseType instanceof Class) && Response.class.isAssignableFrom((Class) invocableResponseType));
309-
312+
this.isCompletionStageResponseType = ParameterizedType.class.isInstance(invocableResponseType)
313+
&& CompletionStage.class.isAssignableFrom((Class<?>) ((ParameterizedType) invocableResponseType).getRawType());
314+
this.completionStageResponseType =
315+
isCompletionStageResponseType ? ((ParameterizedType) invocableResponseType).getActualTypeArguments()[0] : null;
310316
}
311317

312318
private <T> void addNameBoundProviders(
@@ -459,7 +465,7 @@ private Response invoke(final RequestProcessingContext context, final Object res
459465
if (canUseInvocableResponseType
460466
&& response.hasEntity()
461467
&& !(response.getEntityType() instanceof ParameterizedType)) {
462-
response.setEntityType(invocableResponseType);
468+
response.setEntityType(unwrapInvocableResponseType(context.request()));
463469
}
464470

465471
return response;
@@ -478,6 +484,14 @@ private Response invoke(final RequestProcessingContext context, final Object res
478484
return jaxrsResponse;
479485
}
480486

487+
private Type unwrapInvocableResponseType(ContainerRequest request) {
488+
if (isCompletionStageResponseType
489+
&& request.resolveProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.FALSE)) {
490+
return completionStageResponseType;
491+
}
492+
return invocableResponseType;
493+
}
494+
481495
/**
482496
* Get all bound request filters applicable to the {@link #getResourceMethod() resource method}
483497
* wrapped by this invoker.

0 commit comments

Comments
 (0)