Merge remote-tracking branch 'upstream/master' into 3.x Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/NOTICE.md b/NOTICE.md index 0846b57..5533ac7 100644 --- a/NOTICE.md +++ b/NOTICE.md
@@ -70,10 +70,10 @@ * Project: http://www.javassist.org/ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. -Jackson JAX-RS Providers Version 2.11.3 +Jackson JAX-RS Providers Version 2.13.3 * License: Apache License, 2.0 * Project: https://github.com/FasterXML/jackson-jaxrs-providers -* Copyright: (c) 2009-2011 FasterXML, LLC. All rights reserved unless otherwise indicated. +* Copyright: (c) 2009-2022 FasterXML, LLC. All rights reserved unless otherwise indicated. jQuery v1.12.4 * License: jquery.org/license @@ -95,7 +95,7 @@ * Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS * Copyright: Eric Rowell -org.objectweb.asm Version 9.0 +org.objectweb.asm Version 9.3 * License: Modified BSD (https://asm.ow2.io/license.html) * Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved.
diff --git a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/ProxyTest.java b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/ProxyTest.java deleted file mode 100644 index baebc78..0000000 --- a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/ProxyTest.java +++ /dev/null
@@ -1,147 +0,0 @@ -/* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2019 Banco do Brasil S/A. 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.jetty.connector; - -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.glassfish.jersey.client.ClientConfig; -import org.glassfish.jersey.client.ClientProperties; -import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.test.JerseyTest; -import org.junit.Test; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.core.Application; -import jakarta.ws.rs.core.Response; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.Base64; - -import static org.junit.Assert.assertEquals; - -/** - * @author Marcelo Rubim -\ */ -public class ProxyTest extends JerseyTest { - private static final Charset CHARACTER_SET = Charset.forName("iso-8859-1"); - private static final String PROXY_URI = "http://127.0.0.1:9997"; - private static final String PROXY_USERNAME = "proxy-user"; - private static final String PROXY_PASSWORD = "proxy-password"; - - - @Path("") - public static class ProxyResource { - - @GET - public Response getProxy() { - return Response.status(407).header("Proxy-Authenticate", "Basic").build(); - } - - } - - @Path("proxyTest") - public static class ProxyTestResource { - - @GET - public Response getOK() { - return Response.ok().build(); - } - - } - - @Override - protected Application configure() { - ResourceConfig config = new ResourceConfig(ProxyResource.class, ProxyTestResource.class); - return config; - } - - @Override - protected void configureClient(ClientConfig config) { - config.connectorProvider(new JettyConnectorProvider()); - } - - @Test - public void testGet407() { - startFakeProxy(); - client().property(ClientProperties.PROXY_URI, ProxyTest.PROXY_URI); - Response response = target("proxyTest").request().get(); - assertEquals(407, response.getStatus()); - } - - @Test - public void testGetSuccess() { - startFakeProxy(); - client().property(ClientProperties.PROXY_URI, ProxyTest.PROXY_URI); - client().property(ClientProperties.PROXY_USERNAME, ProxyTest.PROXY_USERNAME); - client().property(ClientProperties.PROXY_PASSWORD, ProxyTest.PROXY_PASSWORD); - Response response = target("proxyTest").request().get(); - assertEquals(200, response.getStatus()); - } - - private void startFakeProxy(){ - Server server = new Server(9997); - server.setHandler(new ProxyHandler()); - try { - server.start(); - } catch (Exception e) { - - } - } - - class ProxyHandler extends AbstractHandler { - @Override - public void handle(String target, - Request baseRequest, - HttpServletRequest request, - HttpServletResponse response) throws IOException, - ServletException { - - if (request.getHeader("Proxy-Authorization") != null) { - String proxyAuthorization = request.getHeader("Proxy-Authorization"); - String decoded = new String(Base64.getDecoder().decode(proxyAuthorization.substring(6).getBytes()), - CHARACTER_SET); - final String[] split = decoded.split(":"); - final String username = split[0]; - final String password = split[1]; - - if (!username.equals(PROXY_USERNAME)) { - response.setStatus(400); - System.out.println("Found unexpected username: " + username); - } - - if (!password.equals(PROXY_PASSWORD)) { - response.setStatus(400); - System.out.println("Found unexpected password: " + username); - } - response.setStatus(200); - //TODO Add redirect to requestURI - } else { - response.setStatus(407); - response.addHeader("Proxy-Authenticate", "Basic"); - } - - - baseRequest.setHandled(true); - } - } -}
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java index 176fcb3..71f38ea 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022 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 @@ -19,15 +19,20 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeoutException; +import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response; +import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.ClientRequest; import org.glassfish.jersey.client.ClientResponse; import org.glassfish.jersey.netty.connector.internal.NettyInputStream; +import org.glassfish.jersey.netty.connector.internal.RedirectException; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; @@ -36,6 +41,7 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.timeout.IdleStateEvent; @@ -47,21 +53,32 @@ */ class JerseyClientHandler extends SimpleChannelInboundHandler<HttpObject> { + private static final int DEFAULT_MAX_REDIRECTS = 5; + + // Modified only by the same thread. No need to synchronize it. + private final Set<URI> redirectUriHistory; private final ClientRequest jerseyRequest; private final CompletableFuture<ClientResponse> responseAvailable; private final CompletableFuture<?> responseDone; + private final boolean followRedirects; + private final int maxRedirects; + private final NettyConnector connector; private NettyInputStream nis; private ClientResponse jerseyResponse; private boolean readTimedOut; - JerseyClientHandler(ClientRequest request, - CompletableFuture<ClientResponse> responseAvailable, - CompletableFuture<?> responseDone) { + JerseyClientHandler(ClientRequest request, CompletableFuture<ClientResponse> responseAvailable, + CompletableFuture<?> responseDone, Set<URI> redirectUriHistory, NettyConnector connector) { + this.redirectUriHistory = redirectUriHistory; this.jerseyRequest = request; this.responseAvailable = responseAvailable; this.responseDone = responseDone; + // Follow redirects by default + this.followRedirects = jerseyRequest.resolveProperty(ClientProperties.FOLLOW_REDIRECTS, true); + this.maxRedirects = jerseyRequest.resolveProperty(NettyClientProperties.MAX_REDIRECTS, DEFAULT_MAX_REDIRECTS); + this.connector = connector; } @Override @@ -84,7 +101,41 @@ if (jerseyResponse != null) { ClientResponse cr = jerseyResponse; jerseyResponse = null; - responseAvailable.complete(cr); + int responseStatus = cr.getStatus(); + if (followRedirects + && (responseStatus == HttpResponseStatus.MOVED_PERMANENTLY.code() + || responseStatus == HttpResponseStatus.FOUND.code() + || responseStatus == HttpResponseStatus.SEE_OTHER.code() + || responseStatus == HttpResponseStatus.TEMPORARY_REDIRECT.code() + || responseStatus == HttpResponseStatus.PERMANENT_REDIRECT.code())) { + String location = cr.getHeaderString(HttpHeaders.LOCATION); + if (location == null || location.isEmpty()) { + responseAvailable.completeExceptionally(new RedirectException(LocalizationMessages.REDIRECT_NO_LOCATION())); + } else { + try { + URI newUri = URI.create(location); + boolean alreadyRequested = !redirectUriHistory.add(newUri); + if (alreadyRequested) { + // infinite loop detection + responseAvailable.completeExceptionally( + new RedirectException(LocalizationMessages.REDIRECT_INFINITE_LOOP())); + } else if (redirectUriHistory.size() > maxRedirects) { + // maximal number of redirection + responseAvailable.completeExceptionally( + new RedirectException(LocalizationMessages.REDIRECT_LIMIT_REACHED(maxRedirects))); + } else { + ClientRequest newReq = new ClientRequest(jerseyRequest); + newReq.setUri(newUri); + connector.execute(newReq, redirectUriHistory, responseAvailable); + } + } catch (IllegalArgumentException e) { + responseAvailable.completeExceptionally( + new RedirectException(LocalizationMessages.REDIRECT_ERROR_DETERMINING_LOCATION(location))); + } + } + } else { + responseAvailable.complete(cr); + } } } @@ -92,7 +143,6 @@ public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { if (msg instanceof HttpResponse) { final HttpResponse response = (HttpResponse) msg; - jerseyResponse = new ClientResponse(new Response.StatusType() { @Override public int getStatusCode() {
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java index bf34699..671b08f 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java
@@ -68,4 +68,18 @@ * @see javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String) */ public static final String ENABLE_SSL_HOSTNAME_VERIFICATION = "jersey.config.client.tls.enableHostnameVerification"; + + /** + * The maximal number of redirects during single request. + * <p/> + * Value is expected to be positive {@link Integer}. Default value is {@value #DEFAULT_MAX_REDIRECTS}. + * <p/> + * HTTP redirection must be enabled by property {@link org.glassfish.jersey.client.ClientProperties#FOLLOW_REDIRECTS}, + * otherwise {@code MAX_REDIRECTS} is not applied. + * + * @since 2.36 + * @see org.glassfish.jersey.client.ClientProperties#FOLLOW_REDIRECTS + * @see org.glassfish.jersey.netty.connector.internal.RedirectException + */ + public static final String MAX_REDIRECTS = "jersey.config.client.NettyConnectorProvider.maxRedirects"; }
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java index 3e931f0..77d0d27 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
@@ -19,12 +19,17 @@ import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; @@ -43,6 +48,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; @@ -51,16 +57,20 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpChunkedInput; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpContentDecompressor; import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.proxy.HttpProxyHandler; +import io.netty.handler.proxy.ProxyConnectionEvent; +import io.netty.handler.proxy.ProxyHandler; import io.netty.handler.ssl.ApplicationProtocolConfig; import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.IdentityCipherSuiteFilter; @@ -151,7 +161,9 @@ @Override public ClientResponse apply(ClientRequest jerseyRequest) { try { - return execute(jerseyRequest).join(); + CompletableFuture<ClientResponse> response = new CompletableFuture<>(); + execute(jerseyRequest, new HashSet<>(), response); + return response.join(); } catch (CompletionException cex) { final Throwable t = cex.getCause() == null ? cex : cex.getCause(); throw new ProcessingException(t.getMessage(), t); @@ -162,19 +174,25 @@ @Override public Future<?> apply(final ClientRequest jerseyRequest, final AsyncConnectorCallback jerseyCallback) { - return execute(jerseyRequest).whenCompleteAsync((r, th) -> { - if (th == null) jerseyCallback.response(r); - else jerseyCallback.failure(th); - }, executorService); + CompletableFuture<ClientResponse> response = new CompletableFuture<>(); + response.whenCompleteAsync((r, th) -> { + if (th == null) { + jerseyCallback.response(r); + } else { + jerseyCallback.failure(th); + } + }, executorService); + execute(jerseyRequest, new HashSet<>(), response); + return response; } - protected CompletableFuture<ClientResponse> execute(final ClientRequest jerseyRequest) { + protected void execute(final ClientRequest jerseyRequest, final Set<URI> redirectUriHistory, + final CompletableFuture<ClientResponse> responseAvailable) { Integer timeout = jerseyRequest.resolveProperty(ClientProperties.READ_TIMEOUT, 0); if (timeout == null || timeout < 0) { throw new ProcessingException(LocalizationMessages.WRONG_READ_TIMEOUT(timeout)); } - final CompletableFuture<ClientResponse> responseAvailable = new CompletableFuture<>(); final CompletableFuture<?> responseDone = new CompletableFuture<>(); final URI requestUri = jerseyRequest.getUri(); @@ -213,6 +231,8 @@ } } + Integer connectTimeout = jerseyRequest.resolveProperty(ClientProperties.CONNECT_TIMEOUT, 0); + if (chan == null) { Bootstrap b = new Bootstrap(); b.group(group) @@ -236,8 +256,29 @@ InetSocketAddress proxyAddr = new InetSocketAddress(u.getHost(), u.getPort() == -1 ? 8080 : u.getPort()); - p.addLast(userName == null ? new HttpProxyHandler(proxyAddr) - : new HttpProxyHandler(proxyAddr, userName, password)); + ProxyHandler proxy = createProxyHandler(jerseyRequest, proxyAddr, userName, password, connectTimeout); + p.addLast(proxy); + } else { + ProxySelector sel = ProxySelector.getDefault(); + for (Proxy proxy: sel.select(requestUri)) { + if (Proxy.Type.HTTP.equals(proxy.type())) { + SocketAddress proxyAddress = proxy.address(); + if (InetSocketAddress.class.isInstance(proxy.address())) { + InetSocketAddress proxyAddr = (InetSocketAddress) proxyAddress; + if (proxyAddr.isUnresolved() + && proxyAddr.getHostName() != null + && proxyAddr.getHostName().startsWith("http://")) { + proxyAddress = new InetSocketAddress( + proxyAddr.getHostString().substring(7), proxyAddr.getPort() + ); + } + } + ProxyHandler proxyHandler + = createProxyHandler(jerseyRequest, proxyAddress, null, null, connectTimeout); + p.addLast(proxyHandler); + break; + } + } } // Enable HTTPS if necessary. @@ -274,7 +315,6 @@ }); // connect timeout - Integer connectTimeout = jerseyRequest.resolveProperty(ClientProperties.CONNECT_TIMEOUT, 0); if (connectTimeout > 0) { b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout); } @@ -290,7 +330,8 @@ // assert: it is ok to abort the entire response, if responseDone is completed exceptionally - in particular, nothing // will leak final Channel ch = chan; - JerseyClientHandler clientHandler = new JerseyClientHandler(jerseyRequest, responseAvailable, responseDone); + JerseyClientHandler clientHandler = + new JerseyClientHandler(jerseyRequest, responseAvailable, responseDone, redirectUriHistory, this); // read timeout makes sense really as an inactivity timeout ch.pipeline().addLast(READ_TIMEOUT_HANDLER, new IdleStateHandler(0, 0, timeout, TimeUnit.MILLISECONDS)); @@ -346,9 +387,7 @@ } // headers - for (final Map.Entry<String, List<String>> e : jerseyRequest.getStringHeaders().entrySet()) { - nettyRequest.headers().add(e.getKey(), e.getValue()); - } + setHeaders(jerseyRequest, nettyRequest.headers()); // host header - http 1.1 nettyRequest.headers().add(HttpHeaderNames.HOST, jerseyRequest.getUri().getHost()); @@ -411,8 +450,6 @@ } catch (InterruptedException e) { responseDone.completeExceptionally(e); } - - return responseAvailable; } private String buildPathWithQueryParameters(URI requestUri) { @@ -475,4 +512,24 @@ } } } + + private static ProxyHandler createProxyHandler(ClientRequest jerseyRequest, SocketAddress proxyAddr, + String userName, String password, long connectTimeout) { + HttpHeaders httpHeaders = setHeaders(jerseyRequest, new DefaultHttpHeaders()); + + ProxyHandler proxy = userName == null ? new HttpProxyHandler(proxyAddr, httpHeaders) + : new HttpProxyHandler(proxyAddr, userName, password, httpHeaders); + if (connectTimeout > 0) { + proxy.setConnectTimeoutMillis(connectTimeout); + } + + return proxy; + } + + private static HttpHeaders setHeaders(ClientRequest jerseyRequest, HttpHeaders headers) { + for (final Map.Entry<String, List<String>> e : jerseyRequest.getStringHeaders().entrySet()) { + headers.add(e.getKey(), e.getValue()); + } + return headers; + } }
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/RedirectException.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/RedirectException.java new file mode 100644 index 0000000..bef04e1 --- /dev/null +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/RedirectException.java
@@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 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.netty.connector.internal; + +import org.glassfish.jersey.client.ClientProperties; + +/** + * This Exception is used only if {@link ClientProperties#FOLLOW_REDIRECTS} is set to {@code true}. + * <p/> + * This exception is thrown when any of the Redirect HTTP response status codes (301, 302, 303, 307, 308) is received and: + * <ul> + * <li> + * the chained redirection count exceeds the value of + * {@link org.glassfish.jersey.netty.connector.NettyClientProperties#MAX_REDIRECTS} + * </li> + * <li> + * or an infinite redirection loop is detected + * </li> + * <li> + * or Location response header is missing, empty or does not contain a valid {@link java.net.URI}. + * </li> + * </ul> + * + */ +public class RedirectException extends Exception { + + private static final long serialVersionUID = 4357724300486801294L; + + /** + * Constructor. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public RedirectException(String message) { + super(message); + } + + /** + * Constructor. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public RedirectException(String message, Throwable t) { + super(message, t); + } +}
diff --git a/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties b/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties index bf12db6..9055d62 100644 --- a/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties +++ b/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties
@@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2022 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 @@ -19,4 +19,7 @@ wrong.max.pool.size=Unexpected ("{0}") maximum number of connections per destination. wrong.max.pool.total=Unexpected ("{0}") maximum number of connections total. wrong.max.pool.idle=Unexpected ("{0}") maximum number of idle seconds. - +redirect.no.location="Received redirect that does not contain a location or the location is empty." +redirect.error.determining.location="Error determining redirect location: ({0})." +redirect.infinite.loop="Infinite loop in chained redirects detected." +redirect.limit.reached="Max chained redirect limit ({0}) exceeded."
diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java new file mode 100644 index 0000000..6eb2d93 --- /dev/null +++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java
@@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022 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.netty.connector; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.net.URI; +import java.util.logging.Logger; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Response; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.logging.LoggingFeature; +import org.glassfish.jersey.netty.connector.internal.RedirectException; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; + +public class FollowRedirectsTest extends JerseyTest { + + private static final Logger LOGGER = Logger.getLogger(FollowRedirectsTest.class.getName()); + private static final String TEST_URL = "http://localhost:9998/test"; + + @Path("/test") + public static class RedirectResource { + @GET + public String get() { + return "GET"; + } + + @GET + @Path("redirect") + public Response redirect() { + return Response.seeOther(URI.create(TEST_URL)).build(); + } + + @GET + @Path("loop") + public Response loop() { + return Response.seeOther(URI.create(TEST_URL + "/loop")).build(); + } + + @GET + @Path("redirect2") + public Response redirect2() { + return Response.seeOther(URI.create(TEST_URL + "/redirect")).build(); + } + } + + @Override + protected Application configure() { + ResourceConfig config = new ResourceConfig(RedirectResource.class); + config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY)); + return config; + } + + @Override + protected void configureClient(ClientConfig config) { + config.property(ClientProperties.FOLLOW_REDIRECTS, false); + config.connectorProvider(new NettyConnectorProvider()); + } + + @Test + public void testDoFollow() { + final URI u = target().getUri(); + ClientConfig config = new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS, true); + config.connectorProvider(new NettyConnectorProvider()); + Client c = ClientBuilder.newClient(config); + WebTarget t = c.target(u); + Response r = t.path("test/redirect") + .request().get(); + assertEquals(200, r.getStatus()); + assertEquals("GET", r.readEntity(String.class)); + c.close(); + } + + @Test + public void testDoFollowPerRequestOverride() { + WebTarget t = target("test/redirect"); + t.property(ClientProperties.FOLLOW_REDIRECTS, true); + Response r = t.request().get(); + assertEquals(200, r.getStatus()); + assertEquals("GET", r.readEntity(String.class)); + } + + @Test + public void testDontFollow() { + WebTarget t = target("test/redirect"); + assertEquals(303, t.request().get().getStatus()); + } + + @Test + public void testDontFollowPerRequestOverride() { + final URI u = target().getUri(); + ClientConfig config = new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS, true); + config.connectorProvider(new NettyConnectorProvider()); + Client client = ClientBuilder.newClient(config); + WebTarget t = client.target(u); + t.property(ClientProperties.FOLLOW_REDIRECTS, false); + Response r = t.path("test/redirect").request().get(); + assertEquals(303, r.getStatus()); + client.close(); + } + + @Test + public void testInfiniteLoop() { + WebTarget t = target("test/loop"); + t.property(ClientProperties.FOLLOW_REDIRECTS, true); + try { + t.request().get(); + fail("Expected exception"); + } catch (ProcessingException e) { + assertEquals(RedirectException.class, e.getCause().getClass()); + assertEquals(LocalizationMessages.REDIRECT_INFINITE_LOOP(), e.getCause().getMessage()); + } + } + + @Test + public void testRedirectLimitReached() { + WebTarget t = target("test/redirect2"); + t.property(ClientProperties.FOLLOW_REDIRECTS, true); + t.property(NettyClientProperties.MAX_REDIRECTS, 1); + try { + t.request().get(); + fail("Expected exception"); + } catch (ProcessingException e) { + assertEquals(RedirectException.class, e.getCause().getClass()); + assertEquals(LocalizationMessages.REDIRECT_LIMIT_REACHED(1), e.getCause().getMessage()); + } + } + + @Test + public void testRedirectNoLimitReached() { + WebTarget t = target("test/redirect2"); + t.property(ClientProperties.FOLLOW_REDIRECTS, true); + Response r = t.request().get(); + assertEquals(200, r.getStatus()); + assertEquals("GET", r.readEntity(String.class)); + } +}
diff --git a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java index 6b2021d..d5a1658 100644 --- a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java +++ b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022 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 @@ -256,6 +256,55 @@ public static final String JAXRS_SERVICE_LOADING_ENABLE = "jakarta.ws.rs.loadServices"; /** + * Comma separated list of jackson modules which are only enabled (only those modules will be used) + * for json-jackson processing + * + * @since 2.36 + */ + public static final String JSON_JACKSON_ENABLED_MODULES = "jersey.config.json.jackson.enabled.modules"; + + /** + * Client-specific version of {@link CommonProperties#JSON_JACKSON_ENABLED_MODULES}. + * + * If present, it overrides the generic one for the client environment. + * @since 2.36 + */ + public static final String JSON_JACKSON_ENABLED_MODULES_CLIENT = "jersey.config.client.json.jackson.enabled.modules"; + + /** + * Server-specific version of {@link CommonProperties#JSON_JACKSON_ENABLED_MODULES}. + * + * If present, it overrides the generic one for the server environment. + * @since 2.36 + */ + public static final String JSON_JACKSON_ENABLED_MODULES_SERVER = "jersey.config.server.json.jackson.enabled.modules"; + + /** + * Comma separated list of jackson modules which shall be excluded from json-jackson processing. + * the JaxbAnnotationModule is always excluded (cannot be configured). + * + * @since 2.36 + */ + + public static final String JSON_JACKSON_DISABLED_MODULES = "jersey.config.json.jackson.disabled.modules"; + + /** + * Client-specific version of {@link CommonProperties#JSON_JACKSON_DISABLED_MODULES}. + * + * If present, it overrides the generic one for the client environment. + * @since 2.36 + */ + public static final String JSON_JACKSON_DISABLED_MODULES_CLIENT = "jersey.config.client.json.jackson.disabled.modules"; + + /** + * Server-specific version of {@link CommonProperties#JSON_JACKSON_DISABLED_MODULES}. + * + * If present, it overrides the generic one for the client environment. + * @since 2.36 + */ + public static final String JSON_JACKSON_DISABLED_MODULES_SERVER = "jersey.config.server.json.jackson.disabled.modules"; + + /** * Prevent instantiation. */ private CommonProperties() { @@ -274,7 +323,7 @@ * * @since 2.8 */ - public static Object getValue(final Map<String, ?> properties, final String propertyName, final Class<?> type) { + public static <T> T getValue(final Map<String, ?> properties, final String propertyName, final Class<T> type) { return PropertiesHelper.getValue(properties, propertyName, type, CommonProperties.LEGACY_FALLBACK_MAP); }
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java b/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java index 7a797a0..2b99da6 100644 --- a/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java +++ b/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java
@@ -83,7 +83,7 @@ printRequestLine(b, "Sending client request", id, context.getMethod(), context.getUri()); printPrefixedHeaders(b, id, REQUEST_PREFIX, context.getStringHeaders()); - if (context.hasEntity() && printEntity(verbosity, context.getMediaType())) { + if (printEntity(verbosity, context.getMediaType()) && context.hasEntity()) { final OutputStream stream = new LoggingStream(b, context.getEntityStream()); context.setEntityStream(stream); context.setProperty(ENTITY_LOGGER_PROPERTY, stream); @@ -107,7 +107,7 @@ printResponseLine(b, "Client response received", id, responseContext.getStatus()); printPrefixedHeaders(b, id, RESPONSE_PREFIX, responseContext.getHeaders()); - if (responseContext.hasEntity() && printEntity(verbosity, responseContext.getMediaType())) { + if (printEntity(verbosity, responseContext.getMediaType()) && responseContext.hasEntity()) { responseContext.setEntityStream(logInboundEntity(b, responseContext.getEntityStream(), MessageUtils.getCharset(responseContext.getMediaType()))); }
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java b/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java index 7430859..6ed5dfe 100644 --- a/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java +++ b/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java
@@ -83,7 +83,7 @@ printRequestLine(b, "Server has received a request", id, context.getMethod(), context.getUriInfo().getRequestUri()); printPrefixedHeaders(b, id, REQUEST_PREFIX, context.getHeaders()); - if (context.hasEntity() && printEntity(verbosity, context.getMediaType())) { + if (printEntity(verbosity, context.getMediaType()) && context.hasEntity()) { context.setEntityStream( logInboundEntity(b, context.getEntityStream(), MessageUtils.getCharset(context.getMediaType()))); } @@ -105,7 +105,7 @@ printResponseLine(b, "Server responded with a response", id, responseContext.getStatus()); printPrefixedHeaders(b, id, RESPONSE_PREFIX, responseContext.getStringHeaders()); - if (responseContext.hasEntity() && printEntity(verbosity, responseContext.getMediaType())) { + if (printEntity(verbosity, responseContext.getMediaType()) && responseContext.hasEntity()) { final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream()); responseContext.setEntityStream(stream); requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream);
diff --git a/core-common/src/test/java/org/glassfish/jersey/logging/HasEntityTimeoutTest.java b/core-common/src/test/java/org/glassfish/jersey/logging/HasEntityTimeoutTest.java new file mode 100644 index 0000000..3cd15cb --- /dev/null +++ b/core-common/src/test/java/org/glassfish/jersey/logging/HasEntityTimeoutTest.java
@@ -0,0 +1,277 @@ +/* + * Copyright (c) 2022 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.logging; + +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.client.ClientRequestContext; +import jakarta.ws.rs.client.ClientResponseContext; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerResponseContext; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.UriInfo; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.util.logging.Level; +import java.util.logging.Logger; + + +public class HasEntityTimeoutTest { + + private enum DirectionType { + INBOUND, + OUTBOUND + } + + private static class UriInfoHandler implements InvocationHandler { + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + switch (method.getName()) { + case "getRequestUri": + return URI.create("http://localhost:8080/get"); + } + return null; + } + } + + private static class RequestResponseHandler implements InvocationHandler { + private final DirectionType type; + + private RequestResponseHandler(DirectionType type) { + this.type = type; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + switch (method.getName()) { + case "hasEntity": + throw new ProcessingException(new SocketTimeoutException("Read timed out")); + case "getUri": + return URI.create("http://localhost:8080"); + case "getStringHeaders": + case "getHeaders": + return new MultivaluedHashMap<String, String>(); + case "getMethod": + return "GET"; + case "getMediaType": + return MediaType.SERVER_SENT_EVENTS_TYPE; + case "getEntityStream": + return type == DirectionType.OUTBOUND + ? new ByteArrayOutputStream() + : new ByteArrayInputStream("entity".getBytes()); + case "getStatus": + return 200; + case "getUriInfo": + return Proxy.newProxyInstance( + UriInfo.class.getClassLoader(), + new Class[]{UriInfo.class}, + new UriInfoHandler()); + } + return null; + } + } + + @Test + public void testClientFilterTimedOut() throws IOException { + ClientLoggingFilter loggingFilter = new ClientLoggingFilter( + LoggingFeature.builder() + .withLogger(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)) + .level(Level.INFO) + .verbosity(LoggingFeature.Verbosity.HEADERS_ONLY) + .maxEntitySize(10) + ); + + ClientRequestContext clientRequestContext = (ClientRequestContext) Proxy.newProxyInstance( + ClientRequestContext.class.getClassLoader(), + new Class[]{ClientRequestContext.class}, + new RequestResponseHandler(DirectionType.OUTBOUND)); + loggingFilter.filter(clientRequestContext); + } + + @Test + public void testClientFilterTimedOutException() throws IOException { + ClientLoggingFilter loggingFilter = new ClientLoggingFilter( + LoggingFeature.builder() + .withLogger(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)) + .level(Level.INFO) + .verbosity(LoggingFeature.Verbosity.PAYLOAD_ANY) + .maxEntitySize(10) + ); + + ClientRequestContext clientRequestContext = (ClientRequestContext) Proxy.newProxyInstance( + ClientRequestContext.class.getClassLoader(), + new Class[]{ClientRequestContext.class}, + new RequestResponseHandler(DirectionType.OUTBOUND)); + try { + loggingFilter.filter(clientRequestContext); + throw new RuntimeException("The expected exception has not been thrown"); + } catch (ProcessingException pe) { + // expected + } + } + + @Test + public void testClientFilterResponseTimedOut() throws IOException { + ClientLoggingFilter loggingFilter = new ClientLoggingFilter( + LoggingFeature.builder() + .withLogger(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)) + .level(Level.INFO) + .verbosity(LoggingFeature.Verbosity.HEADERS_ONLY) + .maxEntitySize(10) + ); + + ClientRequestContext clientRequestContext = (ClientRequestContext) Proxy.newProxyInstance( + ClientRequestContext.class.getClassLoader(), + new Class[]{ClientRequestContext.class}, + new RequestResponseHandler(DirectionType.OUTBOUND)); + + ClientResponseContext clientResponseContext = (ClientResponseContext) Proxy.newProxyInstance( + ClientResponseContext.class.getClassLoader(), + new Class[]{ClientResponseContext.class}, + new RequestResponseHandler(DirectionType.INBOUND)); + loggingFilter.filter(clientRequestContext, clientResponseContext); + } + + @Test + public void testClientFilterResponseTimedOutException() throws IOException { + ClientLoggingFilter loggingFilter = new ClientLoggingFilter( + LoggingFeature.builder() + .withLogger(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)) + .level(Level.INFO) + .verbosity(LoggingFeature.Verbosity.PAYLOAD_ANY) + .maxEntitySize(10) + ); + + ClientRequestContext clientRequestContext = (ClientRequestContext) Proxy.newProxyInstance( + ClientRequestContext.class.getClassLoader(), + new Class[]{ClientRequestContext.class}, + new RequestResponseHandler(DirectionType.OUTBOUND)); + + ClientResponseContext clientResponseContext = (ClientResponseContext) Proxy.newProxyInstance( + ClientResponseContext.class.getClassLoader(), + new Class[]{ClientResponseContext.class}, + new RequestResponseHandler(DirectionType.INBOUND)); + + try { + loggingFilter.filter(clientRequestContext, clientResponseContext); + throw new RuntimeException("The expected exception has not been thrown"); + } catch (ProcessingException pe) { + // expected + } + } + + @Test + public void testServerFilterTimedOut() throws IOException { + ServerLoggingFilter loggingFilter = new ServerLoggingFilter( + LoggingFeature.builder() + .withLogger(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)) + .level(Level.INFO) + .verbosity(LoggingFeature.Verbosity.HEADERS_ONLY) + .maxEntitySize(10) + ); + + ContainerRequestContext containerRequestContext = (ContainerRequestContext) Proxy.newProxyInstance( + ContainerRequestContext.class.getClassLoader(), + new Class[]{ContainerRequestContext.class}, + new RequestResponseHandler(DirectionType.INBOUND)); + loggingFilter.filter(containerRequestContext); + } + + @Test + public void testServerFilterTimedOutException() throws IOException { + ServerLoggingFilter loggingFilter = new ServerLoggingFilter( + LoggingFeature.builder() + .withLogger(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)) + .level(Level.INFO) + .verbosity(LoggingFeature.Verbosity.PAYLOAD_ANY) + .maxEntitySize(10) + ); + + ContainerRequestContext containerRequestContext = (ContainerRequestContext) Proxy.newProxyInstance( + ContainerRequestContext.class.getClassLoader(), + new Class[]{ContainerRequestContext.class}, + new RequestResponseHandler(DirectionType.INBOUND)); + + try { + loggingFilter.filter(containerRequestContext); + throw new RuntimeException("The expected exception has not been thrown"); + } catch (ProcessingException pe) { + // expected + } + } + + @Test + public void testServerFilterResponseTimedOut() throws IOException { + ServerLoggingFilter loggingFilter = new ServerLoggingFilter( + LoggingFeature.builder() + .withLogger(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)) + .level(Level.INFO) + .verbosity(LoggingFeature.Verbosity.HEADERS_ONLY) + .maxEntitySize(10) + ); + + ContainerRequestContext containerRequestContext = (ContainerRequestContext) Proxy.newProxyInstance( + ContainerRequestContext.class.getClassLoader(), + new Class[]{ContainerRequestContext.class}, + new RequestResponseHandler(DirectionType.INBOUND)); + + ContainerResponseContext containerResponseContext = (ContainerResponseContext) Proxy.newProxyInstance( + ContainerResponseContext.class.getClassLoader(), + new Class[]{ContainerResponseContext.class}, + new RequestResponseHandler(DirectionType.OUTBOUND)); + + loggingFilter.filter(containerRequestContext, containerResponseContext); + } + + @Test + public void testServerFilterResponseTimedOutException() throws IOException { + ServerLoggingFilter loggingFilter = new ServerLoggingFilter( + LoggingFeature.builder() + .withLogger(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME)) + .level(Level.INFO) + .verbosity(LoggingFeature.Verbosity.PAYLOAD_ANY) + .maxEntitySize(10) + ); + + ContainerRequestContext containerRequestContext = (ContainerRequestContext) Proxy.newProxyInstance( + ContainerRequestContext.class.getClassLoader(), + new Class[]{ContainerRequestContext.class}, + new RequestResponseHandler(DirectionType.INBOUND)); + + ContainerResponseContext containerResponseContext = (ContainerResponseContext) Proxy.newProxyInstance( + ContainerResponseContext.class.getClassLoader(), + new Class[]{ContainerResponseContext.class}, + new RequestResponseHandler(DirectionType.OUTBOUND)); + + try { + loggingFilter.filter(containerRequestContext, containerResponseContext); + throw new RuntimeException("The expected exception has not been thrown"); + } catch (ProcessingException pe) { + // expected + } + } + +}
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java index 8ca2039..0654582 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java
@@ -38,8 +38,8 @@ public abstract class AnnotationVisitor { /** - * The ASM API version implemented by this visitor. The value of this field must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * The ASM API version implemented by this visitor. The value of this field must be one of the + * {@code ASM}<i>x</i> values in {@link Opcodes}. */ protected final int api; @@ -52,22 +52,22 @@ /** * Constructs a new {@link AnnotationVisitor}. * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param api the ASM API version implemented by this visitor. Must be one of the {@code + * ASM}<i>x</i> values in {@link Opcodes}. */ - public AnnotationVisitor(final int api) { + protected AnnotationVisitor(final int api) { this(api, null); } /** * Constructs a new {@link AnnotationVisitor}. * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param api the ASM API version implemented by this visitor. Must be one of the {@code + * ASM}<i>x</i> values in {@link Opcodes}. * @param annotationVisitor the annotation visitor to which this visitor must delegate method * calls. May be {@literal null}. */ - public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) { + protected AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) { if (api != Opcodes.ASM9 && api != Opcodes.ASM8 && api != Opcodes.ASM7
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ByteVector.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ByteVector.java index 7b386a0..472318f 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ByteVector.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ByteVector.java
@@ -66,6 +66,15 @@ } /** + * Returns the actual number of bytes in this vector. + * + * @return the actual number of bytes in this vector. + */ + public int size() { + return length; + } + + /** * Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary. * * @param byteValue a byte. @@ -352,6 +361,9 @@ * @param size number of additional bytes that this byte vector should be able to receive. */ private void enlarge(final int size) { + if (length > data.length) { + throw new AssertionError("Internal error"); + } int doubleCapacity = 2 * data.length; int minimalCapacity = length + size; byte[] newData = new byte[doubleCapacity > minimalCapacity ? doubleCapacity : minimalCapacity];
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java index b0173c8..cd4a6bb 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
@@ -194,7 +194,7 @@ this.b = classFileBuffer; // Check the class' major_version. This field is after the magic and minor_version fields, which // use 4 and 2 bytes respectively. - if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V18) { + if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V19) { throw new IllegalArgumentException( "Unsupported class file major version " + readShort(classFileOffset + 6)); } @@ -308,12 +308,13 @@ * @return the content of the given input stream. * @throws IOException if a problem occurs during reading. */ + @SuppressWarnings("PMD.UseTryWithResources") private static byte[] readStream(final InputStream inputStream, final boolean close) throws IOException { if (inputStream == null) { throw new IOException("Class not found"); } - int bufferSize = calculateBufferSize(inputStream); + int bufferSize = computeBufferSize(inputStream); try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { byte[] data = new byte[bufferSize]; int bytesRead; @@ -334,19 +335,19 @@ } } - private static int calculateBufferSize(final InputStream inputStream) throws IOException { + private static int computeBufferSize(final InputStream inputStream) throws IOException { int expectedLength = inputStream.available(); /* - * Some implementations can return 0 while holding available data - * (e.g. new FileInputStream("/proc/a_file")) - * Also in some pathological cases a very small number might be returned, - * and in this case we use default size + * Some implementations can return 0 while holding available data (e.g. new + * FileInputStream("/proc/a_file")). Also in some pathological cases a very small number might + * be returned, and in this case we use a default size. */ if (expectedLength < 256) { return INPUT_STREAM_DATA_CHUNK_SIZE; } return Math.min(expectedLength, MAX_BUFFER_SIZE); } + // ----------------------------------------------------------------------------------------------- // Accessors // -----------------------------------------------------------------------------------------------
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassVisitor.java index e3a5b60..8ebf594f 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassVisitor.java
@@ -40,8 +40,8 @@ public abstract class ClassVisitor { /** - * The ASM API version implemented by this visitor. The value of this field must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * The ASM API version implemented by this visitor. The value of this field must be one of the + * {@code ASM}<i>x</i> values in {@link Opcodes}. */ protected final int api; @@ -51,23 +51,22 @@ /** * Constructs a new {@link ClassVisitor}. * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param api the ASM API version implemented by this visitor. Must be one of the {@code + * ASM}<i>x</i> values in {@link Opcodes}. */ - public ClassVisitor(final int api) { + protected ClassVisitor(final int api) { this(api, null); } /** * Constructs a new {@link ClassVisitor}. * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7}, {@link - * Opcodes#ASM8} or {@link Opcodes#ASM9}. + * @param api the ASM API version implemented by this visitor. Must be one of the {@code + * ASM}<i>x</i> values in {@link Opcodes}. * @param classVisitor the class visitor to which this visitor must delegate method calls. May be * null. */ - public ClassVisitor(final int api, final ClassVisitor classVisitor) { + protected ClassVisitor(final int api, final ClassVisitor classVisitor) { if (api != Opcodes.ASM9 && api != Opcodes.ASM8 && api != Opcodes.ASM7
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java index 81176b8..de2c441 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java
@@ -65,6 +65,12 @@ */ public static final int COMPUTE_FRAMES = 2; + /** + * The flags passed to the constructor. Must be zero or more of {@link #COMPUTE_MAXS} and {@link + * #COMPUTE_FRAMES}. + */ + private final int flags; + // Note: fields are ordered as in the ClassFile structure, and those related to attributes are // ordered as in Section 4.7 of the JVMS. @@ -248,24 +254,40 @@ * @param classReader the {@link ClassReader} used to read the original class. It will be used to * copy the entire constant pool and bootstrap methods from the original class and also to * copy other fragments of original bytecode where applicable. - * @param flags option flags that can be used to modify the default behavior of this class.Must be - * zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. <i>These option flags do - * not affect methods that are copied as is in the new class. This means that neither the + * @param flags option flags that can be used to modify the default behavior of this class. Must + * be zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. <i>These option flags + * do not affect methods that are copied as is in the new class. This means that neither the * maximum stack size nor the stack frames will be computed for these methods</i>. */ public ClassWriter(final ClassReader classReader, final int flags) { super(/* latest api = */ Opcodes.ASM9); + this.flags = flags; symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader); if ((flags & COMPUTE_FRAMES) != 0) { - this.compute = MethodWriter.COMPUTE_ALL_FRAMES; + compute = MethodWriter.COMPUTE_ALL_FRAMES; } else if ((flags & COMPUTE_MAXS) != 0) { - this.compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL; + compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL; } else { - this.compute = MethodWriter.COMPUTE_NOTHING; + compute = MethodWriter.COMPUTE_NOTHING; } } // ----------------------------------------------------------------------------------------------- + // Accessors + // ----------------------------------------------------------------------------------------------- + + /** + * Returns true if all the given flags were passed to the constructor. + * + * @param flags some option flags. Must be zero or more of {@link #COMPUTE_MAXS} and {@link + * #COMPUTE_FRAMES}. + * @return true if all the given flags, or more, were passed to the constructor. + */ + public boolean hasFlags(final int flags) { + return (this.flags & flags) == flags; + } + + // ----------------------------------------------------------------------------------------------- // Implementation of the ClassVisitor abstract class // -----------------------------------------------------------------------------------------------
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldVisitor.java index 716d6b0..64865d6 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldVisitor.java
@@ -37,9 +37,8 @@ public abstract class FieldVisitor { /** - * The ASM API version implemented by this visitor. The value of this field must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7}, {@link - * Opcodes#ASM8} or {@link Opcodes#ASM9}. + * The ASM API version implemented by this visitor. The value of this field must be one of the + * {@code ASM}<i>x</i> values in {@link Opcodes}. */ protected final int api; @@ -49,24 +48,22 @@ /** * Constructs a new {@link FieldVisitor}. * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7}, {@link - * Opcodes#ASM8} or {@link Opcodes#ASM9}. + * @param api the ASM API version implemented by this visitor. Must be one of the {@code + * ASM}<i>x</i> values in {@link Opcodes}. */ - public FieldVisitor(final int api) { + protected FieldVisitor(final int api) { this(api, null); } /** * Constructs a new {@link FieldVisitor}. * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link - * Opcodes#ASM8}. + * @param api the ASM API version implemented by this visitor. Must be one of the {@code + * ASM}<i>x</i> values in {@link Opcodes}. * @param fieldVisitor the field visitor to which this visitor must delegate method calls. May be * null. */ - public FieldVisitor(final int api, final FieldVisitor fieldVisitor) { + protected FieldVisitor(final int api, final FieldVisitor fieldVisitor) { if (api != Opcodes.ASM9 && api != Opcodes.ASM8 && api != Opcodes.ASM7
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java index 77392c7..805aed5 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java
@@ -51,8 +51,8 @@ private static final String REQUIRES_ASM5 = "This feature requires ASM5"; /** - * The ASM API version implemented by this visitor. The value of this field must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * The ASM API version implemented by this visitor. The value of this field must be one of the + * {@code ASM}<i>x</i> values in {@link Opcodes}. */ protected final int api; @@ -64,22 +64,22 @@ /** * Constructs a new {@link MethodVisitor}. * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param api the ASM API version implemented by this visitor. Must be one of the {@code + * ASM}<i>x</i> values in {@link Opcodes}. */ - public MethodVisitor(final int api) { + protected MethodVisitor(final int api) { this(api, null); } /** * Constructs a new {@link MethodVisitor}. * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param api the ASM API version implemented by this visitor. Must be one of the {@code + * ASM}<i>x</i> values in {@link Opcodes}. * @param methodVisitor the method visitor to which this visitor must delegate method calls. May * be null. */ - public MethodVisitor(final int api, final MethodVisitor methodVisitor) { + protected MethodVisitor(final int api, final MethodVisitor methodVisitor) { if (api != Opcodes.ASM9 && api != Opcodes.ASM8 && api != Opcodes.ASM7 @@ -351,12 +351,12 @@ * * @param opcode the opcode of the local variable instruction to be visited. This opcode is either * ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. - * @param var the operand of the instruction to be visited. This operand is the index of a local - * variable. + * @param varIndex the operand of the instruction to be visited. This operand is the index of a + * local variable. */ - public void visitVarInsn(final int opcode, final int var) { + public void visitVarInsn(final int opcode, final int varIndex) { if (mv != null) { - mv.visitVarInsn(opcode, var); + mv.visitVarInsn(opcode, varIndex); } } @@ -554,12 +554,12 @@ /** * Visits an IINC instruction. * - * @param var index of the local variable to be incremented. + * @param varIndex index of the local variable to be incremented. * @param increment amount to increment the local variable by. */ - public void visitIincInsn(final int var, final int increment) { + public void visitIincInsn(final int varIndex, final int increment) { if (mv != null) { - mv.visitIincInsn(var, increment); + mv.visitIincInsn(varIndex, increment); } }
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java index 24f7f61..ea2e4e4 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java
@@ -466,7 +466,8 @@ /** * Indicates what must be computed. Must be one of {@link #COMPUTE_ALL_FRAMES}, {@link - * #COMPUTE_INSERTED_FRAMES}, {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_NOTHING}. + * #COMPUTE_INSERTED_FRAMES}, {@link COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link + * #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_NOTHING}. */ private final int compute; @@ -904,26 +905,26 @@ } @Override - public void visitVarInsn(final int opcode, final int var) { + public void visitVarInsn(final int opcode, final int varIndex) { lastBytecodeOffset = code.length; // Add the instruction to the bytecode of the method. - if (var < 4 && opcode != Opcodes.RET) { + if (varIndex < 4 && opcode != Opcodes.RET) { int optimizedOpcode; if (opcode < Opcodes.ISTORE) { - optimizedOpcode = Constants.ILOAD_0 + ((opcode - Opcodes.ILOAD) << 2) + var; + optimizedOpcode = Constants.ILOAD_0 + ((opcode - Opcodes.ILOAD) << 2) + varIndex; } else { - optimizedOpcode = Constants.ISTORE_0 + ((opcode - Opcodes.ISTORE) << 2) + var; + optimizedOpcode = Constants.ISTORE_0 + ((opcode - Opcodes.ISTORE) << 2) + varIndex; } code.putByte(optimizedOpcode); - } else if (var >= 256) { - code.putByte(Constants.WIDE).put12(opcode, var); + } else if (varIndex >= 256) { + code.putByte(Constants.WIDE).put12(opcode, varIndex); } else { - code.put11(opcode, var); + code.put11(opcode, varIndex); } // If needed, update the maximum stack size and number of locals, and stack map frames. if (currentBasicBlock != null) { if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(opcode, var, null, null); + currentBasicBlock.frame.execute(opcode, varIndex, null, null); } else { if (opcode == Opcodes.RET) { // No stack size delta. @@ -945,9 +946,9 @@ || opcode == Opcodes.DLOAD || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) { - currentMaxLocals = var + 2; + currentMaxLocals = varIndex + 2; } else { - currentMaxLocals = var + 1; + currentMaxLocals = varIndex + 1; } if (currentMaxLocals > maxLocals) { maxLocals = currentMaxLocals; @@ -1307,21 +1308,21 @@ } @Override - public void visitIincInsn(final int var, final int increment) { + public void visitIincInsn(final int varIndex, final int increment) { lastBytecodeOffset = code.length; // Add the instruction to the bytecode of the method. - if ((var > 255) || (increment > 127) || (increment < -128)) { - code.putByte(Constants.WIDE).put12(Opcodes.IINC, var).putShort(increment); + if ((varIndex > 255) || (increment > 127) || (increment < -128)) { + code.putByte(Constants.WIDE).put12(Opcodes.IINC, varIndex).putShort(increment); } else { - code.putByte(Opcodes.IINC).put11(var, increment); + code.putByte(Opcodes.IINC).put11(varIndex, increment); } // If needed, update the maximum stack size and number of locals, and stack map frames. if (currentBasicBlock != null && (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES)) { - currentBasicBlock.frame.execute(Opcodes.IINC, var, null, null); + currentBasicBlock.frame.execute(Opcodes.IINC, varIndex, null, null); } if (compute != COMPUTE_NOTHING) { - int currentMaxLocals = var + 1; + int currentMaxLocals = varIndex + 1; if (currentMaxLocals > maxLocals) { maxLocals = currentMaxLocals; }
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleVisitor.java index 0089d06..9667590 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleVisitor.java
@@ -53,7 +53,7 @@ * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM6} * or {@link Opcodes#ASM7}. */ - public ModuleVisitor(final int api) { + protected ModuleVisitor(final int api) { this(api, null); } @@ -65,7 +65,7 @@ * @param moduleVisitor the module visitor to which this visitor must delegate method calls. May * be null. */ - public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) { + protected ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) { if (api != Opcodes.ASM9 && api != Opcodes.ASM8 && api != Opcodes.ASM7
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java index 7850f21..0a591ec 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
@@ -284,6 +284,7 @@ int V16 = 0 << 16 | 60; int V17 = 0 << 16 | 61; int V18 = 0 << 16 | 62; + int V19 = 0 << 16 | 63; /** * Version flag indicating that the class is using 'preview' features.
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentVisitor.java index bab13ad..65cf92c 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentVisitor.java
@@ -53,7 +53,7 @@ * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM8} * or {@link Opcodes#ASM9}. */ - public RecordComponentVisitor(final int api) { + protected RecordComponentVisitor(final int api) { this(api, null); } @@ -64,7 +64,7 @@ * @param recordComponentVisitor the record component visitor to which this visitor must delegate * method calls. May be null. */ - public RecordComponentVisitor( + protected RecordComponentVisitor( final int api, final RecordComponentVisitor recordComponentVisitor) { if (api != Opcodes.ASM9 && api != Opcodes.ASM8
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Type.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Type.java index dbe11cc..ab8687e 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Type.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Type.java
@@ -440,7 +440,7 @@ case '(': return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd); default: - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Invalid descriptor: " + descriptorBuffer); } }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java index cdb34b1..60fdf20 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -303,7 +303,7 @@ private static class ClassReaderWrapper { private static final Logger LOGGER = Logger.getLogger(ClassReader.class.getName()); - private static final int WARN_VERSION = Opcodes.V18; + private static final int WARN_VERSION = Opcodes.V19; private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096; private final byte[] b;
diff --git a/core-server/src/main/resources/META-INF/NOTICE.markdown b/core-server/src/main/resources/META-INF/NOTICE.markdown index c95dd3a..9399ca7 100644 --- a/core-server/src/main/resources/META-INF/NOTICE.markdown +++ b/core-server/src/main/resources/META-INF/NOTICE.markdown
@@ -36,7 +36,7 @@ * Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved. * Copyright 2010-2013 Coda Hale and Yammer, Inc. -org.objectweb.asm Version 9.0 +org.objectweb.asm Version 9.3 * License: Modified BSD (https://asm.ow2.io/license.html) * Copyright: (c) 2000-2011 INRIA, France Telecom. All rights reserved.
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml index a3232c6..9a0c4d6 100644 --- a/docs/src/main/docbook/appendix-properties.xml +++ b/docs/src/main/docbook/appendix-properties.xml
@@ -148,6 +148,42 @@ </entry> </row> <row> + <entry>&jersey.common.CommonProperties.JSON_JACKSON_ENABLED_MODULES; / + <entry>&jersey.common.CommonProperties.JSON_JACKSON_ENABLED_MODULES_CLIENT; / + <entry>&jersey.common.CommonProperties.JSON_JACKSON_ENABLED_MODULES_SERVER; + </entry> + <entry> + <literal>jersey.config.json.jackson.enabled.modules</literal> + <literal>jersey.config.client.json.jackson.enabled.modules</literal> + <literal>jersey.config.server.json.jackson.enabled.modules</literal> + </entry> + <entry> + Comma separated list of jackson modules which shall be used for json-jackson provider. + If set, only those modules will be used for JSON processing. + + Default value is <literal>NULL</literal> + @since 2.36 + </entry> + </row> + <row> + <entry>&jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES; / + <entry>&jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT; / + <entry>&jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER; + </entry> + <entry> + <literal>jersey.config.json.jackson.disabled.modules</literal> + <literal>jersey.config.client.json.jackson.disabled.modules</literal> + <literal>jersey.config.server.json.jackson.disabled.modules</literal> + </entry> + <entry> + Comma separated list of jackson modules which shall be excluded from json-jackson provider. + If set, those modules will be excluded from JSON processing. + + Default value is <literal>NULL</literal> + @since 2.36 + </entry> + </row> + <row> <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME; </entry> <entry> @@ -1845,6 +1881,18 @@ </para> </entry> </row> + <row> + <entry>&jersey.netty.NettyClientProperties.MAX_REDIRECTS;</entry> + <entry><literal>jersey.config.client.NettyConnectorProvider.maxRedirect</literal></entry> + <entry> + <para> + This property determines the maximal number of redirects during single request. Value is expected to be + positive number. Default value is 5. + HTTP redirection must be enabled by property org.glassfish.jersey.client.ClientProperties.FOLLOW_REDIRECTS + otherwise &jersey.netty.NettyClientProperties.MAX_REDIRECTS; is not applied. + </para> + </entry> + </row> </tbody> </tgroup> </table>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent index 800c3f9..2f91101 100644 --- a/docs/src/main/docbook/jersey.ent +++ b/docs/src/main/docbook/jersey.ent
@@ -396,6 +396,12 @@ <!ENTITY jersey.common.CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#OUTBOUND_CONTENT_LENGTH_BUFFER_CLIENT'>CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_CLIENT</link>" > <!ENTITY jersey.common.CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER'>CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER</link>" > <!ENTITY jersey.common.CommonProperties.PROVIDER_DEFAULT_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#PROVIDER_DEFAULT_DISABLE'>CommonProperties.PROVIDER_DEFAULT_DISABLE</link>" > +<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_ENABLED_MODULES "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_ENABLED_MODULES'>CommonProperties.JSON_JACKSON_ENABLED_MODULES</link>" > +<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_ENABLED_MODULES_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_ENABLED_MODULES'>CommonProperties.JSON_JACKSON_ENABLED_MODULES_CLIENT</link>" > +<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_ENABLED_MODULES_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_ENABLED_MODULES'>CommonProperties.JSON_JACKSON_ENABLED_MODULES_SERVER</link>" > +<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES</link>" > +<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT</link>" > +<!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER</link>" > <!ENTITY jersey.common.internal.inject.DisposableSupplier "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/DisposableSupplier.html'>DisposableSupplier</link>"> <!ENTITY jersey.common.internal.inject.InjectionManager "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/InjectionManager.html'>InjectionManager</link>"> <!ENTITY jersey.common.internal.inject.AbstractBinder "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/AbstractBinder.html'>AbstractBinder</link>"> @@ -537,6 +543,7 @@ <!ENTITY jersey.netty.NettyClientProperties.IDLE_CONNECTION_PRUNE_TIMEOUT "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#IDLE_CONNECTION_PRUNE_TIMEOUT'>NettyClientProperties.IDLE_CONNECTION_PRUNE_TIMEOUT</link>" > <!ENTITY jersey.netty.NettyClientProperties.MAX_CONNECTIONS "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_CONNECTIONS'>NettyClientProperties.MAX_CONNECTIONS</link>" > <!ENTITY jersey.netty.NettyClientProperties.MAX_CONNECTIONS_TOTAL "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_CONNECTIONS_TOTAL'>NettyClientProperties.MAX_CONNECTIONS_TOTAL</link>" > +<!ENTITY jersey.netty.NettyClientProperties.MAX_REDIRECTS "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_REDIRECTS'>NettyClientProperties.MAX_REDIRECTS</link>" > <!ENTITY jersey.netty.NettyConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyConnectorProvider.html'>NettyConnectorProvider</link>"> <!ENTITY jersey.server.ApplicationHandler "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ApplicationHandler.html'>ApplicationHandler</link>"> <!ENTITY jersey.server.BackgroundScheduler "<link xlink:href='&jersey.javadoc.uri.prefix;/server/BackgroundScheduler.html'>@BackgroundScheduler</link>">
diff --git a/docs/src/main/docbook/media.xml b/docs/src/main/docbook/media.xml index 95581df..f830e90 100644 --- a/docs/src/main/docbook/media.xml +++ b/docs/src/main/docbook/media.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" standalone="no"?> <!-- - Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2012, 2022 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 @@ -570,6 +570,15 @@ </para> <para> + Since the 2.36 version of Jersey it is possible to filter (include/exclude) Jackson modules by properties + &jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES; and &jersey.common.CommonProperties.JSON_JACKSON_ENABLED_MODULES; + (with their client/server derivatives). If the &jersey.common.CommonProperties.JSON_JACKSON_ENABLED_MODULES; property is used, + only those named modules will be used for JSON processing. On the other hand if the &jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES; + property is used, those listed modules will be explicitly excluded from processing while other (not listed) will remain. Please note that + the <literal>JaxbAnnotationModule</literal> module is always excluded from processing and this is not configurable. + </para> + + <para> In order to use Jackson as your JSON (JAXB/POJO) provider you need to register &jersey.media.JacksonFeature; and a &lit.jaxrs.ext.ContextResolver; for &lit.jersey.media.ObjectMapper;, if needed, in your &jaxrs.core.Configurable; (client/server).
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java index f4f0f3f..257b2d0 100644 --- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java +++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
@@ -18,11 +18,16 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.Module; +import org.glassfish.jersey.CommonProperties; import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.Annotations; import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider; +import java.util.Arrays; import java.util.List; +import jakarta.annotation.PostConstruct; import jakarta.inject.Singleton; +import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.core.Context; /** * Entity Data provider based on Jackson JSON provider. @@ -30,32 +35,57 @@ @Singleton public class DefaultJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider { + @Context + private Configuration commonConfig; + //do not register JaxbAnnotationModule because it brakes default annotations processing private static final String[] EXCLUDE_MODULE_NAMES = {"JaxbAnnotationModule", "JakartaXmlBindAnnotationModule"}; public DefaultJacksonJaxbJsonProvider() { super(new JacksonMapperConfigurator(null, DEFAULT_ANNOTATIONS)); - findAndRegisterModules(); } public DefaultJacksonJaxbJsonProvider(final Annotations... annotationsToUse) { super(new JacksonMapperConfigurator(null, annotationsToUse)); - findAndRegisterModules(); } + @PostConstruct private void findAndRegisterModules() { final ObjectMapper defaultMapper = _mapperConfig.getDefaultMapper(); final ObjectMapper mapper = _mapperConfig.getConfiguredMapper(); final List<Module> modules = ObjectMapper.findModules(); - for (String exludeModuleName : EXCLUDE_MODULE_NAMES) { - modules.removeIf(mod -> mod.getModuleName().contains(exludeModuleName)); - } defaultMapper.registerModules(modules); if (mapper != null) { mapper.registerModules(modules); } } + + private List<Module> filterModules() { + final String disabledModules = + CommonProperties.getValue(commonConfig.getProperties(), + commonConfig.getRuntimeType(), + CommonProperties.JSON_JACKSON_DISABLED_MODULES, String.class); + final String enabledModules = + CommonProperties.getValue(commonConfig.getProperties(), + commonConfig.getRuntimeType(), + CommonProperties.JSON_JACKSON_ENABLED_MODULES, String.class); + + final List<Module> modules = ObjectMapper.findModules(); + for (String exludeModuleName : EXCLUDE_MODULE_NAMES) { + modules.removeIf(mod -> mod.getModuleName().contains(exludeModuleName)); + } + + if (enabledModules != null && !enabledModules.isEmpty()) { + final List<String> enabledModulesList = Arrays.asList(enabledModules.split(",")); + modules.removeIf(mod -> !enabledModulesList.contains(mod.getModuleName())); + } else if (disabledModules != null && !disabledModules.isEmpty()) { + final List<String> disabledModulesList = Arrays.asList(disabledModules.split(",")); + modules.removeIf(mod -> disabledModulesList.contains(mod.getModuleName())); + } + + return modules; + } } \ No newline at end of file
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java index 1127535..b793cb1 100644 --- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java +++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java
@@ -12,7 +12,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MultivaluedMap; @@ -63,7 +62,7 @@ */ public final static String HEADER_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"; - protected final static String CLASS_NAME_NO_CONTENT_EXCEPTION = "javax.ws.rs.core.NoContentException"; + protected final static String CLASS_NAME_NO_CONTENT_EXCEPTION = "jakarta.ws.rs.core.NoContentException"; private final static String NO_CONTENT_MESSAGE = "No content (empty input stream)";
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java index 9974df6..0389a9c 100644 --- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java +++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java
@@ -11,7 +11,7 @@ */ public final class PackageVersion implements Versioned { public final static Version VERSION = VersionUtil.parseVersion( - "2.13.0", "com.fasterxml.jackson.jaxrs", "jackson-jaxrs-json-provider"); + "2.13.3", "com.fasterxml.jackson.jaxrs", "jackson-jaxrs-json-provider"); @Override public Version version() {
diff --git a/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown b/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown index c383a09..f35a86b 100644 --- a/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown +++ b/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown
@@ -31,7 +31,7 @@ ## Third-party Content -Jackson JAX-RS Providers version 2.11.3 +Jackson JAX-RS Providers version 2.13.3 * License: Apache License, 2.0 * Project: https://github.com/FasterXML/jackson-jaxrs-providers -* Copyright: (c) 2009-2011 FasterXML, LLC. All rights reserved unless otherwise indicated. \ No newline at end of file +* Copyright: (c) 2009-2022 FasterXML, LLC. All rights reserved unless otherwise indicated.
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForBothModulesTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForBothModulesTest.java new file mode 100644 index 0000000..947d599 --- /dev/null +++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForBothModulesTest.java
@@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 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.jackson.internal; + +import org.glassfish.jersey.jackson.internal.model.ServiceTest; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; + +import jakarta.ws.rs.core.Application; + +import static org.junit.Assert.assertEquals; + +public class DefaultJsonJacksonProviderForBothModulesTest extends JerseyTest { + @Override + protected final Application configure() { + return new ResourceConfig(ServiceTest.class) + .property("jersey.config.json.jackson.enabled.modules", "Jdk8Module"); + } + + @Test + public final void testDisabledModule() { + final String response = target("entity/simple") + .request().get(String.class); + + assertEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response); + } + +}
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java new file mode 100644 index 0000000..a9a86e6 --- /dev/null +++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
@@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 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.jackson.internal; + +import org.glassfish.jersey.CommonProperties; +import org.glassfish.jersey.jackson.JacksonFeature; +import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider; +import org.glassfish.jersey.jackson.internal.model.JAXBServiceTest; +import org.glassfish.jersey.jackson.internal.model.ServiceTest; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; + +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Configuration; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class DefaultJsonJacksonProviderForDisabledModulesTest extends JerseyTest { + @Override + protected final Application configure() { + return new ResourceConfig(ServiceTest.class, JAXBServiceTest.class) + .property("jersey.config.json.jackson.disabled.modules", "Jdk8Module"); + } + + @Test + public final void testDisabledModule() { + getClient() + .register(JacksonFeature.class) + .register(TestJacksonJaxbJsonProvider.class) + .property("jersey.config.json.jackson.disabled.modules", "Jdk8Module"); + final String response = target("JAXBEntity") + .request().get(String.class); + assertNotEquals("{\"key\":\"key\",\"value\":\"value\"}", response); + } + + private static class TestJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider { + + @Inject + private Configuration configuration; + + @PostConstruct + public void checkModulesCount() { + final String disabledModules = + CommonProperties.getValue(configuration.getProperties(), + configuration.getRuntimeType(), + CommonProperties.JSON_JACKSON_DISABLED_MODULES, String.class); + assertEquals("Jdk8Module", disabledModules); + } + + } + +} \ No newline at end of file
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java new file mode 100644 index 0000000..b1d5e9d --- /dev/null +++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
@@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 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.jackson.internal; + +import org.glassfish.jersey.jackson.internal.model.ServiceTest; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; + +import jakarta.ws.rs.core.Application; + +import static org.junit.Assert.assertNotEquals; + +public class DefaultJsonJacksonProviderForEnabledModulesTest extends JerseyTest { + @Override + protected final Application configure() { + return new ResourceConfig(ServiceTest.class) + .property("jersey.config.json.jackson.enabled.modules", "jackson-module-kotlin"); + } + + @Test + public final void testDisabledModule() { + final String response = target("entity/simple") + .request().get(String.class); + assertNotEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response); + } + +}
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/JAXBServiceTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/JAXBServiceTest.java new file mode 100644 index 0000000..ed312a3 --- /dev/null +++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/JAXBServiceTest.java
@@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 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.jackson.internal.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Optional; + +@Path("JAXBEntity") +public class JAXBServiceTest { + @GET + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) + public SimpleEntity get() { + return new SimpleEntity("key", "value"); + } + + @XmlRootElement + public static class SimpleEntity { + @JsonProperty + private String key; + + @JsonProperty + private String value; + + public SimpleEntity() { + key = "key"; + value = "value"; + } + public SimpleEntity(String key, String value) { + this.key = key; + this.value = value; + } + + public Optional<String> getKey() { + return Optional.ofNullable(key); + } + + public Optional<String> getValue() { + return Optional.ofNullable(value); + } + + } +}
diff --git a/pom.xml b/pom.xml index 5305302..1d8f553 100644 --- a/pom.xml +++ b/pom.xml
@@ -2091,7 +2091,7 @@ <jersey.version>${project.version}</jersey.version> <!-- asm is now source integrated - keeping this property to see the version --> <!-- see core-server/src/main/java/jersey/repackaged/asm/.. --> - <asm.version>9.2</asm.version> + <asm.version>9.3</asm.version> <bnd.plugin.version>2.3.6</bnd.plugin.version> <bouncycastle.version>1.68</bouncycastle.version> @@ -2125,7 +2125,7 @@ <hk2.config.version>6.0.0</hk2.config.version> <httpclient.version>4.5.13</httpclient.version> <httpclient5.version>5.1.2</httpclient5.version> - <jackson.version>2.13.0</jackson.version> + <jackson.version>2.13.3</jackson.version> <javassist.version>3.25.0-GA</javassist.version> <jboss.logging.version>3.3.0.Final</jboss.logging.version> <jersey1.version>1.19.3</jersey1.version>
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java new file mode 100644 index 0000000..1741a66 --- /dev/null +++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java
@@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 Banco do Brasil S/A. 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.tests.e2e.client.connector.proxy; + +import org.eclipse.jetty.server.HttpChannel; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; +import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.spi.ConnectorProvider; +import org.glassfish.jersey.jetty.connector.JettyConnectorProvider; +import org.glassfish.jersey.netty.connector.NettyConnectorProvider; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +/** + * Moved from jetty-connector + * @author Marcelo Rubim + */ +@RunWith(Parameterized.class) +public class ProxySelectorTest { + private static final String NO_PASS = "no-pass"; + + @Parameterized.Parameters(name = "{index}: {0}") + public static List<Object[]> testData() { + return Arrays.asList(new Object[][]{ +// {ApacheConnectorProvider.class}, +// {Apache5ConnectorProvider.class}, +// {JettyConnectorProvider.class}, + {NettyConnectorProvider.class}, + }); + } + + private final ConnectorProvider connectorProvider; + + public ProxySelectorTest(Class<? extends ConnectorProvider> connectorProviderClass) + throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + this.connectorProvider = connectorProviderClass.getConstructor().newInstance(); + } + + protected void configureClient(ClientConfig config) { + config.connectorProvider(connectorProvider); + } + + @Test + public void testGetNoPass() { + try (Response response = target("proxyTest").request().header(NO_PASS, 200).get()) { + assertEquals(200, response.getStatus()); + } + } + + @Test + public void testGet407() { + try (Response response = target("proxyTest").request().get()) { + assertEquals(407, response.getStatus()); + } catch (ProcessingException pe) { + Assert.assertTrue(pe.getMessage().contains("407")); // netty + } + } + + private static Server server; + @BeforeClass + public static void startFakeProxy() { + server = new Server(9997); + server.setHandler(new ProxyHandler()); + try { + server.start(); + } catch (Exception e) { + + } + + System.setProperty("http.proxyHost", "http://localhost"); + System.setProperty("http.proxyPort", "9997"); + } + + @AfterClass + public static void tearDownProxy() { + try { + server.stop(); + } catch (Exception e) { + + } finally { + System.clearProperty("http.proxyHost"); + System.clearProperty("http.proxyPort"); + } + } + + private static Client client; + @Before + public void beforeEach() { + ClientConfig config = new ClientConfig(); + this.configureClient(config); + client = ClientBuilder.newClient(config); + } + + private Client client() { + return client; + } + + private WebTarget target(String path) { + // ProxySelector goes DIRECT to localhost, no matter the proxy + return client().target("http://eclipse.org:9998").path(path); + } + + static class ProxyHandler extends AbstractHandler { + Set<HttpChannel> httpConnect = new HashSet<>(); + @Override + public void handle(String target, + Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) { + if (request.getHeader(NO_PASS) != null) { + response.setStatus(Integer.parseInt(request.getHeader(NO_PASS))); + } else { + response.setStatus(407); + response.addHeader("Proxy-Authenticate", "Basic"); + } + + baseRequest.setHandled(true); + } + } +}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java new file mode 100644 index 0000000..1ae437b --- /dev/null +++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java
@@ -0,0 +1,199 @@ +/* + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 Banco do Brasil S/A. 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.tests.e2e.client.connector.proxy; + +import org.eclipse.jetty.server.HttpChannel; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; +import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.spi.ConnectorProvider; +import org.glassfish.jersey.jetty.connector.JettyConnectorProvider; +import org.glassfish.jersey.netty.connector.NettyConnectorProvider; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +/** + * Moved from jetty-connector + * @author Marcelo Rubim + */ +@RunWith(Parameterized.class) +public class ProxyTest { + private static final Charset CHARACTER_SET = Charset.forName("iso-8859-1"); + private static final String PROXY_URI = "http://127.0.0.1:9997"; + private static final String PROXY_USERNAME = "proxy-user"; + private static final String PROXY_PASSWORD = "proxy-password"; + private static final String NO_PASS = "no-pass"; + + @Parameterized.Parameters(name = "{index}: {0}") + public static List<Object[]> testData() { + return Arrays.asList(new Object[][]{ + {ApacheConnectorProvider.class}, + {Apache5ConnectorProvider.class}, + {JettyConnectorProvider.class}, + {NettyConnectorProvider.class}, + }); + } + + private final ConnectorProvider connectorProvider; + + public ProxyTest(Class<? extends ConnectorProvider> connectorProviderClass) + throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + this.connectorProvider = connectorProviderClass.getConstructor().newInstance(); + } + + protected void configureClient(ClientConfig config) { + config.connectorProvider(connectorProvider); + } + + @Test + public void testGetNoPass() { + client().property(ClientProperties.PROXY_URI, ProxyTest.PROXY_URI); + try (Response response = target("proxyTest").request().header(NO_PASS, 200).get()) { + assertEquals(200, response.getStatus()); + } + } + + @Test + public void testGet407() { + client().property(ClientProperties.PROXY_URI, ProxyTest.PROXY_URI); + try (Response response = target("proxyTest").request().get()) { + assertEquals(407, response.getStatus()); + } catch (ProcessingException pe) { + Assert.assertTrue(pe.getMessage().contains("407")); // netty + } + } + + @Test + public void testGetSuccess() { + client().property(ClientProperties.PROXY_URI, ProxyTest.PROXY_URI); + client().property(ClientProperties.PROXY_USERNAME, ProxyTest.PROXY_USERNAME); + client().property(ClientProperties.PROXY_PASSWORD, ProxyTest.PROXY_PASSWORD); + Response response = target("proxyTest").request().get(); + assertEquals(200, response.getStatus()); + } + + private static Server server; + @BeforeClass + public static void startFakeProxy() { + server = new Server(9997); + server.setHandler(new ProxyHandler()); + try { + server.start(); + } catch (Exception e) { + + } + } + + @AfterClass + public static void tearDownProxy() { + try { + server.stop(); + } catch (Exception e) { + + } + } + + private static Client client; + @Before + public void beforeEach() { + ClientConfig config = new ClientConfig(); + this.configureClient(config); + client = ClientBuilder.newClient(config); + } + + private Client client() { + return client; + } + + private WebTarget target(String path) { + return client().target("http://localhost:9998").path(path); + } + + static class ProxyHandler extends AbstractHandler { + Set<HttpChannel> httpConnect = new HashSet<>(); + @Override + public void handle(String target, + Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) { + if (request.getHeader(NO_PASS) != null) { + response.setStatus(Integer.parseInt(request.getHeader(NO_PASS))); + } else if (request.getHeader("Proxy-Authorization") != null) { + String proxyAuthorization = request.getHeader("Proxy-Authorization"); + String decoded = new String(Base64.getDecoder().decode(proxyAuthorization.substring(6).getBytes()), + CHARACTER_SET); + final String[] split = decoded.split(":"); + final String username = split[0]; + final String password = split[1]; + + if (!username.equals(PROXY_USERNAME)) { + response.setStatus(400); + System.out.println("Found unexpected username: " + username); + } + + if (!password.equals(PROXY_PASSWORD)) { + response.setStatus(400); + System.out.println("Found unexpected password: " + username); + } + + if (response.getStatus() != 400) { + response.setStatus(200); + if ("CONNECT".equalsIgnoreCase(baseRequest.getMethod())) { // NETTY way of doing proxy + httpConnect.add(baseRequest.getHttpChannel()); + } + } + //TODO Add redirect to requestURI + } else { + if (httpConnect.contains(baseRequest.getHttpChannel())) { + response.setStatus(200); + } else { + response.setStatus(407); + response.addHeader("Proxy-Authenticate", "Basic"); + } + } + + baseRequest.setHandled(true); + } + } +}
diff --git a/tests/integration/servlet-2.5-reload/pom.xml b/tests/integration/servlet-2.5-reload/pom.xml index 32d8cf6..dbafe7f 100644 --- a/tests/integration/servlet-2.5-reload/pom.xml +++ b/tests/integration/servlet-2.5-reload/pom.xml
@@ -55,10 +55,10 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> </plugin> - <plugin> + <!-- TODO: fix after 2.36 <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> - </plugin> + </plugin>--> </plugins> </build>
diff --git a/tests/integration/servlet-2.5-reload/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadTestIT.java b/tests/integration/servlet-2.5-reload/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadTestIT.java index 313a319..bc57a0e 100644 --- a/tests/integration/servlet-2.5-reload/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadTestIT.java +++ b/tests/integration/servlet-2.5-reload/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadTestIT.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -24,6 +24,7 @@ import org.glassfish.jersey.test.spi.TestContainerException; import org.glassfish.jersey.test.spi.TestContainerFactory; +import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -44,6 +45,7 @@ } @Test + @Ignore //TODO - fix after 2.36 public void testReload() throws Exception { Response response = target().path("helloworld").request().get(); assertEquals(200, response.getStatus());