From 76680c215384095a6ba729e2e7dd77c91f0ae36d Mon Sep 17 00:00:00 2001 From: Jefferson Quesado Date: Fri, 19 Jun 2026 11:22:55 -0300 Subject: [PATCH 1/3] HTTP Query method Basic implementation, no cache example for now --- .../jaxrs/examples/bootstrap/HelloWorld.java | 9 +++++ .../main/java/jakarta/ws/rs/HttpMethod.java | 4 +++ .../src/main/java/jakarta/ws/rs/QUERY.java | 36 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 jaxrs-api/src/main/java/jakarta/ws/rs/QUERY.java diff --git a/examples/src/main/java/jaxrs/examples/bootstrap/HelloWorld.java b/examples/src/main/java/jaxrs/examples/bootstrap/HelloWorld.java index 7a9d6ff57..7c016fdd6 100644 --- a/examples/src/main/java/jaxrs/examples/bootstrap/HelloWorld.java +++ b/examples/src/main/java/jaxrs/examples/bootstrap/HelloWorld.java @@ -20,8 +20,11 @@ import java.util.Set; import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QUERY; import jakarta.ws.rs.core.Application; /** @@ -50,4 +53,10 @@ public String sayHello() { return "Hello, World!"; } + @QUERY + @Consumes("application/json") + @Produces("text/plain") + public String queryIt(final String message) { + return String.format("Got it from query! <%s>", message); + } } diff --git a/jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java b/jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java index 63d29fa26..8ca263c52 100644 --- a/jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java +++ b/jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java @@ -73,6 +73,10 @@ * HTTP OPTIONS method. */ public static final String OPTIONS = "OPTIONS"; + /** + * HTTP OPTIONS method. + */ + public static final String QUERY = "QUERY"; /** * Specifies the name of a HTTP method. E.g. "GET". diff --git a/jaxrs-api/src/main/java/jakarta/ws/rs/QUERY.java b/jaxrs-api/src/main/java/jakarta/ws/rs/QUERY.java new file mode 100644 index 000000000..6eb8d42ee --- /dev/null +++ b/jaxrs-api/src/main/java/jakarta/ws/rs/QUERY.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2010, 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 jakarta.ws.rs; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the annotated method responds to HTTP QUERY requests. + * + * @see HttpMethod + * @since 5.0.0 + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@HttpMethod(HttpMethod.QUERY) +@Documented +public @interface QUERY { +} From ae8bd62993beb89897077bd116bcc728dda8cd89 Mon Sep 17 00:00:00 2001 From: Jefferson Quesado Date: Fri, 19 Jun 2026 11:23:05 -0300 Subject: [PATCH 2/3] HTTP QUERY in client (sync/async) Thanks for @otaviojava issue https://github.com/jakartaee/rest/issues/1345 --- .../client/webdav/WebDavTargetedBuilder.java | 20 +++++ .../jakarta/ws/rs/client/AsyncInvoker.java | 78 +++++++++++++++++++ .../java/jakarta/ws/rs/client/Invocation.java | 7 ++ .../jakarta/ws/rs/client/SyncInvoker.java | 51 ++++++++++++ 4 files changed, 156 insertions(+) diff --git a/examples/src/main/java/jaxrs/examples/client/webdav/WebDavTargetedBuilder.java b/examples/src/main/java/jaxrs/examples/client/webdav/WebDavTargetedBuilder.java index 3fbb44113..bae20a778 100644 --- a/examples/src/main/java/jaxrs/examples/client/webdav/WebDavTargetedBuilder.java +++ b/examples/src/main/java/jaxrs/examples/client/webdav/WebDavTargetedBuilder.java @@ -55,6 +55,11 @@ public Invocation buildGet() { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public Invocation buildQuery() { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public Invocation buildPost(Entity entity) { throw new UnsupportedOperationException("Not supported yet."); @@ -135,6 +140,21 @@ public T get(GenericType responseType) throws ProcessingException { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public Response query(Entity entity) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public T query(Entity entity, Class responseType) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public T query(Entity entity, GenericType responseType) { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public Response put(Entity entity) throws ProcessingException { throw new UnsupportedOperationException("Not supported yet."); diff --git a/jaxrs-api/src/main/java/jakarta/ws/rs/client/AsyncInvoker.java b/jaxrs-api/src/main/java/jakarta/ws/rs/client/AsyncInvoker.java index 847e6be17..17b8677de 100644 --- a/jaxrs-api/src/main/java/jakarta/ws/rs/client/AsyncInvoker.java +++ b/jaxrs-api/src/main/java/jakarta/ws/rs/client/AsyncInvoker.java @@ -96,6 +96,84 @@ public interface AsyncInvoker { */ Future get(InvocationCallback callback); + // QUERY + + /** + * Invoke HTTP QUERY method for the current request asynchronously. + *

