Merge pull request #5543 from jansupol/m242b
Merge 2.x into 3.0
diff --git a/NOTICE.md b/NOTICE.md
index a737757..472637c 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -70,7 +70,7 @@
* Project: http://www.javassist.org/
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
-Jackson JAX-RS Providers Version 2.15.3
+Jackson JAX-RS Providers Version 2.16.1
* License: Apache License, 2.0
* Project: https://github.com/FasterXML/jackson-jaxrs-providers
* Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated.
diff --git a/connectors/jdk-connector/pom.xml b/connectors/jdk-connector/pom.xml
index 54a3c95..72b29d9 100644
--- a/connectors/jdk-connector/pom.xml
+++ b/connectors/jdk-connector/pom.xml
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!--
- Copyright (c) 2011, 2023 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
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS13UrlStoresTest.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS13UrlStoresTest.java
new file mode 100644
index 0000000..b8e96c0
--- /dev/null
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS13UrlStoresTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.jdk.connector.internal;
+
+import org.glassfish.jersey.SslConfigurator;
+
+public class SslFilterTLS13UrlStoresTest extends SslFilterTest {
+
+ public SslFilterTLS13UrlStoresTest() {
+ System.setProperty("jdk.tls.server.protocols", "TLSv1.3");
+ System.setProperty("jdk.tls.client.protocols", "TLSv1.3");
+ }
+
+ @Override
+ protected SslConfigurator getSslConfigurator() {
+ return SslConfigurator.newInstance()
+ .trustStoreUrl(this.getClass().getResource("/truststore_client"))
+ .trustStorePassword("asdfgh")
+ .keyStoreUrl(this.getClass().getResource("/keystore_client"))
+ .keyStorePassword("asdfgh");
+ }
+}
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java
index ef13997..7f3b00a 100644
--- a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.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
@@ -388,11 +388,7 @@
final CountDownLatch completionLatch,
HostnameVerifier customHostnameVerifier)
throws Throwable {
- SslConfigurator sslConfig = SslConfigurator.newInstance()
- .trustStoreFile(this.getClass().getResource("/truststore_client").getPath())
- .trustStorePassword("asdfgh")
- .keyStoreFile(this.getClass().getResource("/keystore_client").getPath())
- .keyStorePassword("asdfgh");
+ SslConfigurator sslConfig = getSslConfigurator();
TransportFilter transportFilter = new TransportFilter(17_000, ThreadPoolConfig.defaultConfig(), 100_000);
final SSLParamConfigurator sslParamConfigurator = SSLParamConfigurator.builder()
@@ -481,6 +477,14 @@
return clientSocket;
}
+ protected SslConfigurator getSslConfigurator() {
+ return SslConfigurator.newInstance()
+ .trustStoreFile(this.getClass().getResource("/truststore_client").getPath())
+ .trustStorePassword("asdfgh")
+ .keyStoreFile(this.getClass().getResource("/keystore_client").getPath())
+ .keyStorePassword("asdfgh");
+ }
+
/**
* SSL echo server. It expects a message to be terminated with \n.
*/
diff --git a/core-common/src/main/java/org/glassfish/jersey/SslConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/SslConfigurator.java
index b3befea..2bef140 100644
--- a/core-common/src/main/java/org/glassfish/jersey/SslConfigurator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/SslConfigurator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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
@@ -21,6 +21,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URL;
import java.nio.file.Files;
import java.security.AccessController;
import java.security.KeyManagementException;
@@ -31,6 +32,7 @@
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;
+import java.util.Objects;
import java.util.Properties;
import java.util.logging.Logger;
@@ -241,6 +243,9 @@
private String trustStoreFile;
private String keyStoreFile;
+ private URL trustStoreUrl;
+ private URL keyStoreUrl;
+
private byte[] trustStoreBytes;
private byte[] keyStoreBytes;
@@ -321,6 +326,8 @@
this.keyPass = that.keyPass;
this.trustStoreFile = that.trustStoreFile;
this.keyStoreFile = that.keyStoreFile;
+ this.keyStoreUrl = that.keyStoreUrl;
+ this.trustStoreUrl = that.trustStoreUrl;
this.trustStoreBytes = that.trustStoreBytes;
this.keyStoreBytes = that.keyStoreBytes;
this.trustManagerFactoryAlgorithm = that.trustManagerFactoryAlgorithm;
@@ -441,8 +448,9 @@
/**
* Set the <em>trust</em> store file name.
* <p>
- * Setting a trust store instance resets any {@link #trustStore(java.security.KeyStore) trust store instance}
- * or {@link #trustStoreBytes(byte[]) trust store payload} value previously set.
+ * Setting a trust store instance resets any {@link #trustStore(java.security.KeyStore) trust store instance},
+ * {@link #trustStoreBytes(byte[]) trust store payload} or {@link #trustStoreUrl(URL) trust store url}
+ * value previously set.
* </p>
*
* @param fileName {@link java.io.File file} name of the <em>trust</em> store.
@@ -451,6 +459,26 @@
public SslConfigurator trustStoreFile(String fileName) {
this.trustStoreFile = fileName;
this.trustStoreBytes = null;
+ this.trustStoreUrl = null;
+ this.trustStore = null;
+ return this;
+ }
+
+ /**
+ * Set the <em>trust</em> store file url.
+ * <p>
+ * Setting a trust store instance resets any {@link #trustStore(java.security.KeyStore) trust store instance},
+ * {@link #trustStoreBytes(byte[]) trust store payload} or {@link #trustStoreUrl(URL) trust store url}
+ * value previously set.
+ * </p>
+ *
+ * @param url {@link java.net.URL url} link of the <em>trust</em> store.
+ * @return updated SSL configurator instance.
+ */
+ public SslConfigurator trustStoreUrl(URL url) {
+ this.trustStoreFile = null;
+ this.trustStoreBytes = null;
+ this.trustStoreUrl = url;
this.trustStore = null;
return this;
}
@@ -458,8 +486,9 @@
/**
* Set the <em>trust</em> store payload as byte array.
* <p>
- * Setting a trust store instance resets any {@link #trustStoreFile(String) trust store file}
- * or {@link #trustStore(java.security.KeyStore) trust store instance} value previously set.
+ * Setting a trust store instance resets any {@link #trustStoreFile(String) trust store file},
+ * {@link #trustStore(java.security.KeyStore) trust store instance} or {@link #trustStoreUrl(URL) trust store url}
+ * value previously set.
* </p>
*
* @param payload <em>trust</em> store payload.
@@ -468,6 +497,7 @@
public SslConfigurator trustStoreBytes(byte[] payload) {
this.trustStoreBytes = payload.clone();
this.trustStoreFile = null;
+ this.trustStoreUrl = null;
this.trustStore = null;
return this;
}
@@ -475,8 +505,8 @@
/**
* Set the <em>key</em> store file name.
* <p>
- * Setting a key store instance resets any {@link #keyStore(java.security.KeyStore) key store instance}
- * or {@link #keyStoreBytes(byte[]) key store payload} value previously set.
+ * Setting a key store instance resets any {@link #keyStore(java.security.KeyStore) key store instance},
+ * {@link #keyStoreBytes(byte[]) key store payload} or {@link #keyStoreUrl(URL) key store url} value previously set.
* </p>
*
* @param fileName {@link java.io.File file} name of the <em>key</em> store.
@@ -484,6 +514,26 @@
*/
public SslConfigurator keyStoreFile(String fileName) {
this.keyStoreFile = fileName;
+ this.keyStoreUrl = null;
+ this.keyStoreBytes = null;
+ this.keyStore = null;
+ return this;
+ }
+
+ /**
+ * Set the <em>key</em> store url.
+ * <p>
+ * Setting a key store instance resets any {@link #keyStore(java.security.KeyStore) key store instance},
+ * {@link #keyStoreBytes(byte[]) key store payload} or {@link #keyStoreFile(String) key store file}
+ * value previously set.
+ * </p>
+ *
+ * @param url {@link java.net.URL url} of the <em>key</em> store.
+ * @return updated SSL configurator instance.
+ */
+ public SslConfigurator keyStoreUrl(URL url) {
+ this.keyStoreFile = null;
+ this.keyStoreUrl = url;
this.keyStoreBytes = null;
this.keyStore = null;
return this;
@@ -492,8 +542,9 @@
/**
* Set the <em>key</em> store payload as byte array.
* <p>
- * Setting a key store instance resets any {@link #keyStoreFile(String) key store file}
- * or {@link #keyStore(java.security.KeyStore) key store instance} value previously set.
+ * Setting a key store instance resets any {@link #keyStoreFile(String) key store file},
+ * {@link #keyStore(java.security.KeyStore) key store instance} or {@link #keyStoreUrl(URL) key store url}
+ * value previously set.
* </p>
*
* @param payload <em>key</em> store payload.
@@ -501,6 +552,7 @@
*/
public SslConfigurator keyStoreBytes(byte[] payload) {
this.keyStoreBytes = payload.clone();
+ this.keyStoreUrl = null;
this.keyStoreFile = null;
this.keyStore = null;
return this;
@@ -573,8 +625,8 @@
/**
* Set the <em>key</em> store instance.
* <p>
- * Setting a key store instance resets any {@link #keyStoreFile(String) key store file}
- * or {@link #keyStoreBytes(byte[]) key store payload} value previously set.
+ * Setting a key store instance resets any {@link #keyStoreFile(String) key store file},
+ * {@link #keyStoreBytes(byte[]) key store payload} or {@link #keyStoreUrl(URL) key store url} value previously set.
* </p>
*
* @param keyStore <em>key</em> store instance.
@@ -584,15 +636,12 @@
this.keyStore = keyStore;
this.keyStoreFile = null;
this.keyStoreBytes = null;
+ this.keyStoreUrl = null;
return this;
}
/**
* Get the <em>trust</em> store instance.
- * <p>
- * Setting a trust store instance resets any {@link #trustStoreFile(String) trust store file}
- * or {@link #trustStoreBytes(byte[]) trust store payload} value previously set.
- * </p>
*
* @return <em>trust</em> store instance or {@code null} if not explicitly set.
*/
@@ -602,12 +651,16 @@
/**
* Set the <em>trust</em> store instance.
- *
+ * <p>
+ * Setting a trust store instance resets any {@link #trustStoreFile(String) trust store file},
+ * {@link #trustStoreBytes(byte[]) trust store payload} or {@link #trustStoreUrl(URL) trust store url} value previously set.
+ * </p>
* @param trustStore <em>trust</em> store instance.
* @return updated SSL configurator instance.
*/
public SslConfigurator trustStore(KeyStore trustStore) {
this.trustStore = trustStore;
+ this.trustStoreUrl = null;
this.trustStoreFile = null;
this.trustStoreBytes = null;
return this;
@@ -623,7 +676,7 @@
KeyManagerFactory keyManagerFactory = null;
KeyStore _keyStore = keyStore;
- if (_keyStore == null && (keyStoreBytes != null || keyStoreFile != null)) {
+ if (_keyStore == null && (keyStoreBytes != null || keyStoreFile != null || keyStoreUrl != null)) {
try {
if (keyStoreProvider != null) {
_keyStore = KeyStore.getInstance(
@@ -635,6 +688,8 @@
try {
if (keyStoreBytes != null) {
keyStoreInputStream = new ByteArrayInputStream(keyStoreBytes);
+ } else if (keyStoreUrl != null) {
+ keyStoreInputStream = keyStoreUrl.openStream();
} else if (!keyStoreFile.equals("NONE")) {
keyStoreInputStream = Files.newInputStream(new File(keyStoreFile).toPath());
}
@@ -697,7 +752,7 @@
}
KeyStore _trustStore = trustStore;
- if (_trustStore == null && (trustStoreBytes != null || trustStoreFile != null)) {
+ if (_trustStore == null && (trustStoreBytes != null || trustStoreFile != null || trustStoreUrl != null)) {
try {
if (trustStoreProvider != null) {
_trustStore = KeyStore.getInstance(
@@ -710,6 +765,8 @@
try {
if (trustStoreBytes != null) {
trustStoreInputStream = new ByteArrayInputStream(trustStoreBytes);
+ } else if (trustStoreUrl != null) {
+ trustStoreInputStream = trustStoreUrl.openStream();
} else if (!trustStoreFile.equals("NONE")) {
trustStoreInputStream = Files.newInputStream(new File(trustStoreFile).toPath());
}
@@ -808,6 +865,9 @@
trustStoreFile = props.getProperty(TRUST_STORE_FILE);
keyStoreFile = props.getProperty(KEY_STORE_FILE);
+ keyStoreUrl = null;
+ trustStoreUrl = null;
+
trustStoreBytes = null;
keyStoreBytes = null;
@@ -857,6 +917,9 @@
trustStoreFile = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(TRUST_STORE_FILE));
keyStoreFile = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(KEY_STORE_FILE));
+ trustStoreUrl = null;
+ keyStoreUrl = null;
+
trustStoreBytes = null;
keyStoreBytes = null;
@@ -876,91 +939,35 @@
if (o == null || getClass() != o.getClass()) {
return false;
}
-
SslConfigurator that = (SslConfigurator) o;
-
- if (keyManagerFactoryAlgorithm != null
- ? !keyManagerFactoryAlgorithm.equals(that.keyManagerFactoryAlgorithm) : that.keyManagerFactoryAlgorithm != null) {
- return false;
- }
- if (keyManagerFactoryProvider != null
- ? !keyManagerFactoryProvider.equals(that.keyManagerFactoryProvider) : that.keyManagerFactoryProvider != null) {
- return false;
- }
- if (!Arrays.equals(keyPass, that.keyPass)) {
- return false;
- }
- if (keyStore != null ? !keyStore.equals(that.keyStore) : that.keyStore != null) {
- return false;
- }
- if (!Arrays.equals(keyStoreBytes, that.keyStoreBytes)) {
- return false;
- }
- if (keyStoreFile != null ? !keyStoreFile.equals(that.keyStoreFile) : that.keyStoreFile != null) {
- return false;
- }
- if (!Arrays.equals(keyStorePass, that.keyStorePass)) {
- return false;
- }
- if (keyStoreProvider != null ? !keyStoreProvider.equals(that.keyStoreProvider) : that.keyStoreProvider != null) {
- return false;
- }
- if (keyStoreType != null ? !keyStoreType.equals(that.keyStoreType) : that.keyStoreType != null) {
- return false;
- }
- if (securityProtocol != null ? !securityProtocol.equals(that.securityProtocol) : that.securityProtocol != null) {
- return false;
- }
- if (trustManagerFactoryAlgorithm != null ? !trustManagerFactoryAlgorithm.equals(that.trustManagerFactoryAlgorithm)
- : that.trustManagerFactoryAlgorithm != null) {
- return false;
- }
- if (trustManagerFactoryProvider != null ? !trustManagerFactoryProvider.equals(that.trustManagerFactoryProvider)
- : that.trustManagerFactoryProvider != null) {
- return false;
- }
- if (trustStore != null ? !trustStore.equals(that.trustStore) : that.trustStore != null) {
- return false;
- }
- if (!Arrays.equals(trustStoreBytes, that.trustStoreBytes)) {
- return false;
- }
- if (trustStoreFile != null ? !trustStoreFile.equals(that.trustStoreFile) : that.trustStoreFile != null) {
- return false;
- }
- if (!Arrays.equals(trustStorePass, that.trustStorePass)) {
- return false;
- }
- if (trustStoreProvider != null ? !trustStoreProvider.equals(that.trustStoreProvider) : that.trustStoreProvider != null) {
- return false;
- }
- if (trustStoreType != null ? !trustStoreType.equals(that.trustStoreType) : that.trustStoreType != null) {
- return false;
- }
-
- return true;
+ return Objects.equals(keyStore, that.keyStore)
+ && Objects.equals(trustStore, that.trustStore)
+ && Objects.equals(trustStoreProvider, that.trustStoreProvider)
+ && Objects.equals(keyStoreProvider, that.keyStoreProvider)
+ && Objects.equals(trustStoreType, that.trustStoreType)
+ && Objects.equals(keyStoreType, that.keyStoreType)
+ && Arrays.equals(trustStorePass, that.trustStorePass)
+ && Arrays.equals(keyStorePass, that.keyStorePass)
+ && Arrays.equals(keyPass, that.keyPass)
+ && Objects.equals(trustStoreFile, that.trustStoreFile)
+ && Objects.equals(keyStoreFile, that.keyStoreFile)
+ && Objects.equals(trustStoreUrl, that.trustStoreUrl)
+ && Objects.equals(keyStoreUrl, that.keyStoreUrl)
+ && Arrays.equals(trustStoreBytes, that.trustStoreBytes)
+ && Arrays.equals(keyStoreBytes, that.keyStoreBytes)
+ && Objects.equals(trustManagerFactoryAlgorithm, that.trustManagerFactoryAlgorithm)
+ && Objects.equals(keyManagerFactoryAlgorithm, that.keyManagerFactoryAlgorithm)
+ && Objects.equals(trustManagerFactoryProvider, that.trustManagerFactoryProvider)
+ && Objects.equals(keyManagerFactoryProvider, that.keyManagerFactoryProvider)
+ && Objects.equals(securityProtocol, that.securityProtocol);
}
@Override
public int hashCode() {
- int result = keyStore != null ? keyStore.hashCode() : 0;
- result = 31 * result + (trustStore != null ? trustStore.hashCode() : 0);
- result = 31 * result + (trustStoreProvider != null ? trustStoreProvider.hashCode() : 0);
- result = 31 * result + (keyStoreProvider != null ? keyStoreProvider.hashCode() : 0);
- result = 31 * result + (trustStoreType != null ? trustStoreType.hashCode() : 0);
- result = 31 * result + (keyStoreType != null ? keyStoreType.hashCode() : 0);
- result = 31 * result + (trustStorePass != null ? Arrays.hashCode(trustStorePass) : 0);
- result = 31 * result + (keyStorePass != null ? Arrays.hashCode(keyStorePass) : 0);
- result = 31 * result + (keyPass != null ? Arrays.hashCode(keyPass) : 0);
- result = 31 * result + (trustStoreFile != null ? trustStoreFile.hashCode() : 0);
- result = 31 * result + (keyStoreFile != null ? keyStoreFile.hashCode() : 0);
- result = 31 * result + (trustStoreBytes != null ? Arrays.hashCode(trustStoreBytes) : 0);
- result = 31 * result + (keyStoreBytes != null ? Arrays.hashCode(keyStoreBytes) : 0);
- result = 31 * result + (trustManagerFactoryAlgorithm != null ? trustManagerFactoryAlgorithm.hashCode() : 0);
- result = 31 * result + (keyManagerFactoryAlgorithm != null ? keyManagerFactoryAlgorithm.hashCode() : 0);
- result = 31 * result + (trustManagerFactoryProvider != null ? trustManagerFactoryProvider.hashCode() : 0);
- result = 31 * result + (keyManagerFactoryProvider != null ? keyManagerFactoryProvider.hashCode() : 0);
- result = 31 * result + (securityProtocol != null ? securityProtocol.hashCode() : 0);
+ int result = Objects.hash(keyStore, trustStore, trustStoreProvider, keyStoreProvider, trustStoreType, keyStoreType,
+ trustStoreFile, keyStoreFile, trustStoreUrl, keyStoreUrl, trustManagerFactoryAlgorithm,
+ keyManagerFactoryAlgorithm, trustManagerFactoryProvider, keyManagerFactoryProvider, securityProtocol,
+ trustStorePass, keyStorePass, keyPass, trustStoreBytes, keyStoreBytes);
return result;
}
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
index 4d9606b..78a628d 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -29,6 +29,7 @@
"org.glassfish.jersey.servlet.ServletProperties",
"org.glassfish.jersey.message.MessageProperties",
"org.glassfish.jersey.apache.connector.ApacheClientProperties",
+ "org.glassfish.jersey.apache5.connector.Apache5ClientProperties",
"org.glassfish.jersey.helidon.connector.HelidonClientProperties",
"org.glassfish.jersey.jdk.connector.JdkConnectorProperties",
"org.glassfish.jersey.jetty.connector.JettyClientProperties",
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
index 9eb4a40..8f31f74 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.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
@@ -23,17 +23,13 @@
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
-import java.net.URI;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
@@ -41,21 +37,16 @@
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.core.Configuration;
-import jakarta.ws.rs.core.Cookie;
-import jakarta.ws.rs.core.EntityTag;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Link;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.ext.ReaderInterceptor;
-import jakarta.ws.rs.ext.RuntimeDelegate;
import javax.xml.transform.Source;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.PropertiesDelegate;
-import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
import org.glassfish.jersey.internal.util.collection.LazyValue;
import org.glassfish.jersey.internal.util.collection.Value;
@@ -67,7 +58,7 @@
*
* @author Marek Potociar
*/
-public abstract class InboundMessageContext {
+public abstract class InboundMessageContext extends MessageHeaderMethods {
private static final InputStream EMPTY = new InputStream() {
@@ -100,7 +91,6 @@
private final boolean translateNce;
private MessageBodyWorkers workers;
private final Configuration configuration;
- private final RuntimeDelegate runtimeDelegateDecorator;
private LazyValue<MediaType> contentTypeCache;
private LazyValue<List<AcceptableMediaType>> acceptTypeCache;
@@ -166,11 +156,11 @@
* as required by JAX-RS specification on the server side.
*/
public InboundMessageContext(Configuration configuration, boolean translateNce) {
+ super(configuration);
this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createInbound());
this.entityContent = new EntityContent();
this.translateNce = translateNce;
this.configuration = configuration;
- runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
contentTypeCache = contentTypeCache();
acceptTypeCache = acceptTypeCache();
@@ -319,42 +309,9 @@
return buffer.toString();
}
- /**
- * Get a single typed header value.
- *
- * @param name header name.
- * @param converter from string conversion function. Is expected to throw {@link ProcessingException}
- * if conversion fails.
- * @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
- * method returns the {@code null} without calling the converter.
- * @return value of the header, or (possibly converted) {@code null} if not present.
- */
- private <T> T singleHeader(String name, Function<String, T> converter, boolean convertNull) {
- final List<String> values = this.headers.get(name);
-
- if (values == null || values.isEmpty()) {
- return convertNull ? converter.apply(null) : null;
- }
- if (values.size() > 1) {
- throw new HeaderValueException(LocalizationMessages.TOO_MANY_HEADER_VALUES(name, values.toString()),
- HeaderValueException.Context.INBOUND);
- }
-
- Object value = values.get(0);
- if (value == null) {
- return convertNull ? converter.apply(null) : null;
- }
-
- try {
- return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
- } catch (ProcessingException ex) {
- throw exception(name, value, ex);
- }
- }
-
- private static HeaderValueException exception(final String headerName, Object headerValue, Exception e) {
- return new HeaderValueException(LocalizationMessages.UNABLE_TO_PARSE_HEADER_VALUE(headerName, headerValue), e,
- HeaderValueException.Context.INBOUND);
+ @Override
+ public HeaderValueException.Context getHeaderValueExceptionContext() {
+ return HeaderValueException.Context.INBOUND;
}
/**
@@ -367,24 +324,6 @@
}
/**
- * Get message date.
- *
- * @return the message date, otherwise {@code null} if not present.
- */
- public Date getDate() {
- return singleHeader(HttpHeaders.DATE, new Function<String, Date>() {
- @Override
- public Date apply(String input) {
- try {
- return HttpHeaderReader.readDate(input);
- } catch (ParseException ex) {
- throw new ProcessingException(ex);
- }
- }
- }, false);
- }
-
- /**
* Get If-Match header.
*
* @return the If-Match header value, otherwise {@code null} if not present.
@@ -419,42 +358,6 @@
}
/**
- * Get the language of the entity.
- *
- * @return the language of the entity or {@code null} if not specified.
- */
- public Locale getLanguage() {
- return singleHeader(HttpHeaders.CONTENT_LANGUAGE, new Function<String, Locale>() {
- @Override
- public Locale apply(String input) {
- try {
- return new LanguageTag(input).getAsLocale();
- } catch (ParseException e) {
- throw new ProcessingException(e);
- }
- }
- }, false);
- }
-
- /**
- * Get Content-Length value.
- *
- * @return Content-Length as integer if present and valid number. In other cases returns -1.
- */
- public int getLength() {
- return singleHeader(HttpHeaders.CONTENT_LENGTH, new Function<String, Integer>() {
- @Override
- public Integer apply(String input) {
- try {
- return (input != null && !input.isEmpty()) ? Integer.parseInt(input) : -1;
- } catch (NumberFormatException ex) {
- throw new ProcessingException(ex);
- }
- }
- }, true);
- }
-
- /**
* Get the media type of the entity.
*
* @return the media type or {@code null} if not specified (e.g. there's no
@@ -569,120 +472,6 @@
}
/**
- * Get any cookies that accompanied the request.
- *
- * @return a read-only map of cookie name (String) to {@link jakarta.ws.rs.core.Cookie}.
- */
- public Map<String, Cookie> getRequestCookies() {
- List<String> cookies = this.headers.get(HttpHeaders.COOKIE);
- if (cookies == null || cookies.isEmpty()) {
- return Collections.emptyMap();
- }
-
- Map<String, Cookie> result = new HashMap<String, Cookie>();
- for (String cookie : cookies) {
- if (cookie != null) {
- result.putAll(HttpHeaderReader.readCookies(cookie));
- }
- }
- return result;
- }
-
- /**
- * Get the allowed HTTP methods from the Allow HTTP header.
- *
- * @return the allowed HTTP methods, all methods will returned as upper case
- * strings.
- */
- public Set<String> getAllowedMethods() {
- final String allowed = getHeaderString(HttpHeaders.ALLOW);
- if (allowed == null || allowed.isEmpty()) {
- return Collections.emptySet();
- }
- try {
- return new HashSet<String>(HttpHeaderReader.readStringList(allowed.toUpperCase(Locale.ROOT)));
- } catch (java.text.ParseException e) {
- throw exception(HttpHeaders.ALLOW, allowed, e);
- }
- }
-
- /**
- * Get any new cookies set on the response message.
- *
- * @return a read-only map of cookie name (String) to a {@link jakarta.ws.rs.core.NewCookie new cookie}.
- */
- public Map<String, NewCookie> getResponseCookies() {
- List<String> cookies = this.headers.get(HttpHeaders.SET_COOKIE);
- if (cookies == null || cookies.isEmpty()) {
- return Collections.emptyMap();
- }
-
- Map<String, NewCookie> result = new HashMap<String, NewCookie>();
- for (String cookie : cookies) {
- if (cookie != null) {
- NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
- String cookieName = newCookie.getName();
- if (result.containsKey(cookieName)) {
- result.put(cookieName, HeaderUtils.getPreferredCookie(result.get(cookieName), newCookie));
- } else {
- result.put(cookieName, newCookie);
- }
- }
- }
- return result;
- }
-
- /**
- * Get the entity tag.
- *
- * @return the entity tag, otherwise {@code null} if not present.
- */
- public EntityTag getEntityTag() {
- return singleHeader(HttpHeaders.ETAG, new Function<String, EntityTag>() {
- @Override
- public EntityTag apply(String value) {
- return EntityTag.valueOf(value);
- }
- }, false);
- }
-
- /**
- * Get the last modified date.
- *
- * @return the last modified date, otherwise {@code null} if not present.
- */
- public Date getLastModified() {
- return singleHeader(HttpHeaders.LAST_MODIFIED, new Function<String, Date>() {
- @Override
- public Date apply(String input) {
- try {
- return HttpHeaderReader.readDate(input);
- } catch (ParseException e) {
- throw new ProcessingException(e);
- }
- }
- }, false);
- }
-
- /**
- * Get the location.
- *
- * @return the location URI, otherwise {@code null} if not present.
- */
- public URI getLocation() {
- return singleHeader(HttpHeaders.LOCATION, new Function<String, URI>() {
- @Override
- public URI apply(String value) {
- try {
- return URI.create(value);
- } catch (IllegalArgumentException ex) {
- throw new ProcessingException(ex);
- }
- }
- }, false);
- }
-
- /**
* Get the links attached to the message as header.
*
* @return links, may return empty {@link java.util.Set} if no links are present. Never
@@ -726,57 +515,6 @@
}
}
- /**
- * Check if link for relation exists.
- *
- * @param relation link relation.
- * @return {@code true} if the for the relation link exists, {@code false}
- * otherwise.
- */
- public boolean hasLink(String relation) {
- for (Link link : getLinks()) {
- List<String> relations = LinkProvider.getLinkRelations(link.getRel());
-
- if (relations != null && relations.contains(relation)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Get the link for the relation.
- *
- * @param relation link relation.
- * @return the link for the relation, otherwise {@code null} if not present.
- */
- public Link getLink(String relation) {
- for (Link link : getLinks()) {
- List<String> relations = LinkProvider.getLinkRelations(link.getRel());
- if (relations != null && relations.contains(relation)) {
- return link;
- }
- }
- return null;
- }
-
- /**
- * Convenience method that returns a {@link jakarta.ws.rs.core.Link.Builder Link.Builder}
- * for the relation.
- *
- * @param relation link relation.
- * @return the link builder for the relation, otherwise {@code null} if not
- * present.
- */
- public Link.Builder getLinkBuilder(String relation) {
- Link link = getLink(relation);
- if (link == null) {
- return null;
- }
-
- return Link.fromLink(link);
- }
-
// Message entity
/**
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageHeaderMethods.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageHeaderMethods.java
new file mode 100644
index 0000000..cd23505
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageHeaderMethods.java
@@ -0,0 +1,461 @@
+/*
+ * 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.message.internal;
+
+import org.glassfish.jersey.internal.LocalizationMessages;
+import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Cookie;
+import jakarta.ws.rs.core.EntityTag;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Link;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.NewCookie;
+import jakarta.ws.rs.ext.RuntimeDelegate;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Common header methods for outbound and inbound messages.
+ */
+public abstract class MessageHeaderMethods {
+ protected RuntimeDelegate runtimeDelegateDecorator;
+
+ protected MessageHeaderMethods(Configuration configuration) {
+ this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
+ }
+
+ protected MessageHeaderMethods(MessageHeaderMethods other) {
+ this.runtimeDelegateDecorator = other.runtimeDelegateDecorator;
+ }
+
+ /**
+ * Get a message header as a single string value.
+ *
+ * Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
+ * is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
+ * class or using its {@code toString} method if a header delegate is not available.
+ *
+ * @param name the message header.
+ * @return the message header value. If the message header is not present then {@code null} is returned. If the message
+ * header is present but has no value then the empty string is returned. If the message header is present more than once
+ * then the values of joined together and separated by a ',' character.
+ */
+ public abstract String getHeaderString(String name);
+
+ /**
+ * Get the mutable message headers multivalued map.
+ *
+ * @return mutable multivalued map of message headers.
+ */
+ public abstract MultivaluedMap<String, ?> getHeaders();
+
+ /**
+ * Return {@link HeaderValueException.Context} type of the message context.
+ * @return {@link HeaderValueException.Context} type of the message context.
+ */
+ protected abstract HeaderValueException.Context getHeaderValueExceptionContext();
+
+ /**
+ * Get the links attached to the message as header.
+ *
+ * @return links, may return empty {@link java.util.Set} if no links are present. Never
+ * returns {@code null}.
+ */
+ public abstract Set<Link> getLinks();
+
+ /**
+ * Checks whether a header with a specific name and value (or item of the token-separated value list) exists.
+ *
+ * Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
+ * is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
+ * class or using its {@code toString} method if a header delegate is not available.
+ *
+ * <p>
+ * For example: {@code containsHeaderString("cache-control", ",", "no-store"::equalsIgnoreCase)} will return {@code true} if
+ * a {@code Cache-Control} header exists that has the value {@code no-store}, the value {@code No-Store} or the value
+ * {@code Max-Age, NO-STORE, no-transform}, but {@code false} when it has the value {@code no-store;no-transform}
+ * (missing comma), or the value {@code no - store} (whitespace within value).
+ *
+ * @param name the message header.
+ * @param valueSeparatorRegex Separates the header value into single values. {@code null} does not split.
+ * @param valuePredicate value must fulfil this predicate.
+ * @return {@code true} if and only if a header with the given name exists, having either a whitespace-trimmed value
+ * matching the predicate, or having at least one whitespace-trimmed single value in a token-separated list of single values.
+ */
+ public boolean containsHeaderString(String name, String valueSeparatorRegex, Predicate<String> valuePredicate) {
+ final String header = getHeaderString(name);
+ if (header == null) {
+ return false;
+ }
+ final String[] split = header.split(valueSeparatorRegex);
+ for (String s : split) {
+ if (valuePredicate.test(s.trim())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether a header with a specific name and value (or item of the comma-separated value list) exists.
+ *
+ * Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
+ * is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
+ * class or using its {@code toString} method if a header delegate is not available.
+ *
+ * <p>
+ * For example: {@code containsHeaderString("cache-control", "no-store"::equalsIgnoreCase)} will return {@code true} if
+ * a {@code Cache-Control} header exists that has the value {@code no-store}, the value {@code No-Store} or the value
+ * {@code Max-Age, NO-STORE, no-transform}, but {@code false} when it has the value {@code no-store;no-transform}
+ * (missing comma), or the value {@code no - store} (whitespace within value).
+ *
+ * @param name the message header.
+ * @param valuePredicate value must fulfil this predicate.
+ * @return {@code true} if and only if a header with the given name exists, having either a whitespace-trimmed value
+ * matching the predicate, or having at least one whitespace-trimmed single value in a comma-separated list of single values.
+ */
+ public boolean containsHeaderString(String name, Predicate<String> valuePredicate) {
+ return containsHeaderString(name, ",", valuePredicate);
+ }
+
+ /**
+ * Get the allowed HTTP methods from the Allow HTTP header.
+ *
+ * @return the allowed HTTP methods, all methods will returned as upper case
+ * strings.
+ */
+ public Set<String> getAllowedMethods() {
+ final String allowed = getHeaderString(HttpHeaders.ALLOW);
+ if (allowed == null || allowed.isEmpty()) {
+ return Collections.emptySet();
+ }
+ try {
+ return new HashSet<String>(HttpHeaderReader.readStringList(allowed.toUpperCase(Locale.ROOT)));
+ } catch (java.text.ParseException e) {
+ throw exception(HttpHeaders.ALLOW, allowed, e);
+ }
+ }
+
+ /**
+ * Get message date.
+ *
+ * @return the message date, otherwise {@code null} if not present.
+ */
+ public Date getDate() {
+ return singleHeader(HttpHeaders.DATE, Date.class, input -> {
+ try {
+ return HttpHeaderReader.readDate(input);
+ } catch (ParseException e) {
+ throw new ProcessingException(e);
+ }
+ }, false);
+ }
+
+ /**
+ * Get the entity tag.
+ *
+ * @return the entity tag, otherwise {@code null} if not present.
+ */
+ public EntityTag getEntityTag() {
+ return singleHeader(HttpHeaders.ETAG, EntityTag.class, new Function<String, EntityTag>() {
+ @Override
+ public EntityTag apply(String value) {
+ try {
+ return value == null ? null : EntityTag.valueOf(value);
+ } catch (IllegalArgumentException ex) {
+ throw new ProcessingException(ex);
+ }
+ }
+ }, false);
+ }
+
+ /**
+ * Get the language of the entity.
+ *
+ * @return the language of the entity or {@code null} if not specified
+ */
+ public Locale getLanguage() {
+ return singleHeader(HttpHeaders.CONTENT_LANGUAGE, Locale.class, input -> {
+ try {
+ return new LanguageTag(input).getAsLocale();
+ } catch (ParseException e) {
+ throw new ProcessingException(e);
+ }
+ }, false);
+ }
+
+ /**
+ * Get the last modified date.
+ *
+ * @return the last modified date, otherwise {@code null} if not present.
+ */
+ public Date getLastModified() {
+ return singleHeader(HttpHeaders.LAST_MODIFIED, Date.class, new Function<String, Date>() {
+ @Override
+ public Date apply(String input) {
+ try {
+ return HttpHeaderReader.readDate(input);
+ } catch (ParseException e) {
+ throw new ProcessingException(e);
+ }
+ }
+ }, false);
+ }
+
+ /**
+ * Get Content-Length value.
+ * <p>
+ * <B>Note</B>: {@link #getLengthLong() getLengthLong()}
+ * should be preferred over this method, since it returns a {@code long}
+ * instead and is therefore more portable.</P>
+ *
+ * @return Content-Length as a postive integer if present and valid number, {@code -1} if negative number.
+ * @throws ProcessingException when {@link Integer#parseInt(String)} (String)} throws {@link NumberFormatException}.
+ */
+ public int getLength() {
+ return singleHeader(HttpHeaders.CONTENT_LENGTH, Integer.class, input -> {
+ try {
+ if (input != null && !input.isEmpty()) {
+ int i = Integer.parseInt(input);
+ if (i >= 0) {
+ return i;
+ }
+ }
+ return -1;
+
+ } catch (NumberFormatException ex) {
+ throw new ProcessingException(ex);
+ }
+ }, true);
+ }
+
+ /**
+ * Get Content-Length value.
+ *
+ * @return Content-Length as a positive long if present and valid number, {@code -1} if negative number.
+ * @throws ProcessingException when {@link Long#parseLong(String)} throws {@link NumberFormatException}.
+ */
+ public long getLengthLong() {
+ return singleHeader(HttpHeaders.CONTENT_LENGTH, Long.class, input -> {
+ try {
+ if (input != null && !input.isEmpty()) {
+ long l = Long.parseLong(input);
+ if (l >= 0) {
+ return l;
+ }
+ }
+ return -1L;
+ } catch (NumberFormatException ex) {
+ throw new ProcessingException(ex);
+ }
+ }, true);
+ }
+
+ /**
+ * Get the link for the relation.
+ *
+ * @param relation link relation.
+ * @return the link for the relation, otherwise {@code null} if not present.
+ */
+ public Link getLink(String relation) {
+ for (Link link : getLinks()) {
+ List<String> relations = LinkProvider.getLinkRelations(link.getRel());
+ if (relations != null && relations.contains(relation)) {
+ return link;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Convenience method that returns a {@link jakarta.ws.rs.core.Link.Builder Link.Builder}
+ * for the relation.
+ *
+ * @param relation link relation.
+ * @return the link builder for the relation, otherwise {@code null} if not
+ * present.
+ */
+ public Link.Builder getLinkBuilder(String relation) {
+ Link link = getLink(relation);
+ if (link == null) {
+ return null;
+ }
+
+ return Link.fromLink(link);
+ }
+
+ /**
+ * Get the location.
+ *
+ * @return the location URI, otherwise {@code null} if not present.
+ */
+ public URI getLocation() {
+ return singleHeader(HttpHeaders.LOCATION, URI.class, value -> {
+ try {
+ return value == null ? null : URI.create(value);
+ } catch (IllegalArgumentException ex) {
+ throw new ProcessingException(ex);
+ }
+ }, false);
+ }
+
+ /**
+ * Get any cookies that accompanied the message.
+ *
+ * @return a read-only map of cookie name (String) to {@link jakarta.ws.rs.core.Cookie}.
+ */
+ public Map<String, Cookie> getRequestCookies() {
+ @SuppressWarnings("unchecked")
+ final List<Object> cookies = (List<Object>) getHeaders().get(HttpHeaders.COOKIE);
+ if (cookies == null || cookies.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, Cookie> result = new HashMap<String, Cookie>();
+ for (String cookie : toStringList(cookies)) {
+ if (cookie != null) {
+ result.putAll(HttpHeaderReader.readCookies(cookie));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get any new cookies set on the message.
+ *
+ * @return a read-only map of cookie name (String) to a {@link jakarta.ws.rs.core.NewCookie new cookie}.
+ */
+ public Map<String, NewCookie> getResponseCookies() {
+ @SuppressWarnings("unchecked")
+ List<Object> cookies = (List<Object>) getHeaders().get(HttpHeaders.SET_COOKIE);
+ if (cookies == null || cookies.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, NewCookie> result = new HashMap<String, NewCookie>();
+ for (String cookie : toStringList(cookies)) {
+ if (cookie != null) {
+ NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
+ String cookieName = newCookie.getName();
+ if (result.containsKey(cookieName)) {
+ result.put(cookieName, HeaderUtils.getPreferredCookie(result.get(cookieName), newCookie));
+ } else {
+ result.put(cookieName, newCookie);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Check if link for relation exists.
+ *
+ * @param relation link relation.
+ * @return {@code true} if the for the relation link exists, {@code false}
+ * otherwise.
+ */
+ public boolean hasLink(String relation) {
+ for (Link link : getLinks()) {
+ List<String> relations = LinkProvider.getLinkRelations(link.getRel());
+
+ if (relations != null && relations.contains(relation)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get a single typed header value.
+ *
+ * @param <T> header value type.
+ * @param name header name.
+ * @param valueType header value class.
+ * @param converter from string conversion function. Is expected to throw {@link ProcessingException}
+ * if conversion fails.
+ * @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
+ * method returns the {@code null} without calling the converter.
+ * @return value of the header, or (possibly converted) {@code null} if not present.
+ */
+ protected <T> T singleHeader(String name, Class<T> valueType, Function<String, T> converter, boolean convertNull) {
+ @SuppressWarnings("unchecked")
+ final List<Object> values = (List<Object>) getHeaders().get(name);
+
+ if (values == null || values.isEmpty()) {
+ return convertNull ? converter.apply(null) : null;
+ }
+ if (values.size() > 1) {
+ throw new HeaderValueException(
+ LocalizationMessages.TOO_MANY_HEADER_VALUES(name, values.toString()),
+ getHeaderValueExceptionContext());
+ }
+
+ Object value = values.get(0);
+ if (value == null) {
+ return convertNull ? converter.apply(null) : null;
+ }
+
+ if (HeaderValueException.Context.OUTBOUND == getHeaderValueExceptionContext() && valueType.isInstance(value)) {
+ return valueType.cast(value);
+ } else {
+ try {
+ return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
+ } catch (ProcessingException ex) {
+ throw exception(name, value, ex);
+ }
+ }
+ }
+
+ /**
+ * Get a single typed header value for Inbound messages
+ *
+ * @param <T> header value type.
+ * @param name header name.
+ * @param converter from string conversion function. Is expected to throw {@link ProcessingException}
+ * if conversion fails.
+ * @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
+ * method returns the {@code null} without calling the converter.
+ * @return value of the header, or (possibly converted) {@code null} if not present.
+ */
+ protected <T> T singleHeader(String name, Function<String, T> converter, boolean convertNull) {
+ return singleHeader(name, null, converter, convertNull);
+ }
+
+ protected HeaderValueException exception(final String headerName, Object headerValue, Exception e) {
+ return new HeaderValueException(LocalizationMessages.UNABLE_TO_PARSE_HEADER_VALUE(headerName, headerValue), e,
+ getHeaderValueExceptionContext());
+ }
+
+ private List<String> toStringList(List list) {
+ return getHeaderValueExceptionContext() == HeaderValueException.Context.OUTBOUND
+ ? HeaderUtils.asStringList(list, runtimeDelegateDecorator)
+ : (List<String>) list;
+ }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
index d33acc3..c69f173 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
@@ -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
@@ -20,38 +20,27 @@
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
-import java.net.URI;
-import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
-import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.core.Configuration;
-import jakarta.ws.rs.core.Cookie;
-import jakarta.ws.rs.core.EntityTag;
import jakarta.ws.rs.core.GenericEntity;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Link;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.NewCookie;
-import jakarta.ws.rs.ext.RuntimeDelegate;
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
-import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
import org.glassfish.jersey.internal.util.collection.LazyValue;
@@ -63,7 +52,7 @@
*
* @author Marek Potociar
*/
-public class OutboundMessageContext {
+public class OutboundMessageContext extends MessageHeaderMethods {
private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
private static final List<MediaType> WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST =
Collections.<MediaType>singletonList(MediaTypes.WILDCARD_ACCEPTABLE_TYPE);
@@ -71,7 +60,6 @@
private final GuardianStringKeyMultivaluedMap<Object> headers;
private final CommittingOutputStream committingOutputStream;
private Configuration configuration;
- private RuntimeDelegate runtimeDelegateDecorator;
private LazyValue<MediaType> mediaTypeCache;
private Object entity;
@@ -107,11 +95,11 @@
* @param configuration the client/server {@link Configuration}. If {@code null}, the default behaviour is expected.
*/
public OutboundMessageContext(Configuration configuration) {
+ super(configuration);
this.configuration = configuration;
this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createOutbound());
this.committingOutputStream = new CommittingOutputStream();
this.entityStream = committingOutputStream;
- this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
this.mediaTypeCache = mediaTypeCache();
headers.setGuard(HttpHeaders.CONTENT_TYPE);
@@ -124,6 +112,7 @@
* @param original the original outbound message context.
*/
public OutboundMessageContext(OutboundMessageContext original) {
+ super(original);
this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createOutbound());
this.headers.setGuard(HttpHeaders.CONTENT_TYPE);
this.headers.putAll(original.headers);
@@ -134,7 +123,6 @@
this.entityType = original.entityType;
this.entityAnnotations = original.entityAnnotations;
this.configuration = original.configuration;
- this.runtimeDelegateDecorator = original.runtimeDelegateDecorator;
this.mediaTypeCache = mediaTypeCache();
}
@@ -190,49 +178,9 @@
return HeaderUtils.asHeaderString(headers.get(name), runtimeDelegateDecorator);
}
- /**
- * Get a single typed header value.
- *
- * @param <T> header value type.
- * @param name header name.
- * @param valueType header value class.
- * @param converter from string conversion function. Is expected to throw {@link ProcessingException}
- * if conversion fails.
- * @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
- * method returns the {@code null} without calling the converter.
- * @return value of the header, or (possibly converted) {@code null} if not present.
- */
- private <T> T singleHeader(String name, Class<T> valueType, Function<String, T> converter, boolean convertNull) {
- final List<Object> values = headers.get(name);
-
- if (values == null || values.isEmpty()) {
- return convertNull ? converter.apply(null) : null;
- }
- if (values.size() > 1) {
- throw new HeaderValueException(
- LocalizationMessages.TOO_MANY_HEADER_VALUES(name, values.toString()),
- HeaderValueException.Context.OUTBOUND);
- }
-
- Object value = values.get(0);
- if (value == null) {
- return convertNull ? converter.apply(null) : null;
- }
-
- if (valueType.isInstance(value)) {
- return valueType.cast(value);
- } else {
- try {
- return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
- } catch (ProcessingException ex) {
- throw exception(name, value, ex);
- }
- }
- }
-
- private static HeaderValueException exception(final String headerName, Object headerValue, Exception e) {
- return new HeaderValueException(LocalizationMessages.UNABLE_TO_PARSE_HEADER_VALUE(headerName, headerValue), e,
- HeaderValueException.Context.OUTBOUND);
+ @Override
+ public HeaderValueException.Context getHeaderValueExceptionContext() {
+ return HeaderValueException.Context.OUTBOUND;
}
/**
@@ -245,36 +193,6 @@
}
/**
- * Get message date.
- *
- * @return the message date, otherwise {@code null} if not present.
- */
- public Date getDate() {
- return singleHeader(HttpHeaders.DATE, Date.class, input -> {
- try {
- return HttpHeaderReader.readDate(input);
- } catch (ParseException e) {
- throw new ProcessingException(e);
- }
- }, false);
- }
-
- /**
- * Get the language of the entity.
- *
- * @return the language of the entity or {@code null} if not specified
- */
- public Locale getLanguage() {
- return singleHeader(HttpHeaders.CONTENT_LANGUAGE, Locale.class, input -> {
- try {
- return new LanguageTag(input).getAsLocale();
- } catch (ParseException e) {
- throw new ProcessingException(e);
- }
- }, false);
- }
-
- /**
* Get the media type of the entity.
*
* @return the media type or {@code null} if not specified (e.g. there's no
@@ -378,174 +296,6 @@
}
/**
- * Get any cookies that accompanied the message.
- *
- * @return a read-only map of cookie name (String) to {@link jakarta.ws.rs.core.Cookie}.
- */
- public Map<String, Cookie> getRequestCookies() {
- final List<Object> cookies = headers.get(HttpHeaders.COOKIE);
- if (cookies == null || cookies.isEmpty()) {
- return Collections.emptyMap();
- }
-
- Map<String, Cookie> result = new HashMap<String, Cookie>();
- for (String cookie : HeaderUtils.asStringList(cookies, runtimeDelegateDecorator)) {
- if (cookie != null) {
- result.putAll(HttpHeaderReader.readCookies(cookie));
- }
- }
- return result;
- }
-
- /**
- * Get the allowed HTTP methods from the Allow HTTP header.
- *
- * @return the allowed HTTP methods, all methods will returned as upper case
- * strings.
- */
- public Set<String> getAllowedMethods() {
- final String allowed = getHeaderString(HttpHeaders.ALLOW);
- if (allowed == null || allowed.isEmpty()) {
- return Collections.emptySet();
- }
- try {
- return new HashSet<String>(HttpHeaderReader.readStringList(allowed));
- } catch (java.text.ParseException e) {
- throw exception(HttpHeaders.ALLOW, allowed, e);
- }
- }
-
- /**
- * Get Content-Length value.
- * <p>
- * <B>Note</B>: {@link #getLengthLong() getLengthLong()}
- * should be preferred over this method, since it returns a {@code long}
- * instead and is therefore more portable.</P>
- *
- * @return Content-Length as a postive integer if present and valid number, {@code -1} if negative number.
- * @throws ProcessingException when {@link Integer#parseInt(String)} (String)} throws {@link NumberFormatException}.
- */
- public int getLength() {
-
- return singleHeader(HttpHeaders.CONTENT_LENGTH, Integer.class, input -> {
- try {
- if (input != null && !input.isEmpty()) {
- int i = Integer.parseInt(input);
- if (i >= 0) {
- return i;
- }
- }
- return -1;
-
- } catch (NumberFormatException ex) {
- throw new ProcessingException(ex);
- }
- }, true);
- }
-
- /**
- * Get Content-Length value.
- *
- * @return Content-Length as a positive long if present and valid number, {@code -1} if negative number.
- * @throws ProcessingException when {@link Long#parseLong(String)} throws {@link NumberFormatException}.
- */
- public long getLengthLong() {
- return singleHeader(HttpHeaders.CONTENT_LENGTH, Long.class, input -> {
- try {
- if (input != null && !input.isEmpty()) {
- long l = Long.parseLong(input);
- if (l >= 0) {
- return l;
- }
- }
- return -1L;
- } catch (NumberFormatException ex) {
- throw new ProcessingException(ex);
- }
- }, true);
- }
-
- /**
- * Get any new cookies set on the message message.
- *
- * @return a read-only map of cookie name (String) to a {@link jakarta.ws.rs.core.NewCookie new cookie}.
- */
- public Map<String, NewCookie> getResponseCookies() {
- List<Object> cookies = headers.get(HttpHeaders.SET_COOKIE);
- if (cookies == null || cookies.isEmpty()) {
- return Collections.emptyMap();
- }
-
- Map<String, NewCookie> result = new HashMap<String, NewCookie>();
- for (String cookie : HeaderUtils.asStringList(cookies, runtimeDelegateDecorator)) {
- if (cookie != null) {
- NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
- String cookieName = newCookie.getName();
- if (result.containsKey(cookieName)) {
- result.put(cookieName, HeaderUtils.getPreferredCookie(result.get(cookieName), newCookie));
- } else {
- result.put(cookieName, newCookie);
- }
- }
- }
- return result;
- }
-
- /**
- * Get the entity tag.
- *
- * @return the entity tag, otherwise {@code null} if not present.
- */
- public EntityTag getEntityTag() {
- return singleHeader(HttpHeaders.ETAG, EntityTag.class, new Function<String, EntityTag>() {
- @Override
- public EntityTag apply(String value) {
- try {
- return value == null ? null : EntityTag.valueOf(value);
- } catch (IllegalArgumentException ex) {
- throw new ProcessingException(ex);
- }
- }
- }, false);
- }
-
- /**
- * Get the last modified date.
- *
- * @return the last modified date, otherwise {@code null} if not present.
- */
- public Date getLastModified() {
- return singleHeader(HttpHeaders.LAST_MODIFIED, Date.class, new Function<String, Date>() {
- @Override
- public Date apply(String input) {
- try {
- return HttpHeaderReader.readDate(input);
- } catch (ParseException e) {
- throw new ProcessingException(e);
- }
- }
- }, false);
- }
-
- /**
- * Get the location.
- *
- * @return the location URI, otherwise {@code null} if not present.
- */
- public URI getLocation() {
- return singleHeader(HttpHeaders.LOCATION, URI.class, new Function<String, URI>() {
- @Override
- public URI apply(String value) {
- try {
- return value == null ? null : URI.create(value);
- } catch (IllegalArgumentException ex) {
- throw new ProcessingException(ex);
- }
- }
- }, false);
- }
-
- /**
* Get the links attached to the message as header.
*
* @return links, may return empty {@link java.util.Set} if no links are present. Never
@@ -583,56 +333,6 @@
return Collections.unmodifiableSet(result);
}
- /**
- * Check if link for relation exists.
- *
- * @param relation link relation.
- * @return {@code true} if the for the relation link exists, {@code false}
- * otherwise.
- */
- public boolean hasLink(String relation) {
- for (Link link : getLinks()) {
- List<String> relations = LinkProvider.getLinkRelations(link.getRel());
- if (relations != null && relations.contains(relation)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Get the link for the relation.
- *
- * @param relation link relation.
- * @return the link for the relation, otherwise {@code null} if not present.
- */
- public Link getLink(String relation) {
- for (Link link : getLinks()) {
- List<String> relations = LinkProvider.getLinkRelations(link.getRel());
- if (relations != null && relations.contains(relation)) {
- return link;
- }
- }
- return null;
- }
-
- /**
- * Convenience method that returns a {@link jakarta.ws.rs.core.Link.Builder Link.Builder}
- * for the relation.
- *
- * @param relation link relation.
- * @return the link builder for the relation, otherwise {@code null} if not
- * present.
- */
- public Link.Builder getLinkBuilder(String relation) {
- Link link = getLink(relation);
- if (link == null) {
- return null;
- }
-
- return Link.fromLink(link);
- }
-
// Message entity
/**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
index 6c5740e..04d820d 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.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
@@ -526,7 +526,7 @@
processingContext.routingContext().setMappedThrowable(throwable);
waeResponse = webApplicationException.getResponse();
- if (waeResponse.hasEntity()) {
+ if (waeResponse != null && waeResponse.hasEntity()) {
LOGGER.log(Level.FINE, LocalizationMessages
.EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()), throwable);
return waeResponse;
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarZipSchemeResourceFinderFactory.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarZipSchemeResourceFinderFactory.java
index aa2874e..f08cfb1 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarZipSchemeResourceFinderFactory.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarZipSchemeResourceFinderFactory.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
@@ -16,13 +16,13 @@
package org.glassfish.jersey.server.internal.scanning;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -155,7 +155,7 @@
return new URL(jarUrlString).openStream();
} catch (final MalformedURLException e) {
return Files.newInputStream(
- new File(UriComponent.decode(jarUrlString, UriComponent.Type.PATH)).toPath());
+ Paths.get(UriComponent.decode(jarUrlString, UriComponent.Type.PATH)));
}
}
}
diff --git a/examples/NOTICE.md b/examples/NOTICE.md
index aafa80c..89e2821 100644
--- a/examples/NOTICE.md
+++ b/examples/NOTICE.md
@@ -71,7 +71,7 @@
* Project: http://www.javassist.org/
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
-Jackson JAX-RS Providers Version 2.15.3
+Jackson JAX-RS Providers Version 2.16.1
* License: Apache License, 2.0
* Project: https://github.com/FasterXML/jackson-jaxrs-providers
* Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated.
diff --git a/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/spi/AbstractTemplateProcessor.java b/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/spi/AbstractTemplateProcessor.java
index ed0c590..79dd79c 100644
--- a/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/spi/AbstractTemplateProcessor.java
+++ b/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/spi/AbstractTemplateProcessor.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
@@ -16,13 +16,13 @@
package org.glassfish.jersey.server.mvc.spi;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -182,7 +182,7 @@
// File-system path.
if (reader == null) {
try {
- reader = new InputStreamReader(Files.newInputStream(new File(template).toPath()), encoding);
+ reader = new InputStreamReader(Files.newInputStream(Paths.get(template)), encoding);
} catch (final IOException ioe) {
// NOOP.
}
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 f715407..b11c121 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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
@@ -129,7 +129,11 @@
final StreamReadConstraints constraints = jsonFactory.streamReadConstraints();
jsonFactory.setStreamReadConstraints(
StreamReadConstraints.builder()
+ // our
.maxStringLength(maxStringLength)
+ // customers
+ .maxDocumentLength(constraints.getMaxDocumentLength())
+ .maxNameLength(constraints.getMaxNameLength())
.maxNestingDepth(constraints.getMaxNestingDepth())
.maxNumberLength(constraints.getMaxNumberLength())
.build()
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.java
index a12d4d0..74f510c 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.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
@@ -26,6 +26,7 @@
import org.glassfish.jersey.message.filtering.spi.EntityGraphProvider;
import org.glassfish.jersey.message.filtering.spi.EntityInspector;
import org.glassfish.jersey.message.filtering.spi.ObjectGraph;
+import org.glassfish.jersey.message.filtering.spi.ScopeProvider;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonMappingException;
@@ -37,7 +38,6 @@
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
-import org.glassfish.jersey.message.filtering.spi.ScopeProvider;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Context;
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java
index ecdfbae..bf365bb 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java
@@ -31,7 +31,6 @@
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.ObjectWriterInjector;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.ObjectWriterModifier;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.util.ClassKey;
-import org.glassfish.jersey.jackson.internal.jackson.jaxrs.util.LRUMap;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
@@ -44,6 +43,7 @@
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.util.LRUMap;
import com.fasterxml.jackson.databind.type.TypeFactory;
public abstract class ProviderBase<
@@ -1056,4 +1056,4 @@
private final THIS _this() {
return (THIS) this;
}
-}
\ No newline at end of file
+}
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/JaxRSFeature.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/JaxRSFeature.java
index 6f864c9..c0f0da5 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/JaxRSFeature.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/JaxRSFeature.java
@@ -40,6 +40,7 @@
* @since 2.15
*/
READ_FULL_STREAM(true),
+
/*
/**********************************************************
/* HTTP headers
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java
index 3a34c28..61edeb7 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java
@@ -11,7 +11,7 @@
*/
public final class PackageVersion implements Versioned {
public final static Version VERSION = VersionUtil.parseVersion(
- "2.15.3", "com.fasterxml.jackson.jaxrs", "jackson-jaxrs-json-provider");
+ "2.16.1", "com.fasterxml.jackson.jaxrs", "jackson-jaxrs-json-provider");
@Override
public Version version() {
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/util/LRUMap.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/util/LRUMap.java
index 7252cd5..c99c987 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/util/LRUMap.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/util/LRUMap.java
@@ -3,11 +3,15 @@
import java.util.LinkedHashMap;
import java.util.Map;
+// TO BE REMOVED FROM JACKSON 2.18 or later
/**
* Helper for simple bounded LRU maps used for reusing lookup values.
*
* @since 2.2
+ *
+ * @deprecated Since 2.16.1 Use one from {@code jackson-databind} instead.
*/
+@Deprecated // since 2.16.1
@SuppressWarnings("serial")
public class LRUMap<K,V> extends LinkedHashMap<K,V>
{
@@ -24,5 +28,4 @@
{
return size() > _maxEntries;
}
-
}
diff --git a/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown b/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown
index 4251b2f..5b5dcd7 100644
--- a/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown
+++ b/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown
@@ -31,7 +31,7 @@
## Third-party Content
-Jackson JAX-RS Providers version 2.15.3
+Jackson JAX-RS Providers version 2.16.1
* License: Apache License, 2.0
* Project: https://github.com/FasterXML/jackson-jaxrs-providers
* Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated.
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
index e75400a..3e40866 100644
--- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
+++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
@@ -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
@@ -30,6 +30,7 @@
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.Response;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -48,9 +49,10 @@
.register(JacksonFeature.class)
.register(TestJacksonJaxbJsonProvider.class)
.property("jersey.config.json.jackson.disabled.modules", "Jdk8Module");
- final String response = target("JAXBEntity")
- .request().get(String.class);
- assertNotEquals("{\"key\":\"key\",\"value\":\"value\"}", response);
+ try (Response response = target("JAXBEntity").request().get()) {
+ assertEquals(400, response.getStatus());
+ assertNotEquals("{\"key\":\"key\",\"value\":\"value\"}", response.readEntity(String.class));
+ }
}
private static class TestJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider {
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
index eaf293d..58a5e5a 100644
--- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
+++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 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
@@ -22,7 +22,9 @@
import org.junit.jupiter.api.Test;
import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class DefaultJsonJacksonProviderForEnabledModulesTest extends JerseyTest {
@@ -34,9 +36,10 @@
@Test
public final void testDisabledModule() {
- final String response = target("entity/simple")
- .request().get(String.class);
- assertNotEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response);
+ try (Response response = target("entity/simple").request().get()) {
+ assertEquals(400, response.getStatus());
+ assertNotEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response.readEntity(String.class));
+ }
}
}
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/StreamReadConstrainsTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/StreamReadConstrainsTest.java
index 925c17e..8fa7cb9 100644
--- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/StreamReadConstrainsTest.java
+++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/StreamReadConstrainsTest.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,6 +50,7 @@
import java.util.List;
public class StreamReadConstrainsTest extends JerseyTest {
+ private static final String ERROR_MSG_PART = "maximum allowed (";
@Override
protected final Application configure() {
@@ -78,7 +79,7 @@
.post(Entity.entity(new MyEntity(8), MediaType.APPLICATION_JSON_TYPE))) {
Assertions.assertEquals(200, response.getStatus());
String errorMsg = response.readEntity(String.class);
- Assertions.assertTrue(errorMsg.contains("maximum length (4)"));
+ Assertions.assertTrue(errorMsg.contains(ERROR_MSG_PART + 4));
}
}
@@ -125,7 +126,7 @@
throw ex;
}
String errorMsg = ex.getCause().getMessage();
- Assertions.assertTrue(errorMsg.contains("maximum length (" + String.valueOf(expectedLength) + ")"));
+ Assertions.assertTrue(errorMsg.contains(ERROR_MSG_PART + String.valueOf(expectedLength)));
}
}
@@ -137,7 +138,7 @@
+ " Please update the code in " + DefaultJacksonJaxbJsonProvider.class.getName()
+ " updateFactoryConstraints method";
Method[] method = StreamReadConstraints.Builder.class.getDeclaredMethods();
- Assertions.assertEquals(4, method.length, message); // three max + build methods
+ Assertions.assertEquals(6, method.length, message); // five setMax... + build() methods
}
@Path("len")
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java
index f95d521..6d77453 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.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
@@ -28,8 +28,6 @@
import org.glassfish.jersey.message.internal.HttpHeaderReader;
import org.glassfish.jersey.uri.UriComponent;
-import jakarta.ws.rs.core.HttpHeaders;
-
/**
* A content disposition header.
*
@@ -60,10 +58,13 @@
private static final Pattern FILENAME_VALUE_CHARS_PATTERN =
Pattern.compile("(%[a-f0-9]{2}|[a-z0-9!#$&+.^_`|~-])+", Pattern.CASE_INSENSITIVE);
+ private static final char QUOTE = '"';
+ private static final char BACK_SLASH = '\\';
+
protected ContentDisposition(final String type, final String fileName, final Date creationDate,
final Date modificationDate, final Date readDate, final long size) {
this.type = type;
- this.fileName = fileName;
+ this.fileName = encodeAsciiFileName(fileName);
this.creationDate = creationDate;
this.modificationDate = modificationDate;
this.readDate = readDate;
@@ -211,6 +212,23 @@
}
}
+ protected String encodeAsciiFileName(String fileName) {
+ if (fileName == null
+ || (fileName.indexOf(QUOTE) == -1
+ && fileName.indexOf(BACK_SLASH) == -1)) {
+ return fileName;
+ }
+ final char[] chars = fileName.toCharArray();
+ final StringBuilder encodedBuffer = new StringBuilder();
+ for (char c : chars) {
+ if (c == QUOTE || c == BACK_SLASH) {
+ encodedBuffer.append(BACK_SLASH);
+ }
+ encodedBuffer.append(c);
+ }
+ return encodedBuffer.toString();
+ }
+
private void createParameters() throws ParseException {
defineFileName();
@@ -229,7 +247,7 @@
final String fileNameExt = parameters.get("filename*");
if (fileNameExt == null) {
- this.fileName = fileName;
+ this.fileName = encodeAsciiFileName(fileName);
return;
}
diff --git a/pom.xml b/pom.xml
index 6ddfcde..d0641e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2291,7 +2291,8 @@
<hk2.jvnet.osgi.version>org.jvnet.hk2.*;version="[2.5,4)"</hk2.jvnet.osgi.version>
<httpclient.version>4.5.14</httpclient.version>
<httpclient5.version>5.2.1</httpclient5.version>
- <jackson.version>2.15.3</jackson.version>
+ <jackson.version>2.16.1</jackson.version>
+ <jackson1.version>1.9.13</jackson1.version>
<javassist.version>3.29.2-GA</javassist.version>
<jboss.logging.version>3.5.3.Final</jboss.logging.version>
<jboss.logging.8.version>3.4.3.Final</jboss.logging.8.version>
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
index c4bcd53..8cecb6b 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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,14 @@
}
@Test
+ void testContentDispositionEncoded() {
+ final Date date = new Date();
+ final ContentDisposition contentDisposition = ContentDisposition.type(contentDispositionType).fileName("\"rm\\ -rf\".sh")
+ .creationDate(date).modificationDate(date).readDate(date).size(312).build();
+ assertEquals("\\\"rm\\\\ -rf\\\".sh", contentDisposition.getFileName());
+ }
+
+ @Test
public void testToString() {
final Date date = new Date();
final ContentDisposition contentDisposition = ContentDisposition.type(contentDispositionType).fileName("test.file")
diff --git a/tests/integration/property-check/pom.xml b/tests/integration/property-check/pom.xml
index df52904..ef3d8ac 100644
--- a/tests/integration/property-check/pom.xml
+++ b/tests/integration/property-check/pom.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2014, 2023 Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2014, 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,6 +74,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-apache5-connector</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>oauth1-server</artifactId>
<version>${project.version}</version>
@@ -109,28 +114,4 @@
</plugin>
</plugins>
</build>
-
- <profiles>
- <profile>
- <id>jdk19+</id>
- <activation>
- <jdk>[19,)</jdk>
- </activation>
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <excludes>
- <exclude>**/SystemPropertiesConfigurationModelTest.java</exclude>
- </excludes>
- </configuration>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
- </profile>
- </profiles>
</project>
diff --git a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
index ddb8e08..916bf81 100644
--- a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
+++ b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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,6 +32,7 @@
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.apache.connector.ApacheClientProperties;
+import org.glassfish.jersey.apache5.connector.Apache5ClientProperties;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.internal.InternalProperties;
import org.glassfish.jersey.internal.util.JdkVersion;
@@ -79,8 +80,13 @@
@Test
public void propertyLoadedWhenSecurityException() {
final String TEST_STRING = "test";
- SecurityManager sm = System.getSecurityManager();
- String policy = System.getProperty("java.security.policy");
+ final boolean isSm = JdkVersion.getJdkVersion().getMajor() < 19;
+ SecurityManager sm = null;
+ String policy = null;
+ if (isSm) {
+ sm = System.getSecurityManager();
+ policy = System.getProperty("java.security.policy");
+ }
try {
System.setProperty(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.TRUE.toString());
System.setProperty(ServerProperties.APPLICATION_NAME, TEST_STRING);
@@ -88,15 +94,20 @@
System.setProperty(ServletProperties.JAXRS_APPLICATION_CLASS, TEST_STRING);
System.setProperty(MessageProperties.IO_BUFFER_SIZE, TEST_STRING);
System.setProperty(ApacheClientProperties.DISABLE_COOKIES, TEST_STRING);
+ System.setProperty(Apache5ClientProperties.DISABLE_COOKIES, TEST_STRING);
System.setProperty(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION, TEST_STRING);
System.setProperty(MultiPartProperties.TEMP_DIRECTORY, TEST_STRING);
System.setProperty(OAuth1ServerProperties.REALM, TEST_STRING);
JerseySystemPropertiesConfigurationModel model = new JerseySystemPropertiesConfigurationModel();
assertTrue(model.as(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.class));
- String securityPolicy = SystemPropertiesConfigurationModelTest.class.getResource("/server.policy").getFile();
- System.setProperty("java.security.policy", securityPolicy);
- SecurityManager manager = new SecurityManager();
- System.setSecurityManager(manager);
+
+ if (isSm) {
+ String securityPolicy = SystemPropertiesConfigurationModelTest.class.getResource("/server.policy").getFile();
+ System.setProperty("java.security.policy", securityPolicy);
+ SecurityManager manager = new SecurityManager();
+ System.setSecurityManager(manager);
+ }
+
Map<String, Object> properties = model.getProperties();
assertEquals(TEST_STRING, properties.get(ServerProperties.APPLICATION_NAME));
assertEquals(Boolean.TRUE.toString(), properties.get(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER));
@@ -109,7 +120,9 @@
assertEquals(TEST_STRING, properties.get(MessageProperties.IO_BUFFER_SIZE));
assertFalse(properties.containsKey(MessageProperties.DEFLATE_WITHOUT_ZLIB));
assertEquals(TEST_STRING, properties.get(ApacheClientProperties.DISABLE_COOKIES));
+ assertEquals(TEST_STRING, properties.get(Apache5ClientProperties.DISABLE_COOKIES));
assertFalse(properties.containsKey(ApacheClientProperties.CONNECTION_MANAGER));
+ assertFalse(properties.containsKey(Apache5ClientProperties.CONNECTION_MANAGER));
assertEquals(TEST_STRING, properties.get(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION));
assertFalse(properties.containsKey(JettyClientProperties.DISABLE_COOKIES));
assertEquals(TEST_STRING, properties.get(MultiPartProperties.TEMP_DIRECTORY));
@@ -120,7 +133,9 @@
if (policy != null) {
System.setProperty("java.security.policy", policy);
}
- System.setSecurityManager(sm);
+ if (isSm) {
+ System.setSecurityManager(sm);
+ }
}
}