Merge remote-tracking branch 2.x into 3.0 Signed-off-by: jansupol <jan.supol@oracle.com>
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..7aa9184 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 @@ -67,7 +67,7 @@ * * @author Marek Potociar */ -public abstract class InboundMessageContext { +public abstract class InboundMessageContext extends MessageHeaderMethods { private static final InputStream EMPTY = new InputStream() { @@ -100,7 +100,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 +165,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 +318,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 +333,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 +367,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 +481,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 +524,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..d2d69b4 --- /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 javax.ws.rs.ProcessingException; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.EntityTag; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Link; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.NewCookie; +import javax.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 javax.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 javax.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 javax.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..80ce79a 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 @@ -63,7 +63,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 +71,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 +106,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 +123,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 +134,6 @@ this.entityType = original.entityType; this.entityAnnotations = original.entityAnnotations; this.configuration = original.configuration; - this.runtimeDelegateDecorator = original.runtimeDelegateDecorator; this.mediaTypeCache = mediaTypeCache(); } @@ -190,49 +189,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 +204,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 +307,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 +344,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..113e9fa 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 @@ -60,10 +60,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 +214,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 +249,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); + } } }