diff --git a/NOTICE.md b/NOTICE.md
index 877100d02f..c2d817bccf 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -70,7 +70,7 @@ Javassist Version 3.30.2-GA
* Project: http://www.javassist.org/
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
-Jackson JAX-RS Providers Version 2.17.0
+Jackson JAX-RS Providers Version 2.17.1
* License: Apache License, 2.0
* Project: https://github.com/FasterXML/jackson-jaxrs-providers
* Copyright: (c) 2009-2024 FasterXML, LLC. All rights reserved unless otherwise indicated.
diff --git a/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
index 45513c7fe7..9f6fb8d961 100644
--- a/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
@@ -1,7 +1,7 @@
-
-
- jersey-quickstart-grizzly2
-
-
-
-
-
-
-
-
diff --git a/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/maven/archetype-metadata.xml
new file mode 100644
index 0000000000..54380e52c4
--- /dev/null
+++ b/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ src/main/java
+
+ **/*
+
+
+
+
+ src/test/java
+
+ **/*
+
+
+
+
\ No newline at end of file
diff --git a/archetypes/jersey-quickstart-grizzly2/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-quickstart-grizzly2/src/main/resources/archetype-resources/pom.xml
index 779f0802af..087417f001 100644
--- a/archetypes/jersey-quickstart-grizzly2/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-quickstart-grizzly2/src/main/resources/archetype-resources/pom.xml
@@ -40,7 +40,7 @@
org.junit.jupiter
junit-jupiter
- \${junit-jupiter.version}
+ \${junit5.version}
test
@@ -50,7 +50,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.1
+ ${compiler.mvn.plugin.version}
true
@@ -60,7 +60,7 @@
org.codehaus.mojo
exec-maven-plugin
- 1.2.1
+ ${exec.mvn.plugin.version}
@@ -83,8 +83,7 @@
${project.version}
- 5.10.2
+ ${junit5.version}
UTF-8
- 3.2.5
diff --git a/archetypes/jersey-quickstart-webapp/pom.xml b/archetypes/jersey-quickstart-webapp/pom.xml
index 25d4ebed9f..1b5e2d3893 100644
--- a/archetypes/jersey-quickstart-webapp/pom.xml
+++ b/archetypes/jersey-quickstart-webapp/pom.xml
@@ -36,7 +36,7 @@
org.apache.maven.plugins
maven-resources-plugin
- 2.5
+ ${resources.mvn.plugin.version}
\
diff --git a/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/archetype.xml b/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/archetype.xml
deleted file mode 100644
index cea1eea5e9..0000000000
--- a/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/archetype.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
- jersey-quickstart-webapp
-
-
-
-
- src/main/webapp/index.jsp
- src/main/webapp/WEB-INF/web.xml
-
-
diff --git a/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/maven/archetype-metadata.xml
new file mode 100644
index 0000000000..bea65b3530
--- /dev/null
+++ b/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ src/main/java
+
+
+ src/main/webapp
+
+ **/*
+
+
+
+ src/test/java
+
+ **/*
+
+
+
+
\ No newline at end of file
diff --git a/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
index fc70d51357..341fb67664 100644
--- a/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
@@ -15,7 +15,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.1
+ ${compiler.mvn.plugin.version}
true
@@ -63,6 +63,5 @@
${project.version}
UTF-8
- 3.4.0
diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml
index 282151f2dc..844d89efde 100644
--- a/bundles/jaxrs-ri/pom.xml
+++ b/bundles/jaxrs-ri/pom.xml
@@ -423,7 +423,7 @@
org.codehaus.mojo
wagon-maven-plugin
- 1.0-beta-4
+ 2.0.2
false
diff --git a/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java b/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
index 3b6bed5ad5..99e5c7a2d7 100644
--- a/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
+++ b/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
@@ -105,6 +105,7 @@
import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.message.internal.HeaderUtils;
import org.glassfish.jersey.message.internal.OutboundMessageContext;
@@ -521,7 +522,7 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing
final HttpEntity entity = response.getEntity();
if (entity != null) {
- if (headers.get(HttpHeaders.CONTENT_LENGTH) == null) {
+ if (headers.get(HttpHeaders.CONTENT_LENGTH) == null && entity.getContentLength() >= 0) {
headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(entity.getContentLength()));
}
@@ -895,7 +896,7 @@ protected void prepareSocket(SSLSocket socket) throws IOException {
}
}
- private static class CancellableInputStream extends InputStream {
+ private static class CancellableInputStream extends InputStreamWrapper {
private final InputStream in;
private final Supplier isCancelled;
@@ -904,58 +905,17 @@ private CancellableInputStream(InputStream in, Supplier isCancelled) {
this.isCancelled = isCancelled;
}
- public int read(byte b[]) throws IOException {
- checkAborted();
- return in.read();
- }
-
- public int read(byte b[], int off, int len) throws IOException {
- checkAborted();
- return in.read(b, off, len);
- }
-
- @Override
- public int read() throws IOException {
- checkAborted();
- return in.read();
- }
-
- public boolean markSupported() {
- return in.markSupported();
- }
-
- @Override
- public long skip(long n) throws IOException {
- checkAborted();
- return in.skip(n);
- }
-
- @Override
- public int available() throws IOException {
- checkAborted();
- return in.available();
- }
-
@Override
- public void close() throws IOException {
- in.close();
+ protected InputStream getWrapped() {
+ return in;
}
@Override
- public void mark(int readlimit) {
- in.mark(readlimit);
- }
-
- @Override
- public void reset() throws IOException {
- checkAborted();
- in.reset();
- }
-
- private void checkAborted() throws IOException {
+ protected InputStream getWrappedIOE() throws IOException {
if (isCancelled.get()) {
throw new IOException(new CancellationException());
}
+ return in;
}
}
diff --git a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpParser.java b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpParser.java
index 4070300e0c..6099e8e5a0 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpParser.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpParser.java
@@ -514,8 +514,15 @@ private void decideTransferEncoding() throws ParseException {
}
return;
+ } else if (httpResponse.getHasContent()) {
+ // missing Content-Length
+ transferEncodingParser = TransferEncodingParser
+ .createFixedLengthParser(httpResponse.getBodyStream(), Long.MAX_VALUE);
+ return;
}
+
+
// TODO what now? Expect no content or fail loudly?
}
diff --git a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java
index 7dccd87375..1a94ff7d32 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java
@@ -63,7 +63,7 @@ boolean parse(ByteBuffer input) throws ParseException {
responseBody.notifyDataAvailable(parsed);
consumedLength += data.length;
- return consumedLength == expectedLength;
+ return consumedLength == expectedLength || expectedLength == Long.MAX_VALUE /* unknown at the beginning */;
}
}
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 5d1d2f294c..adf8e033b6 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
@@ -99,6 +99,8 @@ public void channelInactive(ChannelHandlerContext ctx) {
if (readTimedOut) {
responseDone.completeExceptionally(new TimeoutException("Stream closed: read timeout"));
+ } else if (jerseyRequest.isCancelled()) {
+ responseDone.completeExceptionally(new CancellationException());
} else {
responseDone.completeExceptionally(new IOException("Stream closed"));
}
@@ -187,21 +189,10 @@ public String getReasonPhrase() {
}
// request entity handling.
- if ((response.headers().contains(HttpHeaders.CONTENT_LENGTH) && HttpUtil.getContentLength(response) > 0)
- || HttpUtil.isTransferEncodingChunked(response)) {
-
- nis = new NettyInputStream();
- responseDone.whenComplete((_r, th) -> nis.complete(th));
-
- jerseyResponse.setEntityStream(nis);
- } else {
- jerseyResponse.setEntityStream(new InputStream() {
- @Override
- public int read() throws IOException {
- return -1;
- }
- });
- }
+ nis = new NettyInputStream();
+ responseDone.whenComplete((_r, th) -> nis.complete(th));
+
+ jerseyResponse.setEntityStream(nis);
}
if (msg instanceof HttpContent) {
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
index 8bceac63ab..bdede02a34 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 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
@@ -59,7 +59,8 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
ctx.pipeline().remove(JerseyExpectContinueHandler.class);
}
} else {
- if (!isExpected) {
+ if (!isExpected
+ && ctx.pipeline().context(JerseyExpectContinueHandler.class) != null) {
ctx.pipeline().remove(JerseyExpectContinueHandler.class);
}
ctx.fireChannelRead(msg); //bypass the message to the next handler in line
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 4fbec4ccc0..cec13488e9 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
@@ -89,6 +89,9 @@
import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
import org.glassfish.jersey.client.spi.Connector;
import org.glassfish.jersey.innate.VirtualThreadUtil;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.message.internal.OutboundMessageContext;
import org.glassfish.jersey.netty.connector.internal.NettyEntityWriter;
@@ -104,6 +107,17 @@ class NettyConnector implements Connector {
final Client client;
final HashMap> connections = new HashMap<>();
+ private static final LazyValue NETTY_VERSION = Values.lazy(
+ (Value) () -> {
+ String nettyVersion = null;
+ try {
+ nettyVersion = io.netty.util.Version.identify().values().iterator().next().artifactVersion();
+ } catch (Throwable t) {
+ nettyVersion = "4.1.x";
+ }
+ return "Netty " + nettyVersion;
+ });
+
// If HTTP keepalive is enabled the value of "http.maxConnections" determines the maximum number
// of idle connections that will be simultaneously kept alive, per destination.
private static final String HTTP_KEEPALIVE_STRING = System.getProperty("http.keepAlive");
@@ -525,7 +539,7 @@ private String buildPathWithQueryParameters(URI requestUri) {
@Override
public String getName() {
- return "Netty 4.1.x";
+ return NETTY_VERSION.get();
}
@Override
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyEntityWriter.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyEntityWriter.java
index a9e70409f8..bcd3fd868c 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyEntityWriter.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyEntityWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 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,8 +24,10 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* The Entity Writer is used to write entity in Netty. One implementation is delayed,
@@ -196,10 +198,7 @@ private void _flush() throws IOException {
for (Runnable runnable : delayedOps) {
runnable.run();
}
-
- if (outputStream.b != null) {
- writer.getOutputStream().write(outputStream.b, outputStream.off, outputStream.len);
- }
+ outputStream._flush();
}
}
@@ -216,7 +215,7 @@ public OutputStream getOutputStream() {
@Override
public long getLength() {
- return outputStream.len - outputStream.off;
+ return outputStream.writeLen;
}
@Override
@@ -225,9 +224,9 @@ public Type getType() {
}
private class DelayedOutputStream extends OutputStream {
- private byte[] b;
- private int off;
- private int len;
+ private final List actions = new ArrayList<>();
+ private int writeLen = 0;
+ private AtomicBoolean streamFlushed = new AtomicBoolean(false);
@Override
public void write(int b) throws IOException {
@@ -241,15 +240,39 @@ public void write(byte[] b) throws IOException {
@Override
public void write(byte[] b, int off, int len) throws IOException {
- if (!flushed && this.b == null) {
- this.b = b;
- this.off = off;
- this.len = len;
+ if (!flushed) {
+ actions.add(new WriteAction(b, off, len));
+ writeLen += len;
} else {
- DelayedEntityWriter.this._flush();
+ _flush();
writer.getOutputStream().write(b, off, len);
+ writer.getOutputStream().flush();
+ }
+ }
+
+ public void _flush() throws IOException {
+ if (streamFlushed.compareAndSet(false, true)) {
+ DelayedEntityWriter.this._flush();
+ for (WriteAction action : actions) {
+ action.run();
+ }
+ actions.clear();
}
}
}
+
+ private class WriteAction {
+ private final byte[] b;
+
+ private WriteAction(byte[] b, int off, int len) {
+ this.b = new byte[len]; // b passed in can be reused
+ System.arraycopy(b, off, this.b, 0, len);
+ }
+
+ public void run() throws IOException {
+ writer.getOutputStream().write(b, 0, b.length);
+ writer.getOutputStream().flush();
+ }
+ }
}
}
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
index a6f0c1b081..c2c5bf4cc3 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
+++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
@@ -17,6 +17,8 @@
package org.glassfish.jersey.servlet;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.security.AccessController;
@@ -54,6 +56,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
import org.glassfish.jersey.innate.inject.InternalBinder;
import org.glassfish.jersey.innate.inject.InjectionIds;
import org.glassfish.jersey.innate.inject.ServiceFinderBinder;
@@ -438,7 +441,7 @@ public Integer get() {
}
/**
- * Initialize {@code ContainerRequest} instance to be used to handle {@code servletRequest}.
+ * Initialize {@code ContainerRequest} instance to handle {@code servletRequest}.
*/
private void initContainerRequest(
final ContainerRequest requestContext,
@@ -446,7 +449,21 @@ private void initContainerRequest(
final HttpServletResponse servletResponse,
final ResponseWriter responseWriter) throws IOException {
- requestContext.setEntityStream(servletRequest.getInputStream());
+ try {
+ requestContext.setEntityStream(new InputStreamWrapper() {
+ @Override
+ protected InputStream getWrapped() {
+ try {
+ return servletRequest.getInputStream();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ });
+ } catch (UncheckedIOException e) {
+ throw e.getCause();
+ }
+
requestContext.setRequestScopedInitializer(requestScopedInitializer.get(new RequestContextProvider() {
@Override
public HttpServletRequest getHttpServletRequest() {
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
index 5059c306ef..9876c01135 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
+++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.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
@@ -142,12 +142,7 @@ public OutputStream writeResponseStatusAndHeaders(final long contentLength, fina
}
}
- final String reasonPhrase = responseContext.getStatusInfo().getReasonPhrase();
- if (reasonPhrase != null) {
- response.setStatus(responseContext.getStatus());
- } else {
- response.setStatus(responseContext.getStatus());
- }
+ response.setStatus(responseContext.getStatus());
if (!responseContext.hasEntity()) {
return null;
@@ -214,12 +209,13 @@ public void failure(final Throwable error) {
try {
if (!response.isCommitted()) {
try {
+ final int statusCode = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
if (configSetStatusOverSendError) {
response.reset();
//noinspection deprecation
- response.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+ response.setStatus(statusCode);
} else {
- response.sendError(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Request failed.");
+ response.sendError(statusCode, "Request failed.");
}
} catch (final IllegalStateException ex) {
// a race condition externally committing the response can still occur...
diff --git a/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.java b/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.java
new file mode 100644
index 0000000000..feb899c498
--- /dev/null
+++ b/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.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.servlet.internal;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.servlet.ServletProperties;
+import org.glassfish.jersey.servlet.WebComponent;
+import org.glassfish.jersey.servlet.WebConfig;
+import org.glassfish.jersey.servlet.WebFilterConfig;
+import org.junit.jupiter.api.Test;
+
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Enumeration;
+
+public class RequestInputStreamTest {
+ @Test
+ public void test404RequestInputStream() throws ServletException, IOException {
+ InvocationHandler handler = new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ switch (method.getName()) {
+ case "getHeaderNames":
+ return Collections.emptyEnumeration();
+ case "getInputStream":
+ throw new IllegalStateException("ServletRequest#getInputStream clashes with ServletRequest#getReader");
+ }
+ return null;
+ }
+ };
+
+ FilterConfig filterConfig = new FilterConfig() {
+ @Override
+ public String getFilterName() {
+ return null;
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return (ServletContext) Proxy.newProxyInstance(getClass().getClassLoader(),
+ new Class[]{ServletContext.class},
+ handler);
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ return null;
+ }
+
+ @Override
+ public Enumeration getInitParameterNames() {
+ return null;
+ }
+ };
+ WebConfig dummyWebConfig = new WebFilterConfig(filterConfig);
+ ResourceConfig resourceConfig = new ResourceConfig()
+ .property(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL")
+ .property(ServerProperties.WADL_FEATURE_DISABLE, true)
+ .property(ServletProperties.FILTER_FORWARD_ON_404, true)
+ .property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
+ WebComponent component = new WebComponent(dummyWebConfig, resourceConfig);
+ component.service(URI.create("http://localhost"), URI.create("http://localhost"),
+ (HttpServletRequest) Proxy.newProxyInstance(getClass().getClassLoader(),
+ new Class[] {HttpServletRequest.class},
+ handler
+ ),
+ (HttpServletResponse) Proxy.newProxyInstance(getClass().getClassLoader(),
+ new Class[]{HttpServletResponse.class},
+ handler)
+ );
+ }
+}
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java b/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java
index 74c8cfbef7..fa020cf3c1 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.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
@@ -35,6 +35,7 @@
import org.glassfish.jersey.internal.PropertiesDelegate;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.message.MessageBodyWorkers;
+import org.glassfish.jersey.message.internal.ReaderInterceptorExecutor;
/**
* {@link jakarta.ws.rs.ext.MessageBodyWriter} for {@link ChunkedInput}.
@@ -71,7 +72,7 @@ public ChunkedInput readFrom(Class chunkedInputClass,
return new ChunkedInput(
chunkType,
- inputStream,
+ ReaderInterceptorExecutor.closeableInputStream(inputStream),
annotations,
mediaType,
headers,
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
index 78a7111b37..162e4a0405 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
@@ -91,7 +91,7 @@ public final class NonInjectionManager implements InjectionManager {
*/
private class TypedInstances {
private final MultivaluedMap> singletonInstances = new MultivaluedHashMap<>();
- private final ThreadLocal>> threadInstances = new ThreadLocal<>();
+ private ThreadLocal>> threadInstances = new ThreadLocal<>();
private final List
-
- de.jflex
- maven-jflex-plugin
- 1.4.3
-
-
-
- generate
-
-
- ${project.build.directory}/generated-sources/rsrc-gen
-
-
-
-
diff --git a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
index 00e9fe7a4c..ee9f56d584 100644
--- a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
+++ b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2020 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
@@ -24,6 +24,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -87,6 +88,10 @@ public class EventProcessor implements Runnable, EventListener {
* A map of listeners bound to receive only events of a particular name.
*/
private final Map> boundListeners;
+ /**
+ * A list of Error Consumers.
+ */
+ private final List> throwableConsumers;
/**
* Shutdown handler is invoked when Event processor reaches terminal stage.
@@ -111,6 +116,7 @@ private EventProcessor(final EventProcessor that) {
this.unboundListeners = that.unboundListeners;
this.eventListener = that.eventListener;
this.shutdownHandler = that.shutdownHandler;
+ this.throwableConsumers = that.throwableConsumers;
}
private EventProcessor(Builder builder) {
@@ -128,6 +134,7 @@ private EventProcessor(Builder builder) {
this.unboundListeners = builder.unboundListeners == null ? Collections.EMPTY_LIST : builder.unboundListeners;
this.eventListener = builder.eventListener;
this.shutdownHandler = builder.shutdownHandler;
+ this.throwableConsumers = builder.throwableConsumers;
}
/**
@@ -199,6 +206,16 @@ public void run() {
}
// if we're here, an unrecoverable error has occurred - just turn off the lights...
shutdownHandler.shutdown();
+ // and notify error handlers
+ if (throwableConsumers != null) {
+ for (Consumer consumer : throwableConsumers) {
+ try {
+ consumer.accept(ex);
+ } catch (Throwable throwable) {
+ LOGGER.fine(String.format("User throwable ignored: %s", throwable.getMessage()));
+ }
+ }
+ }
} finally {
if (eventInput != null && !eventInput.isClosed()) {
eventInput.close();
@@ -357,6 +374,7 @@ public static class Builder {
private boolean disableKeepAlive;
private List unboundListeners;
private Map> boundListeners;
+ private List> throwableConsumers = null;
private Builder(WebTarget target,
AtomicReference state,
@@ -420,6 +438,17 @@ public Builder disableKeepAlive() {
return this;
}
+ /**
+ * Set the consumers of {@link Throwable} occurring during connection.
+ *
+ * @param throwableConsumers a list of consumers of throwable.
+ * @return updated builder instance.
+ */
+ public Builder throwableConsumers(List> throwableConsumers) {
+ this.throwableConsumers = throwableConsumers;
+ return this;
+ }
+
/**
* Build the {@link EventProcessor}.
*
diff --git a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/JerseySseEventSource.java b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/JerseySseEventSource.java
index 4f5e87a0ab..501b95f493 100644
--- a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/JerseySseEventSource.java
+++ b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/JerseySseEventSource.java
@@ -16,6 +16,8 @@
package org.glassfish.jersey.media.sse.internal;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Flow;
import java.util.concurrent.TimeUnit;
@@ -72,6 +74,10 @@ public class JerseySseEventSource implements SseEventSource {
* Client provided executor facade.
*/
private final ClientExecutor clientExecutor;
+ /**
+ * List of Throwable consumers passed to EventProcessor.Builder.
+ */
+ private final List> throwableConsumers = new ArrayList<>();
/**
* Private constructor.
@@ -110,11 +116,13 @@ public void register(final Consumer onEvent) {
public void register(final Consumer onEvent, final Consumer onError) {
this.subscribe(DEFAULT_SUBSCRIPTION_HANDLER, onEvent, onError, () -> {
});
+ throwableConsumers.add(onError);
}
@Override
public void register(final Consumer onEvent, final Consumer onError, final Runnable onComplete) {
this.subscribe(DEFAULT_SUBSCRIPTION_HANDLER, onEvent, onError, onComplete);
+ throwableConsumers.add(onError);
}
private void subscribe(final Consumer onSubscribe,
@@ -173,6 +181,7 @@ public void open() {
EventProcessor processor = EventProcessor
.builder(endpoint, state, clientExecutor, this::onEvent, this::close)
.reconnectDelay(reconnectDelay, reconnectTimeUnit)
+ .throwableConsumers(throwableConsumers)
.build();
clientExecutor.submit(processor);
diff --git a/media/sse/src/test/java/org/glassfish/jersey/media/sse/SseEventSourceRegisterErrorHandlerTest.java b/media/sse/src/test/java/org/glassfish/jersey/media/sse/SseEventSourceRegisterErrorHandlerTest.java
new file mode 100644
index 0000000000..52cdcc06a6
--- /dev/null
+++ b/media/sse/src/test/java/org/glassfish/jersey/media/sse/SseEventSourceRegisterErrorHandlerTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.media.sse;
+
+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.BadRequestException;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.InternalServerErrorException;
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.sse.InboundSseEvent;
+import jakarta.ws.rs.sse.Sse;
+import jakarta.ws.rs.sse.SseEventSink;
+import jakarta.ws.rs.sse.SseEventSource;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+public class SseEventSourceRegisterErrorHandlerTest extends JerseyTest {
+ @Path("sse")
+ public static class SseEventSourceRegisterTestSseEndpoint {
+
+ @Path("hello")
+ @GET
+ @Produces(SseFeature.SERVER_SENT_EVENTS)
+ public void hello(@Context SseEventSink output, @Context Sse sse) throws InterruptedException {
+ output.send(sse.newEvent("HELLO"));
+ }
+
+ @Path("close")
+ @GET
+ @Produces(SseFeature.SERVER_SENT_EVENTS)
+ public void close(@Context SseEventSink output, @Context Sse sse) throws IOException {
+ output.close();
+ }
+
+ @Path("500")
+ @GET
+ @Produces(SseFeature.SERVER_SENT_EVENTS)
+ public void throw500(@Context SseEventSink output, @Context Sse sse) throws InterruptedException {
+ throw new WebApplicationException();
+ }
+
+ @Path("400")
+ @GET
+ @Produces(SseFeature.SERVER_SENT_EVENTS)
+ public void throw400(@Context SseEventSink output, @Context Sse sse) throws InterruptedException {
+ throw new BadRequestException();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(SseEventSourceRegisterTestSseEndpoint.class);
+ }
+
+ private static final Consumer EMPTY = event -> {
+ };
+
+ @Test
+ public void testConnection404() throws InterruptedException {
+ WebTarget sseTarget = target("sse");
+ AtomicReference throwable = new AtomicReference<>();
+ CountDownLatch completeLatch = new CountDownLatch(1);
+
+ SseEventSource eventSource = SseEventSource.target(sseTarget).build();
+ eventSource.register(EMPTY, throwable::set, completeLatch::countDown);
+ eventSource.open();
+ completeLatch.await(10_000, TimeUnit.MILLISECONDS);
+ MatcherAssert.assertThat(throwable.get(), Matchers.notNullValue());
+ MatcherAssert.assertThat(throwable.get().getClass(), Matchers.is(NotFoundException.class));
+ }
+
+ @Test
+ public void testError500() throws InterruptedException {
+ WebTarget sseTarget = target("sse/500");
+ AtomicReference throwable = new AtomicReference<>();
+ CountDownLatch completeLatch = new CountDownLatch(1);
+
+ SseEventSource eventSource = SseEventSource.target(sseTarget).build();
+ eventSource.register(EMPTY, throwable::set, completeLatch::countDown);
+ eventSource.open();
+ completeLatch.await(10_000, TimeUnit.MILLISECONDS);
+ MatcherAssert.assertThat(throwable.get(), Matchers.notNullValue());
+ MatcherAssert.assertThat(throwable.get().getClass(), Matchers.is(InternalServerErrorException.class));
+ }
+
+ @Test
+ public void testError400() throws InterruptedException {
+ WebTarget sseTarget = target("sse/400");
+ AtomicReference throwable = new AtomicReference<>();
+ CountDownLatch completeLatch = new CountDownLatch(1);
+
+ SseEventSource eventSource = SseEventSource.target(sseTarget).build();
+ eventSource.register(EMPTY, throwable::set, completeLatch::countDown);
+ eventSource.open();
+ completeLatch.await(10_000, TimeUnit.MILLISECONDS);
+ MatcherAssert.assertThat(throwable.get(), Matchers.notNullValue());
+ MatcherAssert.assertThat(throwable.get().getClass(), Matchers.is(BadRequestException.class));
+ }
+}
diff --git a/pom.xml b/pom.xml
index ada126d97b..f893990bde 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2134,7 +2134,7 @@
3.0.3
3.0.1
3.2.6
- 3.2.6
+ 3.2.8
3.7.1
33.1.0-jre
@@ -2142,10 +2142,11 @@
2.10.0
4.5.14
5.3.1
- 2.17.0
+ 2.17.1
3.30.2-GA
- 3.5.3.Final
1.3.7
+ 3.3.2.Final
+ 3.6.0.Final
1.37
1.49
4.13.2
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java
index 8b54346eac..951de51e5c 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2022 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
@@ -35,6 +35,7 @@
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
@@ -57,7 +58,7 @@ public class ResponseReadAndBufferEntityTest extends JerseyTest {
private static final Logger LOGGER = Logger.getLogger(ResponseReadAndBufferEntityTest.class.getName());
- public static class CorruptableInputStream extends InputStream {
+ public static class CorruptableInputStream extends InputStreamWrapper {
private final AtomicInteger closeCounter = new AtomicInteger(0);
@@ -71,53 +72,16 @@ public CorruptableInputStream() {
}
@Override
- public synchronized int read() throws IOException {
- if (corruptRead) {
- corrupt();
- }
- return delegate.read();
- }
-
- @Override
- public int read(final byte[] b) throws IOException {
- if (corruptRead) {
- corrupt();
- }
- return delegate.read(b);
+ protected InputStream getWrapped() {
+ return delegate;
}
@Override
- public int read(final byte[] b, final int off, final int len) throws IOException {
+ protected InputStream getWrappedIOE() throws IOException {
if (corruptRead) {
corrupt();
}
- return delegate.read(b, off, len);
- }
-
- @Override
- public long skip(final long n) throws IOException {
- if (corruptRead) {
- corrupt();
- }
- return delegate.skip(n);
- }
-
- @Override
- public int available() throws IOException {
- if (corruptRead) {
- corrupt();
- }
- return delegate.available();
- }
-
- @Override
- public boolean markSupported() {
- return delegate.markSupported();
- }
-
- @Override
- public void mark(final int readAheadLimit) {
- delegate.mark(readAheadLimit);
+ return delegate;
}
@Override
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
index 83a52dd8a3..f381d5fe9b 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 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
@@ -16,11 +16,18 @@
package org.glassfish.jersey.tests.e2e.client.connector;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.RequestEntityProcessing;
import org.glassfish.jersey.client.spi.ConnectorProvider;
import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.media.multipart.BodyPart;
import org.glassfish.jersey.media.multipart.BodyPartEntity;
@@ -32,6 +39,8 @@
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.glassfish.jersey.test.spi.TestHelper;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.Test;
@@ -40,16 +49,25 @@
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+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.MultivaluedMap;
import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.Provider;
+import jakarta.ws.rs.ext.WriterInterceptor;
+import jakarta.ws.rs.ext.WriterInterceptorContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.logging.Handler;
import java.util.logging.Level;
+import java.util.logging.LogManager;
import java.util.logging.Logger;
public class MultiPartTest {
@@ -129,5 +147,72 @@ public void testMultipart() {
}
}
}
+
+ @Test
+ public void testNettyBufferedMultipart() {
+// setDebugLevel(Level.FINEST);
+ ClientConfig config = new ClientConfig();
+
+ config.connectorProvider(new NettyConnectorProvider());
+ config.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED);
+ config.register(org.glassfish.jersey.media.multipart.MultiPartFeature.class);
+ config.register(new LoggingHandler(LogLevel.DEBUG));
+ config.register(new LoggingInterceptor());
+ config.property(ClientProperties.ASYNC_THREADPOOL_SIZE, 10);
+ config.property("jersey.config.client.logging.verbosity", LoggingFeature.Verbosity.PAYLOAD_TEXT);
+ config.property("jersey.config.client.logging.logger.level", Level.FINEST.toString());
+
+ Client client = ClientBuilder.newClient(config);
+
+ FormDataMultiPart formData = new FormDataMultiPart();
+ FormDataBodyPart bodyPart1 = new FormDataBodyPart("hello1", "{\"first\":\"firstLine\",\"second\":\"secondLine\"}",
+ MediaType.APPLICATION_JSON_TYPE);
+ formData.bodyPart(bodyPart1);
+ formData.bodyPart(new FormDataBodyPart("hello2",
+ "{\"first\":\"firstLine\",\"second\":\"secondLine\",\"third\":\"thirdLine\"}",
+ MediaType.APPLICATION_JSON_TYPE));
+ formData.bodyPart(new FormDataBodyPart("hello3",
+ "{\"first\":\"firstLine\",\"second\":\"secondLine\",\""
+ + "second\":\"secondLine\",\"second\":\"secondLine\",\"second\":\"secondLine\"}",
+ MediaType.APPLICATION_JSON_TYPE));
+ formData.bodyPart(new FormDataBodyPart("plaintext", "hello"));
+
+ Response response1 = client.target(target().getUri()).path("upload")
+ .request()
+ .post(Entity.entity(formData, formData.getMediaType()));
+
+ MatcherAssert.assertThat(response1.getStatus(), Matchers.is(200));
+ MatcherAssert.assertThat(response1.readEntity(String.class),
+ Matchers.stringContainsInOrder("first", "firstLine", "second", "secondLine"));
+ response1.close();
+ client.close();
+ }
+
+ public static void setDebugLevel(Level newLvl) {
+ Logger rootLogger = LogManager.getLogManager().getLogger("");
+ Handler[] handlers = rootLogger.getHandlers();
+ rootLogger.setLevel(newLvl);
+ for (Handler h : handlers) {
+ h.setLevel(Level.ALL);
+ }
+ Logger nettyLogger = Logger.getLogger("io.netty");
+ nettyLogger.setLevel(Level.FINEST);
+ }
+
+ @Provider
+ public class LoggingInterceptor implements WriterInterceptor {
+
+ @Override
+ public void aroundWriteTo(WriterInterceptorContext context)
+ throws IOException, WebApplicationException {
+ try {
+ MultivaluedMap headers = context.getHeaders();
+ headers.forEach((key, val) -> System.out.println(key + ":" + val));
+ context.proceed();
+ } catch (Exception e) {
+ throw e;
+ }
+ }
+ }
}
}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/NoContentLengthTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/NoContentLengthTest.java
new file mode 100644
index 0000000000..ce8600e7dc
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/NoContentLengthTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.client.connector;
+
+import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class NoContentLengthTest {
+
+ private static final String MSG = "12345678901234567890123456789012345678901234567890";
+
+ private static int port;
+ private static AtomicBoolean running = new AtomicBoolean(false);
+
+ @BeforeEach
+ void beforeEach() {
+ while (!running.compareAndSet(false, true)) {
+ try {
+ Thread.sleep(1000L);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ String _port = System.getProperty("jersey.config.test.container.port");
+ port = Integer.parseInt(_port == null || _port.isEmpty() ? "8080" : _port);
+ ServerSocket serverSocket = new ServerSocket(port);
+ System.err.println("Starting server on port : " + port);
+
+ Socket clientSocket = serverSocket.accept();
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
+
+ String s;
+ while ((s = in.readLine()) != null) {
+ // System.out.println(s);
+ if (s.isEmpty()) {
+ break;
+ }
+ }
+
+ out.write("HTTP/1.0 200 OK\r\n");
+ out.write("Content-Type: text/plain\r\n");
+ out.write("\r\n");
+ out.write(MSG);
+
+ out.close();
+ in.close();
+ clientSocket.close();
+ serverSocket.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ } finally {
+ running.set(false);
+ }
+ }
+ };
+ Thread newThread = new Thread(runnable);
+ newThread.start();
+ }
+
+ public static List providers() {
+ return Arrays.asList(
+ new Apache5ConnectorProvider(),
+ new HttpUrlConnectorProvider(),
+ new NettyConnectorProvider(),
+ new JettyConnectorProvider(),
+ new GrizzlyConnectorProvider(),
+ new JdkConnectorProvider()
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("providers")
+ public void testNoContentLength(ConnectorProvider connectorProvider) {
+ try (Response r = target(connectorProvider).request().get()) {
+ MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
+ MatcherAssert.assertThat(r.getHeaderString(HttpHeaders.CONTENT_LENGTH), Matchers.nullValue());
+ MatcherAssert.assertThat(r.hasEntity(), Matchers.is(true));
+ MatcherAssert.assertThat(r.readEntity(String.class), Matchers.is(MSG));
+ }
+ }
+
+ private WebTarget target(ConnectorProvider connectorProvider) {
+ ClientConfig config = new ClientConfig();
+ config.connectorProvider(connectorProvider);
+ return ClientBuilder.newClient(config).target("http://localhost:" + port);
+ }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java
new file mode 100644
index 0000000000..5c0c944b26
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.entity;
+
+import org.glassfish.jersey.client.ChunkedInput;
+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.Path;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ReaderInterceptor;
+import jakarta.ws.rs.ext.ReaderInterceptorContext;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class ChunkedInputReaderTest extends JerseyTest {
+
+ @Path("/")
+ public static class ChunkedInputReaderTestResource {
+ @GET
+ public String get() {
+ return "To_be_replaced_by_client_reader";
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(ChunkedInputReaderTestResource.class);
+ }
+
+ @Test
+ public void testChunkedInputStreamIsClosed() {
+ AtomicBoolean closed = new AtomicBoolean(false);
+ InputStream inputStream = new ByteArrayInputStream("TEST".getBytes()) {
+ @Override
+ public void close() throws IOException {
+ closed.set(true);
+ super.close();
+ }
+ };
+
+ final GenericType> chunkedInputGenericType = new GenericType(new ParameterizedType() {
+ @Override
+ public Type[] getActualTypeArguments() {
+ return new Type[]{String.class};
+ }
+
+ @Override
+ public Type getRawType() {
+ return ChunkedInput.class;
+ }
+
+ @Override
+ public Type getOwnerType() {
+ return ChunkedInput.class;
+ }
+ });
+
+
+ ChunkedInput response = target().register(new ReaderInterceptor() {
+ @Override
+ public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+ context.setInputStream(inputStream);
+ return context.proceed();
+ };
+ }).request().get(chunkedInputGenericType);
+ MatcherAssert.assertThat(response.read(), Matchers.is("TEST"));
+ response.close();
+ MatcherAssert.assertThat(closed.get(), Matchers.is(true));
+ }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
index 77a74a6e9e..9e872fc6d6 100644
--- a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2022 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
@@ -72,6 +72,7 @@
import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
import org.glassfish.jersey.jettison.JettisonFeature;
import org.glassfish.jersey.message.internal.FileProvider;
+import org.glassfish.jersey.message.internal.PathProvider;
import org.glassfish.jersey.server.ResourceConfig;
import org.codehaus.jettison.json.JSONArray;
@@ -431,6 +432,20 @@ public void testFileRepresentation() throws IOException {
_test(in, FileResource.class);
}
+ @Path("PathResource")
+ public static class PathResource extends AResource {
+ }
+
+ @Test
+ @Execution(ExecutionMode.CONCURRENT)
+ public void testPathRepresentation() throws IOException {
+ final var pp = new PathProvider();
+ final var in = pp.readFrom(java.nio.file.Path.class, java.nio.file.Path.class, null, null, null,
+ new ByteArrayInputStream("CONTENT".getBytes()));
+
+ _test(in, PathResource.class);
+ }
+
@Produces("application/x-www-form-urlencoded")
@Consumes("application/x-www-form-urlencoded")
@Path("FormResource")
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java
index 8af4ef7d9e..0f7b854af8 100644
--- a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java
@@ -157,6 +157,7 @@ private static class CustomClientAsyncExecutor extends ThreadPoolExecutorProvide
@Test
public void test() throws InterruptedException {
final String[] onEventThreadName = {""};
+ final CountDownLatch onEventLatch = new CountDownLatch(1);
SseEventSource eventSource = SseEventSource
.target(target().path("sse/events"))
.build();
@@ -164,6 +165,7 @@ public void test() throws InterruptedException {
eventSource.register((event) -> {
LOGGER.info("Event: " + event + " from: " + Thread.currentThread().getName());
onEventThreadName[0] = Thread.currentThread().getName();
+ onEventLatch.countDown();
}
);
@@ -179,6 +181,7 @@ public void test() throws InterruptedException {
Assertions.assertTrue(sendThreadOk, "send either not invoked at all or from wrong thread");
Assertions.assertTrue(onCompleteThreadOk, "onComplete either not invoked at all or from wrong thread");
+ Assertions.assertTrue(onEventLatch.await(2_000L, TimeUnit.MILLISECONDS));
Assertions.assertTrue(onEventThreadName[0].startsWith("custom-client-executor"),
"Client event called from wrong thread ( " + onEventThreadName[0] + ")");
}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
index 749eb22ebd..899d9ca38b 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2023 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
@@ -30,6 +30,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
@@ -108,6 +109,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -399,6 +401,20 @@ public void testLastModifiedGET() {
assertTrue(r.getHeaders().containsKey("Last-modified"));
}
+ @Test
+ public void testLastModifiedGETOnJPLocale() {
+ Locale defaultLocale = Locale.getDefault();
+ try {
+ Locale.setDefault(new Locale("ja", "JP"));
+ final WebTarget target = target("/application.wadl");
+
+ final Response r = target.queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request().get(Response.class);
+ assertDoesNotThrow(() -> r.getLastModified());
+ } finally {
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
@Test
public void testLastModifiedOPTIONS() {
final WebTarget target = target("/widgets/3/verbose");
diff --git a/tests/e2e-tls/pom.xml b/tests/e2e-tls/pom.xml
index 9aa5f85923..1d5d2473c1 100644
--- a/tests/e2e-tls/pom.xml
+++ b/tests/e2e-tls/pom.xml
@@ -97,7 +97,7 @@
io.specto
hoverfly-java-junit5
- 0.14.0
+ 0.18.1
test
diff --git a/tests/integration/jersey-2776/pom.xml b/tests/integration/jersey-2776/pom.xml
index 0cff060ad8..4c6a2640b3 100644
--- a/tests/integration/jersey-2776/pom.xml
+++ b/tests/integration/jersey-2776/pom.xml
@@ -42,7 +42,7 @@
org.apache.cxf
cxf-rt-rs-client
- 3.0.3
+ 3.5.8
test
diff --git a/tests/jersey-tck/pom.tomcat.xml b/tests/jersey-tck/pom.tomcat.xml
index 081c7731f9..a6e71a9349 100644
--- a/tests/jersey-tck/pom.tomcat.xml
+++ b/tests/jersey-tck/pom.tomcat.xml
@@ -702,7 +702,7 @@
jersey-tck
- 3.1.99-SNAPSHOT
+ 4.0.99-SNAPSHOT
diff --git a/tests/jersey-tck/pom.xml b/tests/jersey-tck/pom.xml
index d8f6816ece..b6254ccd91 100644
--- a/tests/jersey-tck/pom.xml
+++ b/tests/jersey-tck/pom.xml
@@ -1,7 +1,7 @@
+ 4.0.99-SNAPSHOT
diff --git a/tests/performance/runners/jersey-grizzly-runner/pom.xml b/tests/performance/runners/jersey-grizzly-runner/pom.xml
index 8f61ad48b8..4584d61980 100644
--- a/tests/performance/runners/jersey-grizzly-runner/pom.xml
+++ b/tests/performance/runners/jersey-grizzly-runner/pom.xml
@@ -49,7 +49,7 @@
org.apache.maven.plugins
maven-jar-plugin
- 2.4
+ ${jar.mvn.plugin.version}
true
diff --git a/tests/performance/test-cases/monitoring/pom.xml b/tests/performance/test-cases/monitoring/pom.xml
index c4076c2a7d..d5b84bd93c 100644
--- a/tests/performance/test-cases/monitoring/pom.xml
+++ b/tests/performance/test-cases/monitoring/pom.xml
@@ -37,13 +37,13 @@
com.yammer.metrics
metrics-core
- 2.1.2
+ 2.2.0
org.junit.jupiter
junit-jupiter
- 5.9.1
+ 5.10.2
test
@@ -56,13 +56,13 @@
commons-codec
commons-codec
- 1.5
+ 1.17.0
org.slf4j
slf4j-jdk14
- 1.6.1
+ 2.0.13
@@ -97,11 +97,11 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.1
+ 3.13.0
true
-
- 1.7
+
+ 1.8
false
false
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java
index 743d609627..8a97941ab0 100644
--- a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.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
@@ -27,7 +27,14 @@
class ClassVersionChecker {
static TestResult checkClassVersion(JarFile jar, JarEntry entry, Properties properties) throws IOException {
final String jerseyVersion = MavenUtil.getJerseyVersion(properties);
- final int minVersion = jerseyVersion.startsWith("3.1") ? 11 : 8;
+ final int minVersion;
+ if (jerseyVersion.startsWith("4")) {
+ minVersion = 17;
+ } else if (jerseyVersion.startsWith("3.1")) {
+ minVersion = 11;
+ } else {
+ minVersion = 8;
+ }
return checkClassVersion(jar.getInputStream(entry), jar.getName() + File.separator + entry.getName(), minVersion);
}
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java
index b1a7ee3bba..eeea9822e6 100644
--- a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.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
@@ -42,4 +42,28 @@ static Artifact resolveArtifact(org.apache.maven.model.Dependency d, List remoteRepos,
+ RepositorySystem repoSystem, RepositorySystemSession repoSession)
+ throws ArtifactResolutionException {
+ DefaultArtifact artifact = new DefaultArtifact(
+ d.getGroupId(), d.getArtifactId(), "sources", d.getType(), d.getVersion()
+ );
+ ArtifactRequest request = new ArtifactRequest();
+ request.setArtifact(artifact);
+ request.setRepositories(remoteRepos);
+ return repoSystem.resolveArtifact(repoSession, request).getArtifact();
+ }
+
+ static Artifact resolveJavadoc(org.apache.maven.model.Dependency d, List remoteRepos,
+ RepositorySystem repoSystem, RepositorySystemSession repoSession)
+ throws ArtifactResolutionException {
+ DefaultArtifact artifact = new DefaultArtifact(
+ d.getGroupId(), d.getArtifactId(), "javadoc", d.getType(), d.getVersion()
+ );
+ ArtifactRequest request = new ArtifactRequest();
+ request.setArtifact(artifact);
+ request.setRepositories(remoteRepos);
+ return repoSystem.resolveArtifact(repoSession, request).getArtifact();
+ }
}
\ No newline at end of file
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java
index 20e77377b7..98d4616fcf 100644
--- a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.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
@@ -29,6 +29,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Properties;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class MavenUtil {
@@ -37,7 +38,7 @@ public final class MavenUtil {
private static final String PROJECT_VERSION = "project.version";
static File getArtifactJar(File repositoryRoot, Dependency dependency, Properties properties) {
- return getArtifactFile(repositoryRoot, dependency, properties, "jar");
+ return getArtifactFile(repositoryRoot, dependency, properties, dependency.getType());
}
private static File getArtifactFile(File repositoryRoot, Dependency dependency, Properties properties, String extension) {
@@ -52,7 +53,11 @@ private static File getArtifactFile(File repositoryRoot, Dependency dependency,
}
String version = MavenUtil.getDependencyVersion(dependency, properties);
fileSuffix.append(version).append(File.separator);
- fileSuffix.append(dependency.getArtifactId()).append('-').append(version).append(".").append(extension);
+ fileSuffix.append(dependency.getArtifactId()).append('-').append(version);
+ if (dependency.getClassifier() != null) {
+ fileSuffix.append('-').append(dependency.getClassifier());
+ }
+ fileSuffix.append(".").append(extension);
return new File(repositoryRoot, fileSuffix.toString());
}
@@ -103,7 +108,16 @@ static Stream keepJerseyJars(Stream stream, DependencyPa
static Stream streamJerseyJars() throws IOException, XmlPullParserException {
Model model = getModelFromFile("pom.xml");
List deps = getBomPomDependencies(model);
+ return streamJerseyJars(deps);
+ }
+ static Stream streamJerseySources() throws IOException, XmlPullParserException {
+ Model model = getModelFromFile("pom.xml");
+ List deps = getBomPomSources(model);
+ return streamJerseyJars(deps);
+ }
+
+ private static Stream streamJerseyJars(List deps) throws IOException, XmlPullParserException {
return deps.stream()
.filter(dep -> dep.getGroupId().startsWith("org.glassfish.jersey"))
.filter(dep -> dep.getType().equals("jar"));
@@ -139,6 +153,15 @@ private static List getBomPomDependencies(Model model) throws IOExce
return bomPomModel.getDependencyManagement().getDependencies();
}
+ private static List getBomPomSources(Model model) throws XmlPullParserException, IOException {
+ return getBomPomDependencies(model).stream()
+ .map(dependency -> {
+ dependency.setClassifier("sources");
+ return dependency;
+ })
+ .collect(Collectors.toList());
+ }
+
static String getJerseyVersion(Properties properties) {
String property = properties.getProperty(JERSEY_VERSION); // when it is in the pom.file
if (property == null || property.startsWith("${")) {
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ArchetypesTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ArchetypesTest.java
index b42830ba54..5be312c314 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ArchetypesTest.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ArchetypesTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 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
@@ -52,16 +52,21 @@ public void testPropertiesVersion() throws XmlPullParserException, IOException {
continue;
}
// Update the names with the ones in Jersey
- Map.Entry