+ * Note that calling the {@link java.util.concurrent.Future#get()} method on the returned {@code Future} instance may + * throw an {@link java.util.concurrent.ExecutionException} that wraps a {@link jakarta.ws.rs.ProcessingException} thrown + * in case of an invocation processing failure. In case a processing of a properly received response fails, the wrapped + * processing exception will be of {@link ResponseProcessingException} type and will contain the {@link Response} + * instance whose processing has failed. + * + * @param entity request entity, including its full {@link jakarta.ws.rs.core.Variant} information. Any variant-related + * HTTP headers previously set (namely {@code Content-Type}, {@code Content-Language} and {@code Content-Encoding}) will + * be overwritten using the entity variant information. + * @return invocation response {@link Future future}. + */ + Future query(Entity entity); + + /** + * Invoke HTTP QUERY method for the current request asynchronously. + *

+ * Note that calling the {@link java.util.concurrent.Future#get()} method on the returned {@code Future} instance may + * throw an {@link java.util.concurrent.ExecutionException} that wraps either a {@link jakarta.ws.rs.ProcessingException} + * thrown in case of an invocation processing failure or a {@link WebApplicationException} or one of its subclasses + * thrown in case the received response status code is not {@link jakarta.ws.rs.core.Response.Status.Family#SUCCESSFUL + * successful} and the specified response type is not {@link jakarta.ws.rs.core.Response}. In case a processing of a + * properly received response fails, the wrapped processing exception will be of {@link ResponseProcessingException} + * type and will contain the {@link Response} instance whose processing has failed. + * + * @param response entity type. + * @param entity request entity, including its full {@link jakarta.ws.rs.core.Variant} information. Any variant-related + * HTTP headers previously set (namely {@code Content-Type}, {@code Content-Language} and {@code Content-Encoding}) will + * be overwritten using the entity variant information. + * @param responseType Java type the response entity will be converted to. + * @return invocation response {@link Future future}. + */ + Future query(Entity entity, Class responseType); + + /** + * Invoke HTTP QUERY method for the current request asynchronously. + *

+ * Note that calling the {@link java.util.concurrent.Future#get()} method on the returned {@code Future} instance may + * throw an {@link java.util.concurrent.ExecutionException} that wraps either a {@link jakarta.ws.rs.ProcessingException} + * thrown in case of an invocation processing failure or a {@link WebApplicationException} or one of its subclasses + * thrown in case the received response status code is not {@link jakarta.ws.rs.core.Response.Status.Family#SUCCESSFUL + * successful} and the specified response type is not {@link jakarta.ws.rs.core.Response}. In case a processing of a + * properly received response fails, the wrapped processing exception will be of {@link ResponseProcessingException} + * type and will contain the {@link Response} instance whose processing has failed. + * + * @param generic response entity type. + * @param entity request entity, including its full {@link jakarta.ws.rs.core.Variant} information. Any variant-related + * HTTP headers previously set (namely {@code Content-Type}, {@code Content-Language} and {@code Content-Encoding}) will + * be overwritten using the entity variant information. + * @param responseType representation of a generic Java type the response entity will be converted to. + * @return invocation response {@link Future future}. + */ + Future query(Entity entity, GenericType responseType); + + /** + * Invoke HTTP QUERY method for the current request asynchronously. + *

+ * Note that calling the {@link java.util.concurrent.Future#get()} method on the returned {@code Future} instance may + * throw an {@link java.util.concurrent.ExecutionException} that wraps either a {@link jakarta.ws.rs.ProcessingException} + * thrown in case of an invocation processing failure or a {@link WebApplicationException} or one of its subclasses + * thrown in case the received response status code is not {@link jakarta.ws.rs.core.Response.Status.Family#SUCCESSFUL + * successful} and the generic type of the supplied response callback is not {@link jakarta.ws.rs.core.Response}. In case + * a processing of a properly received response fails, the wrapped processing exception will be of + * {@link ResponseProcessingException} type and will contain the {@link Response} instance whose processing has failed. + * + * @param generic response entity type. + * @param entity request entity, including its full {@link jakarta.ws.rs.core.Variant} information. Any variant-related + * HTTP headers previously set (namely {@code Content-Type}, {@code Content-Language} and {@code Content-Encoding}) will + * be overwritten using the entity variant information. + * @param callback asynchronous invocation callback. + * @return invocation response {@link Future future}. + */ + Future query(Entity entity, InvocationCallback callback); + // PUT /** diff --git a/jaxrs-api/src/main/java/jakarta/ws/rs/client/Invocation.java b/jaxrs-api/src/main/java/jakarta/ws/rs/client/Invocation.java index 113f91e4a..f5364fadc 100644 --- a/jaxrs-api/src/main/java/jakarta/ws/rs/client/Invocation.java +++ b/jaxrs-api/src/main/java/jakarta/ws/rs/client/Invocation.java @@ -116,6 +116,13 @@ public static interface Builder extends SyncInvoker { */ public Invocation buildGet(); + /** + * Build a QUERY request invocation. + * + * @return invocation encapsulating the built QUERY request. + */ + public Invocation buildQuery(); + /** * Build a DELETE request invocation. * diff --git a/jaxrs-api/src/main/java/jakarta/ws/rs/client/SyncInvoker.java b/jaxrs-api/src/main/java/jakarta/ws/rs/client/SyncInvoker.java index 2582eda1a..660074d33 100644 --- a/jaxrs-api/src/main/java/jakarta/ws/rs/client/SyncInvoker.java +++ b/jaxrs-api/src/main/java/jakarta/ws/rs/client/SyncInvoker.java @@ -69,6 +69,57 @@ public interface SyncInvoker { */ T get(GenericType responseType); + // QUERY + + /** + * Invoke HTTP QUERY method for the current request synchronously. + * + * @param entity request entity, including its full {@link jakarta.ws.rs.core.Variant} information. Any variant-related + * HTTP headers previously set (namely {@code Content-Type}, {@code Content-Language} and {@code Content-Encoding}) will + * be overwritten using the entity variant information. + * @return invocation response. + * @throws ResponseProcessingException in case processing of a received HTTP response fails (e.g. in a filter or during + * conversion of the response entity data to an instance of a particular Java type). + * @throws ProcessingException in case the request processing or subsequent I/O operation fails. + */ + Response query(Entity entity); + + /** + * Invoke HTTP QUERY method for the current request synchronously. + * + * @param response entity type. + * @param entity request entity, including its full {@link jakarta.ws.rs.core.Variant} information. Any variant-related + * HTTP headers previously set (namely {@code Content-Type}, {@code Content-Language} and {@code Content-Encoding}) will + * be overwritten using the entity variant information. + * @param responseType Java type the response entity will be converted to. + * @return invocation response. + * @throws ResponseProcessingException in case processing of a received HTTP response fails (e.g. in a filter or during + * conversion of the response entity data to an instance of a particular Java type). + * @throws ProcessingException in case the request processing or subsequent I/O operation fails. + * @throws WebApplicationException in case the response status code of the response returned by the server is not + * {@link jakarta.ws.rs.core.Response.Status.Family#SUCCESSFUL successful} and the specified response type is not + * {@link jakarta.ws.rs.core.Response}. + */ + T query(Entity entity, Class responseType); + + /** + * Invoke HTTP QUERY method for the current request synchronously. + * + * @param generic response entity type. + * @param entity request entity, including its full {@link jakarta.ws.rs.core.Variant} information. Any variant-related + * HTTP headers previously set (namely {@code Content-Type}, {@code Content-Language} and {@code Content-Encoding}) will + * be overwritten using the entity variant information. + * @param responseType representation of a generic Java type the response entity will be converted to. + * @return invocation response. + * @throws ResponseProcessingException in case processing of a received HTTP response fails (e.g. in a filter or during + * conversion of the response entity data to an instance of a particular Java type). + * @throws ProcessingException in case the request processing or subsequent I/O operation fails. + * @throws WebApplicationException in case the response status code of the response returned by the server is not + * {@link jakarta.ws.rs.core.Response.Status.Family#SUCCESSFUL successful} and the specified generic response type does + * not represent {@link jakarta.ws.rs.core.Response}. + */ + T query(Entity entity, GenericType responseType); + // PUT /** From e8974738ae1ded766830c62f25ffd8fcc28df4db Mon Sep 17 00:00:00 2001 From: Jefferson Quesado Date: Tue, 30 Jun 2026 00:24:28 -0300 Subject: [PATCH 3/3] Code review --- jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java b/jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java index 8ca263c52..f1452f8ad 100644 --- a/jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java +++ b/jaxrs-api/src/main/java/jakarta/ws/rs/HttpMethod.java @@ -36,6 +36,7 @@ * @see PATCH * @see HEAD * @see OPTIONS + * @see QUERY * @since 1.0 */ @Target({ ElementType.ANNOTATION_TYPE }) @@ -74,7 +75,7 @@ */ public static final String OPTIONS = "OPTIONS"; /** - * HTTP OPTIONS method. + * HTTP QUERY method. */ public static final String QUERY = "QUERY";