diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f42b9dbd0e..9dcb3de3a4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,4 @@
-[//]: # " Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " Copyright (c) 2018, 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 "
@@ -18,13 +18,14 @@ Thanks for your interest in this project.
## Project description
-Eclipse Jersey is a REST framework that provides a JAX-RS (JSR-370) implementation and more.
+Eclipse Jersey is a REST framework that provides a JAX-RS (JSR-370),
+now mainly Jakarta REST, implementation and more.
Jersey provides its own APIs that extend the JAX-RS toolkit with additional features and utilities
to further simplify RESTful service and client development. Jersey also exposes numerous extension
SPIs so that developers may extend Jersey to best suit their needs.
Goals of Jersey project can be summarized in the following points:
-* Track the JAX-RS API and provide regular releases of production quality implementations that ships with GlassFish;
+* Track the JAX-RS/Jakarta REST API and provide regular releases of production quality implementations that ships with GlassFish;
* Provide APIs to extend Jersey & Build a community of users and developers; and finally
@@ -41,6 +42,21 @@ The project maintains the following source code repositories
* https://github.com/eclipse-ee4j/jersey
+## Eclipse Development Process
+
+This Eclipse Foundation open project is governed by the Eclipse Foundation
+Development Process and operates under the terms of the Eclipse IP Policy.
+
+## Specifications
+
+This specification project operates under the terms of the Eclipse Foundation
+Specification process.
+
+* https://eclipse.org/projects/dev_process
+* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf
+* https://www.eclipse.org/projects/efsp/
+* https://www.eclipse.org/legal/efsp_non_assert.php
+
## Eclipse Contributor Agreement
Before your contribution can be accepted by the project team contributors must
diff --git a/bom/pom.xml b/bom/pom.xml
index 80b15568b2..1ca327cac4 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -24,7 +24,7 @@
org.eclipse.ee4jproject
- 1.0.8
+ 1.0.9
@@ -419,6 +419,11 @@
jersey-test-framework-provider-jetty-http2${project.version}
+
+ org.glassfish.jersey.test-framework.providers
+ jersey-test-framework-provider-netty
+ ${project.version}
+ org.glassfish.jersey.test-frameworkjersey-test-framework-util
@@ -435,11 +440,6 @@
2.4true
-
- org.apache.maven.plugins
- maven-site-plugin
- 3.9.1
-
diff --git a/bundles/apidocs/pom.xml b/bundles/apidocs/pom.xml
index c55b8131ed..c65e6a18b3 100644
--- a/bundles/apidocs/pom.xml
+++ b/bundles/apidocs/pom.xml
@@ -127,10 +127,6 @@
javax.servlet3.1
-
- jakarta.persistence
- jakarta.persistence-api
- org.glassfish.jersey.containersjersey-container-simple-http
@@ -300,7 +296,7 @@
org.glassfish.jersey.*:*
- *.internal:*.internal.*:*.innate:*.innate.*
+ *.innate:*.innate.*
diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml
index 2d93a58b06..0161445565 100644
--- a/bundles/jaxrs-ri/pom.xml
+++ b/bundles/jaxrs-ri/pom.xml
@@ -217,7 +217,9 @@
falsefalse
- module-info.javaMETA-INF/versions/11/**
+ module-info.java
+ META-INF/versions/11/**
+ META-INF/versions/21/**
@@ -314,6 +316,7 @@
org.apache.maven.pluginsmaven-shade-plugin
+ ${shade.mvn.plugin.version}package
diff --git a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java
index 1247447a84..11f2091d58 100644
--- a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java
+++ b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.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
@@ -883,7 +883,8 @@ protected void prepareSocket(SSLSocket socket) throws IOException {
Object objectRequest = context.getAttribute(JERSEY_REQUEST_ATTR_NAME);
if (objectRequest != null) {
ClientRequest clientRequest = (ClientRequest) objectRequest;
- SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest).build();
+ SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest)
+ .setSNIHostName(clientRequest.getConfiguration()).build();
sniConfig.setSNIServerName(socket);
}
}
@@ -937,12 +938,12 @@ public void close() throws IOException {
}
@Override
- public synchronized void mark(int readlimit) {
+ public void mark(int readlimit) {
in.mark(readlimit);
}
@Override
- public synchronized void reset() throws IOException {
+ public void reset() throws IOException {
checkAborted();
in.reset();
}
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 6169a16983..4eecc17c2b 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
@@ -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
@@ -882,7 +882,8 @@ protected void prepareSocket(SSLSocket socket) throws IOException {
Object objectRequest = context.getAttribute(JERSEY_REQUEST_ATTR_NAME);
if (objectRequest != null) {
ClientRequest clientRequest = (ClientRequest) objectRequest;
- SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest).build();
+ SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest)
+ .setSNIHostName(clientRequest.getConfiguration()).build();
sniConfig.setSNIServerName(socket);
final int socketTimeout = ((ClientRequest) objectRequest).resolveProperty(ClientProperties.READ_TIMEOUT, -1);
@@ -941,12 +942,12 @@ public void close() throws IOException {
}
@Override
- public synchronized void mark(int readlimit) {
+ public void mark(int readlimit) {
in.mark(readlimit);
}
@Override
- public synchronized void reset() throws IOException {
+ public void reset() throws IOException {
checkAborted();
in.reset();
}
diff --git a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java
index 422d91917e..9b92517739 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -60,8 +60,10 @@ class ConnectorConfiguration {
private final int connectTimeout;
private final ProxyConfiguration proxyConfiguration;
private final AtomicReference sniConfigs = new AtomicReference<>(null);
+ private final Configuration configuration;
ConnectorConfiguration(Client client, Configuration config) {
+ configuration = config;
final Map properties = config.getProperties();
int proposedChunkSize = JdkConnectorProperties.getValue(properties, ClientProperties.CHUNKED_ENCODING_SIZE,
@@ -181,6 +183,10 @@ SSLParamConfigurator getSniConfig() {
return sniConfigs.get();
}
+ Configuration getConfiguration() {
+ return configuration;
+ }
+
@Override
public String toString() {
return "ConnectorConfiguration{"
diff --git a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpConnectionPool.java b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpConnectionPool.java
index b20f00cad2..7413caf34b 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpConnectionPool.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpConnectionPool.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -51,7 +51,8 @@ class HttpConnectionPool {
void send(HttpRequest httpRequest, CompletionHandler completionHandler) {
final Map> headers = new HashMap<>();
httpRequest.getHeaders().forEach((k, v) -> headers.put(k, (List) v));
- final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().uri(httpRequest.getUri()).headers(headers).build();
+ final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().uri(httpRequest.getUri())
+ .headers(headers).setSNIHostName(connectorConfiguration.getConfiguration()).build();
connectorConfiguration.setSniConfig(sniConfig);
final DestinationConnectionPool.DestinationKey destinationKey = new DestinationConnectionPool.DestinationKey(
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 15725e7638..4070300e0c 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -496,7 +496,7 @@ private void decideTransferEncoding() throws ParseException {
if (contentLengths != null) {
try {
- int bodyLength = Integer.parseInt(contentLengths.get(0));
+ long bodyLength = Long.parseLong(contentLengths.get(0));
if (bodyLength == 0) {
expectContent = false;
return;
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 0a3ede0a0a..7dccd87375 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -31,7 +31,7 @@ abstract class TransferEncodingParser {
abstract boolean parse(ByteBuffer input) throws ParseException;
- static TransferEncodingParser createFixedLengthParser(AsynchronousBodyInputStream responseBody, int expectedLength) {
+ static TransferEncodingParser createFixedLengthParser(AsynchronousBodyInputStream responseBody, long expectedLength) {
return new FixedLengthEncodingParser(responseBody, expectedLength);
}
@@ -42,11 +42,11 @@ static TransferEncodingParser createChunkParser(AsynchronousBodyInputStream resp
private static class FixedLengthEncodingParser extends TransferEncodingParser {
- private final int expectedLength;
+ private final long expectedLength;
private final AsynchronousBodyInputStream responseBody;
- private volatile int consumedLength = 0;
+ private volatile long consumedLength = 0;
- FixedLengthEncodingParser(AsynchronousBodyInputStream responseBody, int expectedLength) {
+ FixedLengthEncodingParser(AsynchronousBodyInputStream responseBody, long expectedLength) {
this.expectedLength = expectedLength;
this.responseBody = responseBody;
}
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 ab71e1f18f..e6aad835c9 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -208,7 +208,7 @@ protected void execute(final ClientRequest jerseyRequest, final Set redirec
try {
final SSLParamConfigurator sslConfig = SSLParamConfigurator.builder()
- .request(jerseyRequest).setSNIAlways(true).build();
+ .request(jerseyRequest).setSNIAlways(true).setSNIHostName(jerseyRequest.getConfiguration()).build();
String key = requestUri.getScheme() + "://" + sslConfig.getSNIHostName() + ":" + port;
ArrayList conns;
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
index aa0676bdcb..c5a920c0b4 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.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
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
index e0446d2f0b..90d138d06e 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -94,7 +94,7 @@ public class HttpUrlConnectorProvider implements ConnectorProvider {
/**
* A value of {@code true} declares that the client will try to set
* unsupported HTTP method to {@link java.net.HttpURLConnection} via
- * reflection.
+ * reflection as a workaround for a missing HTTP method.
*
* NOTE: Enabling this property may cause security related warnings/errors
* and it may break when other JDK implementation is used. Use only
@@ -103,6 +103,10 @@ public class HttpUrlConnectorProvider implements ConnectorProvider {
*
The value MUST be an instance of {@link java.lang.Boolean}.
*
The default value is {@code false}.
*
The name of the configuration property is {@value}.
+ *
Since JDK 16 the JDK internal classes are not opened for reflection and the workaround method does not work,
+ * unless {@code --add-opens java.base/java.net=ALL-UNNAMED} for HTTP requests and additional
+ * {@code --add-opens java.base/sun.net.www.protocol.https=ALL-UNNAMED} for HTTPS (HttpsUrlConnection) options are set.
+ *
*/
public static final String SET_METHOD_WORKAROUND =
"jersey.config.client.httpUrlConnection.setMethodWorkaround";
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
index cb32a5c62c..a92adf5472 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.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,14 +16,15 @@
package org.glassfish.jersey.client.innate.http;
-import jakarta.ws.rs.core.Configuration;
-import jakarta.ws.rs.core.HttpHeaders;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.internal.PropertiesResolver;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.UriBuilder;
import org.glassfish.jersey.internal.PropertiesResolver;
import org.glassfish.jersey.internal.util.PropertiesHelper;
@@ -52,6 +53,7 @@ public static final class Builder {
private String sniHostNameHeader = null;
private String sniHostNameProperty = null;
private boolean setAlways = false;
+ private String sniHostPrecedence = null;
/**
* Sets the SNIHostName and {@link URI} from the {@link ClientRequest} instance.
@@ -106,6 +108,62 @@ public Builder setSNIAlways(boolean setAlways) {
return this;
}
+ /**
+ *
+ * Sets the {@code hostName} to be used for calculating the {@link javax.net.ssl.SNIHostName}.
+ * Takes precedence over the HTTP HOST header, if set.
+ *
+ *
+ * By default, the {@code SNIHostName} is set when the HOST HTTP header differs from the HTTP request host.
+ * When the {@code hostName} matches the HTTP request host, the {@code SNIHostName} is not set,
+ * and the HTTP HOST header is not used for setting the {@code SNIHostName}. This allows Domain Fronting.
+ *
+ * @param hostName the host the {@code SNIHostName} should be set for.
+ * @return the builder instance.
+ */
+ public Builder setSNIHostName(String hostName) {
+ sniHostPrecedence = trimSniHostName(hostName);
+ return this;
+ }
+
+ /**
+ *
+ * Sets the {@code hostName} to be used for calculating the {@link javax.net.ssl.SNIHostName}.
+ * The {@code hostName} value is taken from the {@link Configuration} if the property
+ * {@link ClientProperties#SNI_HOST_NAME} is set.
+ * Takes precedence over the HTTP HOST header, if set.
+ *
+ *
+ * By default, the {@code SNIHostName} is set when the HOST HTTP header differs from the HTTP request host.
+ * When the {@code hostName} matches the HTTP request host, the {@code SNIHostName} is not set,
+ * and the HTTP HOST header is not used for setting the {@code SNIHostName}. This allows for Domain Fronting.
+ *
+ * @param configuration the host the {@code SNIHostName} should be set for.
+ * @return the builder instance.
+ */
+ public Builder setSNIHostName(Configuration configuration) {
+ return setSNIHostName((String) configuration.getProperty(ClientProperties.SNI_HOST_NAME));
+ }
+
+ /**
+ *
+ * Sets the {@code hostName} to be used for calculating the {@link javax.net.ssl.SNIHostName}.
+ * The {@code hostName} value is taken from the {@link PropertiesResolver} if the property
+ * {@link ClientProperties#SNI_HOST_NAME} is set.
+ * Takes precedence over the HTTP HOST header, if set.
+ *
+ *
+ * By default, the {@code SNIHostName} is set when the HOST HTTP header differs from the HTTP request host.
+ * When the {@code hostName} matches the HTTPS request host, the {@code SNIHostName} is not set,
+ * and the HTTP HOST header is not used for setting the {@code SNIHostName}. This allows for Domain Fronting.
+ *
+ * @param resolver the host the {@code SNIHostName} should be set for.
+ * @return the builder instance.
+ */
+ public Builder setSNIHostName(PropertiesResolver resolver) {
+ return setSNIHostName(resolver.resolveProperty(ClientProperties.SNI_HOST_NAME, String.class));
+ }
+
/**
* Builds the {@link SSLParamConfigurator} instance.
* @return the configured {@link SSLParamConfigurator} instance.
@@ -121,11 +179,15 @@ private static String getSniHostNameHeader(Map> httpHeaders
}
final String hostHeader = hostHeaders.get(0).toString();
+ return trimSniHostName(hostHeader);
+ }
+
+ private static String trimSniHostName(String sniHostName) {
final String trimmedHeader;
- if (hostHeader != null) {
- int index = hostHeader.indexOf(':'); // RFC 7230 Host = uri-host [ ":" port ] ;
- final String trimmedHeader0 = index != -1 ? hostHeader.substring(0, index).trim() : hostHeader.trim();
- trimmedHeader = trimmedHeader0.isEmpty() ? hostHeader : trimmedHeader0;
+ if (sniHostName != null) {
+ int index = sniHostName.indexOf(':'); // RFC 7230 Host = uri-host [ ":" port ] ;
+ final String trimmedHeader0 = index != -1 ? sniHostName.substring(0, index).trim() : sniHostName.trim();
+ trimmedHeader = trimmedHeader0.isEmpty() ? sniHostName : trimmedHeader0;
} else {
trimmedHeader = null;
}
@@ -137,7 +199,9 @@ private static String getSniHostNameHeader(Map> httpHeaders
private SSLParamConfigurator(SSLParamConfigurator.Builder builder) {
String sniHostName = builder.sniHostNameHeader == null ? builder.sniHostNameProperty : builder.sniHostNameHeader;
uri = builder.uri;
- sniConfigurator = SniConfigurator.createWhenHostHeader(uri, sniHostName, builder.setAlways);
+ sniConfigurator = SniConfigurator.createWhenHostHeader(uri,
+ builder.sniHostPrecedence != null ? builder.sniHostPrecedence : sniHostName,
+ builder.setAlways);
}
/**
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
index 9e766f539f..d5d916db19 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.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
@@ -50,12 +50,13 @@ String getHostName() {
}
/**
- * Create ClientSNI when {@link HttpHeaders#HOST} is set different from the request URI host (or {@code whenDiffer}.is false).
+ * Create {@link SniConfigurator} when {@link HttpHeaders#HOST} is set different from the request URI host
+ * (or {@code whenDiffer}.is false).
* @param hostUri the Uri of the HTTP request
* @param sniHostName the SniHostName either from HttpHeaders or the
* {@link org.glassfish.jersey.client.ClientProperties#SNI_HOST_NAME} property from Configuration object.
* @param whenDiffer create {@SniConfigurator only when different from the request URI host}
- * @return ClientSNI or empty when {@link HttpHeaders#HOST}
+ * @return Optional {@link SniConfigurator} or empty when {@link HttpHeaders#HOST} is equal to the requestHost
*/
static Optional createWhenHostHeader(URI hostUri, String sniHostName, boolean whenDiffer) {
if (sniHostName == null) {
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
index c89cf198e3..f19b109f55 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
@@ -357,7 +357,8 @@ public SSLSocketFactory get() {
private ClientResponse _apply(final ClientRequest request) throws IOException {
final HttpURLConnection uc;
final Optional proxy = ClientProxy.proxyFromRequest(request);
- final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(request).build();
+ final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(request)
+ .setSNIHostName(request.getConfiguration()).build();
final URI sniUri;
if (sniConfig.isSNIRequired()) {
sniUri = sniConfig.toIPRequestUri();
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java b/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java
index 1f7de34626..c075af927b 100644
--- a/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java
+++ b/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -46,8 +46,8 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.same;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.same;
/**
* {@code ClientRequest} unit tests.
diff --git a/core-common/pom.xml b/core-common/pom.xml
index a83931f398..13ab2a5b8a 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -253,10 +253,162 @@
+
+ build.jdk20-
+
+ [11,21)
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add.java20-
+ generate-sources
+
+ add-source
+
+
+
+
+
+
+
+
+
+
+
+
+
+ build.jdk21
+
+ [21,)
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add.java20-
+ generate-sources
+
+ add-source
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ default-compile
+
+
+ 21
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ validate
+
+ run
+
+
+
+ Building for JDK 21+
+
+
+
+
+ compile-java21
+ process-resources
+
+
+
+
+
+
+
+ run
+
+
+
+
+
+
+
+
+ copyJDK21FilesToMultiReleaseJar
+
+
+
+ target/classes-java21/org/glassfish/jersey/innate/VirtualThreadSupport.class
+
+ [11,21)
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ true
+
+
+ copy-jdk21-sources
+ prepare-package
+
+ copy-resources
+
+
+ ${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/21/org/glassfish/jersey/innate
+
+
+ ${java21.sourceDirectory}/org/glassfish/jersey/innate
+
+
+
+
+
+ copy-jdk21-classes-to-meta-inf
+ prepare-package
+
+ copy-resources
+
+
+ ${project.build.outputDirectory}/META-INF/versions/21
+
+
+ ${java21.build.outputDirectory}
+
+
+
+
+
+
+
+
+
+
securityOff
-
+
@@ -293,6 +445,13 @@
-Djava.security.manager -Djava.security.policy=${project.build.directory}/test-classes/surefire.policy
+ ${project.basedir}/src/main/jsr166
+ ${project.build.directory}/classes-java8
+ ${project.basedir}/src/main/java8
+ ${project.build.directory}/classes-java11
+ ${project.basedir}/src/main/java11
+ ${project.build.directory}/classes-java21
+ ${project.basedir}/src/main/java21
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/inject/package-info.java b/core-common/src/main/java/org/glassfish/jersey/innate/inject/package-info.java
new file mode 100644
index 0000000000..b276f3c87d
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/inject/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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
+ */
+
+/**
+ * Jersey innate injection related packages. The innate packages will not be opened by JPMS outside of Jersey.
+ * Not for public use.
+ */
+package org.glassfish.jersey.innate.inject;
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/ExternalRegistrables.java b/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/ExternalRegistrables.java
new file mode 100644
index 0000000000..f5c5f8f97a
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/ExternalRegistrables.java
@@ -0,0 +1,53 @@
+/*
+ * 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.innate.inject.spi;
+
+import jakarta.ws.rs.RuntimeType;
+import java.util.List;
+
+/**
+ * Provide a list of classes or interfaces the InjectionManager can support.
+ */
+public interface ExternalRegistrables {
+
+ /**
+ * Contract - RuntimeType pair. For a contract applicable on both client and server, use {@code null} as RuntimeType.
+ */
+ public static final class ClassRuntimeTypePair {
+ private final Class> contract;
+ private final RuntimeType runtimeType;
+
+ public ClassRuntimeTypePair(Class> contract, RuntimeType runtimeType) {
+ this.contract = contract;
+ this.runtimeType = runtimeType;
+ }
+
+ public Class> getContract() {
+ return contract;
+ }
+
+ public RuntimeType getRuntimeType() {
+ return runtimeType;
+ }
+ }
+
+ /**
+ * List of contracts that can be registered into Jersey to be passed by the external injection framework.
+ * @return list of contracts allowed to be registered in Jersey.
+ */
+ List registrableContracts();
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/package-info.java b/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/package-info.java
new file mode 100644
index 0000000000..5ad36ddc40
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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
+ */
+
+/**
+ * Jersey innate injection related packages. The innate packages will not be opened by JPMS outside of Jersey.
+ * Not for public use.
+ */
+package org.glassfish.jersey.innate.inject.spi;
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java b/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
index b0648e7d05..a1835bb74a 100644
--- a/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 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
@@ -16,5 +16,6 @@
/**
* Jersey innate packages. The innate packages will not be opened by JPMS outside of Jersey.
+ * Not for public use.
*/
package org.glassfish.jersey.innate;
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java
index 4bbab3e6ab..f5c467ab25 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.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
@@ -43,7 +43,9 @@
import jakarta.annotation.Priority;
import org.glassfish.jersey.JerseyPriorities;
+import org.glassfish.jersey.innate.inject.spi.ExternalRegistrables;
import org.glassfish.jersey.internal.LocalizationMessages;
+import org.glassfish.jersey.internal.ServiceFinder;
import org.glassfish.jersey.model.ContractProvider;
import org.glassfish.jersey.model.internal.RankedComparator;
import org.glassfish.jersey.model.internal.RankedProvider;
@@ -103,6 +105,14 @@ private static Map, ProviderRuntime> getExternalProviderInterfaces() {
interfaces.putAll(JAX_RS_PROVIDER_INTERFACE_WHITELIST);
interfaces.put(jakarta.ws.rs.core.Feature.class, ProviderRuntime.BOTH);
interfaces.put(Binder.class, ProviderRuntime.BOTH);
+
+ try {
+ ServiceFinder registerables = ServiceFinder.find(ExternalRegistrables.class, true);
+ registerables.forEach(regs -> regs.registrableContracts()
+ .forEach(pair -> interfaces.put(pair.getContract(), ProviderRuntime.fromRuntimeType(pair.getRuntimeType()))));
+ } catch (Throwable t) {
+ LOGGER.warning(LocalizationMessages.ERROR_EXTERNAL_REGISTERABLES_IGNORED(t.getMessage()));
+ }
return interfaces;
}
@@ -119,6 +129,10 @@ private ProviderRuntime(final RuntimeType runtime) {
public RuntimeType getRuntime() {
return runtime;
}
+
+ private static ProviderRuntime fromRuntimeType(RuntimeType type) {
+ return type == null ? BOTH : (type == RuntimeType.SERVER ? SERVER : CLIENT);
+ }
}
/**
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
index 29be49fabb..e9f1bc2037 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.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
@@ -23,6 +23,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.glassfish.jersey.innate.VirtualThreadSupport;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.guava.Preconditions;
@@ -58,6 +59,8 @@
public final class CommittingOutputStream extends OutputStream {
private static final Logger LOGGER = Logger.getLogger(CommittingOutputStream.class.getName());
+ private final boolean isVirtualThread = VirtualThreadSupport.isVirtualThread();
+
/**
* Null stream provider.
*/
@@ -275,7 +278,13 @@ private void flushBuffer(boolean endOfStream) throws IOException {
commitStream(currentSize);
if (buffer != null) {
- buffer.writeTo(adaptedOutput);
+ if (isVirtualThread && adaptedOutput != null) {
+ adaptedOutput.write(buffer.toByteArray());
+ } else {
+ // Virtual thread in JDK 21 are blocked by synchronized writeTo
+ // but about 10% faster than ^ without virtual threads.
+ buffer.writeTo(adaptedOutput);
+ }
}
}
}
diff --git a/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java b/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
new file mode 100644
index 0000000000..90cafba6a2
--- /dev/null
+++ b/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
@@ -0,0 +1,38 @@
+/*
+ * 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.innate;
+
+/**
+ * Utility class for the virtual thread support.
+ */
+public final class VirtualThreadSupport {
+
+ /**
+ * Do not instantiate.
+ */
+ private VirtualThreadSupport() {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Informs whether the given {@link Thread} is virtual.
+ * @return true when the current thread is virtual.
+ */
+ public static boolean isVirtualThread() {
+ return false;
+ }
+}
diff --git a/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java b/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
new file mode 100644
index 0000000000..74f58ba9da
--- /dev/null
+++ b/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
@@ -0,0 +1,38 @@
+/*
+ * 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.innate;
+
+/**
+ * Utility class for the virtual thread support.
+ */
+public final class VirtualThreadSupport {
+
+ /**
+ * Do not instantiate.
+ */
+ private VirtualThreadSupport() {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Informs whether the given {@link Thread} is virtual.
+ * @return true when the current thread is virtual.
+ */
+ public static boolean isVirtualThread() {
+ return Thread.currentThread().isVirtual();
+ }
+}
diff --git a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties
index b60bf25045..e9cd448388 100644
--- a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties
+++ b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2012, 2021 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
@@ -39,6 +39,7 @@ error.entity.stream.closed=Entity input stream has already been closed.
error.entity.provider.basictypes.character.morechars=A single character expected in the entity input stream.
error.entity.provider.basictypes.constructor=Error converting entity to {0} type by single String constructor.
error.entity.provider.basictypes.unkwnown=Unsupported basic type {0}.
+error.external.registerables.ignored=Error reading external registrable contracts: {0}.
error.finding.exception.mapper.type=Could not find exception type for given ExceptionMapper class: {0}.
error.interceptor.reader.proceed=Last reader interceptor in the chain called the method proceed.
error.interceptor.writer.proceed=Last writer interceptor in the chain called the method proceed.
diff --git a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
index ed4d5dc4b8..a138afb246 100644
--- a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -32,8 +32,8 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
index b671e68665..a0fedf4c8e 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
@@ -53,7 +53,7 @@
public class ChunkedOutput extends GenericType implements Closeable {
private static final byte[] ZERO_LENGTH_DELIMITER = new byte[0];
- private final BlockingDeque queue = new LinkedBlockingDeque<>();
+ private final BlockingDeque queue;
private final byte[] chunkDelimiter;
private final AtomicBoolean resumed = new AtomicBoolean(false);
private final Lock lock = new ReentrantLock();
@@ -72,12 +72,59 @@ public class ChunkedOutput extends GenericType implements Closeable {
private volatile ContainerResponse responseContext;
private volatile ConnectionCallback connectionCallback;
-
/**
* Create new {@code ChunkedOutput}.
*/
protected ChunkedOutput() {
this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
+ queue = new LinkedBlockingDeque<>();
+ }
+
+ /**
+ * Create new {@code ChunkedOutput} based on builder.
+ *
+ * @param builder the builder to use
+ */
+ protected ChunkedOutput(Builder builder) {
+ super();
+ if (builder.queueCapacity > 0) {
+ queue = new LinkedBlockingDeque<>(builder.queueCapacity);
+ } else {
+ queue = new LinkedBlockingDeque<>();
+ }
+ if (builder.chunkDelimiter != null) {
+ this.chunkDelimiter = new byte[builder.chunkDelimiter.length];
+ System.arraycopy(builder.chunkDelimiter, 0, this.chunkDelimiter, 0, builder.chunkDelimiter.length);
+ } else {
+ this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
+ }
+ if (builder.asyncContextProvider != null) {
+ this.asyncContext = builder.asyncContextProvider.get();
+ }
+ }
+
+ /**
+ * Create new {@code ChunkedOutput} based on builder.
+ *
+ * @param builder the builder to use
+ */
+ private ChunkedOutput(TypedBuilder builder) {
+ super(builder.chunkType);
+
+ if (builder.queueCapacity > 0) {
+ queue = new LinkedBlockingDeque<>(builder.queueCapacity);
+ } else {
+ queue = new LinkedBlockingDeque<>();
+ }
+ if (builder.chunkDelimiter != null) {
+ this.chunkDelimiter = new byte[builder.chunkDelimiter.length];
+ System.arraycopy(builder.chunkDelimiter, 0, this.chunkDelimiter, 0, builder.chunkDelimiter.length);
+ } else {
+ this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
+ }
+ if (builder.asyncContextProvider != null) {
+ this.asyncContext = builder.asyncContextProvider.get();
+ }
}
/**
@@ -88,6 +135,7 @@ protected ChunkedOutput() {
public ChunkedOutput(final Type chunkType) {
super(chunkType);
this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
+ queue = new LinkedBlockingDeque<>();
}
/**
@@ -103,6 +151,7 @@ protected ChunkedOutput(final byte[] chunkDelimiter) {
} else {
this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
}
+ queue = new LinkedBlockingDeque<>();
}
/**
@@ -120,6 +169,7 @@ protected ChunkedOutput(final byte[] chunkDelimiter, Provider asyn
}
this.asyncContext = asyncContextProvider == null ? null : asyncContextProvider.get();
+ queue = new LinkedBlockingDeque<>();
}
/**
@@ -137,6 +187,7 @@ public ChunkedOutput(final Type chunkType, final byte[] chunkDelimiter) {
} else {
this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
}
+ queue = new LinkedBlockingDeque<>();
}
/**
@@ -151,6 +202,7 @@ protected ChunkedOutput(final String chunkDelimiter) {
} else {
this.chunkDelimiter = chunkDelimiter.getBytes();
}
+ queue = new LinkedBlockingDeque<>();
}
/**
@@ -167,6 +219,26 @@ public ChunkedOutput(final Type chunkType, final String chunkDelimiter) {
} else {
this.chunkDelimiter = chunkDelimiter.getBytes();
}
+ queue = new LinkedBlockingDeque<>();
+ }
+
+ /**
+ * Returns a builder to create a ChunkedOutput with custom configuration.
+ *
+ * @return builder
+ */
+ public static Builder builder() {
+ return new Builder<>();
+ }
+
+ /**
+ * Returns a builder to create a ChunkedOutput with custom configuration.
+ *
+ * @param chunkType chunk type. Must not be {code null}.
+ * @return builder
+ */
+ public static TypedBuilder builder(Type chunkType) {
+ return new TypedBuilder<>(chunkType);
}
/**
@@ -181,7 +253,12 @@ public void write(final T chunk) throws IOException {
}
if (chunk != null) {
- queue.add(chunk);
+ try {
+ queue.put(chunk);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException(e);
+ }
}
flushQueue();
@@ -351,7 +428,6 @@ public void close() throws IOException {
/**
* Get state information.
- *
* Please note that {@code ChunkedOutput} can be closed by the client side - client can close connection
* from its side.
*
@@ -363,10 +439,12 @@ public boolean isClosed() {
/**
* Executed only in case of close being triggered by client.
+ *
* @param e Exception causing the close
*/
- protected void onClose(Exception e){
-
+ protected void onClose(Exception e) {
+ // drain queue when an exception occurs to prevent deadlocks
+ queue.clear();
}
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@@ -409,4 +487,78 @@ void setContext(final RequestScope requestScope,
this.connectionCallback = connectionCallbackRunner;
flushQueue();
}
+
+ /**
+ * Builder that allows to create a new ChunkedOutput based on the given configuration options.
+ *
+ * @param
+ */
+ public static class Builder {
+ byte[] chunkDelimiter;
+ int queueCapacity = -1;
+ Provider asyncContextProvider;
+
+ private Builder() {
+ // hide constructor
+ }
+
+ /**
+ * Set the chunk delimiter, in bytes.
+ * @param chunkDelimiter the chunk delimiter in bytes
+ * @return builder
+ */
+ public Builder chunkDelimiter(byte[] chunkDelimiter) {
+ this.chunkDelimiter = chunkDelimiter;
+ return this;
+ }
+
+ /**
+ * Set the queue capacity. If greater than 0, the queue is bounded and will block when full.
+ * @param queueCapacity the queue capacity
+ * @return builder
+ */
+ public Builder queueCapacity(int queueCapacity) {
+ this.queueCapacity = queueCapacity;
+ return this;
+ }
+
+ /**
+ * Set the async context provider.
+ * @param asyncContextProvider the async context provider
+ * @return builder
+ */
+ public Builder asyncContextProvider(Provider asyncContextProvider) {
+ this.asyncContextProvider = asyncContextProvider;
+ return this;
+ }
+
+ /**
+ * Build the ChunkedOutput based on the given configuration.
+ * @return the ChunkedOutput
+ */
+ public ChunkedOutput build() {
+ return new ChunkedOutput<>(this);
+ }
+ }
+
+ /**
+ * Builder that allows to create a new ChunkedOutput based on the given configuration options.
+ *
+ * @param
+ */
+ public static class TypedBuilder extends Builder {
+ private Type chunkType;
+
+ private TypedBuilder(Type chunkType) {
+ this.chunkType = chunkType;
+ }
+
+ /**
+ * Build the ChunkedOutput based on the given configuration.
+ * @return the ChunkedOutput
+ */
+ public ChunkedOutput build() {
+ return new ChunkedOutput<>(this);
+ }
+ }
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java
index 40faecc8d6..d936c51711 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -34,6 +34,9 @@
import jakarta.ws.rs.core.UriBuilder;
import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap;
+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.TracingLogger;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.internal.ServerTraceEvent;
@@ -66,7 +69,7 @@ public class UriRoutingContext implements RoutingContext {
private final LinkedList matchedLocators = new LinkedList<>();
private final LinkedList locatorSubResources = new LinkedList<>();
- private final TracingLogger tracingLogger;
+ private final LazyValue tracingLogger;
private volatile ResourceMethod matchedResourceMethod = null;
private volatile Throwable mappedThrowable = null;
@@ -86,7 +89,8 @@ public class UriRoutingContext implements RoutingContext {
*/
public UriRoutingContext(final ContainerRequest requestContext) {
this.requestContext = requestContext;
- this.tracingLogger = TracingLogger.getInstance(requestContext);
+ // Tracing Logger is initialized after UriContext before pushMatchedResource
+ this.tracingLogger = Values.lazy((Value) () -> TracingLogger.getInstance(requestContext));
}
// RoutingContext
@@ -97,7 +101,7 @@ public void pushMatchResult(final MatchResult matchResult) {
@Override
public void pushMatchedResource(final Object resource) {
- tracingLogger.log(ServerTraceEvent.MATCH_RESOURCE, resource);
+ tracingLogger.get().log(ServerTraceEvent.MATCH_RESOURCE, resource);
matchedResources.push(resource);
}
@@ -108,7 +112,7 @@ public Object peekMatchedResource() {
@Override
public void pushMatchedLocator(final ResourceMethod resourceLocator) {
- tracingLogger.log(ServerTraceEvent.MATCH_LOCATOR, resourceLocator.getInvocable().getHandlingMethod());
+ tracingLogger.get().log(ServerTraceEvent.MATCH_LOCATOR, resourceLocator.getInvocable().getHandlingMethod());
matchedLocators.push(resourceLocator);
}
@@ -189,14 +193,14 @@ public Endpoint getEndpoint() {
@Override
public void setMatchedResourceMethod(final ResourceMethod resourceMethod) {
- tracingLogger.log(ServerTraceEvent.MATCH_RESOURCE_METHOD, resourceMethod.getInvocable().getHandlingMethod());
+ tracingLogger.get().log(ServerTraceEvent.MATCH_RESOURCE_METHOD, resourceMethod.getInvocable().getHandlingMethod());
this.matchedResourceMethod = resourceMethod;
}
@Override
public void pushMatchedRuntimeResource(final RuntimeResource runtimeResource) {
- if (tracingLogger.isLogEnabled(ServerTraceEvent.MATCH_RUNTIME_RESOURCE)) {
- tracingLogger.log(ServerTraceEvent.MATCH_RUNTIME_RESOURCE,
+ if (tracingLogger.get().isLogEnabled(ServerTraceEvent.MATCH_RUNTIME_RESOURCE)) {
+ tracingLogger.get().log(ServerTraceEvent.MATCH_RUNTIME_RESOURCE,
runtimeResource.getResources().get(0).getPath(),
runtimeResource.getResources().get(0).getPathPattern().getRegex(),
matchResults.peek().group()
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index aa4aee9d24..9a1b29dab1 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -1,7 +1,7 @@
-
-
- 4.0.0
-
- org.eclipse.ee4j
- project
- 1.0.9
-
-
-
- org.glassfish.jersey
- release-helper-util
- 1.0.1-SNAPSHOT
- pom
-
- Jersey Release Helper
-
- Generated POM for Jersey Release Helper utils
- https://projects.eclipse.org/projects/ee4j.jersey
-
-
- scm:git:git://github.com/eclipse-ee4j/jersey
- scm:git:git://github.com/eclipse-ee4j/jersey
- https://github.com/eclipse-ee4j/jersey
-
-
-
-EOT
-
-MVN_HOME="/opt/tools/apache-maven/latest/"
-PATH="${MVN_HOME}/bin:${JAVA_HOME}:/bin:${PATH}"
-
-export STAGING_PARAMS='-DnexusUrl=https://jakarta.oss.sonatype.org/ -DserverId=ossrh'
-export STAGING_PLUGIN='org.sonatype.plugins:nexus-staging-maven-plugin:1.6.7'
-
-
-mvn -B ${STAGING_PARAMS} ${STAGING_PLUGIN}:rc-list | grep orgglassfishjersey > closed_stagings.txt || true
-
-
-#
-#
-# Archive the artifacts:
-# closed_stagings.txt
-#
diff --git a/etc/scripts/release/Release-After/populate-documentation.sh b/etc/scripts/release/Release-After/populate-documentation.sh
deleted file mode 100644
index 025e512af5..0000000000
--- a/etc/scripts/release/Release-After/populate-documentation.sh
+++ /dev/null
@@ -1,166 +0,0 @@
-#!/bin/bash -lex
-#
-# 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
-#
-
-#
-# script for api docs publishing. Publishes api docs from the bundle in the Maven Central to the project's site.
-#
-# Input Parameters:
-# RELEASE_VERSION - type: String
-# - example: 3.1.6
-# - description: version for which project info is being published
-# DRY_RUN - type: Boolean
-# - description: If checked nothing is being published/generated
-# WEBSITE_URL - type: String
-# - value: git@github.com:eclipse-ee4j/jersey.github.io.git
-# - description: GitHub url for the project info/apidocs repository.
-# Mandatory and changes only in exceptional cases
-# BRANCH_SPECIFIER - type: String
-# - example: 2.x
-# - description: Branch for which the api docs are being published. Used only in Git commit message
-# UPDATE_LATEST - type: Boolean
-# - description: If checked updates the latest api docs (distinguishes for EE8/EE9/EE10) along
-# with publication into the RELEASE_VERSION folder.
-# Configuration:
-#
-# JDK:
-# oracle-jdk8-latest
-# Git:
-# ----none----
-#
-# SSH agent:
-# GitHub bot SSH
-#
-#
-#
-
-
-#
-# The first shell execution
-#
-
-
-TOOLS_PREFIX=/opt/tools
-#JAVA_PREFIX=/opt/tools/java/oracle
-MVN_HOME=/opt/tools/apache-maven/latest
-#JAVA_HOME=/opt/tools/java/oracle/jdk-8/latest
-PATH=/opt/tools/apache-maven/latest/bin:${JAVA_HOME}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
-
-export CENTRAL_URL='https://repo1.maven.org/maven2'
-export STAGING_URL='https://jakarta.oss.sonatype.org/content/repositories/staging'
-
-# !!! if build docbook it's essentially important to turn the pre-release profile ON !!!
-#mvn clean install -Pstaging,pre-release -DskipTests -V -q -e -U -B -f docs/pom.xml -Djersey.version=$RELEASE_VERSION
-# however we download already published docbook from staging/central:
-mkdir -p $WORKSPACE/docs/target/docbook/index
-cd $WORKSPACE/docs/target/docbook/index
-wget -nv ${STAGING_URL}/org/glassfish/jersey/jersey-documentation/${RELEASE_VERSION}/jersey-documentation-${RELEASE_VERSION}-docbook.zip
-unzip -o jersey-documentation-${RELEASE_VERSION}-docbook.zip
-rm jersey-documentation-${RELEASE_VERSION}-docbook.zip
-
-mkdir -p $WORKSPACE/target/site/apidocs
-cd $WORKSPACE/target/site/apidocs
-wget -nv ${STAGING_URL}/org/glassfish/jersey/bundles/apidocs/${RELEASE_VERSION}/apidocs-${RELEASE_VERSION}-javadoc.jar -O apidocs-javadoc.jar
-jar -xf apidocs-javadoc.jar
-rm apidocs-javadoc.jar
-rm -rf META-INF
-
-
-#
-# The second shell execution
-#
-
-
-#!/bin/bash -lex
-
-TOOLS_PREFIX=/opt/tools
-#JAVA_PREFIX=/opt/tools/java/oracle
-MVN_HOME=/opt/tools/apache-maven/latest
-#JAVA_HOME=/opt/tools/java/oracle/jdk-8/latest
-PATH=/opt/tools/apache-maven/latest/bin:${JAVA_HOME}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
-
-DIRS="$RELEASE_VERSION"
-if $UPDATE_LATEST;
-then
- if [[ $RELEASE_VERSION == 2* ]] ;
- then
- export DIRS="latest $DIRS"
- elif [[ $RELEASE_VERSION == 3.1* ]] ;
- then
- export DIRS="latest31x $DIRS"
- else
- export DIRS="latest30x $DIRS"
- fi
- echo ${DIRS} for ${RELEASE_VERSION} release
-fi
-
-#export PATH=/opt/csw/bin:$PATH
-WEB_DIR=$WORKSPACE/target/jersey-web
-
-
-function copyDocs {
- APIDOCS_DIR=$WEB_DIR/apidocs/$1
- DOCS_DIR=$WEB_DIR/documentation/$1
-
- #
- # API docs
- #
- if test ! -e $APIDOCS_DIR ; then
- mkdir -p $APIDOCS_DIR
- fi
- cd $APIDOCS_DIR
-
- rm -rf jersey || true
- cp -R $WORKSPACE/target/site/apidocs ./jersey
-
- #
- # user guide
- #
- rm -rf $DOCS_DIR || true
- mkdir -p $DOCS_DIR
- cp -r $WORKSPACE/docs/target/docbook/index/* $DOCS_DIR
- rm $DOCS_DIR/*.fo || true
-}
-
-if test -e $WEB_DIR ; then
- rm -rf $WEB_DIR
-fi
-
-# would couse shallow reject: git clone --depth 1 $WEBSITE_URL $WEB_DIR
-git clone $WEBSITE_URL $WEB_DIR
-
-cd $WEB_DIR
-
-for dir in $DIRS; do
- copyDocs $dir
-done
-
-cd $WEB_DIR
-
-git config --local user.email "jersey-bot@eclipse.org"
-git config --local user.name "jersey-bot"
-git add -A .
-git diff --cached --exit-code || git commit -m "[jenkins] automatic javadoc and documentation update [$RELEASE_VERSION @ $BRANCH_SPECIFIER]"
-
-if [ "$DRY_RUN" = "false" ]; then
- echo "Pushing Web sources to $WEBSITE_URL"
- git push origin master
-else
- echo "Dry run .. not pushing to the master"
- git push origin master --dry-run
-fi
-
-
diff --git a/etc/scripts/release/Release-After/publish-release-notes.sh b/etc/scripts/release/Release-After/publish-release-notes.sh
deleted file mode 100644
index d547134dba..0000000000
--- a/etc/scripts/release/Release-After/publish-release-notes.sh
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/bin/bash
-#
-# 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
-#
-
-#
-# script for publishing listed stagings from the staging repo into the Maven Central.
-#
-# Input Parameters:
-# VERSION - type: String
-# - example: 3.1.6
-# - description: version for which release notes are being published
-# DRY_RUN - type: Boolean
-# - description: If checked nothing is being published/generated
-# PUBLISH_TO_GITHUB - type: Boolean
-# - description: If checked generated notes are propagated to GitHub
-# Configuration:
-#
-# JDK:
-# oracle-jdk8-latest
-# Git:
-# https://github.com/eclipse-ee4j/jersey
-#
-# Bindings:
-# Username and password (separated):
-# Username Variable:
-# BOT_NAME
-# Password Variable:
-# BOT_PASSWORD
-# Credentials:
-# GitHub bot (username/token)
-#
-#
-#
-
-
-cat <> pom.xml
-
-
-
-
- 4.0.0
-
- org.eclipse.ee4j
- project
- 1.0.9
-
-
-
- org.glassfish.jersey
- release-helper-util
- 1.0.1-SNAPSHOT
- pom
-
- Jersey Release Helper
-
- Generated POM for Jersey Release Helper utils
- https://projects.eclipse.org/projects/ee4j.jersey
-
-
- scm:git:git://github.com/eclipse-ee4j/jersey
- scm:git:git://github.com/eclipse-ee4j/jersey
- https://github.com/eclipse-ee4j/jersey
-
-
-
-EOT
-
-# Execution environment
-readonly MVN_HOME="/opt/tools/apache-maven/latest/"
-PATH="${MVN_HOME}/bin:${JAVA_HOME}:/bin:${PATH}"
-readonly RELEASE_DATE=`date -I`
-
-mkdir -p target
-
-mvn -B -V org.glassfish.jersey.tools.plugins:jersey-release-notes-maven-plugin:1.0.1:release-notes -DskipTests \
--DreleaseVersion=${VERSION} -DgithubLogin=${BOT_NAME} -DgithubPassword=${BOT_PASSWORD} \
--DtemplateFilePath=release-note.html -DreleaseDate=${RELEASE_DATE} \
--DdryRun=${DRY_RUN} -DpublishToGithub=${PUBLISH_TO_GITHUB}
-
-cp -a `pwd`/target/release-notes ${WORKSPACE}
\ No newline at end of file
diff --git a/etc/scripts/release/Release-After/release-note.html b/etc/scripts/release/Release-After/release-note.html
deleted file mode 100644
index f0e5996340..0000000000
--- a/etc/scripts/release/Release-After/release-note.html
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
-
-
- Release Notes - Jersey @LATEST_VERSION@
-
-
-
-
-
-
-
-
diff --git a/etc/scripts/release/Release-After/release-project-info.sh b/etc/scripts/release/Release-After/release-project-info.sh
deleted file mode 100644
index ebfe9b125b..0000000000
--- a/etc/scripts/release/Release-After/release-project-info.sh
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/bin/bash -lex
-#
-# 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
-#
-
-#
-# script for project info publishing. MUST BE RUN ONLY AFTER THE APIDOCS PUBLISHING.
-#
-# Input Parameters:
-# RELEASE_VERSION - type: String
-# - example: 3.1.6
-# - description: version for which project info is being published
-# DRY_RUN - type: Boolean
-# - description: If checked nothing is being published/generated
-# WEBSITE_URL - type: String
-# - value: git@github.com:eclipse-ee4j/jersey.github.io.git
-# - description: GitHub url for the project info/apidocs repository.
-# Mandatory and changes only in exceptional cases
-# Configuration:
-#
-# JDK:
-# openjdk-jdk11-latest
-# Git:
-# https://github.com/eclipse-ee4j/jersey
-#
-# Branches to build:
-# tags/${RELEASE_VERSION}
-#
-# SSH agent:
-# GitHub bot SSH
-#
-#
-#
-
-
-#
-# The first shell execution
-#
-
-MVN_HOME=/opt/tools/apache-maven/latest
-PATH=${PATH}:${MVN_HOME}/bin:${JAVA_HOME}/bin
-
-mvn clean install -B -V -q -PtestsSkip,checkstyleSkip -Dtests.excluded -DskipTests '-Dmaven.test.skip=true'
-
-mvn -B -V -q -Pproject-info,checkstyleSkip,testsSkip,findbugsSkip,staging site site:stage \
- -DgenerateProjectInfo=true -Dtests.excluded -Ddependency.locations.enabled=false \
- -Dmaven.jxr.skip=true -Dmaven.javadoc.skip=true -Dcheckstyle.skip=true -DskipTests \
- -Dfindbugs.skip=true
-
-#
-# The second shell execution
-#
-
-#!/bin/bash -lex
-
-export PATH=/opt/csw/bin:$PATH
-WEB_DIR=$WORKSPACE/target/jersey-web
-PROJECT_INFO_DIR=$WEB_DIR/project-info/$RELEASE_VERSION
-
-if test -e $WEB_DIR ; then
- rm -rf $WEB_DIR
-fi
-
-# would couse shallow reject: git clone --depth 1 $WEBSITE_SOURCE_URL $WEB_DIR
-git clone $WEBSITE_URL $WEB_DIR
-cd $WEB_DIR
-
-if test ! -e $PROJECT_INFO_DIR ; then
- mkdir -p $PROJECT_INFO_DIR
-fi
-
-cd $PROJECT_INFO_DIR
-
-rm -rf jersey || true
-cp -R $WORKSPACE/target/staging ./jersey
-
-cd $WEB_DIR
-
-git config --local user.email "jersey-bot@eclipse.org"
-git config --local user.name "jersey-bot"
-git add -A .
-#git diff --cached --exit-code ||
-git commit -m "[jenkins] automatic project-info update [$RELEASE_VERSION]"
-
-if [ "$DRY_RUN" = "false" ]; then
- echo Pushing Web sources to $WEBSITE_URL
- git push origin master
-else
- echo "Dry run .. not pushing to the master"
- git push origin master --dry-run
-fi
\ No newline at end of file
diff --git a/etc/scripts/release/Release-After/release-staging-to-central.sh b/etc/scripts/release/Release-After/release-staging-to-central.sh
deleted file mode 100644
index b470730610..0000000000
--- a/etc/scripts/release/Release-After/release-staging-to-central.sh
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/bin/bash
-#
-# 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
-#
-
-#
-# script for publishing listed stagings from the staging repo into the Maven Central.
-#
-# Input Parameters:
-# STAGING_REPO_ID - type: String
-# - example: orgglassfishjersey-1226,orgglassfishjersey-1227,orgglassfishjersey-1228,orgglassfishjersey-1229,orgglassfishjersey-1230
-# - description: list all staggings (comma separated) to be published to the Maven Central
-# STAGING_DESC - type: String
-# - example:org.glassfish.jersey:2.42
-# - description: description of what is published. Usually :
-# Configuration:
-#
-# JDK:
-# (System)
-# Git:
-# ----none----
-#
-#
-
-cat <> pom.xml
-
-
-
-
- 4.0.0
-
- org.eclipse.ee4j
- project
- 1.0.9
-
-
-
- org.glassfish.jersey
- release-helper-util
- 1.0.1-SNAPSHOT
- pom
-
- Jersey Release Helper
-
- Generated POM for Jersey Release Helper utils
- https://projects.eclipse.org/projects/ee4j.jersey
-
-
- scm:git:git://github.com/eclipse-ee4j/jersey
- scm:git:git://github.com/eclipse-ee4j/jersey
- https://github.com/eclipse-ee4j/jersey
-
-
-
-EOT
-
-MVN_HOME="/opt/tools/apache-maven/latest/"
-PATH="${MVN_HOME}/bin:${JAVA_HOME}:/bin:${PATH}"
-
-export STAGING_PARAMS='-DnexusUrl=https://jakarta.oss.sonatype.org/ -DserverId=ossrh'
-export STAGING_PLUGIN='org.sonatype.plugins:nexus-staging-maven-plugin:1.6.7'
-
-
-mvn ${STAGING_PARAMS} -B -C -V ${STAGING_PLUGIN}:rc-release -DstagingRepositoryId="${STAGING_REPO_ID}" -DstagingDescription="${STAGING_DESC}"
-
diff --git a/etc/scripts/validation/dependency-convergence.sh b/etc/scripts/validation/dependency-convergence.sh
index 92ffeea916..9bcf4fed54 100755
--- a/etc/scripts/validation/dependency-convergence.sh
+++ b/etc/scripts/validation/dependency-convergence.sh
@@ -40,5 +40,11 @@ sed -e "/${VALIDATION_DEPENDENCIES_MATCH}/ {" -e "r ${CURRENT_LOCATION}/${TEMP_F
#run validation
mvn ${MVN_ARGS} ${MVN_CLI} -f ${CURRENT_LOCATION}/${VALIDATION_POM} -Djersey.version=${JERSEY_VERSION}
+#save exit status
+export MAVEN_BUILD_RESULT=$?
+
#clean the mess
-rm ${CURRENT_LOCATION}/${TEMP_FILE}
\ No newline at end of file
+rm ${CURRENT_LOCATION}/${TEMP_FILE}
+
+#exit with saved exit stateus
+exit $MAVEN_BUILD_RESULT
diff --git a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyTags.java b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyTags.java
index d723c7c1b9..29494f413c 100644
--- a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyTags.java
+++ b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyTags.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 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
@@ -43,6 +43,8 @@ public final class JerseyTags {
private static final Tag URI_ROOT = Tag.of("uri", "root");
+ private static final Tag URI_UNKNOWN = Tag.of("uri", "UNKNOWN");
+
private static final Tag EXCEPTION_NONE = Tag.of("exception", "None");
private static final Tag STATUS_SERVER_ERROR = Tag.of("status", "500");
@@ -95,7 +97,10 @@ public static Tag uri(RequestEvent event) {
}
}
String matchingPattern = getMatchingPattern(event);
- if (matchingPattern.equals("/")) {
+ if (matchingPattern == null) {
+ return URI_UNKNOWN;
+ }
+ else if (matchingPattern.equals("/")) {
return URI_ROOT;
}
return Tag.of("uri", matchingPattern);
@@ -108,7 +113,9 @@ static boolean isRedirection(int status) {
static String getMatchingPattern(RequestEvent event) {
ExtendedUriInfo uriInfo = event.getUriInfo();
List templates = uriInfo.getMatchedTemplates();
-
+ if (templates.isEmpty()) {
+ return null;
+ }
StringBuilder sb = new StringBuilder();
sb.append(uriInfo.getBaseUri().getPath());
for (int i = templates.size() - 1; i >= 0; i--) {
diff --git a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListener.java b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListener.java
index cca1d138ca..0b0544224f 100644
--- a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListener.java
+++ b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 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
@@ -79,7 +79,7 @@ public void onEvent(RequestEvent event) {
switch (event.getType()) {
case ON_EXCEPTION:
- if (!isNotFoundException(event)) {
+ if (!isClientError(event)) {
break;
}
time(event, containerRequest);
@@ -122,13 +122,14 @@ private void time(RequestEvent event, ContainerRequest containerRequest) {
}
}
- private boolean isNotFoundException(RequestEvent event) {
+ private boolean isClientError(RequestEvent event) {
Throwable t = event.getException();
if (t == null) {
return false;
}
- String className = t.getClass().getCanonicalName();
- return className.equals("jakarta.ws.rs.NotFoundException") || className.equals("jakarta.ws.rs.NotFoundException");
+ String className = t.getClass().getSuperclass().getCanonicalName();
+ return className.equals("jakarta.ws.rs.ClientErrorException")
+ || className.equals("javax.ws.rs.ClientErrorException");
}
private Set shortTimers(Set timed, RequestEvent event) {
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/DefaultJerseyTagsProviderTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/DefaultJerseyTagsProviderTest.java
index d0445f97da..93a9e4d654 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/DefaultJerseyTagsProviderTest.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/DefaultJerseyTagsProviderTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 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
@@ -51,7 +51,7 @@ class DefaultJerseyTagsProviderTest {
@Test
void testRootPath() {
- assertThat(tagsProvider.httpRequestTags(event(200, null, "/", (String[]) null)))
+ assertThat(tagsProvider.httpRequestTags(event(200, null, "/", "/")))
.containsExactlyInAnyOrder(tagsFrom("root", 200, null, "SUCCESS"));
}
@@ -86,21 +86,21 @@ void redirectsAreShunted() {
@Test
@SuppressWarnings("serial")
void exceptionsAreMappedCorrectly() {
- assertThat(tagsProvider.httpRequestTags(event(500, new IllegalArgumentException(), "/app", (String[]) null)))
+ assertThat(tagsProvider.httpRequestTags(event(500, new IllegalArgumentException(), "/app", "/")))
.containsExactlyInAnyOrder(tagsFrom("/app", 500, "IllegalArgumentException", "SERVER_ERROR"));
assertThat(tagsProvider.httpRequestTags(
- event(500, new IllegalArgumentException(new NullPointerException()), "/app", (String[]) null)))
+ event(500, new IllegalArgumentException(new NullPointerException()), "/app", "/")))
.containsExactlyInAnyOrder(tagsFrom("/app", 500, "NullPointerException", "SERVER_ERROR"));
- assertThat(tagsProvider.httpRequestTags(event(406, new NotAcceptableException(), "/app", (String[]) null)))
+ assertThat(tagsProvider.httpRequestTags(event(406, new NotAcceptableException(), "/app", "/")))
.containsExactlyInAnyOrder(tagsFrom("/app", 406, "NotAcceptableException", "CLIENT_ERROR"));
assertThat(tagsProvider.httpRequestTags(event(500, new Exception("anonymous") {
- }, "/app", (String[]) null))).containsExactlyInAnyOrder(tagsFrom("/app", 500,
+ }, "/app", "/"))).containsExactlyInAnyOrder(tagsFrom("/app", 500,
"org.glassfish.jersey.micrometer.server.DefaultJerseyTagsProviderTest$1", "SERVER_ERROR"));
}
@Test
void longRequestTags() {
- assertThat(tagsProvider.httpLongRequestTags(event(0, null, "/app", (String[]) null)))
+ assertThat(tagsProvider.httpLongRequestTags(event(0, null, "/app", "/")))
.containsExactlyInAnyOrder(Tag.of("method", "GET"), Tag.of("uri", "/app"));
}
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListenerTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListenerTest.java
index 96324b2a8a..ce2bc4a1b5 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListenerTest.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListenerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 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
@@ -20,6 +20,7 @@
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
@@ -149,6 +150,11 @@ void exceptionsAreMappedCorrectly() {
}
catch (Exception ignored) {
}
+ try {
+ target("produces-text-plain").request(MediaType.APPLICATION_JSON).get();
+ }
+ catch (Exception ignored) {
+ }
assertThat(registry.get(METRIC_NAME)
.tags(tagsFrom("/throws-exception", "500", "SERVER_ERROR", "IllegalArgumentException"))
@@ -164,6 +170,11 @@ void exceptionsAreMappedCorrectly() {
.tags(tagsFrom("/throws-mappable-exception", "410", "CLIENT_ERROR", "ResourceGoneException"))
.timer()
.count()).isEqualTo(1);
+
+ assertThat(registry.get(METRIC_NAME)
+ .tags(tagsFrom("UNKNOWN", "406", "CLIENT_ERROR", "NotAcceptableException"))
+ .timer()
+ .count()).isEqualTo(1);
}
private static Iterable tagsFrom(String uri, String status, String outcome, String exception) {
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/resources/TestResource.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/resources/TestResource.java
index 41e529d9a4..36abeb2cee 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/resources/TestResource.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/resources/TestResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 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
@@ -89,6 +89,13 @@ public String throwsMappableException() {
throw new ResourceGoneException("Resource has been permanently removed.");
}
+ @GET
+ @Path("produces-text-plain")
+ @Produces(MediaType.TEXT_PLAIN)
+ public String producesTextPlain() {
+ return "hello";
+ }
+
@GET
@Path("redirect/{status}")
public Response redirect(@PathParam("status") int status) {
diff --git a/ext/mvc-thymeleaf/pom.xml b/ext/mvc-thymeleaf/pom.xml
new file mode 100644
index 0000000000..181a53a3d1
--- /dev/null
+++ b/ext/mvc-thymeleaf/pom.xml
@@ -0,0 +1,91 @@
+
+
+
+
+ 4.0.0
+
+
+ project
+ org.glassfish.jersey.ext
+ 3.1.99-SNAPSHOT
+
+
+ jersey-mvc-thymeleaf
+ jersey-ext-mvc-thymeleaf
+
+
+ Jersey extension module providing support for Thymeleaf templates.
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+ true
+
+
+ org.glassfish.jersey.server.mvc.thymeleaf.*;version=${project.version}
+
+ true
+
+
+
+
+
+ ${project.build.directory}/legal
+
+
+
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ ${servlet6.version}
+ provided
+
+
+
+ org.glassfish.jersey.ext
+ jersey-mvc
+ ${project.version}
+
+
+
+ org.thymeleaf
+ thymeleaf
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.javassist
+ javassist
+
+
+ ${thymeleaf.version}
+
+
+
+
\ No newline at end of file
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafConfigurationFactory.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafConfigurationFactory.java
new file mode 100644
index 0000000000..aba4aac990
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafConfigurationFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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.server.mvc.thymeleaf;
+
+import org.thymeleaf.TemplateEngine;
+
+public interface ThymeleafConfigurationFactory {
+ TemplateEngine getTemplateEngine();
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafDefaultConfigurationFactory.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafDefaultConfigurationFactory.java
new file mode 100644
index 0000000000..588c47542b
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafDefaultConfigurationFactory.java
@@ -0,0 +1,106 @@
+/*
+ * 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.server.mvc.thymeleaf;
+
+import jakarta.ws.rs.core.Configuration;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.messageresolver.IMessageResolver;
+import org.thymeleaf.messageresolver.StandardMessageResolver;
+import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
+import org.thymeleaf.templateresolver.ITemplateResolver;
+
+import java.util.Map;
+
+/**
+ * Handy {@link ThymeleafConfigurationFactory} that supplies a minimally
+ * configured {@link org.thymeleaf.TemplateEngine } able to
+ * render Thymeleaf templates.
+ * The recommended method to provide custom Thymeleaf engine settings is to
+ * sub-class this class, further customize the
+ * {@link org.thymeleaf.TemplateEngine settings} as desired in that
+ * class, and then register the sub-class with the {@link ThymeleafMvcFeature}
+ * TEMPLATE_OBJECT_FACTORY property.
+ *
+ * @author Dmytro Dovnar (dimonmc@gmail.com)
+ */
+public class ThymeleafDefaultConfigurationFactory implements ThymeleafConfigurationFactory {
+ private final Configuration config;
+ private final TemplateEngine templateEngine;
+
+ public ThymeleafDefaultConfigurationFactory(Configuration config) {
+ this.config = config;
+ this.templateEngine = initTemplateEngine();
+ }
+
+ @Override
+ public TemplateEngine getTemplateEngine() {
+ return templateEngine;
+ }
+
+ private ITemplateResolver getTemplateResolver() {
+ Map properties = config.getProperties();
+ String basePath = (String) PropertiesHelper.getValue(properties,
+ "jersey.config.server.mvc.templateBasePath" + ThymeleafMvcFeature.SUFFIX,
+ String.class, (Map) null);
+ if (basePath == null) {
+ basePath = (String) PropertiesHelper.getValue(properties,
+ "jersey.config.server.mvc.templateBasePath", "", (Map) null);
+ }
+
+ if (basePath != null && !basePath.startsWith("/")) {
+ basePath = "/" + basePath;
+ }
+
+ String templateFileSuffix = (String) PropertiesHelper.getValue(properties,
+ "jersey.config.server.mvc.templateFileSuffix" + ThymeleafMvcFeature.SUFFIX,
+ ".html", (Map) null);
+
+ String templateFileMode = (String) PropertiesHelper.getValue(properties,
+ "jersey.config.server.mvc.templateMode" + ThymeleafMvcFeature.SUFFIX,
+ "HTML5", (Map) null);
+
+ Boolean cacheEnabled = (Boolean) PropertiesHelper.getValue(properties,
+ "jersey.config.server.mvc.caching" + ThymeleafMvcFeature.SUFFIX, Boolean.class, (Map) null);
+ if (cacheEnabled == null) {
+ cacheEnabled = (Boolean) PropertiesHelper.getValue(properties,
+ "jersey.config.server.mvc.caching", false, (Map) null);
+ }
+
+ Long cacheLiveMs = (Long) PropertiesHelper.getValue(properties,
+ "jersey.config.server.mvc.cacheTTLMs" + ThymeleafMvcFeature.SUFFIX, 3600000L, (Map) null);
+
+ ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
+ templateResolver.setPrefix(basePath);
+ templateResolver.setSuffix(templateFileSuffix);
+ templateResolver.setTemplateMode(templateFileMode);
+ templateResolver.setCacheTTLMs(cacheLiveMs);
+ templateResolver.setCacheable(cacheEnabled);
+ return templateResolver;
+ }
+
+ private TemplateEngine initTemplateEngine() {
+ TemplateEngine templateEngine = new TemplateEngine();
+ templateEngine.setTemplateResolver(getTemplateResolver());
+ return templateEngine;
+ }
+
+ private IMessageResolver getMessageResolver() {
+ StandardMessageResolver messageResolver = new StandardMessageResolver();
+ return messageResolver;
+ }
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafMvcFeature.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafMvcFeature.java
new file mode 100644
index 0000000000..b43b9a6b48
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafMvcFeature.java
@@ -0,0 +1,54 @@
+/*
+ * 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.server.mvc.thymeleaf;
+
+import jakarta.ws.rs.ConstrainedTo;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Feature;
+import jakarta.ws.rs.core.FeatureContext;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+
+@ConstrainedTo(RuntimeType.SERVER)
+public final class ThymeleafMvcFeature implements Feature {
+ public static final String SUFFIX = ".thymeleaf";
+ public static final String TEMPLATE_BASE_PATH = MvcFeature.TEMPLATE_BASE_PATH + SUFFIX;
+ public static final String CACHE_TEMPLATES = MvcFeature.CACHE_TEMPLATES + SUFFIX;
+ public static final String TEMPLATE_OBJECT_FACTORY = MvcFeature.TEMPLATE_OBJECT_FACTORY + SUFFIX;
+ public static final String ENCODING = MvcFeature.ENCODING + SUFFIX;
+
+ public static final String TEMPLATE_FILE_SUFFIX = "jersey.config.server.mvc.templateFileSuffix" + SUFFIX;
+ public static final String TEMPLATE_MODE = "jersey.config.server.mvc.templateMode" + SUFFIX;
+ public static final String CACHE_TTLMS = "jersey.config.server.mvc.cacheTTLMs" + SUFFIX;
+
+ @Override
+ public boolean configure(FeatureContext context) {
+ final Configuration config = context.getConfiguration();
+
+ if (!config.isRegistered(ThymeleafViewProcessor.class)) {
+ context.register(ThymeleafViewProcessor.class);
+
+ // MvcFeature.
+ if (!config.isRegistered(MvcFeature.class)) {
+ context.register(MvcFeature.class);
+ }
+
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafSuppliedConfigurationFactory.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafSuppliedConfigurationFactory.java
new file mode 100644
index 0000000000..4f21ce1cb6
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafSuppliedConfigurationFactory.java
@@ -0,0 +1,45 @@
+/*
+ * 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.server.mvc.thymeleaf;
+
+import org.thymeleaf.TemplateEngine;
+
+/**
+ * {@link ThymeleafConfigurationFactory} that supplies an unchanged
+ * {@link ThymeleafConfigurationFactory Configuration} as passed-in to
+ * the constructor.
+ *
+ * Used to support backwards-compatibility in {@link ThymeleafViewProcessor}
+ * to wrap directly-configured {@link org.thymeleaf.TemplateEngine}
+ * objects instead of the recommended {@link ThymeleafDefaultConfigurationFactory}
+ * or a sub-class thereof.
+ *
+ * @author Dmytro Dovnar (dimonmc@gmail.com)
+ */
+public class ThymeleafSuppliedConfigurationFactory implements ThymeleafConfigurationFactory {
+ private final ThymeleafConfigurationFactory configurationFactory;
+
+ public ThymeleafSuppliedConfigurationFactory(ThymeleafConfigurationFactory configurationFactory) {
+ this.configurationFactory = configurationFactory;
+ }
+
+ @Override
+ public TemplateEngine getTemplateEngine() {
+ return configurationFactory.getTemplateEngine();
+ }
+
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafViewProcessor.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafViewProcessor.java
new file mode 100644
index 0000000000..fc8139c65b
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafViewProcessor.java
@@ -0,0 +1,104 @@
+/*
+ * 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.server.mvc.thymeleaf;
+
+import jakarta.inject.Inject;
+import jakarta.servlet.ServletContext;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.util.collection.Values;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.server.mvc.spi.AbstractTemplateProcessor;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * {@link org.glassfish.jersey.server.mvc.spi.TemplateProcessor Template processor} providing support for Thymeleaf templates.
+ *
+ * @author Dmytro Dovnar (dimonmc@gmail.com)
+ */
+public final class ThymeleafViewProcessor extends AbstractTemplateProcessor {
+ private final ThymeleafConfigurationFactory factory;
+
+ /**
+ * Create an instance of this processor with injected {@link jakarta.ws.rs.core.Configuration config}.
+ *
+ * @param config config to configure this processor from.
+ * @param injectionManager injection manager.
+ */
+ @Inject
+ public ThymeleafViewProcessor(Configuration config, InjectionManager injectionManager) {
+ super(config, injectionManager.getInstance(ServletContext.class), "thymeleaf", "html");
+ this.factory = getTemplateObjectFactory(injectionManager::createAndInitialize, ThymeleafConfigurationFactory.class,
+ () -> {
+ ThymeleafConfigurationFactory configuration =
+ getTemplateObjectFactory(
+ injectionManager::createAndInitialize,
+ ThymeleafConfigurationFactory.class, Values.empty());
+ if (configuration == null) {
+ return new ThymeleafDefaultConfigurationFactory(config);
+ } else {
+ return new ThymeleafSuppliedConfigurationFactory(configuration);
+ }
+ });
+ }
+
+ @Override
+ protected TemplateEngine resolve(final String templatePath, final Reader reader) throws Exception {
+ return factory.getTemplateEngine();
+ }
+
+ @Override
+ public void writeTo(final TemplateEngine templateEngine, final Viewable viewable, final MediaType mediaType,
+ final MultivaluedMap httpHeaders, final OutputStream out) throws IOException {
+ Context context = new Context();
+
+ Object model = viewable.getModel();
+ if (!(model instanceof Map)) {
+ context.setVariable("model", viewable.getModel());
+ } else {
+ context.setVariables((Map) viewable.getModel());
+ }
+
+ if (context.containsVariable("lang")) {
+ Object langValue = context.getVariable("lang");
+ if (langValue instanceof Locale) {
+ context.setLocale((Locale) langValue);
+ } else if (langValue instanceof String) {
+ Locale locale = Locale.forLanguageTag((String) langValue);
+ context.setLocale(locale);
+ }
+ }
+
+ Charset encoding = setContentType(mediaType, httpHeaders);
+
+ final Writer writer = new BufferedWriter(new OutputStreamWriter(out, encoding));
+ templateEngine.process(viewable.getTemplateName(), context, writer);
+ }
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/package-info.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/package-info.java
new file mode 100644
index 0000000000..d8cd7ae4a9
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/package-info.java
@@ -0,0 +1,17 @@
+/*
+ * 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.server.mvc.thymeleaf;
diff --git a/ext/pom.xml b/ext/pom.xml
index 5257ff1a88..76629a4815 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -50,6 +50,7 @@
mvc-freemarkermvc-jspmvc-mustache
+ mvc-thymeleafproxy-clientrxspring6
diff --git a/ext/spring6/pom.xml b/ext/spring6/pom.xml
index 70e9dc3e67..41e38f45c4 100644
--- a/ext/spring6/pom.xml
+++ b/ext/spring6/pom.xml
@@ -93,6 +93,18 @@
org.ow2.asmasm
+
+ org.ow2.asm
+ asm-util
+
+
+ org.ow2.asm
+ asm-tree
+
+
+ org.ow2.asm
+ asm-analysis
+ jakarta.annotationjakarta.annotation-api
diff --git a/ext/spring6/src/main/java17/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java b/ext/spring6/src/main/java17/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java
index a489695202..11f798478b 100644
--- a/ext/spring6/src/main/java17/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java
+++ b/ext/spring6/src/main/java17/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -98,7 +98,9 @@ public void resetAttributes(final ContainerRequestContext requestContext) {
final AbstractRequestAttributes attributes =
(AbstractRequestAttributes) requestContext.getProperty(REQUEST_ATTRIBUTES_PROPERTY);
RequestContextHolder.resetRequestAttributes();
- attributes.requestCompleted();
+ if (attributes != null) {
+ attributes.requestCompleted();
+ }
}
} : EMPTY_ATTRIBUTE_CONTROLLER;
}
diff --git a/ext/spring6/src/test/java/org/glassfish/jersey/server/spring/filter/SpringRequestContextFilterTest.java b/ext/spring6/src/test/java/org/glassfish/jersey/server/spring/filter/SpringRequestContextFilterTest.java
new file mode 100644
index 0000000000..3e08125ff1
--- /dev/null
+++ b/ext/spring6/src/test/java/org/glassfish/jersey/server/spring/filter/SpringRequestContextFilterTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.server.spring.filter;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.WebApplicationContext;
+
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerResponseContext;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class SpringRequestContextFilterTest {
+ @Test
+ public void testMissingAttributes() throws IOException {
+ WebApplicationContext webAppCtx = (WebApplicationContext) Proxy.newProxyInstance(
+ WebApplicationContext.class.getClassLoader(),
+ new Class[]{WebApplicationContext.class},
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return null;
+ }
+ });
+
+ InjectionManager injectionManager = Injections.createInjectionManager();
+ injectionManager.register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(webAppCtx).to(ApplicationContext.class);
+ }
+ });
+ injectionManager.completeRegistration();
+
+ ContainerRequestContext requestContext = (ContainerRequestContext) Proxy.newProxyInstance(
+ ContainerRequestContext.class.getClassLoader(),
+ new Class[]{ContainerRequestContext.class},
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return null;
+ }
+ });
+
+ RequestContextFilter filter = new RequestContextFilter(injectionManager);
+ filter.filter(requestContext, (ContainerResponseContext) null);
+ }
+}
diff --git a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2BootstrapBinder.java b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2BootstrapBinder.java
index 7794aa6cf8..f3fd9d7598 100644
--- a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2BootstrapBinder.java
+++ b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2BootstrapBinder.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
@@ -43,6 +43,10 @@ public class Hk2BootstrapBinder extends AbstractBinder {
@Override
protected void configure() {
+ // Singletons, install once
+ if (serviceLocator.getService(RequestScope.class) != null) {
+ return;
+ }
install(
// Jersey-like class analyzer that is able to choose the right services' constructor.
new JerseyClassAnalyzer.Binder(serviceLocator),
diff --git a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Helper.java b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Helper.java
index 0f731f6c9f..bf3c229f56 100644
--- a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Helper.java
+++ b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Helper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018 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
@@ -20,8 +20,10 @@
import java.lang.reflect.Type;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
+import org.glassfish.hk2.api.Factory;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.inject.AliasBinding;
import org.glassfish.jersey.internal.inject.Binding;
@@ -121,14 +123,25 @@ private static void bindBinding(ServiceLocator locator, Binding, ?> binding) {
* @param dc HK2 Dynamic configuration to bind the object.
* @param binding Jersey descriptor as a holder of information about an injection point.
*/
- private static void bindBinding(ServiceLocator locator, DynamicConfiguration dc, Binding, ?> binding) {
+ private static void bindBinding(ServiceLocator locator, DynamicConfiguration dc, Binding binding) {
if (ClassBinding.class.isAssignableFrom(binding.getClass())) {
- ActiveDescriptor> activeDescriptor = translateToActiveDescriptor((ClassBinding>) binding);
- bindBinding(locator, dc, activeDescriptor, binding.getAliases());
+ final Class> implClass = binding.getImplementationType();
+ if (Factory.class.isAssignableFrom(implClass)) {
+ final Class extends Factory> factoryClass = (Class extends Factory>) implClass;
+ bindFactory(locator, binding, (binder) -> binder.bindFactory(factoryClass));
+ } else {
+ ActiveDescriptor> activeDescriptor = translateToActiveDescriptor((ClassBinding>) binding);
+ bindBinding(locator, dc, activeDescriptor, binding.getAliases());
+ }
} else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) {
- ActiveDescriptor> activeDescriptor = translateToActiveDescriptor((InstanceBinding>) binding);
- bindBinding(locator, dc, activeDescriptor, binding.getAliases());
+ if (Factory.class.isAssignableFrom(binding.getImplementationType())) {
+ final Factory> factory = (Factory) ((InstanceBinding) binding).getService();
+ bindFactory(locator, binding, (binder) -> binder.bindFactory(factory));
+ } else {
+ ActiveDescriptor> activeDescriptor = translateToActiveDescriptor((InstanceBinding>) binding);
+ bindBinding(locator, dc, activeDescriptor, binding.getAliases());
+ }
} else if (InjectionResolverBinding.class.isAssignableFrom(binding.getClass())) {
InjectionResolverBinding resolverDescriptor = (InjectionResolverBinding) binding;
@@ -234,6 +247,13 @@ private static void bindSupplierClassBinding(ServiceLocator locator, SupplierCla
ServiceLocatorUtilities.bind(locator, createBinder(bindConsumer));
}
+ private static void bindFactory(ServiceLocator locator,
+ Binding binding,
+ Function builder) {
+ ServiceLocatorUtilities
+ .bind(locator, createBinder((binder) -> setupSupplierFactoryBridge(binding, builder.apply(binder))));
+ }
+
private static void setupSupplierFactoryBridge(Binding, ?> binding, ServiceBindingBuilder> builder) {
builder.named(binding.getName());
binding.getContracts().forEach(builder::to);
diff --git a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Registrables.java b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Registrables.java
new file mode 100644
index 0000000000..6231f74a58
--- /dev/null
+++ b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Registrables.java
@@ -0,0 +1,32 @@
+/*
+ * 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.inject.hk2;
+
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.innate.inject.spi.ExternalRegistrables;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Hk2Registrables implements ExternalRegistrables {
+ @Override
+ public List registrableContracts() {
+ List list = new ArrayList<>();
+ list.add(new ClassRuntimeTypePair(AbstractBinder.class, null));
+ return list;
+ }
+}
diff --git a/inject/hk2/src/main/resources/META-INF/services/org.glassfish.jersey.innate.inject.spi.ExternalRegistrables b/inject/hk2/src/main/resources/META-INF/services/org.glassfish.jersey.innate.inject.spi.ExternalRegistrables
new file mode 100644
index 0000000000..6f7da1acd0
--- /dev/null
+++ b/inject/hk2/src/main/resources/META-INF/services/org.glassfish.jersey.innate.inject.spi.ExternalRegistrables
@@ -0,0 +1 @@
+org.glassfish.jersey.inject.hk2.Hk2Registrables
\ No newline at end of file
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
index b11c1214cf..d7b9f9c096 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
@@ -31,6 +31,7 @@
import java.lang.annotation.Annotation;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import jakarta.annotation.PostConstruct;
@@ -104,7 +105,13 @@ private List filterModules() {
commonConfig.getRuntimeType(),
CommonProperties.JSON_JACKSON_ENABLED_MODULES, String.class);
- final List modules = ObjectMapper.findModules();
+ final List modules;
+ try {
+ modules = ObjectMapper.findModules();
+ } catch (Throwable e) {
+ LOGGER.warning(LocalizationMessages.ERROR_MODULES_NOT_LOADED(e.getMessage()));
+ return Collections.emptyList();
+ }
for (String exludeModuleName : EXCLUDE_MODULE_NAMES) {
modules.removeIf(mod -> mod.getModuleName().contains(exludeModuleName));
}
diff --git a/media/json-jackson/src/main/resources/org/glassfish/jersey/jackson/localization.properties b/media/json-jackson/src/main/resources/org/glassfish/jersey/jackson/localization.properties
index f8b59da71c..a9144fd496 100644
--- a/media/json-jackson/src/main/resources/org/glassfish/jersey/jackson/localization.properties
+++ b/media/json-jackson/src/main/resources/org/glassfish/jersey/jackson/localization.properties
@@ -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
@@ -14,4 +14,5 @@
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
#
-error.jackson.streamreadconstraints=Error setting StreamReadConstraints: {0}. Possibly not Jackson 2.15?
\ No newline at end of file
+error.jackson.streamreadconstraints=Error setting StreamReadConstraints: {0}. Possibly not Jackson 2.15?
+error.modules.not.loaded=Jackson modules could not be loaded: {0}
\ No newline at end of file
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartProperties.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartProperties.java
index 68abedb2ee..00d6aba110 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartProperties.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartProperties.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
@@ -23,6 +23,7 @@
import jakarta.ws.rs.ext.ContextResolver;
import org.glassfish.jersey.internal.util.PropertiesClass;
+import org.glassfish.jersey.message.internal.ReaderWriter;
/**
* Injectable JavaBean containing the configuration parameters for
@@ -38,12 +39,17 @@ public class MultiPartProperties {
/**
* Default threshold size for buffer.
*/
- public static final int DEFAULT_BUFFER_THRESHOLD = 4096;
+ public static final int DEFAULT_BUFFER_THRESHOLD = ReaderWriter.BUFFER_SIZE;
/**
+ *
* Name of a properties resource that (if found in the classpath
* for this application) will be used to configure the settings returned
* by our getter methods.
+ *
+ *
+ * The resource name is {@code jersey-multipart-config.properties}.
+ *
*/
public static final String MULTI_PART_CONFIG_RESOURCE = "jersey-multipart-config.properties";
@@ -51,7 +57,7 @@ public class MultiPartProperties {
* Name of the resource property for the threshold size (in bytes) above which a body part entity will be
* buffered to disk instead of being held in memory.
*
- * The default value is {@value #DEFAULT_BUFFER_THRESHOLD}.
+ * The default value is {@link #DEFAULT_BUFFER_THRESHOLD}.
*/
public static final String BUFFER_THRESHOLD = "jersey.config.multipart.bufferThreshold";
@@ -60,9 +66,21 @@ public class MultiPartProperties {
*/
public static final int BUFFER_THRESHOLD_MEMORY_ONLY = -1;
+ /**
+ *
+ * Limit the maximum number of parts the multipart entity can have. If the limit is over,
+ * the error response status {@link jakarta.ws.rs.core.Response.Status#REQUEST_ENTITY_TOO_LARGE} is returned.
+ *
+ *
+ * By default, the number is unlimited.
+ *
+ * @since 2.44
+ */
+ public static final String MAX_PARTS = "jersey.config.multipart.maxParts";
+
/**
* Name of the resource property for the directory to store temporary files containing body parts of multipart message that
- * extends allowed memory threshold..
+ * extends allowed memory threshold.
*
* The default value is not set (will be taken from {@code java.io.tmpdir} system property).
*/
@@ -79,6 +97,11 @@ public class MultiPartProperties {
*/
private String tempDir = null;
+ /**
+ * Maximum number of entity parts allowed.
+ */
+ private int maxParts = Integer.MAX_VALUE;
+
/**
* Load and customize (if necessary) the configuration values for the
* {@code jersey-multipart} injection binder.
@@ -113,6 +136,15 @@ public String getTempDir() {
return tempDir;
}
+ /**
+ * Return maximum number of entity parts allowed.
+ * @return maximum number of parts.
+ * @since 2.44
+ */
+ public int getMaxParts() {
+ return maxParts;
+ }
+
/**
* Set the size (in bytes) of the entity of an incoming {@link BodyPart} before it will be buffered to disk.
*
@@ -138,6 +170,17 @@ public MultiPartProperties tempDir(final String path) {
return this;
}
+ /**
+ * Set the maximum number of received parts of a multipart entity.
+ * @param maxParts The maximum number of entity parts.
+ * @return {@code MultiPartProperties} instance.
+ * @since 2.44
+ */
+ public MultiPartProperties maxParts(int maxParts) {
+ this.maxParts = maxParts;
+ return this;
+ }
+
/**
* Configure the values returned by this instance's getters based on
* the contents of a properties resource, if it exists on the classpath
@@ -169,6 +212,9 @@ private void configure() {
if (props.containsKey(TEMP_DIRECTORY)) {
this.tempDir = props.getProperty(TEMP_DIRECTORY);
}
+ if (props.contains(MAX_PARTS)) {
+ this.maxParts = Integer.parseInt(props.getProperty(MAX_PARTS));
+ }
} catch (final IOException e) {
throw new IllegalArgumentException(e);
} finally {
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
index 6c834d916a..277855b730 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2021 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
@@ -20,6 +20,8 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -234,7 +236,13 @@ public FormDataParamValueProvider(Parameter parameter, MultivaluedParameterExtra
@Override
public Object apply(ContainerRequest request) {
// Return the field value for the field specified by the sourceName property.
- final List parts = getEntity(request).getFields(parameter.getSourceName());
+ final String sourceName = parameter.getAnnotations().length == 1
+ ? parameter.getSourceName()
+ : Arrays.stream(parameter.getAnnotations())
+ .filter(ann -> FormDataParam.class.isInstance(ann))
+ .map(ann -> FormDataParam.class.cast(ann))
+ .findFirst().get().value();
+ final List parts = getEntity(request).getFields(sourceName);
final FormDataBodyPart part = parts != null ? parts.get(0) : null;
final MediaType mediaType = part != null ? part.getMediaType() : MediaType.TEXT_PLAIN_TYPE;
@@ -396,44 +404,48 @@ public FormDataParamValueParamProvider(Provider {
*/
private Provider messageBodyWorkers;
private final MIMEConfig mimeConfig;
+ private final int maxParts;
/**
* Accepts constructor injection of the configuration parameters for this
@@ -98,6 +101,8 @@ public MultiPartReaderClientSide(@Context final Providers providers,
properties = new MultiPartProperties();
}
+ maxParts = properties.getMaxParts();
+
this.messageBodyWorkers = messageBodyWorkers;
mimeConfig = createMimeConfig(properties);
}
@@ -205,7 +210,12 @@ protected MultiPart readMultiPart(final Class type,
fileNameFix = userAgent != null && userAgent.contains(" MSIE ");
}
- for (final MIMEPart mimePart : getMimeParts(mimeMessage)) {
+ final List mimeParts = getMimeParts(mimeMessage);
+ if (mimeParts.size() > maxParts) {
+ throw new ClientErrorException(Response.Status.REQUEST_ENTITY_TOO_LARGE);
+ }
+
+ for (final MIMEPart mimePart : mimeParts) {
final BodyPart bodyPart = formData ? new FormDataBodyPart(fileNameFix) : new BodyPart();
// Configure providers.
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/OrderParamTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/OrderParamTest.java
new file mode 100644
index 0000000000..23cc7a8b29
--- /dev/null
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/OrderParamTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.multipart.internal;
+
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.ParamQualifier;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class OrderParamTest extends JerseyTest {
+ @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
+ @Retention(RetentionPolicy.RUNTIME)
+ @ParamQualifier
+ public static @interface AnnoWithValue {
+ String value() default "";
+ }
+
+ @Path("/order")
+ public static class OrderTestResource {
+ @POST
+ @Path("/dataAfter")
+ @Consumes(value = MediaType.MULTIPART_FORM_DATA)
+ public String orderBefore(@FormDataParam("file") @AnnoWithValue("xxx") InputStream inputStream) throws IOException {
+ return ReaderWriter.readFromAsString(inputStream, MediaType.TEXT_PLAIN_TYPE);
+ }
+
+ @POST
+ @Path("/dataBefore")
+ @Consumes(value = MediaType.MULTIPART_FORM_DATA)
+ public String orderAfter(@AnnoWithValue("zzz") @FormDataParam("file") InputStream inputStream) throws IOException {
+ return ReaderWriter.readFromAsString(inputStream, MediaType.TEXT_PLAIN_TYPE);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(OrderTestResource.class).register(MultiPartFeature.class);
+ }
+
+ @Test
+ public void testOrder() {
+ final String MSG = "Hello";
+ FormDataMultiPart multiPart = new FormDataMultiPart();
+ multiPart.field("file", MSG, MediaType.TEXT_PLAIN_TYPE);
+ try (Response response = target("order")
+ .register(MultiPartFeature.class)
+ .path("dataBefore").request()
+ .post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+ assertEquals(200, response.getStatus());
+ assertEquals(MSG, response.readEntity(String.class));
+ }
+
+ try (Response response = target("order")
+ .register(MultiPartFeature.class)
+ .path("dataAfter").request()
+ .post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+ assertEquals(200, response.getStatus());
+ assertEquals(MSG, response.readEntity(String.class));
+ }
+ }
+}
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/RestrictionsTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/RestrictionsTest.java
new file mode 100644
index 0000000000..9906f11994
--- /dev/null
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/RestrictionsTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.multipart.internal;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.media.multipart.MultiPartProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+public class RestrictionsTest extends JerseyTest {
+ @Path("/")
+ public static class RestrictionsTestResource {
+ @POST
+ @Path("max.parts")
+ public String postMaxPart(@FormDataParam("part1") String part1, @FormDataParam("part2") String part2) {
+ return part1 + part2;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(RestrictionsTestResource.class)
+ .register(MultiPartFeature.class)
+ .register(new MultiPartProperties().maxParts(2).resolver());
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(MultiPartFeature.class);
+ }
+
+ @Test
+ public void testPassNumberOfParts() {
+ FormDataMultiPart multiPart = new FormDataMultiPart();
+ multiPart.field("part1", "he", MediaType.TEXT_PLAIN_TYPE);
+ multiPart.field("part2", "llo", MediaType.TEXT_PLAIN_TYPE);
+ try (Response r = target("max.parts").request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+ Assertions.assertEquals(200, r.getStatus());
+ Assertions.assertEquals("hello", r.readEntity(String.class));
+ }
+ }
+
+ @Test
+ public void testFailsNumberOfParts() {
+ FormDataMultiPart multiPart = new FormDataMultiPart();
+ multiPart.field("part1", "he", MediaType.TEXT_PLAIN_TYPE);
+ multiPart.field("part2", "llo", MediaType.TEXT_PLAIN_TYPE);
+ multiPart.field("part3", "!", MediaType.TEXT_PLAIN_TYPE);
+ try (Response r = target("max.parts").request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+ Assertions.assertEquals(Response.Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode(), r.getStatus());
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 493e21ff24..6045c837e6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,6 +142,9 @@
Stepan Vavra
+
+ Dmytro Dovnar
+
@@ -305,7 +308,7 @@
org.apache.maven.pluginsmaven-clean-plugin
- 2.5
+ ${clean.mvn.plugin.version}org.apache.maven.plugins
@@ -313,6 +316,8 @@
${compiler.mvn.plugin.version}true
+
+ ${java.version}
@@ -468,7 +473,7 @@
Jersey ${jersey.version} API DocumentationJersey ${jersey.version} API
- Oracle
and/or its affiliates.
All Rights Reserved. Use is subject to license terms.]]>
@@ -479,7 +484,7 @@
https://eclipse-ee4j.github.io/jersey.github.io/apidocs/latest/jersey/
- *.internal.*:*.innate.*:*.tests.*
+ *.innate:*.innate.*:*.tests:*.tests.*
false
@@ -543,11 +548,6 @@
-
- org.apache.maven.plugins
- maven-site-plugin
- ${site.mvn.plugin.version}
- org.codehaus.mojoexec-maven-plugin
@@ -793,60 +793,10 @@
org.apache.antant
- 1.10.12
+ ${mvn.ant.version}
-
- org.fortasoft
- gradle-maven-plugin
- 1.0.8
-
-
- com.github.wvengen
- proguard-maven-plugin
- ${proguard.mvn.plugin.version}
-
-
- net.sf.proguard
- proguard-base
- 6.2.2
- runtime
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- true
-
-
- ${java.version}
-
-
-
-
- base-compile
-
- compile
-
-
-
-
- module-info.java
-
-
-
-
-
@@ -895,7 +845,6 @@
org.codehaus.mojobuild-helper-maven-plugin
- ${buildhelper.mvn.plugin.version}jersey.config.test.container.port
@@ -959,7 +908,6 @@
true
- true
@@ -1135,7 +1083,7 @@
org.apache.maven.pluginsmaven-project-info-reports-plugin
- 3.4.5
+ ${project.info.reports.mvn.plugin.version}
@@ -1295,51 +1243,6 @@
-
-
- travis_e2e_skip
-
- true
-
-
-
-
- travis_e2e
-
- false
- true
-
-
-
-
- eclipse_repo
-
-
-
- true
-
- repo.jaxrs-api.eclipse.org
- JAX-RS API Repository - Snapshots
- https://repo.eclipse.org/content/repositories/jax-rs-api-snapshots
-
-
- license_check
@@ -1363,7 +1266,7 @@
org.eclipse.dashlicense-tool-plugin
- 1.0.2
+ 1.1.0license-check
@@ -1416,20 +1319,20 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 3.4.1
+ ${javadoc.mvn.plugin.version}falseJersey ${jersey.version} API DocumentationJersey ${jersey.version} API
- Oracle
and/or its affiliates.
All Rights Reserved. Use is subject to license terms.]]>
- com.sun.ws.rs.ext:*.examples.*:*.internal.*:*.tests.*
+ com.sun.ws.rs.ext:*.examples.*:*.innate:*.innate.*:*.tests:*.tests.*
https://jax-rs.github.io/apidocs/2.1
@@ -1469,7 +1372,7 @@
org.apache.maven.pluginsmaven-jxr-plugin
- 2.3
+ ${jxr.mvn.plugin.version}
@@ -2131,7 +2034,6 @@
UTF-8
- falsefalse
@@ -2142,13 +2044,15 @@
3.1.0
+ 1.10.143.7.1
+ 3.3.23.4.13.2.03.5.03.2.03.3.1
- 10.14.2
+ 10.16.03.13.03.9.03.6.1
- 3.1.1
+ 3.1.23.3.03.2.55.1.93.0.55.1
- 3.1.1
+ 3.1.24.2.0
- 3.3.0
+ 3.4.13.6.33.3.21.2.4
- 2.6.1
+ 3.5.03.3.1
- 3.5.2
- 3.12.1
- 3.3.0
+ 3.5.3
+ 3.3.13.2.53.4.02.11.0
@@ -2193,7 +2096,7 @@
1.9.221.70
- 2.15.1
+ 2.16.11.16.11.3.1
@@ -2203,8 +2106,8 @@
7.0.51.72.3.32
- 2.0.25
- 4.0.20
+ 2.0.26
+ 4.0.212.10.1
@@ -2225,9 +2128,9 @@
1.4.143.7.1
- 31.1-jre
+ 33.1.0-jre2.2
- 2.9.1
+ 2.10.04.5.145.3.12.17.0
@@ -2241,9 +2144,9 @@
1.10.21.10.04.0.3
- 3.12.4
- 0.9.11
- 4.1.108.Final
+ 4.11.0
+ 0.9.12
+ 4.1.109.Final0.33.06.0.01.10.0
@@ -2259,10 +2162,11 @@
6.0.06.0.1
- 2.0.12
+ 2.0.136.0.187.9.06.9.13.6
+ 3.1.2.RELEASE5.1.1.Final3.1.9.Final
@@ -2272,7 +2176,7 @@
2.12.2
- 20.3.13
+ 20.3.147.0.6
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml
index 5abee5ca73..3a155a8a39 100644
--- a/tests/e2e-client/pom.xml
+++ b/tests/e2e-client/pom.xml
@@ -41,7 +41,6 @@
1falsefalse
- ${skip.e2e}true
diff --git a/tests/e2e-entity/pom.xml b/tests/e2e-entity/pom.xml
index 68c4cfccde..2cb4920b04 100644
--- a/tests/e2e-entity/pom.xml
+++ b/tests/e2e-entity/pom.xml
@@ -41,7 +41,6 @@
1falsefalse
- ${skip.e2e}
diff --git a/tests/e2e-inject/hk2/pom.xml b/tests/e2e-inject/hk2/pom.xml
index e8ccf7aadd..e2dc477ded 100644
--- a/tests/e2e-inject/hk2/pom.xml
+++ b/tests/e2e-inject/hk2/pom.xml
@@ -36,6 +36,11 @@
pomtest
+
+ org.glassfish.jersey.core
+ jersey-client
+ test
+
diff --git a/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2FactoryBindingTest.java b/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2FactoryBindingTest.java
new file mode 100644
index 0000000000..abc0981c8b
--- /dev/null
+++ b/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2FactoryBindingTest.java
@@ -0,0 +1,340 @@
+/*
+ * 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.inject.hk2;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.InjectionManagerClientProvider;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.Injectee;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionResolver;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.annotation.Priority;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.Priorities;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Feature;
+import jakarta.ws.rs.core.FeatureContext;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public class HK2FactoryBindingTest {
+
+ public static interface ConfigurationProperties {
+ Map getProperties();
+ }
+
+ public static class ConfigurationPropertiesFactory implements org.glassfish.hk2.api.Factory {
+
+ private final Configuration configuration;
+
+ @Inject
+ public ConfigurationPropertiesFactory(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public ConfigurationProperties provide() {
+ return new ConfigurationProperties() {
+ @Override
+ public Map getProperties() {
+ return configuration.getProperties();
+ }
+ };
+ }
+
+ @Override
+ public void dispose(ConfigurationProperties configurationProperties) {
+
+ }
+ }
+
+ @Priority(Priorities.USER)
+ public static class ConfigurationPropertiesFilter implements ClientRequestFilter {
+ @Inject
+ ConfigurationProperties properties;
+
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.abortWith(Response.ok(properties.getProperties().get(PROPERTY_NAME)).build());
+ }
+ }
+
+ private static final String PROPERTY_NAME = "TEST_PROPERTY";
+ private static final String PROPERTY_VALUE = "HELLO_PROPERTY";
+
+ @Test
+ public void testFactoryClassBinding() {
+ ClientConfig config = new ClientConfig();
+ config.property(PROPERTY_NAME, PROPERTY_VALUE);
+ String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(ConfigurationPropertiesFactory.class).to(ConfigurationProperties.class).proxy(true)
+ .proxyForSameScope(false).in(RequestScoped.class);
+ }
+ }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+ Assertions.assertEquals(PROPERTY_VALUE, response);
+ }
+
+ @Test
+ public void testFactoryInstanceBinding() {
+ ClientConfig config = new ClientConfig();
+ config.property(PROPERTY_NAME, PROPERTY_VALUE);
+ String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(new ConfigurationPropertiesFactory(config)).to(ConfigurationProperties.class).proxy(true)
+ .proxyForSameScope(false).in(RequestScoped.class);
+ }
+ }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+ Assertions.assertEquals(PROPERTY_VALUE, response);
+ }
+
+ static final class ConfigurationPropertiesImpl implements ConfigurationProperties {
+ private final Configuration configuration;
+ @Inject
+ public ConfigurationPropertiesImpl(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public Map getProperties() {
+ return configuration.getProperties();
+ }
+ }
+
+ static class ConfigurationPropertiesSupplier implements Supplier {
+ private final Configuration configuration;
+
+ @Inject
+ ConfigurationPropertiesSupplier(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public ConfigurationPropertiesImpl get() {
+ return new ConfigurationPropertiesImpl(configuration);
+ }
+ }
+
+ @Test
+ public void testSupplierJerseyInstanceBinding() {
+ ClientConfig config = new ClientConfig();
+ config.property(PROPERTY_NAME, PROPERTY_VALUE);
+ String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bindFactory(() -> new ConfigurationPropertiesImpl(config)).to(ConfigurationProperties.class).proxy(true)
+ .proxyForSameScope(false).in(RequestScoped.class);
+ }
+ }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+ Assertions.assertEquals(PROPERTY_VALUE, response);
+ }
+
+ @Test
+ public void testSupplierJerseyClassBinding() {
+ ClientConfig config = new ClientConfig();
+ config.property(PROPERTY_NAME, PROPERTY_VALUE);
+ String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bindFactory(ConfigurationPropertiesSupplier.class).to(ConfigurationProperties.class).proxy(true)
+ .proxyForSameScope(false).in(RequestScoped.class);
+ }
+ }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+ Assertions.assertEquals(PROPERTY_VALUE, response);
+ }
+
+ public static interface ConfigurationPropertiesProvider {
+ Map getProperties();
+
+ ConfigurationProperties getConfigurationProperties();
+ }
+
+ public static class ConfigurationPropertiesProviderImpl implements ConfigurationPropertiesProvider {
+ private final Configuration configuration;
+ @Inject
+ public ConfigurationPropertiesProviderImpl(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public Map getProperties() {
+ return configuration.getProperties();
+ }
+
+ @Override
+ public ConfigurationProperties getConfigurationProperties() {
+ return new ConfigurationPropertiesImpl(configuration);
+ }
+ }
+
+ public static class ConfigurationPropertiesProviderSupplier implements Supplier {
+ final ConfigurationPropertiesProvider impl;
+
+ @Inject
+ public ConfigurationPropertiesProviderSupplier(ConfigurationPropertiesProvider impl) {
+ this.impl = impl;
+ }
+
+ @Override
+ public ConfigurationProperties get() {
+ return impl.getConfigurationProperties();
+ }
+ }
+
+ public static class ConfigurationProperties2Filter implements ClientRequestFilter {
+ @Inject
+ ConfigurationProperties properties;
+
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.abortWith(Response.ok(properties.getProperties().get(PROPERTY_NAME)).build());
+ }
+ }
+
+ @Test
+ public void testSupplierOfProviderClassBinding() {
+ ClientConfig config = new ClientConfig();
+ config.property(PROPERTY_NAME, PROPERTY_VALUE);
+ String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(ConfigurationPropertiesProviderImpl.class).to(ConfigurationPropertiesProvider.class).proxy(true)
+ .proxyForSameScope(false).in(RequestScoped.class);
+ bindFactory(ConfigurationPropertiesProviderSupplier.class).to(ConfigurationProperties.class).proxy(true)
+ .proxyForSameScope(false).in(RequestScoped.class);
+ }
+ }).register(ConfigurationProperties2Filter.class).target("http://test.com").request().get(String.class);
+ Assertions.assertEquals(PROPERTY_VALUE, response);
+ }
+
+ @Test
+ public void testFactoryHk2ClassBinding() {
+ ClientConfig config = new ClientConfig();
+ config.property(PROPERTY_NAME, PROPERTY_VALUE);
+ String response = ClientBuilder.newClient(config).register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
+ @Override
+ protected void configure() {
+ bindFactory(ConfigurationPropertiesFactory.class).to(ConfigurationProperties.class).proxy(true)
+ .proxyForSameScope(false).in(RequestScoped.class);
+ }
+ }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+ Assertions.assertEquals(PROPERTY_VALUE, response);
+ }
+
+ @Test
+ public void testFactoryHk2InstanceBinding() {
+ ClientConfig config = new ClientConfig();
+ config.property(PROPERTY_NAME, PROPERTY_VALUE);
+ String response = ClientBuilder.newClient(config).register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
+ @Override
+ protected void configure() {
+ bindFactory(new ConfigurationPropertiesFactory(config)).to(ConfigurationProperties.class).proxy(true)
+ .proxyForSameScope(false).in(RequestScoped.class);
+ }
+ }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+ Assertions.assertEquals(PROPERTY_VALUE, response);
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ static @interface ConfigurationPropertiesInject {
+
+ }
+
+ static class ConfigurationPropertiesInjectResolver implements InjectionResolver {
+
+ private final InjectionManager injectionManager;
+
+ ConfigurationPropertiesInjectResolver(InjectionManager injectionManager) {
+ this.injectionManager = injectionManager;
+ }
+
+ @Override
+ public Object resolve(Injectee injectee) {
+ if (injectee.getRequiredType() == ConfigurationProperties.class) {
+ return new ConfigurationPropertiesImpl(injectionManager.getInstance(Configuration.class));
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isConstructorParameterIndicator() {
+ return false;
+ }
+
+ @Override
+ public boolean isMethodParameterIndicator() {
+ return false;
+ }
+
+ @Override
+ public Class getAnnotation() {
+ return ConfigurationPropertiesInject.class;
+ }
+ }
+
+ public static class ConfigurationPropertiesInjectFilter implements ClientRequestFilter {
+ @ConfigurationPropertiesInject
+ ConfigurationProperties properties;
+
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.abortWith(Response.ok(properties.getProperties().get(PROPERTY_NAME)).build());
+ }
+ }
+
+ @Test
+ public void testInjectionResolverBinding() {
+ ClientConfig config = new ClientConfig();
+ config.property(PROPERTY_NAME, PROPERTY_VALUE);
+ String response = ClientBuilder.newClient(config).register(new Feature() {
+ @Override
+ public boolean configure(FeatureContext context) {
+ final InjectionManager injectionManager = InjectionManagerClientProvider.getInjectionManager(context);
+ context.register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(new ConfigurationPropertiesInjectResolver(injectionManager))
+ //.to(ConfigurationProperties.class)
+ .to(new GenericType(){})
+ .in(Singleton.class);
+ }
+ });
+ return true;
+ }
+ }).register(ConfigurationPropertiesInjectFilter.class).target("http://test.com").request().get(String.class);
+ Assertions.assertEquals(PROPERTY_VALUE, response);
+ }
+}
diff --git a/tests/e2e-jdk-specifics/pom.xml b/tests/e2e-jdk-specifics/pom.xml
new file mode 100644
index 0000000000..015079a601
--- /dev/null
+++ b/tests/e2e-jdk-specifics/pom.xml
@@ -0,0 +1,98 @@
+
+
+
+
+ 4.0.0
+
+
+ org.glassfish.jersey.tests
+ project
+ 3.1.99-SNAPSHOT
+
+
+ e2e-jdk-specifics
+ jersey-tests-e2e-specifics
+ jar
+
+ Jersey E2E tests for testing JDK 17+ specifics
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ 1
+ false
+ false
+
+ ${http.patch.addopens}
+
+
+
+
+
+
+
+
+ org.glassfish.jersey.test-framework.providers
+ jersey-test-framework-provider-bundle
+ pom
+ test
+
+
+ org.glassfish.jersey.test-framework
+ jersey-test-framework-util
+ test
+
+
+ org.hamcrest
+ hamcrest
+ test
+
+
+ org.junit.platform
+ junit-platform-suite
+ ${junit-platform-suite.version}
+ test
+
+
+
+
+
+ jdk15-
+
+ [8, 16)
+
+
+
+
+
+
+ jdk16+
+
+ [16, )
+
+
+ --add-opens java.base/java.net=ALL-UNNAMED
+
+
+
+
+
diff --git a/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk17/HttpPatchTest.java b/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk17/HttpPatchTest.java
new file mode 100644
index 0000000000..c949958c1e
--- /dev/null
+++ b/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk17/HttpPatchTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.jdk17;
+
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+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.HttpMethod;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+public class HttpPatchTest extends JerseyTest {
+
+ public static final String PATCH_ENTITY = "HelloPatch";
+
+ @Target({ElementType.METHOD})
+ @Retention(RetentionPolicy.RUNTIME)
+ @HttpMethod(HttpMethod.PATCH)
+ public @interface PATCH {
+
+ }
+
+ @Path("/")
+ public static class HttpPatchResource {
+ @PATCH
+ public String patchEcho(String entity) {
+ return entity;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(HttpPatchResource.class);
+ }
+
+ @Test
+ void testPatchWithHttpUrlConnector() {
+ try (Response response = target()
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+ .request().method(HttpMethod.PATCH, Entity.text(PATCH_ENTITY))) {
+ MatcherAssert.assertThat(200, Matchers.equalTo(response.getStatus()));
+ response.bufferEntity();
+ System.out.println(response.readEntity(String.class));
+ MatcherAssert.assertThat(PATCH_ENTITY, Matchers.equalTo(response.readEntity(String.class)));
+ }
+ }
+}
diff --git a/tests/e2e-server/pom.xml b/tests/e2e-server/pom.xml
index 34d73a810f..64d5f26a5c 100644
--- a/tests/e2e-server/pom.xml
+++ b/tests/e2e-server/pom.xml
@@ -41,7 +41,6 @@
1falsefalse
- ${skip.e2e}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java
index 64c0422927..f8dab62fd3 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.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
@@ -60,6 +60,18 @@ public class ChunkedInputOutputTest extends JerseyTest {
*/
@Path("/test")
public static class TestResource {
+ /**
+ * Get chunk stream with a queue capacity of 2.
+ *
+ * @return chunk stream.
+ */
+ @GET
+ @Path("/testWithBuilder")
+ public ChunkedOutput getWithBuilder() {
+ return getOutput(ChunkedOutput.builder(String.class).queueCapacity(2)
+ .chunkDelimiter("\r\n".getBytes()).build());
+ }
+
/**
* Get chunk stream.
*
@@ -67,7 +79,15 @@ public static class TestResource {
*/
@GET
public ChunkedOutput get() {
- final ChunkedOutput output = new ChunkedOutput<>(String.class, "\r\n");
+ return getOutput(new ChunkedOutput<>(String.class, "\r\n"));
+ }
+
+ /**
+ * Get chunk stream.
+ *
+ * @return chunk stream.
+ */
+ private ChunkedOutput getOutput(ChunkedOutput output) {
new Thread() {
@Override
@@ -182,6 +202,19 @@ public void testChunkedOutputToSingleString() throws Exception {
"Unexpected value of chunked response unmarshalled as a single string.");
}
+ /**
+ * Test retrieving chunked response stream as a single response string, when a builder with capacity is used.
+ *
+ * @throws Exception in case of a failure during the test execution.
+ */
+ @Test
+ public void testChunkedOutputToSingleStringWithBuilder() throws Exception {
+ final String response = target().path("test/testWithBuilder").request().get(String.class);
+
+ assertEquals("test\r\ntest\r\ntest\r\n", response,
+ "Unexpected value of chunked response unmarshalled as a single string.");
+ }
+
/**
* Test retrieving chunked response stream sequentially as individual chunks using chunked input.
*
diff --git a/tests/e2e-testng/pom.xml b/tests/e2e-testng/pom.xml
index 05a15026f8..022ebc78f5 100644
--- a/tests/e2e-testng/pom.xml
+++ b/tests/e2e-testng/pom.xml
@@ -46,7 +46,6 @@
1falsefalse
- ${skip.e2e}
diff --git a/tests/e2e-tls/pom.xml b/tests/e2e-tls/pom.xml
index ffb3b3f57f..55665dde39 100644
--- a/tests/e2e-tls/pom.xml
+++ b/tests/e2e-tls/pom.xml
@@ -40,8 +40,6 @@
1false
- false
- ${skip.e2e}trueHost
@@ -51,6 +49,24 @@
+
+
+ HttpsPatch
+
+ test
+
+
+
+ **/HttpsPatchTest.java
+
+
+ true
+
+
+ ${http.patch.addopens}
+
+
+
@@ -124,6 +140,15 @@
+
+ jdk8
+
+ 8
+
+
+
+
+ jdk11+
@@ -139,6 +164,10 @@
-Djdk.tls.server.protocols=TLSv1.2
+
+ --add-opens java.base/java.net=ALL-UNNAMED
+ --add-opens java.base/sun.net.www.protocol.https=ALL-UNNAMED
+
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
index 3964fcea33..7da4a6b7db 100644
--- a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.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
@@ -74,10 +74,29 @@ public void server1Test(ConnectorProvider provider) {
ClientConfig clientConfig = new ClientConfig();
clientConfig.connectorProvider(provider);
clientConfig.property(ClientProperties.SNI_HOST_NAME, "www.host1.com");
- serverTest(clientConfig, provider, "www.host1.com");
+ serverTest(clientConfig, provider, "www.host1.com", "www.host1.com");
}
- public void serverTest(ClientConfig clientConfig, ConnectorProvider provider, String hostName) {
+ @ParameterizedTest
+ @MethodSource("getConnectors")
+ public void sniHostNamePropertyTest(ConnectorProvider provider) {
+ ClientConfig clientConfig = new ClientConfig();
+ clientConfig.connectorProvider(provider);
+ clientConfig.property(ClientProperties.SNI_HOST_NAME, "www.host3.com");
+ serverTest(clientConfig, provider, "www.host4.com", "www.host3.com");
+ }
+
+ @ParameterizedTest
+ @MethodSource("getConnectors")
+ public void turnOffSniTest(ConnectorProvider provider) {
+ ClientConfig clientConfig = new ClientConfig();
+ clientConfig.connectorProvider(provider);
+ clientConfig.property(ClientProperties.SNI_HOST_NAME, LOCALHOST);
+ serverTest(clientConfig, provider, "www.host4.com", null);
+ }
+
+
+ public void serverTest(ClientConfig clientConfig, ConnectorProvider provider, String hostName, String resultHostName) {
String newHostName = replaceWhenHostNotKnown(hostName);
final List serverNames = new LinkedList<>();
final String[] requestHostName = new String[1];
@@ -121,12 +140,14 @@ public void filter(ClientRequestContext requestContext) throws IOException {
server.stop();
- if (serverNames.isEmpty()) {
+ if (resultHostName != null && serverNames.isEmpty()) {
throw new IllegalStateException("ServerNames are empty");
+ } else if (resultHostName == null) {
+ return;
}
String clientSniName = new String(serverNames.get(0).getEncoded());
- if (!hostName.equals(clientSniName)) {
+ if (!resultHostName.equals(clientSniName)) {
throw new IllegalStateException("Unexpected client SNI name " + clientSniName);
}
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslContextPerRequestTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslContextPerRequestTest.java
index 944f284459..d2564e9360 100644
--- a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslContextPerRequestTest.java
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslContextPerRequestTest.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
@@ -53,10 +53,8 @@
import java.util.function.Supplier;
import java.util.stream.Stream;
-public class SslContextPerRequestTest extends JerseyTest {
+public class SslContextPerRequestTest extends SslParentTest {
- private SSLContext serverSslContext;
- private SSLParameters serverSslParameters;
private static final String MESSAGE = "Message for Netty with SSL";
@Override
@@ -77,33 +75,6 @@ protected Application configure() {
return new ResourceConfig(TestResource.class);
}
- @Override
- protected URI getBaseUri() {
- return UriBuilder
- .fromUri("https://localhost")
- .port(getPort())
- .build();
- }
-
- @Override
- protected Optional getSslContext() {
- if (serverSslContext == null) {
- serverSslContext = SslUtils.createServerSslContext();
- }
-
- return Optional.of(serverSslContext);
- }
-
- @Override
- protected Optional getSslParameters() {
- if (serverSslParameters == null) {
- serverSslParameters = new SSLParameters();
- serverSslParameters.setNeedClientAuth(false);
- }
-
- return Optional.of(serverSslParameters);
- }
-
public static Stream connectorProviders() {
return Stream.of(
new HttpUrlConnectorProvider(),
@@ -168,43 +139,4 @@ public void testSslOnClient(ConnectorProvider connectorProvider) {
String s = target.request().get(String.class);
Assertions.assertEquals(MESSAGE, s);
}
-
- private static class SslUtils {
-
- private static final String SERVER_IDENTITY_PATH = "server-identity.jks";
- private static final char[] SERVER_IDENTITY_PASSWORD = "secret".toCharArray();
-
- private static final String CLIENT_TRUSTSTORE_PATH = "client-truststore.jks";
- private static final char[] CLIENT_TRUSTSTORE_PASSWORD = "secret".toCharArray();
-
- private static final String KEYSTORE_TYPE = "PKCS12";
-
- private SslUtils() {}
-
- public static SSLContext createServerSslContext() {
- return new SslContextClientBuilder()
- .keyStore(getKeyStore(SERVER_IDENTITY_PATH, SERVER_IDENTITY_PASSWORD), SERVER_IDENTITY_PASSWORD)
- .get();
- }
-
- public static Supplier createClientSslContext() {
- return new SslContextClientBuilder()
- .trustStore(getKeyStore(CLIENT_TRUSTSTORE_PATH, CLIENT_TRUSTSTORE_PASSWORD));
-
- }
-
- private static KeyStore getKeyStore(String path, char[] keyStorePassword) {
- try (InputStream inputStream = getResource(path)) {
- KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
- keyStore.load(inputStream, keyStorePassword);
- return keyStore;
- } catch (Exception e) {
- throw new ProcessingException(e);
- }
- }
-
- private static InputStream getResource(String path) {
- return SslUtils.class.getClassLoader().getResourceAsStream(path);
- }
- }
}
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslParentTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslParentTest.java
new file mode 100644
index 0000000000..abfe2457c7
--- /dev/null
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslParentTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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
+ * 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.tls;
+
+import org.glassfish.jersey.client.SslContextClientBuilder;
+import org.glassfish.jersey.test.JerseyTest;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.UriBuilder;
+import java.io.InputStream;
+import java.net.URI;
+import java.security.KeyStore;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+public class SslParentTest extends JerseyTest {
+
+ protected SSLContext serverSslContext;
+ protected SSLParameters serverSslParameters;
+
+ @Override
+ protected Optional getSslContext() {
+ if (serverSslContext == null) {
+ serverSslContext = SslUtils.createServerSslContext();
+ }
+
+ return Optional.of(serverSslContext);
+ }
+
+ @Override
+ protected Optional getSslParameters() {
+ if (serverSslParameters == null) {
+ serverSslParameters = new SSLParameters();
+ serverSslParameters.setNeedClientAuth(false);
+ }
+
+ return Optional.of(serverSslParameters);
+ }
+
+ @Override
+ protected URI getBaseUri() {
+ return UriBuilder
+ .fromUri("https://localhost")
+ .port(getPort())
+ .build();
+ }
+
+ protected static class SslUtils {
+
+ private static final String SERVER_IDENTITY_PATH = "server-identity.jks";
+ private static final char[] SERVER_IDENTITY_PASSWORD = "secret".toCharArray();
+
+ private static final String CLIENT_TRUSTSTORE_PATH = "client-truststore.jks";
+ private static final char[] CLIENT_TRUSTSTORE_PASSWORD = "secret".toCharArray();
+
+ private static final String KEYSTORE_TYPE = "PKCS12";
+
+ private SslUtils() {}
+
+ public static SSLContext createServerSslContext() {
+ return new SslContextClientBuilder()
+ .keyStore(getKeyStore(SERVER_IDENTITY_PATH, SERVER_IDENTITY_PASSWORD), SERVER_IDENTITY_PASSWORD)
+ .get();
+ }
+
+ public static Supplier createClientSslContext() {
+ return new SslContextClientBuilder()
+ .trustStore(getKeyStore(CLIENT_TRUSTSTORE_PATH, CLIENT_TRUSTSTORE_PASSWORD));
+
+ }
+
+ private static KeyStore getKeyStore(String path, char[] keyStorePassword) {
+ try (InputStream inputStream = getResource(path)) {
+ KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
+ keyStore.load(inputStream, keyStorePassword);
+ return keyStore;
+ } catch (Exception e) {
+ throw new ProcessingException(e);
+ }
+ }
+
+ private static InputStream getResource(String path) {
+ return SslUtils.class.getClassLoader().getResourceAsStream(path);
+ }
+ }
+}
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/patch/HttpsPatchTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/patch/HttpsPatchTest.java
new file mode 100644
index 0000000000..37b63c8186
--- /dev/null
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/patch/HttpsPatchTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.tls.patch;
+
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.tests.e2e.tls.SslParentTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import javax.net.ssl.SSLContext;
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.function.Supplier;
+
+public class HttpsPatchTest extends SslParentTest {
+
+ public static final String PATCH_ENTITY = "HelloPatch";
+
+ @Target({ElementType.METHOD})
+ @Retention(RetentionPolicy.RUNTIME)
+ @HttpMethod(HttpMethod.PATCH)
+ public @interface PATCH {
+
+ }
+
+ @Path("/")
+ public static class HttpPatchResource {
+ @PATCH
+ public String patchEcho(String entity) {
+ return entity;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(HttpPatchResource.class);
+ }
+
+ @Test
+ void testPatchWithHttpUrlConnector() {
+ String value = System.getProperty("jersey.added.opens");
+ if (null == value) {
+ System.out.println("JDK add-opens not set - exiting...");
+ return;
+ }
+
+ Supplier clientSslContext = SslUtils.createClientSslContext();
+ try (Response response = target()
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+ .property(ClientProperties.SSL_CONTEXT_SUPPLIER, clientSslContext)
+ .request().method(HttpMethod.PATCH, Entity.text(PATCH_ENTITY))) {
+ MatcherAssert.assertThat(200, Matchers.equalTo(response.getStatus()));
+ response.bufferEntity();
+ System.out.println(response.readEntity(String.class));
+ MatcherAssert.assertThat(PATCH_ENTITY, Matchers.equalTo(response.readEntity(String.class)));
+ }
+ }
+}
diff --git a/tests/e2e/pom.xml b/tests/e2e/pom.xml
index f17cda47bc..1d0afddf3b 100644
--- a/tests/e2e/pom.xml
+++ b/tests/e2e/pom.xml
@@ -41,10 +41,10 @@
1falsefalse
- ${skip.e2e}org/glassfish/jersey/tests/e2e/server/wadl/NoJAXBNoWadlTest.java
+ org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java
@@ -211,6 +211,7 @@
org/glassfish/jersey/tests/e2e/container/Jersey2462Test.javaorg/glassfish/jersey/tests/e2e/container/JettyEmptyHeaderParamTest.java
+ org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java
new file mode 100644
index 0000000000..2c4cc35cbb
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.inject;
+
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.jupiter.api.Test;
+
+import jakarta.inject.Inject;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.container.DynamicFeature;
+import jakarta.ws.rs.container.ResourceInfo;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.FeatureContext;
+import jakarta.ws.rs.core.GenericType;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SingleRequestScopeInjectionTest extends JerseyTest {
+ @Path("hello")
+ public static class HelloResource {
+ @GET
+ public String getHello() {
+ return "Hello World!";
+ }
+ }
+ @Override
+ protected Application configure() {
+ ResourceConfig resourceConfig = new ResourceConfig(HelloResource.class);
+ resourceConfig.register(new InjectedFilterRegistrar(InjectedFilter.class));
+ return resourceConfig;
+ }
+ @Override
+ protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+ return new JettyTestContainerFactory();
+ }
+ @Test
+ public void test() {
+ final String hello = target("hello").request().get(String.class);
+ assertEquals("Hello World!", hello);
+ }
+ public static class InjectedFilter implements ContainerRequestFilter {
+ @Inject
+ private InjectionManager injectionManager;
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws IOException {
+ Ref requestRef =
+ injectionManager.getInstance((new GenericType>() {}).getType());
+ if (requestRef == null || requestRef.get() == null) {
+ throw new IllegalStateException("Request not injected");
+ }
+ }
+ }
+ public static class InjectedFilterRegistrar implements DynamicFeature {
+ private final Class> filterToRegister;
+ public InjectedFilterRegistrar(Class> filterToRegister) {
+ this.filterToRegister = filterToRegister;
+ }
+ @Override
+ public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+ context.register(filterToRegister);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/integration/tracing-support/pom.xml b/tests/integration/tracing-support/pom.xml
index a7e5a9cfb2..83e185fe75 100644
--- a/tests/integration/tracing-support/pom.xml
+++ b/tests/integration/tracing-support/pom.xml
@@ -43,10 +43,25 @@
jersey-test-framework-provider-externaltest
+
+
+ org.glassfish.jersey.test-framework.providers
+ jersey-test-framework-provider-grizzly2
+ test
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ ${project.build.testOutputDirectory}/logging.properties
+
+
+ org.apache.maven.pluginsmaven-compiler-plugin
@@ -89,13 +104,32 @@
- jdk11
+ jdk11+
- [11,21)
+ [11,)
-
- ${jetty.tracing.version}
-
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+
+ org/glassfish/jersey/tests/integration/tracing/AllTracingSupportITCase.java
+
+
+
+
+
+
diff --git a/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/TracingMatchResourceMethodTest.java b/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/TracingMatchResourceMethodTest.java
new file mode 100644
index 0000000000..6afcd91fed
--- /dev/null
+++ b/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/TracingMatchResourceMethodTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.integration.tracing;
+
+import org.glassfish.jersey.message.internal.TracingLogger;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.TracingConfig;
+import org.glassfish.jersey.server.internal.ServerTraceEvent;
+import org.glassfish.jersey.test.JerseyTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+public class TracingMatchResourceMethodTest extends JerseyTest {
+ GatheringHandler handler = new GatheringHandler();
+ Logger logger;
+
+ @Path("/echo")
+ public static class TracingMatchResourceMethodResource {
+ @Path("echo")
+ @POST
+ public String echo(String entity) {
+ return entity;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(TracingMatchResourceMethodResource.class)
+ .property(ServerProperties.TRACING, TracingConfig.ALL.name())
+ .property(ServerProperties.TRACING_THRESHOLD, TracingLogger.Level.VERBOSE.name());
+ }
+
+ @Test
+ public void testEcho() {
+ logger = Logger.getLogger("org.glassfish.jersey.tracing.general");
+ logger.addHandler(handler);
+
+ try (Response r = target("echo").path("echo").request().post(Entity.entity("ECHO", MediaType.TEXT_PLAIN_TYPE))) {
+ MatcherAssert.assertThat(r.getStatus(), Matchers.equalTo(200));
+ MatcherAssert.assertThat(r.readEntity(String.class), Matchers.equalTo("ECHO"));
+ }
+
+ List matched = handler.logRecords.stream()
+ .filter(logRecord -> logRecord.getMessage().startsWith(ServerTraceEvent.MATCH_RESOURCE_METHOD.name()))
+ .collect(Collectors.toList());
+ MatcherAssert.assertThat(matched.size(), Matchers.equalTo(1));
+ }
+
+ private static class GatheringHandler extends Handler {
+
+ List logRecords = new ArrayList<>();
+
+ @Override
+ public void publish(LogRecord record) {
+ logRecords.add(record);
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() throws SecurityException {
+ }
+ }
+}
diff --git a/tests/pom.xml b/tests/pom.xml
index d5488977c4..8611c67bfc 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -42,6 +42,7 @@
e2e-core-commone2e-entitye2e-inject
+ e2e-jdk-specificse2e-servere2e-testnge2e-tls
diff --git a/tests/release-test/src/test/resources/non-bom-pom-deps.xml b/tests/release-test/src/test/resources/non-bom-pom-deps.xml
index 7ced6b0ded..e3aca57725 100644
--- a/tests/release-test/src/test/resources/non-bom-pom-deps.xml
+++ b/tests/release-test/src/test/resources/non-bom-pom-deps.xml
@@ -1,7 +1,7 @@