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 adf8e033b6..e9d55c1df0 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 @@ -18,7 +18,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.net.URI; import java.util.Iterator; import java.util.List; @@ -48,7 +47,6 @@ import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.timeout.IdleStateEvent; import org.glassfish.jersey.uri.internal.JerseyUriBuilder; @@ -146,7 +144,21 @@ protected void notifyResponse() { ClientRequest newReq = new ClientRequest(jerseyRequest); newReq.setUri(newUri); restrictRedirectRequest(newReq, cr); - connector.execute(newReq, redirectUriHistory, responseAvailable); + + final NettyConnector newConnector = new NettyConnector(newReq.getClient()); + newConnector.execute(newReq, redirectUriHistory, new CompletableFuture() { + @Override + public boolean complete(ClientResponse value) { + newConnector.close(); + return responseAvailable.complete(value); + } + + @Override + public boolean completeExceptionally(Throwable ex) { + newConnector.close(); + return responseAvailable.completeExceptionally(ex); + } + }); } } catch (IllegalArgumentException e) { responseAvailable.completeExceptionally( 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 454415773f..ebdfda414f 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 @@ -416,11 +416,7 @@ protected void initChannel(SocketChannel ch) throws Exception { // headers if (!jerseyRequest.hasEntity()) { setHeaders(jerseyRequest, nettyRequest.headers(), false); - - // host header - http 1.1 - if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) { - nettyRequest.headers().add(HttpHeaderNames.HOST, jerseyRequest.getUri().getHost()); - } + setHostHeader(jerseyRequest, nettyRequest); } if (jerseyRequest.hasEntity()) { @@ -468,9 +464,7 @@ public void operationComplete(io.netty.util.concurrent.Future futu @Override public OutputStream getOutputStream(int contentLength) throws IOException { replaceHeaders(jerseyRequest, nettyRequest.headers()); // WriterInterceptor changes - if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) { - nettyRequest.headers().add(HttpHeaderNames.HOST, jerseyRequest.getUri().getHost()); - } + setHostHeader(jerseyRequest, nettyRequest); headersSet.countDown(); return entityWriter.getOutputStream(); @@ -620,4 +614,18 @@ private static HttpHeaders replaceHeaders(ClientRequest jerseyRequest, HttpHeade private static boolean additionalProxyHeadersToKeep(String key) { return key.length() > 2 && (key.charAt(0) == 'x' || key.charAt(0) == 'X') && (key.charAt(1) == '-'); } + + private static void setHostHeader(ClientRequest jerseyRequest, HttpRequest nettyRequest) { + // host header - http 1.1 + if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) { + int requestPort = jerseyRequest.getUri().getPort(); + final String hostHeader; + if (requestPort != 80 && requestPort != 443) { + hostHeader = jerseyRequest.getUri().getHost() + ":" + requestPort; + } else { + hostHeader = jerseyRequest.getUri().getHost(); + } + nettyRequest.headers().add(HttpHeaderNames.HOST, hostHeader); + } + } } 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 index 3a1cf69010..d63b9041bb 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024 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,16 +24,20 @@ import java.util.logging.Logger; import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; 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.Entity; import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.RequestEntityProcessing; import org.glassfish.jersey.logging.LoggingFeature; import org.glassfish.jersey.netty.connector.internal.RedirectException; import org.glassfish.jersey.server.ResourceConfig; @@ -60,6 +64,11 @@ public String get() { return "GET"; } + @POST + public String post() { + return "POST"; + } + @GET @Path("redirect") public Response redirect() { @@ -77,6 +86,12 @@ public Response loop() { public Response redirect2() { return Response.seeOther(URI.create(TEST_URL_REF.get() + "/redirect")).build(); } + + @POST + @Path("status307") + public Response status307() { + return Response.temporaryRedirect(URI.create(TEST_URL_REF.get())).build(); + } } @Override @@ -169,4 +184,15 @@ public void testRedirectNoLimitReached() { assertEquals(200, r.getStatus()); assertEquals("GET", r.readEntity(String.class)); } + + @Test + public void testRedirect307PostBuffered() { + try (Response response = target("test/status307") + .property(ClientProperties.FOLLOW_REDIRECTS, true) + .property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED) + .request().post(Entity.entity("Something", MediaType.TEXT_PLAIN_TYPE))) { + assertEquals(200, response.getStatus()); + assertEquals("POST", response.readEntity(String.class)); + } + } } diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java new file mode 100644 index 0000000000..a456acdeeb --- /dev/null +++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 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 org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +public class HostHeaderTest extends JerseyTest { + + private static final String HTTP_HEADER_NAME = "HTTP_PORT_INT"; + + @Path("/") + public static class HostHeaderTestEchoResource { + + @POST + public String post(@Context HttpHeaders headers) { + return get(headers); + } + + @GET + public String get(@Context HttpHeaders headers) { + String sPort = headers.getHeaderString(HTTP_HEADER_NAME); + String hostPort = headers.getHeaderString(HttpHeaders.HOST); + int indexColon = hostPort.indexOf(':'); + if (indexColon != -1) { + hostPort = hostPort.substring(indexColon + 1); + } + if (sPort.equals(hostPort.trim())) { + return GET.class.getName(); + } else { + return "Expected port " + sPort + " but found " + hostPort; + } + } + } + + @Override + protected Application configure() { + return new ResourceConfig(HostHeaderTestEchoResource.class); + } + + @Test + public void testHostHeaderAndPort() { + int port = getPort(); + ClientConfig config = new ClientConfig(); + config.connectorProvider(new NettyConnectorProvider()); + try (Response response = ClientBuilder.newClient(config).target(target().getUri()) + .request() + .header(HTTP_HEADER_NAME, port) + .get()) { + MatcherAssert.assertThat(response.getStatus(), Matchers.is(200)); + MatcherAssert.assertThat(response.readEntity(String.class), Matchers.is(GET.class.getName())); + } + } + + @Test + public void testHostHeaderAndPortAfterRemovedFromFilter() { + int port = getPort(); + ClientConfig config = new ClientConfig(); + config.connectorProvider(new NettyConnectorProvider()); + try (Response response = ClientBuilder.newClient(config) + .target(target().getUri()) + .request() + .header(HTTP_HEADER_NAME, port) + .post(Entity.entity("xxx", MediaType.TEXT_PLAIN_TYPE))) { + MatcherAssert.assertThat(response.getStatus(), Matchers.is(200)); + MatcherAssert.assertThat(response.readEntity(String.class), Matchers.is(GET.class.getName())); + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java b/core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java index c2109fe99f..abcee6b286 100644 --- a/core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java +++ b/core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; /** * Generic wrapper template for InputStream. @@ -54,6 +55,22 @@ public int read(byte[] b, int off, int len) throws IOException { return getWrappedIOE().read(b, off, len); } + @Override + public byte[] readAllBytes() throws IOException { + return getWrappedIOE().readAllBytes(); + } + + @Override + public int readNBytes(byte[] b, int off, int len) throws IOException { + return getWrappedIOE().readNBytes(b, off, len); + } + + @Override + public long transferTo(OutputStream out) throws IOException { + return getWrappedIOE().transferTo(out); + } + + @Override public long skip(long n) throws IOException { return getWrappedIOE().skip(n); diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java index 5798695f80..aafb278942 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 Payara Foundation and/or its affiliates. * * This program and the accompanying materials are made available under the @@ -125,7 +125,7 @@ public String toString(final T value) throws IllegalArgumentException { @Singleton public static class StringConstructor extends ParamConverterCompliance implements ParamConverterProvider { - private StringConstructor(boolean canReturnNull) { + protected StringConstructor(boolean canReturnNull) { super(canReturnNull); } @@ -154,7 +154,7 @@ protected T _fromString(final String value) throws Exception { @Singleton public static class TypeValueOf extends ParamConverterCompliance implements ParamConverterProvider { - private TypeValueOf(boolean canReturnNull) { + protected TypeValueOf(boolean canReturnNull) { super(canReturnNull); } @@ -182,7 +182,7 @@ public T _fromString(final String value) throws Exception { @Singleton public static class TypeFromString extends ParamConverterCompliance implements ParamConverterProvider { - private TypeFromString(boolean canReturnNull) { + protected TypeFromString(boolean canReturnNull) { super(canReturnNull); } @@ -210,7 +210,7 @@ public T _fromString(final String value) throws Exception { @Singleton public static class TypeFromStringEnum extends TypeFromString { - private TypeFromStringEnum(boolean canReturnNull) { + protected TypeFromStringEnum(boolean canReturnNull) { super(canReturnNull); } @@ -225,7 +225,7 @@ public ParamConverter getConverter(final Class rawType, @Singleton public static class CharacterProvider extends ParamConverterCompliance implements ParamConverterProvider { - private CharacterProvider(boolean canReturnNull) { + protected CharacterProvider(boolean canReturnNull) { super(canReturnNull); } @@ -270,7 +270,7 @@ public String toString(T value) { @Singleton public static class DateProvider extends ParamConverterCompliance implements ParamConverterProvider { - private DateProvider(boolean canReturnNull) { + protected DateProvider(boolean canReturnNull) { super(canReturnNull); } @@ -346,7 +346,7 @@ public static class OptionalCustomProvider extends ParamConverterCompliance impl // Delegates to this provider when the type of Optional is extracted. private final InjectionManager manager; - public OptionalCustomProvider(InjectionManager manager, boolean canReturnNull) { + protected OptionalCustomProvider(InjectionManager manager, boolean canReturnNull) { super(canReturnNull); this.manager = manager; } @@ -402,6 +402,8 @@ public String toString(T value) throws IllegalArgumentException { @Singleton public static class OptionalProvider implements ParamConverterProvider { + protected OptionalProvider() {} + @Override public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { final Optionals optionals = Optionals.getOptional(rawType); diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java index f94d5fd216..2610e17c5f 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024 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 @@ -22,6 +22,7 @@ import jakarta.ws.rs.ProcessingException; +import org.glassfish.jersey.innate.io.InputStreamWrapper; import org.glassfish.jersey.internal.LocalizationMessages; /** @@ -33,7 +34,7 @@ * * @author Marek Potociar */ -public class EntityInputStream extends InputStream { +public class EntityInputStream extends InputStreamWrapper { private InputStream input; private boolean closed = false; @@ -64,40 +65,6 @@ public EntityInputStream(InputStream input) { this.input = input; } - @Override - public int read() throws IOException { - return input.read(); - } - - @Override - public int read(byte[] b) throws IOException { - return input.read(b); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - return input.read(b, off, len); - } - - @Override - public long skip(long n) throws IOException { - return input.skip(n); - } - - @Override - public int available() throws IOException { - return input.available(); - } - - @Override - public void mark(int readLimit) { - input.mark(readLimit); - } - - @Override - public boolean markSupported() { - return input.markSupported(); - } /** * {@inheritDoc} @@ -232,4 +199,9 @@ public final InputStream getWrappedStream() { public final void setWrappedStream(InputStream wrapped) { input = wrapped; } + + @Override + protected InputStream getWrapped() { + return input; + } } diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java index 4db8b21d92..87fc281a16 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024 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 @@ -26,6 +26,8 @@ import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -200,7 +202,12 @@ public boolean bufferEntity() throws ProcessingException { @Override public void close() throws ProcessingException { closed = true; - context.close(); + try { + context.close(); + } catch (Exception e) { + // Just log the exception + Logger.getLogger(OutboundJaxrsResponse.class.getName()).log(Level.FINE, e.getMessage(), e); + } if (buffered) { // release buffer context.setEntity(null); diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java index b1b7745bbd..ceb1540627 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; @@ -27,8 +28,6 @@ import java.util.Locale; import java.util.Set; import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import jakarta.ws.rs.core.Configuration; @@ -557,6 +556,7 @@ public boolean isCommitted() { /** * Closes the context. Flushes and closes the entity stream. + * @throws UncheckedIOException if IO errors */ public void close() { if (hasEntity()) { @@ -567,11 +567,7 @@ public void close() { } es.close(); } catch (IOException e) { - // Happens when the client closed connection before receiving the full response. - // This is OK and not interesting in the vast majority of the cases - // hence the log level set to FINE to make sure it does not flood the log unnecessarily - // (especially for clients disconnecting from SSE listening, which is very common). - Logger.getLogger(OutboundMessageContext.class.getName()).log(Level.FINE, e.getMessage(), e); + throw new UncheckedIOException(e); } finally { // In case some of the output stream wrapper does not delegate close() call we // close the root stream manually to make sure it commits the data. @@ -579,8 +575,7 @@ public void close() { try { committingOutputStream.close(); } catch (IOException e) { - // Just log the exception - Logger.getLogger(OutboundMessageContext.class.getName()).log(Level.FINE, e.getMessage(), e); + throw new UncheckedIOException(e); } } } diff --git a/core-common/src/main/java/org/glassfish/jersey/model/Parameter.java b/core-common/src/main/java/org/glassfish/jersey/model/Parameter.java index 4f7af42e64..4183568f1b 100644 --- a/core-common/src/main/java/org/glassfish/jersey/model/Parameter.java +++ b/core-common/src/main/java/org/glassfish/jersey/model/Parameter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024 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 @@ -446,14 +446,20 @@ protected static List

+ String.format("Unable to get the %s annotation value property", a.getClass().getName())); } catch (Exception ex) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.log(Level.FINER, diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java index bd7f7a3fa5..a44e60e3c4 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024 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 @@ -26,6 +26,8 @@ import java.net.URI; import java.util.Date; import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.Map; import java.util.Set; @@ -400,9 +402,14 @@ public boolean isCommitted() { public void close() { if (!closed) { closed = true; - messageContext.close(); - requestContext.getResponseWriter().commit(); - requestContext.setWorkers(null); + try { + messageContext.close(); + requestContext.setWorkers(null); + requestContext.getResponseWriter().commit(); + } catch (Exception e) { + Logger.getLogger(ContainerResponse.class.getName()).log(Level.FINE, e.getMessage(), e); + requestContext.getResponseWriter().failure(e); + } } } diff --git a/docs/src/main/docbook/media.xml b/docs/src/main/docbook/media.xml index 34d2a7df21..f27d2f0cd5 100644 --- a/docs/src/main/docbook/media.xml +++ b/docs/src/main/docbook/media.xml @@ -1129,7 +1129,7 @@ public JaxbBean getSimpleJSONP() { public class JsonbContextResolver implements ContextResolver<Jsonb> { @Override - public Jsonb getContext(Class>?< type) { + public Jsonb getContext(Class<?> type) { JsonbConfig config = new JsonbConfig(); // configure JsonbConfig ... diff --git a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/DisposableSupplierTest.java b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/DisposableSupplierTest.java index 6bade9bf61..10eec31cdf 100644 --- a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/DisposableSupplierTest.java +++ b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/DisposableSupplierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024 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 @@ -17,6 +17,8 @@ package org.glassfish.jersey.inject.cdi.se; import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -368,9 +370,17 @@ public void testDisposeComposedObjectWithPerLookupFields() { // All instances should be the same because they are request scoped. ComposedObject instance = injectionManager.getInstance(ComposedObject.class); - assertEquals("1", instance.getFirst()); - assertEquals("2", instance.getSecond()); - assertEquals("3", instance.getThird()); + Set set1 = new HashSet() {{ + add("1"); + add("2"); + add("3"); + }}; + Set set2 = new HashSet() {{ + add(instance.getFirst().toString()); + add(instance.getSecond().toString()); + add(instance.getThird().toString()); + }}; + assertEquals(set1, set2); }); Supplier cleanedSupplier = atomicSupplier.get(); diff --git a/inject/hk2/src/test/java/org/glassfish/jersey/inject/hk2/DisposableSupplierTest.java b/inject/hk2/src/test/java/org/glassfish/jersey/inject/hk2/DisposableSupplierTest.java index 89d2db3b7b..bb290a206e 100644 --- a/inject/hk2/src/test/java/org/glassfish/jersey/inject/hk2/DisposableSupplierTest.java +++ b/inject/hk2/src/test/java/org/glassfish/jersey/inject/hk2/DisposableSupplierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024 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 @@ -17,6 +17,8 @@ package org.glassfish.jersey.inject.hk2; import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -374,9 +376,17 @@ public void testDisposeComposedObjectWithPerLookupFields() { // All instances should be the same because they are request scoped. ComposedObject instance = injectionManager.getInstance(ComposedObject.class); - assertEquals("1", instance.first); - assertEquals("2", instance.second); - assertEquals("3", instance.third); + Set set1 = new HashSet() {{ + add("1"); + add("2"); + add("3"); + }}; + Set set2 = new HashSet() {{ + add(instance.first.toString()); + add(instance.second.toString()); + add(instance.third.toString()); + }}; + assertEquals(set1, set2); }); Supplier cleanedSupplier = atomicSupplier.get(); 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 index 59d19c1cc2..e9b621a44d 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 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 @@ -23,6 +23,9 @@ import jakarta.ws.rs.core.Application; +import java.util.List; +import java.util.Arrays; +import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; public class DefaultJsonJacksonProviderForBothModulesTest extends JerseyTest { @@ -36,8 +39,12 @@ protected final Application configure() { public final void testDisabledModule() { final String response = target("entity/simple") .request().get(String.class); + String expected = "{\"name\":\"Hello\",\"value\":\"World\"}"; + List response_list = Arrays.asList(response.replaceAll("[{}]", "").split(",")); + List expected_list = Arrays.asList(expected.replaceAll("[{}]", "").split(",")); + Collections.sort(response_list); - assertEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response); + assertEquals(expected_list, response_list); } } diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/Issue5783Test.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/Issue5783Test.java new file mode 100644 index 0000000000..ab7b3017ea --- /dev/null +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/Issue5783Test.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 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.tests.e2e.server; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerResponseContext; +import jakarta.ws.rs.container.ContainerResponseFilter; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.Provider; + +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.spi.ContainerResponseWriter; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.jupiter.api.Test; + +public class Issue5783Test extends JerseyTest { + + private static final String ERROR = "Intentional issue5783 exception"; + private static volatile String exceptionMessage; + + @Override + protected Application configure() { + return new ResourceConfig(Resource.class, ResponseFilter.class); + } + + @Test + public void closeException() throws InterruptedException { + target("/test").request().get(); + assertEquals(ERROR, exceptionMessage); + } + + @Path("/test") + public static class Resource { + + @GET + public Response closeException(@Context ContainerRequest request) { + // Save the exception when response.getRequestContext().getResponseWriter().failure(e) + ContainerResponseWriter writer = request.getResponseWriter(); + ContainerResponseWriter proxy = (ContainerResponseWriter) Proxy.newProxyInstance( + ContainerResponseWriter.class.getClassLoader(), + new Class[]{ContainerResponseWriter.class}, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("failure".equals(method.getName())) { + exceptionMessage = ((Throwable) args[0]).getCause().getMessage(); + } + return method.invoke(writer, args); + } + }); + request.setWriter(proxy); + return Response.ok().build(); + } + } + + @Provider + public static class ResponseFilter implements ContainerResponseFilter { + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + // Hack it to make ContainerResponse#close throws one exception + responseContext.setEntity("something"); + responseContext.setEntityStream(new ByteArrayOutputStream() { + @Override + public void close() throws IOException { + throw new IOException(ERROR); + } + }); + } + } +}