Merge remote-tracking branch 'upstream/3.x' into 3.1 Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/NOTICE.md b/NOTICE.md index 0846b57..3519c88 100644 --- a/NOTICE.md +++ b/NOTICE.md
@@ -43,7 +43,7 @@ Bean Validation API 3.0.0 * License: Apache License, 2.0 -* Project: http://beanvalidation.org/1.1/ +* Project: https://projects.eclipse.org/projects/ee4j.bean-validation * Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors * by the @authors tag. @@ -61,7 +61,7 @@ * License: Apache License, 2.0 * Copyright (C) 2009 The Guava Authors -javax.inject Version: 1 +jakarta.inject Version: 1 * License: Apache License, 2.0 * Copyright (C) 2009 The JSR-330 Expert Group @@ -70,7 +70,7 @@ * Project: http://www.javassist.org/ * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. -Jackson JAX-RS Providers Version 2.11.3 +Jackson JAX-RS Providers Version 2.13.0 * License: Apache License, 2.0 * Project: https://github.com/FasterXML/jackson-jaxrs-providers * Copyright: (c) 2009-2011 FasterXML, LLC. All rights reserved unless otherwise indicated. @@ -95,7 +95,7 @@ * Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS * Copyright: Eric Rowell -org.objectweb.asm Version 9.0 +org.objectweb.asm Version 9.2 * License: Modified BSD (https://asm.ow2.io/license.html) * Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved.
diff --git a/bom/pom.xml b/bom/pom.xml index e64e12f..32bf17d 100644 --- a/bom/pom.xml +++ b/bom/pom.xml
@@ -80,6 +80,11 @@ </dependency> <dependency> <groupId>org.glassfish.jersey.connectors</groupId> + <artifactId>jersey-java-connector</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.connectors</groupId> <artifactId>jersey-jetty-connector</artifactId> <version>${project.version}</version> </dependency>
diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml index e5a5b3c..6c8ac8b 100644 --- a/bundles/jaxrs-ri/pom.xml +++ b/bundles/jaxrs-ri/pom.xml
@@ -182,7 +182,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency> @@ -254,7 +253,7 @@ <includeGroupIds>jakarta.ws.rs,org.glassfish.jersey.core,org.glassfish.jersey.containers,org.glassfish.jersey.jaxb,org.glassfish.jersey.inject</includeGroupIds> <includeClassifiers>sources</includeClassifiers> <outputDirectory>${generated.src.dir}</outputDirectory> - <excludes>**/NOTICE.md,**/NOTICE.markdown</excludes> + <excludes>module-info.*,META-INF/MANIFEST.MF,**/NOTICE.md,**/NOTICE.markdown</excludes> </configuration> </execution> </executions> @@ -278,9 +277,9 @@ jersey.repackaged.org.objectweb.asm.*;version=${project.version} </Export-Package> <Import-Package><![CDATA[ - jakarta.servlet.annotation.*;resolution:=optional;version="[5.0,6.0)", - jakarta.servlet.descriptor.*;resolution:=optional;version="[5.0,6.0)", - jakarta.servlet.*;version="[5.0,6.0)", + jakarta.servlet.annotation.*;resolution:=optional;version="[5.0,7.0)", + jakarta.servlet.descriptor.*;resolution:=optional;version="[5.0,7.0)", + jakarta.servlet.*;version="[5.0,7.0)", ${jakarta.annotation.osgi.version}, jakarta.persistence.*;resolution:=optional, jakarta.validation.*;resolution:=optional;version="[3,4)",
diff --git a/connectors/helidon-connector/pom.xml b/connectors/helidon-connector/pom.xml index 2da4ae3..c501994 100644 --- a/connectors/helidon-connector/pom.xml +++ b/connectors/helidon-connector/pom.xml
@@ -81,6 +81,7 @@ <artifactId>maven-javadoc-plugin</artifactId> <configuration> <source>8</source> + <detectJavaApiLink>false</detectJavaApiLink> </configuration> </plugin> </plugins>
diff --git a/connectors/java-connector/pom.xml b/connectors/java-connector/pom.xml new file mode 100644 index 0000000..c2f1ef0 --- /dev/null +++ b/connectors/java-connector/pom.xml
@@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 2021 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 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>project</artifactId> + <groupId>org.glassfish.jersey.connectors</groupId> + <version>3.1.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>jersey-java-connector</artifactId> + <packaging>jar</packaging> + <name>jersey-connectors-java</name> + + <description>Jersey Client Transport via Java's HttpClient</description> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>org.glassfish.jersey.test-framework.providers</groupId> + <artifactId>jersey-test-framework-provider-bundle</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>com.sun.istack</groupId> + <artifactId>istack-commons-maven-plugin</artifactId> + <inherited>true</inherited> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <inherited>true</inherited> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file
diff --git a/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaClientProperties.java b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaClientProperties.java new file mode 100644 index 0000000..cb8f60e --- /dev/null +++ b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaClientProperties.java
@@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import org.glassfish.jersey.internal.util.PropertiesClass; + +import java.net.http.HttpClient; + +/** + * Provides configuration properties for a {@link JavaConnector}. + * + * @author Steffen Nießing + */ +@PropertiesClass +public class JavaClientProperties { + /** + * Configuration of the {@link java.net.CookieHandler} that should be used by the {@link HttpClient}. + * If this option is not set, {@link HttpClient#cookieHandler()} will return an empty {@link java.util.Optional} + * and therefore no cookie handler will be used. + * + * A provided value to this option has to be of type {@link java.net.CookieHandler}. + */ + public static final String COOKIE_HANDLER = "jersey.config.java.client.cookieHandler"; + + /** + * Configuration of SSL parameters used by the {@link HttpClient}. + * If this option is not set, then the {@link HttpClient} will use <it>implementation specific</it> default values. + * + * A provided value to this option has to be of type {@link javax.net.ssl.SSLParameters}. + */ + public static final String SSL_PARAMETERS = "jersey.config.java.client.sslParameters"; + + /** + * Prevent this class from instantiation. + */ + private JavaClientProperties() {} +}
diff --git a/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnector.java b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnector.java new file mode 100644 index 0000000..9c104ab --- /dev/null +++ b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnector.java
@@ -0,0 +1,234 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.core.MultivaluedMap; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.ClientRequest; +import org.glassfish.jersey.client.ClientResponse; +import org.glassfish.jersey.client.spi.AsyncConnectorCallback; +import org.glassfish.jersey.client.spi.Connector; +import org.glassfish.jersey.internal.Version; +import org.glassfish.jersey.message.internal.OutboundMessageContext; +import org.glassfish.jersey.message.internal.Statuses; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.CookieHandler; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.logging.Logger; + +/** + * Provides a Jersey client {@link Connector}, which internally uses Java's {@link HttpClient}. + * The following properties are provided to Java's {@link HttpClient.Builder} during creation of the {@link HttpClient}: + * <ul> + * <li>{@link ClientProperties#CONNECT_TIMEOUT}</li> + * <li>{@link ClientProperties#FOLLOW_REDIRECTS}</li> + * <li>{@link JavaClientProperties#COOKIE_HANDLER}</li> + * <li>{@link JavaClientProperties#SSL_PARAMETERS}</li> + * </ul> + * + * @author Steffen Nießing + */ +public class JavaConnector implements Connector { + private static final Logger LOGGER = Logger.getLogger(JavaConnector.class.getName()); + + private final HttpClient httpClient; + + /** + * Constructs a new {@link Connector} for a Jersey client instance using Java's {@link HttpClient}. + * + * @param client a Jersey client instance to get additional configuration properties from (e.g. {@link SSLContext}) + * @param configuration the configuration properties for this connector + */ + public JavaConnector(final Client client, final Configuration configuration) { + HttpClient.Builder httpClientBuilder = HttpClient.newBuilder(); + httpClientBuilder.version(HttpClient.Version.HTTP_1_1); + SSLContext sslContext = client.getSslContext(); + if (sslContext != null) { + httpClientBuilder.sslContext(sslContext); + } + Integer connectTimeout = getPropertyOrNull(configuration, ClientProperties.CONNECT_TIMEOUT, Integer.class); + if (connectTimeout != null) { + httpClientBuilder.connectTimeout(Duration.of(connectTimeout, ChronoUnit.MILLIS)); + } + CookieHandler cookieHandler = getPropertyOrNull(configuration, JavaClientProperties.COOKIE_HANDLER, CookieHandler.class); + if (cookieHandler != null) { + httpClientBuilder.cookieHandler(cookieHandler); + } + Boolean redirect = getPropertyOrNull(configuration, ClientProperties.FOLLOW_REDIRECTS, Boolean.class); + if (redirect != null) { + httpClientBuilder.followRedirects(redirect ? HttpClient.Redirect.ALWAYS : HttpClient.Redirect.NEVER); + } else { + httpClientBuilder.followRedirects(HttpClient.Redirect.NORMAL); + } + SSLParameters sslParameters = getPropertyOrNull(configuration, JavaClientProperties.SSL_PARAMETERS, SSLParameters.class); + if (sslParameters != null) { + httpClientBuilder.sslParameters(sslParameters); + } + this.httpClient = httpClientBuilder.build(); + } + + /** + * Implements a {@link org.glassfish.jersey.message.internal.OutboundMessageContext.StreamProvider} + * for a {@link ByteArrayOutputStream}. + */ + private static class ByteArrayOutputStreamProvider implements OutboundMessageContext.StreamProvider { + private ByteArrayOutputStream byteArrayOutputStream; + + public ByteArrayOutputStream getByteArrayOutputStream() { + return byteArrayOutputStream; + } + + @Override + public OutputStream getOutputStream(int contentLength) throws IOException { + return this.byteArrayOutputStream = new ByteArrayOutputStream(contentLength); + } + } + + /** + * Builds a request for the {@link HttpClient} from Jersey's {@link ClientRequest}. + * + * @param request the Jersey request to get request data from + * @return the {@link HttpRequest} instance for the {@link HttpClient} request + */ + private HttpRequest getHttpRequest(ClientRequest request) { + HttpRequest.Builder builder = HttpRequest.newBuilder(); + builder.uri(request.getUri()); + HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.noBody(); + if (request.hasEntity()) { + try { + request.enableBuffering(); + ByteArrayOutputStreamProvider byteBufferStreamProvider = new ByteArrayOutputStreamProvider(); + request.setStreamProvider(byteBufferStreamProvider); + request.writeEntity(); + bodyPublisher = HttpRequest.BodyPublishers.ofByteArray( + byteBufferStreamProvider.getByteArrayOutputStream().toByteArray() + ); + } catch (IOException e) { + throw new ProcessingException(LocalizationMessages.ERROR_INVALID_ENTITY(), e); + } + } + builder.method(request.getMethod(), bodyPublisher); + for (Map.Entry<String, List<String>> entry : request.getRequestHeaders().entrySet()) { + String headerName = entry.getKey(); + for (String headerValue : entry.getValue()) { + builder.header(headerName, headerValue); + } + } + return builder.build(); + } + + /** + * Retrieves a property from the configuration, if it was provided. + * + * @param configuration the {@link Configuration} to get the property information from + * @param propertyKey the name of the property to retrieve + * @param resultClass the type to which the property value should be case + * @param <T> the generic type parameter of the result type + * @return the requested property or {@code null}, if it was not provided or has the wrong type + */ + @SuppressWarnings("unchecked") + private <T> T getPropertyOrNull(final Configuration configuration, final String propertyKey, final Class<T> resultClass) { + Object propertyObject = configuration.getProperty(propertyKey); + if (propertyObject == null) { + return null; + } + if (!resultClass.isInstance(propertyObject)) { + LOGGER.warning(LocalizationMessages.ERROR_INVALID_CLASS(propertyKey, resultClass.getName())); + return null; + } + return (T) propertyObject; + } + + /** + * Translates a {@link HttpResponse} from the {@link HttpClient} to a Jersey {@link ClientResponse}. + * + * @param request the {@link ClientRequest} to get additional information (e.g. header values) from + * @param response the {@link HttpClient} response object + * @return the translated Jersey {@link ClientResponse} object + */ + private ClientResponse buildClientResponse(ClientRequest request, HttpResponse<InputStream> response) { + ClientResponse clientResponse = new ClientResponse(Statuses.from(response.statusCode()), request); + MultivaluedMap<String, String> headers = clientResponse.getHeaders(); + for (Map.Entry<String, List<String>> entry : response.headers().map().entrySet()) { + String headerName = entry.getKey(); + if (headers.get(headerName) != null) { + headers.get(headerName).addAll(entry.getValue()); + } else { + headers.put(headerName, entry.getValue()); + } + } + clientResponse.setEntityStream(response.body()); + return clientResponse; + } + + /** + * Returns the underlying {@link HttpClient} instance used by this connector. + * + * @return the Java {@link HttpClient} instance + */ + public HttpClient getHttpClient() { + return httpClient; + } + + @Override + public ClientResponse apply(ClientRequest request) { + HttpRequest httpRequest = getHttpRequest(request); + try { + HttpResponse<InputStream> response = this.httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofInputStream()); + return buildClientResponse(request, response); + } catch (IOException | InterruptedException e) { + throw new ProcessingException(e); + } + } + + @Override + public Future<?> apply(ClientRequest request, AsyncConnectorCallback callback) { + HttpRequest httpRequest = getHttpRequest(request); + CompletableFuture<ClientResponse> response = this.httpClient + .sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream()) + .thenApply(httpResponse -> buildClientResponse(request, httpResponse)); + response.thenAccept(callback::response); + return response; + } + + @Override + public String getName() { + return "Java HttpClient Connector " + Version.getVersion(); + } + + @Override + public void close() { + + } +}
diff --git a/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnectorProvider.java b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnectorProvider.java new file mode 100644 index 0000000..1fa38ab --- /dev/null +++ b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnectorProvider.java
@@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.core.Configurable; +import jakarta.ws.rs.core.Configuration; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.Initializable; +import org.glassfish.jersey.client.spi.Connector; +import org.glassfish.jersey.client.spi.ConnectorProvider; + +import java.net.http.HttpClient; + +/** + * A provider class for a Jersey client {@link Connector} using Java's {@link HttpClient}. + * <p> + * The following configuration properties are available: + * <ul> + * <li>{@link ClientProperties#CONNECT_TIMEOUT}</li> + * <li>{@link ClientProperties#FOLLOW_REDIRECTS} (defaults to {@link java.net.http.HttpClient.Redirect#NORMAL} when unset)</li> + * <li>{@link JavaClientProperties#COOKIE_HANDLER}</li> + * <li>{@link JavaClientProperties#SSL_PARAMETERS}</li> + * </ul> + * </p> + * + * @author Steffen Nießing + */ +public class JavaConnectorProvider implements ConnectorProvider { + @Override + public Connector getConnector(Client client, Configuration runtimeConfig) { + return new JavaConnector(client, runtimeConfig); + } + + /** + * Retrieve the Java {@link HttpClient} used by the provided {@link JavaConnector}. + * + * @param component the component from which the {@link JavaConnector} should be retrieved + * @return a Java {@link HttpClient} instance + * @throws java.lang.IllegalArgumentException if a {@link JavaConnector} cannot be provided from the given {@code component} + */ + public static HttpClient getHttpClient(Configurable<?> component) { + try { + final Initializable<?> initializable = (Initializable<?>) component; + + Connector connector = initializable.getConfiguration().getConnector() != null + ? initializable.getConfiguration().getConnector() + : initializable.preInitialize().getConfiguration().getConnector(); + + if (connector instanceof JavaConnector) { + return ((JavaConnector) connector).getHttpClient(); + } else { + throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED()); + } + } catch (ClassCastException classCastException) { + throw new IllegalArgumentException( + LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName()), + classCastException + ); + } + } +}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/package-info.java similarity index 71% copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java copy to connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/package-info.java index dd25372..e50e0bb 100644 --- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java +++ b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/package-info.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -14,7 +14,8 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 */ -package org.glassfish.jersey.internal.jsr166; - -public interface JerseyFlowSubscriber<T> extends Flow.Subscriber<T> { -} +/** + * Jersey client {@link org.glassfish.jersey.client.spi.Connector connector} based on + * Java's {@link java.net.http.HttpClient}. + */ +package org.glassfish.jersey.java.connector; \ No newline at end of file
diff --git a/connectors/java-connector/src/main/resources/org/glassfish/jersey/java/connector/localization.properties b/connectors/java-connector/src/main/resources/org/glassfish/jersey/java/connector/localization.properties new file mode 100644 index 0000000..ca98e99 --- /dev/null +++ b/connectors/java-connector/src/main/resources/org/glassfish/jersey/java/connector/localization.properties
@@ -0,0 +1,21 @@ +# +# Copyright (c) 2021 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 +# + +error.body.publisher=Could not determine BodyPublisher for entity. +error.invalid.class={0} is not an instance of {1}. Ignoring property. +error.invalid.entity=Could not serialize entity. +invalid.configurable.component.type=The supplied component "{0}" is not assignable from JerseyClient or JerseyWebTarget. +expected.connector.provider.not.used=The supplied component is not configured to use a JavaConnectorProvider.
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AbstractJavaConnectorTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AbstractJavaConnectorTest.java new file mode 100644 index 0000000..7d3980d --- /dev/null +++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AbstractJavaConnectorTest.java
@@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.container.Suspended; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.logging.LoggingFeature; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +/** + * An abstract base class for tests of the {@link JavaConnector} providing common resources and utility methods. + */ +abstract class AbstractJavaConnectorTest extends JerseyTest { + private static final Logger LOGGER = Logger.getLogger(AbstractJavaConnectorTest.class.getName()); + public static final String RESOURCE_PATH = "java-connector"; + + @Path(RESOURCE_PATH) + public static class JavaConnectorTestResource { + @GET + public String helloWorld() { + return "Hello World!"; + } + + @GET + @Path("redirect") + public Response redirectToHelloWorld() throws URISyntaxException { + return Response.seeOther(new URI(RESOURCE_PATH)).build(); + } + + @POST + @Path("echo") + @Produces(MediaType.TEXT_PLAIN) + @Consumes(MediaType.TEXT_PLAIN) + public String echo(String entity) { + return entity; + } + + @POST + @Path("echo-byte-array") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Consumes(MediaType.APPLICATION_OCTET_STREAM) + public byte[] echoByteArray(byte[] byteArray) { + return byteArray; + } + + @POST + @Path("async") + public void asyncPostWithTimeout(@QueryParam("timeout") @DefaultValue("10") Long timeoutSeconds, + @Suspended final AsyncResponse asyncResponse, + String message) { + asyncResponse.setTimeoutHandler(asyncResponse1 -> + asyncResponse1.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("Timeout").build())); + asyncResponse.setTimeout(timeoutSeconds, TimeUnit.SECONDS); + CompletableFuture.runAsync(() -> { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + }) + .handleAsync((unused, throwable) -> throwable != null ? "INTERRUPTED" : message) + .thenApplyAsync(asyncResponse::resume); + } + } + + protected Response request(String path) { + return target().path(path).request().get(); + } + + protected Response requestWithEntity(String path, String method, Entity<?> entity) { + return target().path(path).request().method(method, entity); + } + + protected Future<Response> requestAsync(String path) { + return target().path(path).request().async().get(); + } + + protected Future<Response> requestAsyncWithEntity(String path, String method, Entity<?> entity) { + return target().path(path).request().async().method(method, entity); + } + + @Override + protected Application configure() { + return new ResourceConfig(JavaConnectorTestResource.class) + .register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY)); + } + + @Override + protected void configureClient(ClientConfig config) { + config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY)); + config.connectorProvider(new JavaConnectorProvider()); + } +}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AsyncTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AsyncTest.java new file mode 100644 index 0000000..dc08521 --- /dev/null +++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AsyncTest.java
@@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Response; +import org.junit.Test; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.awaitility.Awaitility.await; + +/** + * Tests asynchronous and interleaved requests. + */ +public class AsyncTest extends AbstractJavaConnectorTest { + /** + * Checks, that 3 interleaved requests all complete and return their associated responses. + * Additionally checks, that all requests complete in 3 times the running time on the server. + */ + @Test + public void testAsyncRequestsWithoutTimeout() { + Future<Response> request1 = this.requestAsyncWithEntity("java-connector/async", "POST", Entity.text("request1")); + Future<Response> request2 = this.requestAsyncWithEntity("java-connector/async", "POST", Entity.text("request2")); + Future<Response> request3 = this.requestAsyncWithEntity("java-connector/async", "POST", Entity.text("request3")); + + assertThatCode(() -> { + // wait 3 times the processing time and throw if not completed until then + await().atMost(3 * 3000, TimeUnit.MILLISECONDS) + .until(() -> request1.isDone() && request2.isDone() && request3.isDone()); + String response1 = request1.get().readEntity(String.class); + String response2 = request2.get().readEntity(String.class); + String response3 = request3.get().readEntity(String.class); + assertThat(response1).isEqualTo("request1"); + assertThat(response2).isEqualTo("request2"); + assertThat(response3).isEqualTo("request3"); + }).doesNotThrowAnyException(); + } + + /** + * Checks, that a status {@link Response.Status#SERVICE_UNAVAILABLE} is thrown, if a request computes too long. + */ + @Test + public void testAsyncRequestsWithTimeout() throws ExecutionException, InterruptedException { + try { + Response response = target().path("java-connector").path("async").queryParam("timeout", 1) + .request().async().post(Entity.text("")).get(); + assertThat(response.getStatus()).isEqualTo(Response.Status.SERVICE_UNAVAILABLE.getStatusCode()); + assertThat(response.readEntity(String.class)).isEqualTo("Timeout"); + } catch (InterruptedException | ExecutionException ex) { + throw new RuntimeException("Could not correctly get response", ex); + } + } +}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/BodyPublisherTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/BodyPublisherTest.java new file mode 100644 index 0000000..dbbbe82 --- /dev/null +++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/BodyPublisherTest.java
@@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + +/** + * Checks, that request entities are correctly serialized and deserialized. + */ +public class BodyPublisherTest extends AbstractJavaConnectorTest { + /** + * Checks with a simple plain text entity. + */ + @Test + public void testStringEntity() { + Response response = this.requestWithEntity("java-connector/echo", "POST", Entity.text("Echo")); + assertThat(response.getStatus()).isEqualTo(200); + assertThatCode(() -> { + assertThat(response.readEntity(String.class)).isEqualTo("Echo"); + }).doesNotThrowAnyException(); + } + + /** + * Checks with an octet stream entity. + */ + @Test + public void testByteArrayEntity() { + String test = "test-string"; + Response response = this.requestWithEntity("java-connector/echo-byte-array", "POST", + Entity.entity(test.getBytes(StandardCharsets.UTF_8), MediaType.APPLICATION_OCTET_STREAM_TYPE)); + assertThat(response.getStatus()).isEqualTo(200); + assertThatCode(() -> { + assertThat(response.readEntity(byte[].class)) + .satisfies(bytes -> assertThat(new String(bytes, StandardCharsets.UTF_8)).isEqualTo("test-string")); + }).doesNotThrowAnyException(); + } +}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/OptionsMethodTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/OptionsMethodTest.java new file mode 100644 index 0000000..27ec125 --- /dev/null +++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/OptionsMethodTest.java
@@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import jakarta.ws.rs.core.Response; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Checks, that an {@code OPTIONS} request may be sent to a {@code GET} endpoint. + */ +public class OptionsMethodTest extends AbstractJavaConnectorTest { + /** + * Sends an {@code OPTIONS} request to the root {@code GET} endpoint and assumes a code 200. + */ + @Test + public void testOptionsMethod() { + assertThat(this.requestWithEntity("java-connector", "OPTIONS", null).getStatus()) + .isEqualTo(Response.Status.OK.getStatusCode()); + } +}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RedirectTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RedirectTest.java new file mode 100644 index 0000000..1cd0c3d --- /dev/null +++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RedirectTest.java
@@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import jakarta.ws.rs.core.Response; +import org.glassfish.jersey.client.ClientProperties; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Checks that the connector provider correctly handles redirects. + */ +public class RedirectTest extends AbstractJavaConnectorTest { + /** + * Checks, that without further configuration redirects are taken. + */ + @Test + public void testRedirect() { + assertThat(this.request("java-connector/redirect").readEntity(String.class)).isEqualTo("Hello World!"); + } + + /** + * Checks, that no redirect happens, if the redirects are switched off. + */ + @Test + public void testNotFollowRedirects() { + Response response = target().path("java-connector").path("redirect") + .property(ClientProperties.FOLLOW_REDIRECTS, false) + .request() + .get(); + assertThat(response.getStatus()).isEqualTo(Response.Status.SEE_OTHER.getStatusCode()); + } +}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RetrieveHttpClientFromConnectorProviderTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RetrieveHttpClientFromConnectorProviderTest.java new file mode 100644 index 0000000..aa831de --- /dev/null +++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RetrieveHttpClientFromConnectorProviderTest.java
@@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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.java.connector; + +import org.junit.Test; + +import java.net.http.HttpClient; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests access to the {@link HttpClient} instance provided from the {@link JavaConnectorProvider}. + */ +public class RetrieveHttpClientFromConnectorProviderTest extends AbstractJavaConnectorTest { + /** + * Checks, that the {@link jakarta.ws.rs.client.Client} and {@link jakarta.ws.rs.client.WebTarget} instances + * correctly return the internally used {@link HttpClient}. + */ + @Test + public void testClientUsesJavaConnector() { + assertThat(JavaConnectorProvider.getHttpClient(client())).isInstanceOf(HttpClient.class); + assertThat(JavaConnectorProvider.getHttpClient(target())).isInstanceOf(HttpClient.class); + assertThat(JavaConnectorProvider.getHttpClient(client())) + .isEqualTo(JavaConnectorProvider.getHttpClient(target())); + } +}
diff --git a/connectors/jdk-connector/pom.xml b/connectors/jdk-connector/pom.xml index da0ccd9..37a68e9 100644 --- a/connectors/jdk-connector/pom.xml +++ b/connectors/jdk-connector/pom.xml
@@ -34,6 +34,8 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 --> + <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline> </properties> <dependencies> @@ -78,16 +80,6 @@ <profiles> <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <properties> - <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 --> - <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline> - </properties> - </profile> - <profile> <id>disable_tls1and11</id> <!-- TLS 1 and TLS 1.1 are disabled for JDK 16 --> <activation> @@ -110,5 +102,4 @@ </build> </profile> </profiles> - </project>
diff --git a/connectors/jetty-connector/pom.xml b/connectors/jetty-connector/pom.xml index 41fb47c..e7038d3 100644 --- a/connectors/jetty-connector/pom.xml +++ b/connectors/jetty-connector/pom.xml
@@ -34,10 +34,6 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <java8.build.outputDirectory>${project.basedir}/target</java8.build.outputDirectory> - <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory> - <java11.build.outputDirectory>${project.basedir}/target11</java11.build.outputDirectory> - <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory> </properties> <dependencies> @@ -62,18 +58,28 @@ <version>${project.version}</version> <scope>test</scope> </dependency> -<!-- <dependency>--> -<!-- <groupId>org.glassfish.jersey.media</groupId>--> -<!-- <artifactId>jersey-media-json-jackson</artifactId>--> -<!-- <version>${project.version}</version>--> -<!-- <scope>test</scope>--> -<!-- </dependency>--> + <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-json-jackson</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId> <artifactId>jersey-test-framework-provider-jetty</artifactId> <version>${project.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>jakarta.xml.bind</groupId> + <artifactId>jakarta.xml.bind-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-osgi</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> @@ -92,174 +98,6 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> - <plugin> - <!-- TODO remove after jakartification --> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <executions> - <execution> - <id>default-test</id> <!-- jakartification-excluded-tests --> - <configuration> - <excludes> - <exclude>org/glassfish/jersey/jetty/connector/EntityTest.java</exclude> - </excludes> - </configuration> - </execution> - </executions> - </plugin> </plugins> </build> - - <profiles> - <profile> - <id>JettyExclude</id> - <activation> - <jdk>1.8</jdk> - </activation> - <properties> - <jetty.version>9.4.28.v20200408</jetty.version> - </properties> - <dependencies> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-client</artifactId> - <version>${jetty.version}</version> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-util</artifactId> - <version>${jetty.version}</version> - </dependency> - </dependencies> - <build> - <directory>${java8.build.outputDirectory}</directory> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>${java8.sourceDirectory}</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <testExcludes> - <testExclude>org/glassfish/jersey/jetty/connector/*.java</testExclude> - </testExcludes> - </configuration> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>Jetty11</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <build> - <directory>${java11.build.outputDirectory}</directory> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>${java11.sourceDirectory}</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>copyJDK11FilesToMultiReleaseJar</id> - <activation> - <file> - <!-- ${java11.build.outputDirectory} does not work here --> - <exists>target11/classes/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.class</exists> - </file> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <inherited>true</inherited> - <extensions>true</extensions> - <configuration> - <instructions> - <Multi-Release>true</Multi-Release> - </instructions> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-resources-plugin</artifactId> - <inherited>true</inherited> - <executions> - <execution> - <id>copy-jdk11-classes</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory> - <resources> - <resource> - <directory>${java11.build.outputDirectory}/classes</directory> - </resource> - </resources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-antrun-plugin</artifactId> - <executions> - <execution> - <id>copy-jdk11-sources</id> - <phase>package</phase> - <configuration> - <target> - <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/> - <echo>sources-jar: ${sources-jar}</echo> - <zip destfile="${sources-jar}" update="true"> - <zipfileset dir="${java11.sourceDirectory}" prefix="META-INF/versions/11"/> - </zip> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> - </project>
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java similarity index 100% rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java rename to connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java similarity index 100% rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java rename to connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java similarity index 100% rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java rename to connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java similarity index 100% rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java rename to connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java b/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java deleted file mode 100644 index 8723025..0000000 --- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java +++ /dev/null
@@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020 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.jetty.connector; - -import jakarta.ws.rs.ProcessingException; -import jakarta.ws.rs.client.Client; -import jakarta.ws.rs.core.Configuration; - -import org.glassfish.jersey.client.spi.Connector; -import org.glassfish.jersey.client.spi.ConnectorProvider; - -import org.glassfish.jersey.internal.util.JdkVersion; - -/** - * JDK 1.8 Jetty Connector stub which only throws exception when running on JDK 1.8 - * New Jetty (11+) does not support JDKs prior to 11 - * - * @since 3.0.0 - */ -public class JettyConnectorProvider implements ConnectorProvider { - - @Override - public Connector getConnector(Client client, Configuration runtimeConfig) { - if (JdkVersion.getJdkVersion().getMajor() < 11) { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - return null; // does not work at JDK 1.8 - } - -}
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java b/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java deleted file mode 100644 index b061ef5..0000000 --- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java +++ /dev/null
@@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021 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.jetty.connector; - -import org.eclipse.jetty.client.HttpClient; -import org.glassfish.jersey.spi.Contract; - -/** - * A contract that allows for an optional registration of user predefined Jetty {@code HttpClient} - * that is consequently used by {@link JettyConnector} - */ -@Contract -public interface JettyHttpClientContract { - /** - * Supply a user predefined HttpClient - * @return a user predefined HttpClient - */ - HttpClient getHttpClient(); -}
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java b/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java deleted file mode 100644 index 59c8fd3..0000000 --- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java +++ /dev/null
@@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021 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.jetty.connector; - -import jakarta.ws.rs.ProcessingException; -import org.eclipse.jetty.client.HttpClient; -import org.glassfish.jersey.internal.util.JdkVersion; - -/** - * Jetty HttpClient supplier to be registered into Jersey configuration to be used by {@link JettyConnector}. - * Not every possible configuration option is covered by the Jetty Connector and this supplier offers a way to provide - * an HttpClient that has configured the options not covered by the Jetty Connector. - * <p> - * Typical usage: - * </p> - * <pre> - * {@code - * HttpClient httpClient = ... - * - * ClientConfig config = new ClientConfig(); - * config.connectorProvider(new JettyConnectorProvider()); - * config.register(new JettyHttpClientSupplier(httpClient)); - * Client client = ClientBuilder.newClient(config); - * } - * </pre> - * <p> - * The {@code HttpClient} is configured as if it was created by {@link JettyConnector} the usual way. - * </p> - */ -public class JettyHttpClientSupplier implements JettyHttpClientContract { - private final HttpClient httpClient; - - /** - * {@code HttpClient} supplier to be optionally registered to a {@link org.glassfish.jersey.client.ClientConfig} - * @param httpClient a HttpClient to be supplied when {@link JettyConnector#getHttpClient()} is called. - */ - public JettyHttpClientSupplier(HttpClient httpClient) { - this.httpClient = httpClient; - } - - @Override - public HttpClient getHttpClient() { - if (JdkVersion.getJdkVersion().getMajor() < 11) { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - return null; // does not work at JDK 1.8 - } -}
diff --git a/connectors/pom.xml b/connectors/pom.xml index 188cda3..ade8b73 100644 --- a/connectors/pom.xml +++ b/connectors/pom.xml
@@ -41,6 +41,7 @@ <module>jdk-connector</module> <module>jetty-connector</module> <module>netty-connector</module> +<!-- <module>java-connector</module> temporarily for M3--> </modules> <dependencies>
diff --git a/containers/grizzly2-http/pom.xml b/containers/grizzly2-http/pom.xml index 5c628c8..bddcf00 100644 --- a/containers/grizzly2-http/pom.xml +++ b/containers/grizzly2-http/pom.xml
@@ -50,6 +50,11 @@ <groupId>org.glassfish.grizzly</groupId> <artifactId>grizzly-http-server</artifactId> </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.glassfish.grizzly</groupId> @@ -102,6 +107,15 @@ <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <inherited>true</inherited> + <configuration> + <instructions> + <Import-Package> + org.glassfish.grizzly.*;version="[3.0,5.0)", + * + </Import-Package> + </instructions> + <unpackBundle>true</unpackBundle> + </configuration> </plugin> <plugin> @@ -157,33 +171,5 @@ </pluginManagement> </build> </profile> - <profile> - <id>jdk8</id> - <activation> - <jdk>1.8</jdk> - </activation> - <build> - <pluginManagement> - <plugins> - <plugin> - <artifactId>maven-compiler-plugin</artifactId> - <executions> - <execution> - <id>default-testCompile</id> - <configuration> - <!-- - Jetty client is not compatible with JDK8, older versions need different setup - JDK HTTP client is not in JDK8 at all - Jersey Client doesn't support HTTP/2 (at least not directly) - --> - <skip>true</skip> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> </profiles> </project>
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java index 686f50f..616ea14 100644 --- a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java +++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 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 @@ -307,9 +307,16 @@ * @param application JAX-RS / Jersey application to be deployed on Grizzly HTTP container. */ /* package */ GrizzlyHttpContainer(final Application application) { - this.appHandler = new ApplicationHandler(application, new GrizzlyBinder()); - cacheConfigSetStatusOverSendError(); - cacheConfigEnableLeadingContextPathSlashes(); + this(new ApplicationHandler(application, new GrizzlyBinder())); + } + + /** + * Create a new Grizzly HTTP container. + * + * @param applicationClass JAX-RS / Jersey application to be deployed on Grizzly HTTP container. + */ + /* package */ GrizzlyHttpContainer(final Class<? extends Application> applicationClass) { + this(new ApplicationHandler(applicationClass, new GrizzlyBinder())); } /** @@ -319,7 +326,11 @@ * @param parentContext DI provider specific context with application's registered bindings. */ /* package */ GrizzlyHttpContainer(final Application application, final Object parentContext) { - this.appHandler = new ApplicationHandler(application, new GrizzlyBinder(), parentContext); + this(new ApplicationHandler(application, new GrizzlyBinder(), parentContext)); + } + + private GrizzlyHttpContainer(ApplicationHandler applicationHandler) { + this.appHandler = applicationHandler; cacheConfigSetStatusOverSendError(); cacheConfigEnableLeadingContextPathSlashes(); }
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServer.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServer.java new file mode 100644 index 0000000..68d7048 --- /dev/null +++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServer.java
@@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.grizzly2.httpserver; + +import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.MANDATORY; +import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.OPTIONAL; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import javax.net.ssl.SSLContext; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.core.Application; + +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.ssl.SSLEngineConfigurator; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; + +/** + * Jersey {@code Server} implementation based on Grizzly {@link HttpServer}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +final class GrizzlyHttpServer implements WebServer { + + private final GrizzlyHttpContainer container; + + private final HttpServer httpServer; + + GrizzlyHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) { + this(new GrizzlyHttpContainer(application), configuration); + } + + GrizzlyHttpServer(final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + this(new GrizzlyHttpContainer(applicationClass), configuration); + } + + private GrizzlyHttpServer(final GrizzlyHttpContainer container, final JerseySeBootstrapConfiguration configuration) { + final SSLContext sslContext = configuration.sslContext(); + final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration + .sslClientAuthentication(); + + this.container = container; + this.httpServer = GrizzlyHttpServerFactory.createHttpServer( + configuration.uri(true), + this.container, + configuration.isHttps(), + configuration.isHttps() ? new SSLEngineConfigurator(sslContext, false, + sslClientAuthentication == MANDATORY, + sslClientAuthentication == OPTIONAL) : null, + configuration.autoStart()); + } + + @Override + public final GrizzlyHttpContainer container() { + return this.container; + } + + @Override + public final int port() { + return this.httpServer.getListener("grizzly").getPort(); + } + + @Override + public final CompletableFuture<Void> start() { + return CompletableFuture.runAsync(() -> { + try { + this.httpServer.start(); + } catch (final IOException e) { + throw new CompletionException(e); + } + }); + } + + @Override + public final CompletableFuture<Void> stop() { + return CompletableFuture.runAsync(this.httpServer::shutdownNow); + } + + @Override + public final <T> T unwrap(final Class<T> nativeClass) { + return nativeClass.cast(this.httpServer); + } + +}
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProvider.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProvider.java new file mode 100644 index 0000000..a3f911c --- /dev/null +++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProvider.java
@@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.grizzly2.httpserver; + +import jakarta.ws.rs.core.Application; + +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; + +/** + * Server provider for servers based on Grizzly {@link HttpServer}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class GrizzlyHttpServerProvider implements WebServerProvider { + + @Override + public final <T extends WebServer> T createServer(final Class<T> type, final Application application, + final JerseySeBootstrapConfiguration configuration) { + return GrizzlyHttpServer.class == type || WebServer.class == type + ? type.cast(new GrizzlyHttpServer(application, configuration)) + : null; + } + + @Override + public <T extends WebServer> T createServer(Class<T> type, Class<? extends Application> applicationClass, + JerseySeBootstrapConfiguration configuration) { + return GrizzlyHttpServer.class == type || WebServer.class == type + ? type.cast(new GrizzlyHttpServer(applicationClass, configuration)) + : null; + } +}
diff --git a/containers/grizzly2-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/grizzly2-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider new file mode 100644 index 0000000..dea5f90 --- /dev/null +++ b/containers/grizzly2-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@ +org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerProvider \ No newline at end of file
diff --git a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProviderTest.java b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProviderTest.java new file mode 100644 index 0000000..a7310d0 --- /dev/null +++ b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProviderTest.java
@@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.grizzly2.httpserver; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertThat; + +import java.security.AccessController; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.SSLContext; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.UriBuilder; + +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.jersey.internal.util.PropertiesHelper; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.spi.Container; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; +import org.junit.Test; + +/** + * Unit tests for {@link GrizzlyHttpServerProvider}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class GrizzlyHttpServerProviderTest { + + @Test(timeout = 15000) + public void shouldProvideServer() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + shouldProvideServer(ShouldProvideServerApplication.class, resource); + } + + @Test(timeout = 15000) + public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + final Application application = new ShouldProvideServerApplication(); + shouldProvideServer(application.getClass(), resource); + } + + private void shouldProvideServer(final Object application, final Resource resource) + throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new GrizzlyHttpServerProvider(); + final SeBootstrap.Configuration configuration = configuration(getPort()); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = Application.class.isInstance(application) + ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig) + : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig); + final Object nativeHandle = webServer.unwrap(Object.class); + final CompletionStage<?> start = webServer.start(); + final Object startResult = start.toCompletableFuture().get(); + final Container container = webServer.container(); + final int port = webServer.port(); + final String entity = ClientBuilder.newClient() + .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request() + .get(String.class); + final CompletionStage<?> stop = webServer.stop(); + final Object stopResult = stop.toCompletableFuture().get(); + + // then + assertThat(webServer, is(instanceOf(GrizzlyHttpServer.class))); + assertThat(nativeHandle, is(instanceOf(HttpServer.class))); + assertThat(startResult, is(nullValue())); + assertThat(container, is(instanceOf(GrizzlyHttpContainer.class))); + assertThat(port, is(greaterThan(0))); + assertThat(entity, is(resource.toString())); + assertThat(stopResult, is(nullValue())); + } + + @Path("/") + protected static final class Resource { + @GET + @Override + public String toString() { + return Resource.class.getName(); + } + } + + protected static class ShouldProvideServerApplication extends Application { + @Override + public final Set<Object> getSingletons() { + return Collections.singleton(new Resource()); + } + } + + private static final Logger LOGGER = Logger.getLogger(GrizzlyHttpServerProviderTest.class.getName()); + + private static final int DEFAULT_PORT = 0; + + private static final int getPort() { + final String value = AccessController + .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); + if (value != null) { + try { + final int i = Integer.parseInt(value); + if (i < 0) { + throw new NumberFormatException("Value is negative."); + } + return i; + } catch (final NumberFormatException e) { + LOGGER.log(Level.CONFIG, + "Value of 'jersey.config.test.container.port'" + + " property is not a valid non-negative integer [" + value + "]." + + " Reverting to default [" + DEFAULT_PORT + "].", + e); + } + } + + return DEFAULT_PORT; + } + + @Test(timeout = 15000) + public void shouldScanFreePort() { + // given + final WebServerProvider webServerProvider = new GrizzlyHttpServerProvider(); + final Application application = new Application(); + final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig); + + // then + assertThat(webServer.port(), is(greaterThan(0))); + } + + private SeBootstrap.Configuration configuration(int port) { + return (SeBootstrap.Configuration) name -> { + switch (name) { + case SeBootstrap.Configuration.PROTOCOL: + return "HTTP"; + case SeBootstrap.Configuration.HOST: + return "localhost"; + case SeBootstrap.Configuration.PORT: + return port; + case SeBootstrap.Configuration.ROOT_PATH: + return "/"; + case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION: + return SSLClientAuthentication.NONE; + case SeBootstrap.Configuration.SSL_CONTEXT: + try { + return SSLContext.getDefault(); + } catch (final NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + case ServerProperties.WEBSERVER_AUTO_START: + return TRUE; + default: + return null; + } + }; + } + +}
diff --git a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerTest.java b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerTest.java index 5fb5e08..695c906 100644 --- a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerTest.java +++ b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022 Payara Foundation 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/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/application/TestedEndpoint.java b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/application/TestedEndpoint.java index 3ad7fb8..0a195f1 100644 --- a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/application/TestedEndpoint.java +++ b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/application/TestedEndpoint.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022 Payara Foundation 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/containers/grizzly2-servlet/pom.xml b/containers/grizzly2-servlet/pom.xml index dbe618f..f44f63c 100644 --- a/containers/grizzly2-servlet/pom.xml +++ b/containers/grizzly2-servlet/pom.xml
@@ -36,7 +36,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> </dependency> <dependency> @@ -72,6 +71,7 @@ <instructions> <Import-Package> jakarta.servlet.*;version="[5.0,7.0)", + org.glassfish.grizzly.*;version="[3.0,5.0)", * </Import-Package> </instructions>
diff --git a/containers/jdk-http/pom.xml b/containers/jdk-http/pom.xml index 8a82c15..4fb6f49 100644 --- a/containers/jdk-http/pom.xml +++ b/containers/jdk-http/pom.xml
@@ -39,6 +39,11 @@ <scope>test</scope> <version>${commons.io.version}</version> </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> @@ -75,45 +80,8 @@ </resources> </build> - <profiles> - <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <properties> - <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 --> - <surefire.security.argline>-Djdk.tls.client.protocols=TLSv1.2</surefire.security.argline> - </properties> - </profile> - <profile> - <id>windows</id> - <activation> - <jdk>1.8</jdk> - <os> - <family>windows</family> - </os> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <!-- Exclude unit tests regarding JDK HTTP Server because of failing a server shutdown in a class - with several tests. "java.net.BindException: Address already in use: bind" - bug reported on https://bugs.openjdk.java.net/browse/JDK-8015692 --> - <excludes> - <exclude>org/glassfish/jersey/jdkhttp/BasicJdkHttpServerTest.java</exclude> - <exclude>org/glassfish/jersey/jdkhttp/JdkHttpPackageTest.java</exclude> - <exclude>org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java</exclude> - <exclude>org/glassfish/jersey/jdkhttp/LifecycleListenerTest.java</exclude> - </excludes> - </configuration> - </plugin> - </plugins> - </build> - </profile> - </profiles> - + <properties> + <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 --> + <surefire.security.argline>-Djdk.tls.client.protocols=TLSv1.2</surefire.security.argline> + </properties> </project>
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpHandlerContainer.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpHandlerContainer.java index 89f47d2..5acf6a1 100644 --- a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpHandlerContainer.java +++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpHandlerContainer.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 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 @@ -64,7 +64,7 @@ private volatile ApplicationHandler appHandler; /** - * Create new lightweight Java SE HTTP server container. + * Create new lightweight Java SE HTTP server container. * * @param application JAX-RS / Jersey application to be deployed on the container. */ @@ -73,7 +73,16 @@ } /** - * Create new lightweight Java SE HTTP server container. + * Create new lightweight Java SE HTTP server container. + * + * @param applicationClass class of JAX-RS / Jersey application to be deployed on the container. + */ + JdkHttpHandlerContainer(final Class<? extends Application> applicationClass) { + this.appHandler = new ApplicationHandler(applicationClass); + } + + /** + * Create new lightweight Java SE HTTP server container. * * @param application JAX-RS / Jersey application to be deployed on the container. * @param parentContext DI provider specific context with application's registered bindings.
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServer.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServer.java new file mode 100644 index 0000000..33d0bcf --- /dev/null +++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServer.java
@@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.jdkhttp; + +import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.MANDATORY; +import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.OPTIONAL; + +import java.util.concurrent.CompletableFuture; + +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; + +import com.sun.net.httpserver.HttpServer; + +/** + * Jersey {@code Server} implementation based on JDK {@link HttpServer}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +final class JdkHttpServer implements WebServer { + + private final JdkHttpHandlerContainer container; + + private final HttpServer httpServer; + + JdkHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) { + this(new JdkHttpHandlerContainer(application), configuration); + } + + JdkHttpServer(final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + this(new JdkHttpHandlerContainer(applicationClass), configuration); + } + + JdkHttpServer(final JdkHttpHandlerContainer container, final JerseySeBootstrapConfiguration configuration) { + final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration + .sslClientAuthentication(); + + this.container = container; + this.httpServer = JdkHttpServerFactory.createHttpServer( + configuration.uri(true), + this.container, + configuration.sslContext(), + sslClientAuthentication == OPTIONAL, + sslClientAuthentication == MANDATORY, + configuration.autoStart()); + } + + @Override + public final JdkHttpHandlerContainer container() { + return this.container; + } + + @Override + public final int port() { + return this.httpServer.getAddress().getPort(); + } + + @Override + public final CompletableFuture<Void> start() { + return CompletableFuture.runAsync(this.httpServer::start); + } + + @Override + public final CompletableFuture<Void> stop() { + return CompletableFuture.runAsync(() -> this.httpServer.stop(0)); + } + + @Override + public final <T> T unwrap(final Class<T> nativeClass) { + return nativeClass.cast(this.httpServer); + } + +}
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java index 1c60336..a136edf 100644 --- a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java +++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java
@@ -26,6 +26,7 @@ import jakarta.ws.rs.ProcessingException; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; import org.glassfish.jersey.jdkhttp.internal.LocalizationMessages; @@ -37,6 +38,7 @@ import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsParameters; import com.sun.net.httpserver.HttpsServer; /** @@ -177,8 +179,17 @@ } private static HttpServer createHttpServer(final URI uri, + final JdkHttpHandlerContainer handler, + final SSLContext sslContext, + final boolean start) { + return createHttpServer(uri, handler, sslContext, false, false, start); + } + + static HttpServer createHttpServer(final URI uri, final JdkHttpHandlerContainer handler, final SSLContext sslContext, + final boolean sslClientAuthWanted, + final boolean sslClientAuthNeeded, final boolean start) { if (uri == null) { throw new IllegalArgumentException(LocalizationMessages.ERROR_CONTAINER_URI_NULL()); @@ -187,7 +198,14 @@ final String scheme = uri.getScheme(); final boolean isHttp = "http".equalsIgnoreCase(scheme); final boolean isHttps = "https".equalsIgnoreCase(scheme); - final HttpsConfigurator httpsConfigurator = sslContext != null ? new HttpsConfigurator(sslContext) : null; + final HttpsConfigurator httpsConfigurator = sslContext != null ? new HttpsConfigurator(sslContext) { + public final void configure(final HttpsParameters httpsParameters) { + final SSLParameters sslParameters = sslContext.getDefaultSSLParameters(); + sslParameters.setWantClientAuth(sslClientAuthWanted); + sslParameters.setNeedClientAuth(sslClientAuthNeeded); + httpsParameters.setSSLParameters(sslParameters); + } + } : null; if (isHttp) { if (httpsConfigurator != null) {
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProvider.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProvider.java new file mode 100644 index 0000000..4510c53 --- /dev/null +++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProvider.java
@@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.jdkhttp; + +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; + +import com.sun.net.httpserver.HttpServer; + +/** + * Server provider for servers based on JDK {@link HttpServer}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class JdkHttpServerProvider implements WebServerProvider { + + @Override + public <T extends WebServer> T createServer(final Class<T> type, final Application application, + final JerseySeBootstrapConfiguration configuration) { + return JdkHttpServer.class == type || WebServer.class == type + ? type.cast(new JdkHttpServer(application, configuration)) + : null; + } + + @Override + public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + return JdkHttpServer.class == type || WebServer.class == type + ? type.cast(new JdkHttpServer(applicationClass, configuration)) + : null; + } +}
diff --git a/containers/jdk-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/jdk-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider new file mode 100644 index 0000000..2d8abc3 --- /dev/null +++ b/containers/jdk-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@ +org.glassfish.jersey.jdkhttp.JdkHttpServerProvider \ No newline at end of file
diff --git a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/AbstractJdkHttpServerTester.java b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/AbstractJdkHttpServerTester.java index c7cd54b..03070a9 100644 --- a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/AbstractJdkHttpServerTester.java +++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/AbstractJdkHttpServerTester.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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.util.logging.Level; import java.util.logging.Logger; +import javax.net.ssl.SSLContext; import jakarta.ws.rs.core.UriBuilder; import com.sun.net.httpserver.HttpServer; @@ -37,7 +38,7 @@ public abstract class AbstractJdkHttpServerTester { public static final String CONTEXT = ""; - private final int DEFAULT_PORT = 9998; + private final int DEFAULT_PORT = 0; private static final Logger LOGGER = Logger.getLogger(AbstractJdkHttpServerTester.class.getName()); @@ -47,20 +48,24 @@ * @return The HTTP port of the URI */ protected final int getPort() { - final String value = - AccessController.doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); + if (server != null) { + return server.getAddress().getPort(); + } + + final String value = AccessController.doPrivileged( + PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); if (value != null) { try { final int i = Integer.parseInt(value); - if (i <= 0) { - throw new NumberFormatException("Value not positive."); + if (i < 0) { + throw new NumberFormatException("Value is negative."); } return i; } catch (NumberFormatException e) { LOGGER.log(Level.CONFIG, "Value of 'jersey.config.test.container.port'" - + " property is not a valid positive integer [" + value + "]." + + " property is not a valid non-negative integer [" + value + "]." + " Reverting to default [" + DEFAULT_PORT + "].", e); } @@ -76,17 +81,23 @@ public void startServer(Class... resources) { ResourceConfig config = new ResourceConfig(resources); - config.register(LoggingFeature.class); - final URI baseUri = getBaseUri(); - server = JdkHttpServerFactory.createHttpServer(baseUri, config); - LOGGER.log(Level.INFO, "jdk-http server started on base uri: " + baseUri); + startServer(config); } public void startServer(ResourceConfig config) { final URI baseUri = getBaseUri(); config.register(LoggingFeature.class); server = JdkHttpServerFactory.createHttpServer(baseUri, config); - LOGGER.log(Level.INFO, "jdk-http server started on base uri: " + baseUri); + LOGGER.log(Level.INFO, "jdk-http server started on base uri: " + getBaseUri()); + } + + public HttpServer startServer(final URI uri, final ResourceConfig config, + final SSLContext sslContext, final boolean start) { + config.register(LoggingFeature.class); + server = JdkHttpServerFactory.createHttpServer(uri, config, sslContext, start); + LOGGER.log(Level.INFO, + "jdk-http server started on base uri: " + UriBuilder.fromUri(uri).port(getPort()).build()); + return server; } public URI getBaseUri() {
diff --git a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProviderTest.java b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProviderTest.java new file mode 100644 index 0000000..ade5284 --- /dev/null +++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProviderTest.java
@@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.jdkhttp; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertThat; + +import java.security.AccessController; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.SSLContext; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.UriBuilder; + +import org.glassfish.jersey.internal.util.PropertiesHelper; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.spi.Container; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; +import org.junit.Test; + +import com.sun.net.httpserver.HttpServer; + +/** + * Unit tests for {@link JdkHttpServerProvider}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class JdkHttpServerProviderTest { + + @Test(timeout = 15000) + public void shouldProvideServer() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + shouldProvideServer(ShouldProvideServerApplication.class, resource); + } + + @Test(timeout = 15000) + public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + final Application application = new ShouldProvideServerApplication(); + shouldProvideServer(application.getClass(), resource); + } + + private void shouldProvideServer(final Object application, final Resource resource) + throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new JdkHttpServerProvider(); + final SeBootstrap.Configuration configuration = configuration(getPort()); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = Application.class.isInstance(application) + ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig) + : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig); + final Object nativeHandle = webServer.unwrap(Object.class); + final CompletionStage<?> start = webServer.start(); + final Object startResult = start.toCompletableFuture().get(); + final Container container = webServer.container(); + final int port = webServer.port(); + final String entity = ClientBuilder.newClient() + .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request() + .get(String.class); + final CompletionStage<?> stop = webServer.stop(); + final Object stopResult = stop.toCompletableFuture().get(); + + // then + assertThat(webServer, is(instanceOf(JdkHttpServer.class))); + assertThat(nativeHandle, is(instanceOf(HttpServer.class))); + assertThat(startResult, is(nullValue())); + assertThat(container, is(instanceOf(JdkHttpHandlerContainer.class))); + assertThat(port, is(greaterThan(0))); + assertThat(entity, is(resource.toString())); + assertThat(stopResult, is(nullValue())); + } + + @Path("/") + protected static final class Resource { + @GET + @Override + public String toString() { + return Resource.class.getName(); + } + } + + protected static class ShouldProvideServerApplication extends Application { + @Override + public Set<Object> getSingletons() { + return Collections.singleton(new Resource()); + } + } + + private static final Logger LOGGER = Logger.getLogger(JdkHttpServerProviderTest.class.getName()); + + private static final int DEFAULT_PORT = 0; + + private static final int getPort() { + final String value = AccessController + .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); + if (value != null) { + try { + final int i = Integer.parseInt(value); + if (i < 0) { + throw new NumberFormatException("Value is negative."); + } + return i; + } catch (final NumberFormatException e) { + LOGGER.log(Level.CONFIG, + "Value of 'jersey.config.test.container.port'" + + " property is not a valid non-negative integer [" + value + "]." + + " Reverting to default [" + DEFAULT_PORT + "].", + e); + } + } + + return DEFAULT_PORT; + } + + @Test(timeout = 15000) + public final void shouldScanFreePort() throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new JdkHttpServerProvider(); + final Application application = new Application(); + final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig); + + // then + assertThat(webServer.port(), is(greaterThan(0))); + } + + private SeBootstrap.Configuration configuration(int port) { + return (SeBootstrap.Configuration) name -> { + switch (name) { + case SeBootstrap.Configuration.PROTOCOL: + return "HTTP"; + case SeBootstrap.Configuration.HOST: + return "localhost"; + case SeBootstrap.Configuration.PORT: + return port; + case SeBootstrap.Configuration.ROOT_PATH: + return "/"; + case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION: + return SSLClientAuthentication.NONE; + case SeBootstrap.Configuration.SSL_CONTEXT: + try { + return SSLContext.getDefault(); + } catch (final NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + case ServerProperties.WEBSERVER_AUTO_START: + return FALSE; + default: + return null; + } + }; + } +}
diff --git a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java index 1185b81..3dae9c4 100644 --- a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java +++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
@@ -32,10 +32,8 @@ import org.apache.commons.io.IOUtils; import org.glassfish.jersey.SslConfigurator; -import org.glassfish.jersey.internal.util.JdkVersion; import org.glassfish.jersey.server.ResourceConfig; -import org.junit.After; import org.junit.Test; import com.sun.net.httpserver.HttpServer; @@ -64,9 +62,6 @@ private static final String TRUSTSTORE_SERVER_FILE = "./truststore_server"; private static final String TRUSTSTORE_SERVER_PWD = "asdfgh"; - private HttpServer server; - private final URI httpsUri = UriBuilder.fromUri("https://localhost/").port(getPort()).build(); - private final URI httpUri = UriBuilder.fromUri("http://localhost/").port(getPort()).build(); private final ResourceConfig rc = new ResourceConfig(TestResource.class); @Path("/testHttps") @@ -83,12 +78,8 @@ */ @Test public void testCreateHttpsServerNoSslContext() throws Exception { - server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, false); + HttpServer server = startServer(getHttpsUri(), rc, null, false); assertThat(server, instanceOf(HttpsServer.class)); - - if (JdkVersion.getJdkVersion().getMajor() > 8) { - server.start(); // Address already in bind otherwise - } } /** @@ -97,7 +88,7 @@ */ @Test(expected = IllegalArgumentException.class) public void testStartHttpServerNoSslContext() throws Exception { - server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, true); + startServer(getHttpsUri(), rc, null, true); } /** @@ -107,13 +98,13 @@ */ @Test(expected = SSLHandshakeException.class) public void testCreateHttpsServerDefaultSslContext() throws Throwable { - server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, SSLContext.getDefault(), true); + HttpServer server = startServer(getHttpsUri(), rc, SSLContext.getDefault(), true); assertThat(server, instanceOf(HttpsServer.class)); // access the https server with not configured client final Client client = ClientBuilder.newBuilder().newClient(); try { - client.target(httpsUri).path("testHttps").request().get(String.class); + client.target(UriBuilder.fromUri("https://localhost/").port(getPort())).path("testHttps").request().get(String.class); } catch (final ProcessingException e) { throw e.getCause(); } @@ -126,13 +117,13 @@ */ @Test(expected = IOException.class) public void testHttpsServerNoSslContextDelayedStart() throws Throwable { - server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, false); + HttpServer server = startServer(getHttpsUri(), rc, null, false); assertThat(server, instanceOf(HttpsServer.class)); server.start(); final Client client = ClientBuilder.newBuilder().newClient(); try { - client.target(httpsUri).path("testHttps").request().get(String.class); + client.target(getHttpsUri()).path("testHttps").request().get(String.class); } catch (final ProcessingException e) { throw e.getCause(); } @@ -144,7 +135,7 @@ */ @Test(expected = IllegalStateException.class) public void testConfigureSslContextAfterStart() throws Throwable { - server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, false); + HttpServer server = startServer(getHttpsUri(), rc, null, false); assertThat(server, instanceOf(HttpsServer.class)); server.start(); ((HttpsServer) server).setHttpsConfigurator(new HttpsConfigurator(getServerSslContext())); @@ -158,14 +149,14 @@ public void testCreateHttpsServerRoundTrip() throws IOException { final SSLContext serverSslContext = getServerSslContext(); - server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, serverSslContext, true); + HttpServer server = startServer(getHttpsUri(), rc, serverSslContext, true); final SSLContext foundContext = ((HttpsServer) server).getHttpsConfigurator().getSSLContext(); assertEquals(serverSslContext, foundContext); final SSLContext clientSslContext = getClientSslContext(); final Client client = ClientBuilder.newBuilder().sslContext(clientSslContext).build(); - final String response = client.target(httpsUri).path("testHttps").request().get(String.class); + final String response = client.target(UriBuilder.fromUri("https://localhost/").port(getPort())).path("testHttps").request().get(String.class); assertEquals("test", response); } @@ -176,7 +167,7 @@ */ @Test public void testHttpWithSsl() throws IOException { - server = JdkHttpServerFactory.createHttpServer(httpUri, rc, getServerSslContext(), true); + HttpServer server = startServer(getBaseUri(), rc, getServerSslContext(), true); assertThat(server, instanceOf(HttpServer.class)); assertThat(server, not(instanceOf(HttpsServer.class))); } @@ -208,11 +199,7 @@ return sslConfigServer.createSSLContext(); } - @After - public void tearDown() { - if (server != null) { - server.stop(0); - server = null; - } + private URI getHttpsUri() { + return UriBuilder.fromUri("https://localhost/").port(getPort()).build(); } }
diff --git a/containers/jersey-servlet-core/pom.xml b/containers/jersey-servlet-core/pom.xml index 2348e36..2bcfc85 100644 --- a/containers/jersey-servlet-core/pom.xml +++ b/containers/jersey-servlet-core/pom.xml
@@ -36,7 +36,7 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + <version>${servlet6.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java index d91c43a..1f3d80a 100644 --- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java +++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
@@ -317,8 +317,7 @@ final Response.Status badRequest = Response.Status.BAD_REQUEST; if (webComponent.configSetStatusOverSendError) { response.reset(); - //noinspection deprecation - response.setStatus(badRequest.getStatusCode(), badRequest.getReasonPhrase()); + response.setStatus(badRequest.getStatusCode()); } else { response.sendError(badRequest.getStatusCode(), badRequest.getReasonPhrase()); }
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java index 3aaf758..0a12abf 100644 --- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java +++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -401,8 +401,7 @@ if (configSetStatusOverSendError) { servletResponse.reset(); - //noinspection deprecation - servletResponse.setStatus(status.getStatusCode(), status.getReasonPhrase()); + servletResponse.setStatus(status.getStatusCode()); } else { servletResponse.sendError(status.getStatusCode(), status.getReasonPhrase()); }
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java index 5b51a2f..bb39876 100644 --- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java +++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
@@ -144,7 +144,7 @@ final String reasonPhrase = responseContext.getStatusInfo().getReasonPhrase(); if (reasonPhrase != null) { - response.setStatus(responseContext.getStatus(), reasonPhrase); + response.setStatus(responseContext.getStatus()); } else { response.setStatus(responseContext.getStatus()); } @@ -217,7 +217,7 @@ if (configSetStatusOverSendError) { response.reset(); //noinspection deprecation - response.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Request failed."); + response.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); } else { response.sendError(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Request failed."); }
diff --git a/containers/jersey-servlet/pom.xml b/containers/jersey-servlet/pom.xml index 578e1e1..b5244a6 100644 --- a/containers/jersey-servlet/pom.xml +++ b/containers/jersey-servlet/pom.xml
@@ -36,7 +36,7 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + <version>${servlet6.version}</version> <scope>provided</scope> </dependency>
diff --git a/containers/jetty-http/pom.xml b/containers/jetty-http/pom.xml index e601b0b..48523f2 100644 --- a/containers/jetty-http/pom.xml +++ b/containers/jetty-http/pom.xml
@@ -50,7 +50,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency> @@ -62,6 +61,11 @@ <artifactId>httpclient</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> @@ -91,162 +95,4 @@ </resources> </build> - <properties> - <java8.build.outputDirectory>${project.basedir}/target</java8.build.outputDirectory> - <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory> - <java11.build.outputDirectory>${project.basedir}/target11</java11.build.outputDirectory> - <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory> - </properties> - - <profiles> - <profile> - <id>JettyExclude</id> - <activation> - <jdk>1.8</jdk> - </activation> - <properties> - <jetty.version>9.4.28.v20200408</jetty.version> - </properties> - <dependencies> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-client</artifactId> - <version>${jetty.version}</version> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-util</artifactId> - <version>${jetty.version}</version> - </dependency> - </dependencies> - <build> - <directory>${java8.build.outputDirectory}</directory> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>${java8.sourceDirectory}</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <testExcludes> - <testExclude>org/glassfish/jersey/jetty/*.java</testExclude> - </testExcludes> - </configuration> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>Jetty11</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <build> - <directory>${java11.build.outputDirectory}</directory> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>${java11.sourceDirectory}</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>copyJDK11FilesToMultiReleaseJar</id> - <activation> - <file> - <!-- ${java11.build.outputDirectory} does not work here --> - <exists>target11/classes/org/glassfish/jersey/jetty/JettyHttpContainer.class</exists> - </file> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <inherited>true</inherited> - <extensions>true</extensions> - <configuration> - <instructions> - <Multi-Release>true</Multi-Release> - </instructions> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-resources-plugin</artifactId> - <inherited>true</inherited> - <executions> - <execution> - <id>copy-jdk11-classes</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory> - <resources> - <resource> - <directory>${java11.build.outputDirectory}/classes</directory> - </resource> - </resources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-antrun-plugin</artifactId> - <executions> - <execution> - <id>copy-jdk11-sources</id> - <phase>package</phase> - <configuration> - <target> - <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/> - <echo>sources-jar: ${sources-jar}</echo> - <zip destfile="${sources-jar}" update="true"> - <zipfileset dir="${java11.sourceDirectory}" prefix="META-INF/versions/11"/> - </zip> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> </project>
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java similarity index 97% rename from containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java rename to containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java index 26ccd92..fb0b790 100644 --- a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java +++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 Contributors to the Eclipse Foundation * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -36,7 +37,6 @@ import jakarta.servlet.AsyncListener; import jakarta.ws.rs.core.Application; import jakarta.ws.rs.core.GenericType; -import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.SecurityContext; import jakarta.inject.Inject; @@ -198,7 +198,7 @@ if (configSetStatusOverSendError) { response.reset(); //noinspection deprecation - response.setStatus(BAD_REQUEST_STATUS.getStatusCode(), BAD_REQUEST_STATUS.getReasonPhrase()); + response.setStatus(BAD_REQUEST_STATUS.getStatusCode()); } else { response.sendError(BAD_REQUEST_STATUS.getStatusCode(), BAD_REQUEST_STATUS.getReasonPhrase()); } @@ -492,6 +492,17 @@ } /** + * Create a new Jetty HTTP container. + * + * @param applicationClass JAX-RS / Jersey class of application to be deployed on Jetty HTTP container. + */ + JettyHttpContainer(final Class<? extends Application> applicationClass) { + this.appHandler = new ApplicationHandler(applicationClass, new JettyBinder()); + + cacheConfigSetStatusOverSendError(); + } + + /** * The method reads and caches value of configuration property * {@link ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR} for future purposes. */
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java similarity index 99% rename from containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java rename to containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java index 39f23e8..3ccb7b8 100644 --- a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java +++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java new file mode 100644 index 0000000..64af030 --- /dev/null +++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java
@@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.jetty; + +import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.MANDATORY; +import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.OPTIONAL; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.core.Application; + +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.glassfish.jersey.server.ContainerFactory; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; + +/** + * Jersey {@code Server} implementation based on Jetty + * {@link org.eclipse.jetty.server.Server Server}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +final class JettyHttpServer implements WebServer { + + private final JettyHttpContainer container; + + private final org.eclipse.jetty.server.Server httpServer; + + JettyHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) { + this(ContainerFactory.createContainer(JettyHttpContainer.class, application), configuration); + } + + JettyHttpServer(final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + this(new JettyHttpContainer(applicationClass), configuration); + } + + JettyHttpServer(final JettyHttpContainer container, final JerseySeBootstrapConfiguration configuration) { + final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration + .sslClientAuthentication(); + final SslContextFactory.Server sslContextFactory; + if (configuration.isHttps()) { + sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setSslContext(configuration.sslContext()); + sslContextFactory.setWantClientAuth(sslClientAuthentication == OPTIONAL); + sslContextFactory.setNeedClientAuth(sslClientAuthentication == MANDATORY); + } else { + sslContextFactory = null; + } + this.container = container; + this.httpServer = JettyHttpContainerFactory.createServer( + configuration.uri(true), + sslContextFactory, + this.container, + configuration.autoStart()); + } + + @Override + public final JettyHttpContainer container() { + return this.container; + } + + @Override + public final int port() { + final ServerConnector serverConnector = (ServerConnector) this.httpServer.getConnectors()[0]; + final int configuredPort = serverConnector.getPort(); + final int localPort = serverConnector.getLocalPort(); + return localPort < 0 ? configuredPort : localPort; + } + + @Override + public final CompletableFuture<Void> start() { + return CompletableFuture.runAsync(() -> { + try { + this.httpServer.start(); + } catch (final Exception e) { + throw new CompletionException(e); + } + }); + } + + @Override + public final CompletableFuture<Void> stop() { + return CompletableFuture.runAsync(() -> { + try { + this.httpServer.stop(); + } catch (final Exception e) { + throw new CompletionException(e); + } + }); + } + + @Override + public final <T> T unwrap(final Class<T> nativeClass) { + return nativeClass.cast(this.httpServer); + } + +}
diff --git a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java new file mode 100644 index 0000000..fe776b9 --- /dev/null +++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java
@@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.jetty; + +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; + +/** + * Server provider for servers based on Jetty + * {@link org.eclipse.jetty.server.Server Server}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class JettyHttpServerProvider implements WebServerProvider { + + @Override + public final <T extends WebServer> T createServer(final Class<T> type, final Application application, + final JerseySeBootstrapConfiguration configuration) { + return JettyHttpServer.class == type || WebServer.class == type + ? type.cast(new JettyHttpServer(application, configuration)) + : null; + } + + @Override + public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + return JettyHttpServer.class == type || WebServer.class == type + ? type.cast(new JettyHttpServer(applicationClass, configuration)) + : null; + } +}
diff --git a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainer.java deleted file mode 100644 index b852e54..0000000 --- a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainer.java +++ /dev/null
@@ -1,58 +0,0 @@ -/* - * Copyright (c) 2020 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.jetty; - -import jakarta.ws.rs.ProcessingException; -import jakarta.ws.rs.core.Application; -import org.glassfish.jersey.jetty.internal.LocalizationMessages; -import org.glassfish.jersey.server.ApplicationHandler; -import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.server.spi.Container; - -/** - * Jersey {@code Container} stub based on Jetty {@link org.eclipse.jetty.server.Handler}. - * - * For JDK 1.8 only since Jetty 11 does not support JDKs below 11 - * - */ -public final class JettyHttpContainer implements Container { - - public JettyHttpContainer(Application application) { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - @Override - public ResourceConfig getConfiguration() { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - @Override - public ApplicationHandler getApplicationHandler() { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - @Override - public void reload() { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - @Override - public void reload(ResourceConfig configuration) { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - -}
diff --git a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java deleted file mode 100644 index 1218c2e..0000000 --- a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java +++ /dev/null
@@ -1,93 +0,0 @@ -/* - * Copyright (c) 2021 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.jetty; - -import jakarta.ws.rs.ProcessingException; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.glassfish.jersey.internal.util.JdkVersion; -import org.glassfish.jersey.jetty.internal.LocalizationMessages; -import org.glassfish.jersey.server.ResourceConfig; - -import java.net.URI; - -/** - * Jersey {@code Container} stub. - * - * For JDK 1.8 only since Jetty 11 does not support JDKs below 11 - * - */ -public final class JettyHttpContainerFactory { - - private JettyHttpContainerFactory() { - } - - public static Server createServer(final URI uri) throws ProcessingException { - validateJdk(); - return null; // does not work at JDK 1.8 - } - - public static Server createServer(final URI uri, final boolean start) throws ProcessingException { - validateJdk(); - return null; // does not work at JDK 1.8 - } - - public static Server createServer(final URI uri, final ResourceConfig config) - throws ProcessingException { - - validateJdk(); - return null; // does not work at JDK 1.8 - } - - public static Server createServer(final URI uri, final ResourceConfig configuration, final boolean start) - throws ProcessingException { - validateJdk(); - return null; // does not work at JDK 1.8 - } - - public static Server createServer(final URI uri, final ResourceConfig config, final boolean start, - final Object parentContext) { - validateJdk(); - return null; // does not work at JDK 1.8 - } - - public static Server createServer(final URI uri, final ResourceConfig config, final Object parentContext) { - validateJdk(); - return null; // does not work at JDK 1.8 - } - - public static Server createServer(final URI uri, final SslContextFactory.Server sslContextFactory, - final ResourceConfig config) - throws ProcessingException { - validateJdk(); - return null; // does not work at JDK 1.8 } - } - - public static Server createServer(final URI uri, - final SslContextFactory.Server sslContextFactory, - final JettyHttpContainer handler, - final boolean start) { - validateJdk(); - return null; // does not work at JDK 1.8 - } - - private static void validateJdk() { - if (JdkVersion.getJdkVersion().getMajor() < 11) { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - } -}
diff --git a/containers/jetty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/jetty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider new file mode 100644 index 0000000..403cc50 --- /dev/null +++ b/containers/jetty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@ +org.glassfish.jersey.jetty.JettyHttpServerProvider \ No newline at end of file
diff --git a/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java index 32ffbcd..e8c700d 100644 --- a/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java +++ b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java
@@ -1,5 +1,6 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2010, 2021 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,6 +29,7 @@ import org.glassfish.jersey.server.ResourceConfig; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; import org.junit.After; /** @@ -42,7 +44,7 @@ private static final Logger LOGGER = Logger.getLogger(AbstractJettyServerTester.class.getName()); public static final String CONTEXT = ""; - private static final int DEFAULT_PORT = 9998; + private static final int DEFAULT_PORT = 0; /** * Get the port to be used for test application deployments. @@ -50,20 +52,24 @@ * @return The HTTP port of the URI */ protected final int getPort() { + if (server != null) { + return ((ServerConnector) server.getConnectors()[0]).getLocalPort(); + } + final String value = AccessController .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); if (value != null) { try { final int i = Integer.parseInt(value); - if (i <= 0) { - throw new NumberFormatException("Value not positive."); + if (i < 0) { + throw new NumberFormatException("Value is negative."); } return i; } catch (NumberFormatException e) { LOGGER.log(Level.CONFIG, "Value of 'jersey.config.test.container.port'" - + " property is not a valid positive integer [" + value + "]." + + " property is not a valid non-negative integer [" + value + "]." + " Reverting to default [" + DEFAULT_PORT + "].", e); } @@ -77,18 +83,16 @@ return UriBuilder.fromUri("http://localhost").port(getPort()).path(CONTEXT); } - public void startServer(Class... resources) { + public void startServer(Class<?>... resources) { ResourceConfig config = new ResourceConfig(resources); config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY)); - final URI baseUri = getBaseUri(); - server = JettyHttpContainerFactory.createServer(baseUri, config); - LOGGER.log(Level.INFO, "Jetty-http server started on base uri: " + baseUri); + startServer(config); } public void startServer(ResourceConfig config) { final URI baseUri = getBaseUri(); server = JettyHttpContainerFactory.createServer(baseUri, config); - LOGGER.log(Level.INFO, "Jetty-http server started on base uri: " + baseUri); + LOGGER.log(Level.INFO, "Jetty-http server started on base uri: " + getBaseUri()); } public URI getBaseUri() {
diff --git a/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java new file mode 100644 index 0000000..1aa8b45 --- /dev/null +++ b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java
@@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.jetty; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertThat; + +import java.security.AccessController; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.SSLContext; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.UriBuilder; + +import org.glassfish.jersey.internal.util.PropertiesHelper; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.spi.Container; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; +import org.junit.Test; + +/** + * Unit tests for {@link JettyHttpServerProvider}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class JettyHttpServerProviderTest { + + @Test(timeout = 15000) + public void shouldProvideServer() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + shouldProvideServer(ShouldProvideServerApplication.class, resource); + } + + @Test(timeout = 15000) + public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + final Application application = new ShouldProvideServerApplication(); + shouldProvideServer(application.getClass(), resource); + } + + private void shouldProvideServer(final Object application, final Resource resource) + throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new JettyHttpServerProvider(); + final SeBootstrap.Configuration configuration = configuration(getPort(), FALSE); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = Application.class.isInstance(application) + ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig) + : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig); + final Object nativeHandle = webServer.unwrap(Object.class); + final CompletionStage<?> start = webServer.start(); + final Object startResult = start.toCompletableFuture().get(); + final Container container = webServer.container(); + final int port = webServer.port(); + final String entity = ClientBuilder.newClient() + .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request() + .get(String.class); + final CompletionStage<?> stop = webServer.stop(); + final Object stopResult = stop.toCompletableFuture().get(); + + // then + assertThat(webServer, is(instanceOf(JettyHttpServer.class))); + assertThat(nativeHandle, is(instanceOf(org.eclipse.jetty.server.Server.class))); + assertThat(startResult, is(nullValue())); + assertThat(container, is(instanceOf(JettyHttpContainer.class))); + assertThat(port, is(greaterThan(0))); + assertThat(entity, is(resource.toString())); + assertThat(stopResult, is(nullValue())); + } + + @Path("/") + protected static final class Resource { + @GET + @Override + public String toString() { + return Resource.class.getName(); + } + } + + protected static class ShouldProvideServerApplication extends Application { + @Override + public Set<Object> getSingletons() { + return Collections.singleton(new Resource()); + } + } + + private static final Logger LOGGER = Logger.getLogger(JettyHttpServerProviderTest.class.getName()); + + private static final int DEFAULT_PORT = 0; + + private static final int getPort() { + final String value = AccessController + .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); + if (value != null) { + try { + final int i = Integer.parseInt(value); + if (i < 0) { + throw new NumberFormatException("Value is negative."); + } + return i; + } catch (final NumberFormatException e) { + LOGGER.log(Level.CONFIG, + "Value of 'jersey.config.test.container.port'" + + " property is not a valid non-negative integer [" + value + "]." + + " Reverting to default [" + DEFAULT_PORT + "].", + e); + } + } + + return DEFAULT_PORT; + } + + @Test(timeout = 15000) + public final void shouldScanFreePort() throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new JettyHttpServerProvider(); + final Application application = new Application(); + final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT, TRUE); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig); + + // then + assertThat(webServer.port(), is(greaterThan(0))); + } + + private SeBootstrap.Configuration configuration(int port, boolean autoStart) { + return (SeBootstrap.Configuration) name -> { + switch (name) { + case SeBootstrap.Configuration.PROTOCOL: + return "HTTP"; + case SeBootstrap.Configuration.HOST: + return "localhost"; + case SeBootstrap.Configuration.PORT: + return port; + case SeBootstrap.Configuration.ROOT_PATH: + return "/"; + case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION: + return SSLClientAuthentication.NONE; + case SeBootstrap.Configuration.SSL_CONTEXT: + try { + return SSLContext.getDefault(); + } catch (final NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + case ServerProperties.WEBSERVER_AUTO_START: + return autoStart; + default: + return null; + } + }; + } + +}
diff --git a/containers/jetty-servlet/pom.xml b/containers/jetty-servlet/pom.xml index f14891f..aad1198 100644 --- a/containers/jetty-servlet/pom.xml +++ b/containers/jetty-servlet/pom.xml
@@ -46,7 +46,15 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-webapp</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-jetty-http</artifactId> + <version>${project.version}</version> </dependency> </dependencies> @@ -75,190 +83,4 @@ </plugins> </build> - <properties> - <java8.build.outputDirectory>${project.basedir}/target</java8.build.outputDirectory> - <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory> - <java11.build.outputDirectory>${project.basedir}/target11</java11.build.outputDirectory> - <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory> - <jetty.javax.version>9.4.28.v20200408</jetty.javax.version> - </properties> - - <profiles> - <profile> - <id>JettyExclude</id> - <activation> - <jdk>1.8</jdk> - </activation> - <dependencies> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - <version>${jetty.javax.version}</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-webapp</artifactId> - <version>${jetty.javax.version}</version> - <scope>provided</scope> - <exclusions> - <exclusion> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>org.glassfish.jersey.containers</groupId> - <artifactId>jersey-container-jetty-http</artifactId> - <version>${project.version}</version> - <exclusions> - <exclusion> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - </exclusion> - </exclusions> - </dependency> - </dependencies> - <build> - <directory>${java8.build.outputDirectory}</directory> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>${java8.sourceDirectory}</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <testExcludes> - <testExclude>org/glassfish/jersey/jetty/*.java</testExclude> - </testExcludes> - </configuration> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>Jetty11</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <dependencies> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-webapp</artifactId> - </dependency> - <dependency> - <groupId>org.glassfish.jersey.containers</groupId> - <artifactId>jersey-container-jetty-http</artifactId> - <version>${project.version}</version> - </dependency> - </dependencies> - <build> - <directory>${java11.build.outputDirectory}</directory> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>${java11.sourceDirectory}</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>copyJDK11FilesToMultiReleaseJar</id> - <activation> - <file> - <!-- ${java11.build.outputDirectory} does not work here --> - <exists>target11/classes/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.class</exists> - </file> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <inherited>true</inherited> - <extensions>true</extensions> - <configuration> - <instructions> - <Multi-Release>true</Multi-Release> - </instructions> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-resources-plugin</artifactId> - <inherited>true</inherited> - <executions> - <execution> - <id>copy-jdk11-classes</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory> - <resources> - <resource> - <directory>${java11.build.outputDirectory}/classes</directory> - </resource> - </resources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-antrun-plugin</artifactId> - <executions> - <execution> - <id>copy-jdk11-sources</id> - <phase>package</phase> - <configuration> - <target> - <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/> - <echo>sources-jar: ${sources-jar}</echo> - <zip destfile="${sources-jar}" update="true"> - <zipfileset dir="${java11.sourceDirectory}" prefix="META-INF/versions/11"/> - </zip> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> </project>
diff --git a/containers/jetty-servlet/src/main/java11/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java b/containers/jetty-servlet/src/main/java/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java similarity index 99% rename from containers/jetty-servlet/src/main/java11/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java rename to containers/jetty-servlet/src/main/java/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java index 7f46e7b..a663a50 100644 --- a/containers/jetty-servlet/src/main/java11/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java +++ b/containers/jetty-servlet/src/main/java/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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/containers/jetty-servlet/src/main/java8/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java b/containers/jetty-servlet/src/main/java8/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java deleted file mode 100644 index 3d87ae8..0000000 --- a/containers/jetty-servlet/src/main/java8/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java +++ /dev/null
@@ -1,90 +0,0 @@ -/* - * Copyright (c) 2020 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.jetty.servlet; - -import java.net.URI; -import java.util.Map; - -import jakarta.servlet.Servlet; - -import jakarta.ws.rs.ProcessingException; -import org.eclipse.jetty.server.Server; -import org.glassfish.jersey.jetty.internal.LocalizationMessages; - -/** - * Jersey {@code Server} stub based on Jetty {@link org.eclipse.jetty.server.Server}. - * <p> - * For JDK 1.8 only since Jetty 11 does not support JDKs below 11 - */ -public final class JettyWebContainerFactory { - - private JettyWebContainerFactory() { - } - - - public static Server create(String u) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - public static Server create(String u, Map<String, String> initParams) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - public static Server create(URI u) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - public static Server create(URI u, Map<String, String> initParams) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - public static Server create(String u, Class<? extends Servlet> c) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - public static Server create(String u, Class<? extends Servlet> c, - Map<String, String> initParams) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - public static Server create(URI u, Class<? extends Servlet> c) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - public static Server create(URI u, Class<? extends Servlet> c, Map<String, String> initParams) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - private static Server create(URI u, Class<? extends Servlet> c, Servlet servlet, - Map<String, String> initParams, Map<String, String> contextInitParams) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } - - public static Server create(URI u, Servlet servlet, Map<String, String> initParams, Map<String, String> contextInitParams) - throws Exception { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } -} \ No newline at end of file
diff --git a/containers/netty-http/pom.xml b/containers/netty-http/pom.xml index 77adb41..894c547 100644 --- a/containers/netty-http/pom.xml +++ b/containers/netty-http/pom.xml
@@ -42,6 +42,11 @@ <artifactId>jersey-netty-connector</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build>
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyHttp2ServerHandler.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyHttp2ServerHandler.java index 924fb9a..3212cd7 100644 --- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyHttp2ServerHandler.java +++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyHttp2ServerHandler.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022 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 @@ -135,6 +135,11 @@ private final Map<String, Object> properties = new HashMap<>(); @Override + public boolean hasProperty(final String name) { + return properties.containsKey(name); + } + + @Override public Object getProperty(String name) { return properties.get(name); }
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java index dbe8dc7..00758b4 100644 --- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java +++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022 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 @@ -170,6 +170,11 @@ private final Map<String, Object> properties = new HashMap<>(); @Override + public boolean hasProperty(final String name) { + return properties.containsKey(name); + } + + @Override public Object getProperty(String name) { return properties.get(name); }
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainer.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainer.java index bbbb5dc..fc5fced 100644 --- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainer.java +++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainer.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021 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 @@ -41,6 +41,11 @@ this.appHandler.onStartup(this); } + NettyHttpContainer(Class<? extends Application> applicationClass) { + this.appHandler = new ApplicationHandler(applicationClass); + this.appHandler.onStartup(this); + } + @Override public ResourceConfig getConfiguration() { return appHandler.getConfiguration();
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainerProvider.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainerProvider.java index 6f926c0..3d78837 100644 --- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainerProvider.java +++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainerProvider.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021 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 @@ -58,7 +58,7 @@ * Create and start Netty server. * * @param baseUri base uri. - * @param configuration Jersey configuration. + * @param container Jersey container. * @param sslContext Netty SSL context (can be null). * @param block when {@code true}, this method will block until the server is stopped. When {@code false}, the * execution will @@ -66,25 +66,64 @@ * @return Netty channel instance. * @throws ProcessingException when there is an issue with creating new container. */ - public static Channel createServer(final URI baseUri, final ResourceConfig configuration, SslContext sslContext, + public static Channel createServer(final URI baseUri, final NettyHttpContainer container, SslContext sslContext, final boolean block) throws ProcessingException { + final ServerBootstrap serverBootstrap = createServerBootstrap(baseUri, container, sslContext); + return startServer(getPort(baseUri), container, serverBootstrap, block); + } + + /** + * Create but not start Netty server. + * + * @param baseUri base uri. + * @param container Jersey container. + * @param sslContext Netty SSL context (can be null). + * @return Netty bootstrap instance. + * @throws ProcessingException when there is an issue with creating new container. + */ + static ServerBootstrap createServerBootstrap(final URI baseUri, final NettyHttpContainer container, SslContext sslContext) { + final JerseyServerInitializer jerseyServerInitializer = + new JerseyServerInitializer(baseUri, sslContext, container, container.getConfiguration()); + return createServerBootstrap(jerseyServerInitializer); + } + + private static ServerBootstrap createServerBootstrap(final JerseyServerInitializer jerseyServerInitializer) { // Configure the server. final EventLoopGroup bossGroup = new NioEventLoopGroup(1); final EventLoopGroup workerGroup = new NioEventLoopGroup(); - final NettyHttpContainer container = new NettyHttpContainer(configuration); + + ServerBootstrap b = new ServerBootstrap(); + b.option(ChannelOption.SO_BACKLOG, 1024); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(jerseyServerInitializer); + + return b; + } + + /** + * Start Netty server. + * + * @param port IP port to listen. + * @param container Jersey container. + * @param serverBootstrap Netty server bootstrap (i. e. prepared but unstarted server instance) + * @param block when {@code true}, this method will block until the server is stopped. When {@code false}, the + * execution will + * end immediately after the server is started. + * @return Netty channel instance. + * @throws ProcessingException when there is an issue with creating new container. + */ + static Channel startServer(final int port, final NettyHttpContainer container, final ServerBootstrap serverBootstrap, + final boolean block) + throws ProcessingException { try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new JerseyServerInitializer(baseUri, sslContext, container, configuration)); + final EventLoopGroup bossGroup = serverBootstrap.config().group(); + final EventLoopGroup workerGroup = serverBootstrap.config().childGroup(); - int port = getPort(baseUri); - - Channel ch = b.bind(port).sync().channel(); + Channel ch = serverBootstrap.bind(port).sync().channel(); ch.closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() { @Override @@ -112,6 +151,24 @@ * * @param baseUri base uri. * @param configuration Jersey configuration. + * @param sslContext Netty SSL context (can be null). + * @param block when {@code true}, this method will block until the server is stopped. When {@code false}, the + * execution will + * end immediately after the server is started. + * @return Netty channel instance. + * @throws ProcessingException when there is an issue with creating new container. + */ + public static Channel createServer(final URI baseUri, final ResourceConfig configuration, SslContext sslContext, + final boolean block) + throws ProcessingException { + return createServer(baseUri, new NettyHttpContainer(configuration), sslContext, block); + } + + /** + * Create and start Netty server. + * + * @param baseUri base uri. + * @param configuration Jersey configuration. * @param block when {@code true}, this method will block until the server is stopped. When {@code false}, the * execution will * end immediately after the server is started. @@ -140,39 +197,18 @@ public static Channel createHttp2Server(final URI baseUri, final ResourceConfig configuration, SslContext sslContext) throws ProcessingException { - final EventLoopGroup bossGroup = new NioEventLoopGroup(1); - final EventLoopGroup workerGroup = new NioEventLoopGroup(); final NettyHttpContainer container = new NettyHttpContainer(configuration); - try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new JerseyServerInitializer(baseUri, sslContext, container, configuration, true)); + final JerseyServerInitializer jerseyServerInitializer = + new JerseyServerInitializer(baseUri, sslContext, container, configuration, true); + ServerBootstrap serverBootstrap = createServerBootstrap(jerseyServerInitializer); - int port = getPort(baseUri); + int port = getPort(baseUri); - Channel ch = b.bind(port).sync().channel(); - - ch.closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() { - @Override - public void operationComplete(Future<? super Void> future) throws Exception { - container.getApplicationHandler().onShutdown(container); - - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - }); - - return ch; - - } catch (InterruptedException e) { - throw new ProcessingException(e); - } + return startServer(port, container, serverBootstrap, false); } - private static int getPort(URI uri) { + static int getPort(URI uri) { if (uri.getPort() == -1) { if ("http".equalsIgnoreCase(uri.getScheme())) { return 80;
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServer.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServer.java new file mode 100644 index 0000000..7b28779 --- /dev/null +++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServer.java
@@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.netty.httpserver; + +import java.net.InetSocketAddress; +import java.net.URI; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import javax.net.ssl.SSLContext; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.handler.ssl.ClientAuth; +import io.netty.handler.ssl.JdkSslContext; + +/** + * Jersey {@code Server} implementation based on Netty {@link Channel}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +final class NettyHttpServer implements WebServer { + + private final NettyHttpContainer container; + + private final ServerBootstrap serverBootstrap; + + private volatile Channel channel; + + private final int port; + + NettyHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) { + this(new NettyHttpContainer(application), configuration); + } + + NettyHttpServer(final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + this(new NettyHttpContainer(applicationClass), configuration); + } + + NettyHttpServer(final NettyHttpContainer container, final JerseySeBootstrapConfiguration configuration) { + final SSLContext sslContext = configuration.sslContext(); + final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration + .sslClientAuthentication(); + + final URI uri = configuration.uri(true); + this.port = NettyHttpContainerProvider.getPort(uri); + + this.container = container; + this.serverBootstrap = NettyHttpContainerProvider.createServerBootstrap( + uri, + this.container, + configuration.isHttps() + ? new JdkSslContext(sslContext, false, nettyClientAuth(sslClientAuthentication)) + : null + ); + + if (configuration.autoStart()) { + this.channel = NettyHttpContainerProvider.startServer(this.port, this.container, this.serverBootstrap, false); + } + } + + private static final ClientAuth nettyClientAuth( + final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication) { + switch (sslClientAuthentication) { + case MANDATORY: + return ClientAuth.REQUIRE; + case OPTIONAL: + return ClientAuth.OPTIONAL; + default: + return ClientAuth.NONE; + } + } + + @Override + public final NettyHttpContainer container() { + return this.container; + } + + @Override + public final int port() { + return this.channel == null ? this.port : ((InetSocketAddress) this.channel.localAddress()).getPort(); + } + + @Override + public final CompletableFuture<Object> start() { + return this.channel != null ? CompletableFuture.completedFuture(this.channel) + : CompletableFuture.supplyAsync(() -> { + try { + this.channel = NettyHttpContainerProvider.startServer(this.port, this.container, + this.serverBootstrap, false); + return this.channel; + } catch (final Exception e) { + throw new CompletionException(e); + } + }); + } + + @Override + public final CompletableFuture<Void> stop() { + return this.channel == null ? CompletableFuture.completedFuture(null) : CompletableFuture.supplyAsync(() -> { + try { + return this.channel.close().get(); + } catch (final Exception e) { + throw new CompletionException(e); + } + }); + } + + @Override + public final <T> T unwrap(final Class<T> nativeClass) { + return nativeClass.cast(this.channel == null ? this.serverBootstrap : this.channel); + } + +}
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProvider.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProvider.java new file mode 100644 index 0000000..34c700e --- /dev/null +++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProvider.java
@@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.netty.httpserver; + +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; + +import io.netty.channel.Channel; + +/** + * Server provider for servers based on Netty {@link Channel}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class NettyHttpServerProvider implements WebServerProvider { + + @Override + public <T extends WebServer> T createServer(final Class<T> type, final Application application, + final JerseySeBootstrapConfiguration configuration) { + return NettyHttpServer.class == type || WebServer.class == type + ? type.cast(new NettyHttpServer(application, configuration)) + : null; + } + + @Override + public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + return NettyHttpServer.class == type || WebServer.class == type + ? type.cast(new NettyHttpServer(applicationClass, configuration)) + : null; + } +}
diff --git a/containers/netty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/netty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider new file mode 100644 index 0000000..61e6e1b --- /dev/null +++ b/containers/netty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@ +org.glassfish.jersey.netty.httpserver.NettyHttpServerProvider \ No newline at end of file
diff --git a/containers/netty-http/src/test/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProviderTest.java b/containers/netty-http/src/test/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProviderTest.java new file mode 100644 index 0000000..3837377 --- /dev/null +++ b/containers/netty-http/src/test/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProviderTest.java
@@ -0,0 +1,197 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.netty.httpserver; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertThat; + +import java.security.AccessController; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.SSLContext; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.UriBuilder; + +import org.glassfish.jersey.internal.util.PropertiesHelper; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.spi.Container; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; +import org.junit.Test; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; + +/** + * Unit tests for {@link NettyHttpServerProvider}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class NettyHttpServerProviderTest { + + @Test(timeout = 15000) + public void shouldProvideServer2() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + shouldProvideServer(ShouldProvideServerApplication.class, resource); + } + + @Test(timeout = 15000) + public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + final Application application = new ShouldProvideServerApplication(); + shouldProvideServer(application.getClass(), resource); + } + + private void shouldProvideServer(final Object application, final Resource resource) + throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new NettyHttpServerProvider(); + final SeBootstrap.Configuration configuration = configuration(getPort(), FALSE); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = Application.class.isInstance(application) + ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig) + : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig); + final Object nativeHandle = webServer.unwrap(Object.class); + final CompletionStage<?> start = webServer.start(); + final Object startResult = start.toCompletableFuture().get(); + final Container container = webServer.container(); + final int port = webServer.port(); + final String entity = ClientBuilder.newClient() + .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request() + .get(String.class); + final CompletionStage<?> stop = webServer.stop(); + final Object stopResult = stop.toCompletableFuture().get(); + + // then + assertThat(webServer, is(instanceOf(NettyHttpServer.class))); + assertThat(nativeHandle, is(instanceOf(ServerBootstrap.class))); + assertThat(startResult, is(instanceOf(Channel.class))); + assertThat(container, is(instanceOf(NettyHttpContainer.class))); + assertThat(port, is(greaterThan(0))); + assertThat(entity, is(resource.toString())); + assertThat(stopResult, is(nullValue())); + } + + + @Path("/") + protected static final class Resource { + @GET + @Override + public String toString() { + return Resource.class.getName(); + } + } + + protected static class ShouldProvideServerApplication extends Application { + @Override + public Set<Object> getSingletons() { + return Collections.singleton(new Resource()); + } + } + + private static final Logger LOGGER = Logger.getLogger(NettyHttpServerProviderTest.class.getName()); + + private static final int DEFAULT_PORT = 0; + + private static final int getPort() { + final String value = AccessController + .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); + if (value != null) { + try { + final int i = Integer.parseInt(value); + if (i < 0) { + throw new NumberFormatException("Value is negative."); + } + return i; + } catch (final NumberFormatException e) { + LOGGER.log(Level.CONFIG, + "Value of 'jersey.config.test.container.port'" + + " property is not a valid non-negative integer [" + value + "]." + + " Reverting to default [" + DEFAULT_PORT + "].", + e); + } + } + + return DEFAULT_PORT; + } + + @Test(timeout = 15000) + public final void shouldScanFreePort() throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new NettyHttpServerProvider(); + final Application application = new Application(); + final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT, TRUE); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig); + + // then + assertThat(webServer.port(), is(greaterThan(0))); + } + + private SeBootstrap.Configuration configuration(int port, boolean autoStart) { + return (SeBootstrap.Configuration) name -> { + switch (name) { + case SeBootstrap.Configuration.PROTOCOL: + return "HTTP"; + case SeBootstrap.Configuration.HOST: + return "localhost"; + case SeBootstrap.Configuration.PORT: + return port; + case SeBootstrap.Configuration.ROOT_PATH: + return "/"; + case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION: + return SSLClientAuthentication.NONE; + case SeBootstrap.Configuration.SSL_CONTEXT: + try { + return SSLContext.getDefault(); + } catch (final NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + case ServerProperties.WEBSERVER_AUTO_START: + return autoStart; + default: + return null; + } + }; + } + +}
diff --git a/containers/simple-http/pom.xml b/containers/simple-http/pom.xml index 3aa97bb..f9a3206 100644 --- a/containers/simple-http/pom.xml +++ b/containers/simple-http/pom.xml
@@ -49,6 +49,11 @@ <groupId>org.simpleframework</groupId> <artifactId>simple-common</artifactId> </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build>
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java index 327b2cf..6e4e885 100644 --- a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java +++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 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 @@ -465,4 +465,15 @@ this.appHandler = new ApplicationHandler(application, new SimpleBinder()); this.scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class)); } + + /** + * Create a new Simple framework HTTP container. + * + * @param applicationClass JAX-RS / Jersey application class to be deployed on Simple framework HTTP + * container. + */ + SimpleContainer(final Class<? extends Application> applicationClass) { + this.appHandler = new ApplicationHandler(applicationClass, new SimpleBinder()); + this.scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class)); + } }
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java index 6740c6b..519fe7d 100644 --- a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java +++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 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 @@ -19,19 +19,19 @@ import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.net.URI; -import jakarta.ws.rs.ProcessingException; - import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication; +import jakarta.ws.rs.ProcessingException; import org.glassfish.jersey.internal.util.collection.UnsafeValue; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.simple.internal.LocalizationMessages; - import org.simpleframework.http.core.Container; import org.simpleframework.http.core.ContainerSocketProcessor; +import org.simpleframework.transport.Socket; import org.simpleframework.transport.SocketProcessor; import org.simpleframework.transport.connect.Connection; import org.simpleframework.transport.connect.SocketConnection; @@ -149,7 +149,55 @@ public SocketProcessor get() throws IOException { return new ContainerSocketProcessor(container); } - }); + }, true); + } + + /** + * Create a {@link Closeable} that registers an {@link Container} that in turn manages all root + * resource and provider classes found by searching the classes referenced in the java classpath. + * + * @param address the URI to create the http server. The URI scheme must be equal to {@code https} + * . The URI user information and host are ignored. If the URI port is not present then + * port {@value org.glassfish.jersey.server.spi.Container#DEFAULT_HTTPS_PORT} will be used. + * The URI path, query and fragment components are ignored. + * @param context this is the SSL context used for SSL connections. + * @param container the Simple container with ResourceConfig. + * @param sslClientAuthentication Secure socket client authentication policy. + * @param start whether the server shall listen to connections immediately + * @return the closeable connection, with the endpoint started. + * @throws ProcessingException thrown when problems during server creation. + * @throws IllegalArgumentException if {@code address} is {@code null}. + */ + public static SimpleServer create(final URI address, final SSLContext context, + final SSLClientAuthentication sslClientAuthentication, final SimpleContainer container, final boolean start) { + return _create(address, context, container, new UnsafeValue<SocketProcessor, IOException>() { + @Override + public SocketProcessor get() throws IOException { + return new ContainerSocketProcessor(container) { + @Override + public final void process(final Socket socket) throws IOException { + final SSLEngine sslEngine = socket.getEngine(); + if (sslEngine != null) { + switch (sslClientAuthentication) { + case MANDATORY: { + sslEngine.setNeedClientAuth(true); + break; + } + case OPTIONAL: { + sslEngine.setWantClientAuth(true); + break; + } + default: { + sslEngine.setNeedClientAuth(false); + break; + } + } + } + super.process(socket); + } + }; + } + }, start); } /** @@ -201,12 +249,13 @@ public SocketProcessor get() throws IOException { return new ContainerSocketProcessor(container, count, select); } - }); + }, true); } private static SimpleServer _create(final URI address, final SSLContext context, final SimpleContainer container, - final UnsafeValue<SocketProcessor, IOException> serverProvider) + final UnsafeValue<SocketProcessor, IOException> serverProvider, + final boolean start) throws ProcessingException { if (address == null) { throw new IllegalArgumentException(LocalizationMessages.URI_CANNOT_BE_NULL()); @@ -236,22 +285,26 @@ final SocketProcessor server = serverProvider.get(); connection = new SocketConnection(server, analyzer); + final SimpleServer simpleServer = new SimpleServer() { + private InetSocketAddress socketAddr = listen; - final SocketAddress socketAddr = connection.connect(listen, context); - container.onServerStart(); - - return new SimpleServer() { + @Override + public final void start() throws IOException { + this.socketAddr = (InetSocketAddress) connection.connect(listen, context); + container.onServerStart(); + } @Override public void close() throws IOException { container.onServerStop(); analyzer.stop(); connection.close(); + this.socketAddr = listen; } @Override public int getPort() { - return ((InetSocketAddress) socketAddr).getPort(); + return this.socketAddr.getPort(); } @Override @@ -268,6 +321,12 @@ } } }; + + if (start) { + simpleServer.start(); + } + + return simpleServer; } catch (final IOException ex) { throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), ex); }
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServer.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServer.java new file mode 100644 index 0000000..1b9ac18 --- /dev/null +++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServer.java
@@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.simple; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; + +/** + * Jersey {@code Server} implementation based on Simple framework + * {@link SimpleServer}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +final class SimpleHttpServer implements WebServer { + + private final SimpleContainer container; + + private final SimpleServer simpleServer; + + SimpleHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) { + this(new SimpleContainer(application), configuration); + } + + SimpleHttpServer(final Class<?extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + this(new SimpleContainer(applicationClass), configuration); + } + + SimpleHttpServer(final SimpleContainer container, final JerseySeBootstrapConfiguration configuration) { + this.container = container; + this.simpleServer = SimpleContainerFactory.create( + configuration.uri(true), + configuration.sslContext(), + configuration.sslClientAuthentication(), + this.container, + configuration.autoStart()); + } + + @Override + public final SimpleContainer container() { + return this.container; + } + + @Override + public final int port() { + return this.simpleServer.getPort(); + } + + @Override + public final CompletableFuture<Void> start() { + return CompletableFuture.runAsync(() -> { + try { + this.simpleServer.start(); + } catch (final Exception e) { + throw new CompletionException(e); + } + }); + } + + @Override + public final CompletableFuture<Void> stop() { + return CompletableFuture.runAsync(() -> { + try { + this.simpleServer.close(); + } catch (final Exception e) { + throw new CompletionException(e); + } + }); + } + + @Override + public final <T> T unwrap(final Class<T> nativeClass) { + return nativeClass.cast(this.simpleServer); + } + +}
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServerProvider.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServerProvider.java new file mode 100644 index 0000000..c4a26a7 --- /dev/null +++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServerProvider.java
@@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.simple; + +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; + +/** + * Server provider for servers based on Simple framework {@link SimpleServer}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class SimpleHttpServerProvider implements WebServerProvider { + + @Override + public <T extends WebServer> T createServer(final Class<T> type, final Application application, + final JerseySeBootstrapConfiguration configuration) { + return SimpleHttpServer.class == type || WebServer.class == type + ? type.cast(new SimpleHttpServer(application, configuration)) + : null; + } + + @Override + public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + return SimpleHttpServer.class == type || WebServer.class == type + ? type.cast(new SimpleHttpServer(applicationClass, configuration)) + : null; + } +}
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java index 02aec75..7560c52 100644 --- a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java +++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -17,6 +17,7 @@ package org.glassfish.jersey.simple; import java.io.Closeable; +import java.io.IOException; /** * Simple server facade providing convenient methods to obtain info about the server (i.e. port). @@ -26,6 +27,8 @@ */ public interface SimpleServer extends Closeable { + public void start() throws IOException; + /** * The port the server is listening to for incomming HTTP connections. If the port is not * specified the {@link org.glassfish.jersey.server.spi.Container.DEFAULT_PORT} is used.
diff --git a/containers/simple-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/simple-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider new file mode 100644 index 0000000..54d8c53 --- /dev/null +++ b/containers/simple-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@ +org.glassfish.jersey.simple.SimpleHttpServerProvider \ No newline at end of file
diff --git a/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java index 3df302e..3878a4e 100644 --- a/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java +++ b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 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 @@ -39,7 +39,7 @@ public abstract class AbstractSimpleServerTester { public static final String CONTEXT = ""; - private final int DEFAULT_PORT = 9998; + private final int DEFAULT_PORT = 0; private static final Logger LOGGER = Logger.getLogger(AbstractSimpleServerTester.class.getName()); @@ -49,21 +49,25 @@ * @return The HTTP port of the URI */ protected final int getPort() { + if (server != null) { + return server.getPort(); + } + final String value = AccessController .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); if (value != null) { try { final int i = Integer.parseInt(value); - if (i <= 0) { - throw new NumberFormatException("Value not positive."); + if (i < 0) { + throw new NumberFormatException("Value is negative."); } return i; } catch (NumberFormatException e) { LOGGER.log( Level.CONFIG, "Value of 'jersey.config.test.container.port'" - + " property is not a valid positive integer [" + value + "]." + + " property is not a valid non-negative integer [" + value + "]." + " Reverting to default [" + DEFAULT_PORT + "].", e); } @@ -83,28 +87,28 @@ config.register(LoggingFeature.class); final URI baseUri = getBaseUri(); server = SimpleContainerFactory.create(baseUri, config); - LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri); + LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + getBaseUri()); } public void startServerNoLoggingFilter(Class... resources) { ResourceConfig config = new ResourceConfig(resources); final URI baseUri = getBaseUri(); server = SimpleContainerFactory.create(baseUri, config); - LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri); + LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + getBaseUri()); } public void startServer(ResourceConfig config) { final URI baseUri = getBaseUri(); config.register(LoggingFeature.class); server = SimpleContainerFactory.create(baseUri, config); - LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri); + LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + getBaseUri()); } public void startServer(ResourceConfig config, int count, int select) { final URI baseUri = getBaseUri(); config.register(LoggingFeature.class); server = SimpleContainerFactory.create(baseUri, config, count, select); - LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri); + LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + getBaseUri()); } public URI getBaseUri() {
diff --git a/containers/simple-http/src/test/java/org/glassfish/jersey/simple/SimpleHttpServerProviderTest.java b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/SimpleHttpServerProviderTest.java new file mode 100644 index 0000000..02314ea --- /dev/null +++ b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/SimpleHttpServerProviderTest.java
@@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. 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.simple; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertThat; + +import java.security.AccessController; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.SSLContext; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.UriBuilder; + +import org.glassfish.jersey.internal.util.PropertiesHelper; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.spi.Container; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +/** + * Unit tests for {@link SimpleHttpServerProvider}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class SimpleHttpServerProviderTest { + + @Test(timeout = 15000) + public void shouldProvideServer2() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + shouldProvideServer(ShouldProvideServerApplication.class, resource); + } + + @Test(timeout = 15000) + public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException { + // given + final Resource resource = new Resource(); + final Application application = new ShouldProvideServerApplication(); + shouldProvideServer(application.getClass(), resource); + } + + private void shouldProvideServer(final Object application, final Resource resource) + throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new SimpleHttpServerProvider(); + final SeBootstrap.Configuration configuration = configuration(getPort(), FALSE); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = Application.class.isInstance(application) + ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig) + : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig); + final Object nativeHandle = webServer.unwrap(Object.class); + final CompletionStage<?> start = webServer.start(); + final Object startResult = start.toCompletableFuture().get(); + final Container container = webServer.container(); + final int port = webServer.port(); + final String entity = ClientBuilder.newClient() + .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request() + .get(String.class); + final CompletionStage<?> stop = webServer.stop(); + final Object stopResult = stop.toCompletableFuture().get(); + + // then + assertThat(webServer, is(instanceOf(SimpleHttpServer.class))); + assertThat(nativeHandle, is(instanceOf(SimpleServer.class))); + assertThat(startResult, is(nullValue())); + assertThat(container, is(instanceOf(SimpleContainer.class))); + assertThat(port, is(greaterThan(0))); + assertThat(entity, is(resource.toString())); + assertThat(stopResult, is(nullValue())); + } + + @Path("/") + protected static final class Resource { + @GET + @Override + public String toString() { + return Resource.class.getName(); + } + } + + protected static class ShouldProvideServerApplication extends Application { + @Override + public Set<Object> getSingletons() { + return Collections.singleton(new Resource()); + } + } + + private static final Logger LOGGER = Logger.getLogger(SimpleHttpServerProviderTest.class.getName()); + + private static final int DEFAULT_PORT = 0; + + private static final int getPort() { + final String value = AccessController + .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port")); + if (value != null) { + try { + final int i = Integer.parseInt(value); + if (i < 0) { + throw new NumberFormatException("Value is negative."); + } + return i; + } catch (final NumberFormatException e) { + LOGGER.log(Level.CONFIG, + "Value of 'jersey.config.test.container.port'" + + " property is not a valid non-negative integer [" + value + "]." + + " Reverting to default [" + DEFAULT_PORT + "].", + e); + } + } + + return DEFAULT_PORT; + } + + @Test(timeout = 15000) + public final void shouldScanFreePort() throws InterruptedException, ExecutionException { + // given + final WebServerProvider webServerProvider = new SimpleHttpServerProvider(); + final Application application = new Application(); + final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT, TRUE); + + // when + final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig); + + // then + assertThat(webServer.port(), is(greaterThan(0))); + } + + private SeBootstrap.Configuration configuration(int port, boolean autoStart) { + return (SeBootstrap.Configuration) name -> { + switch (name) { + case SeBootstrap.Configuration.PROTOCOL: + return "HTTP"; + case SeBootstrap.Configuration.HOST: + return "localhost"; + case SeBootstrap.Configuration.PORT: + return port; + case SeBootstrap.Configuration.ROOT_PATH: + return "/"; + case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION: + return SSLClientAuthentication.NONE; + case SeBootstrap.Configuration.SSL_CONTEXT: + try { + return SSLContext.getDefault(); + } catch (final NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + case ServerProperties.WEBSERVER_AUTO_START: + return autoStart; + default: + return null; + } + }; + } + +}
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java index 43737f1..fb98632 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 Payara Foundation and/or its affiliates. * * This program and the accompanying materials are made available under the @@ -330,6 +330,11 @@ } @Override + public boolean hasProperty(final String name) { + return commonConfig.getConfiguration().hasProperty(name); + } + + @Override public Object getProperty(final String name) { return commonConfig.getConfiguration().getProperty(name); }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java index f9539b0..45cb994 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -139,6 +139,11 @@ } @Override + public boolean hasProperty(final String name) { + return propertiesDelegate.hasProperty(name); + } + + @Override public Object getProperty(final String name) { return propertiesDelegate.getProperty(name); } @@ -229,9 +234,19 @@ return clientConfig; } + /** + * Get the values of an HTTP request header if the header exists on the current request. The returned value will be + * a read-only List if the specified header exists or {@code null} if it does not. This is a shortcut for + * {@code getRequestHeaders().get(name)}. + * + * @param name the header name, case insensitive. + * @return a read-only list of header values if the specified header exists, otherwise {@code null}. + * @throws java.lang.IllegalStateException if called outside the scope of a request. + */ @Override public List<String> getRequestHeader(String name) { - return HeaderUtils.asStringList(getHeaders().get(name), clientConfig.getConfiguration()); + final List<Object> values = getHeaders().get(name); + return values == null ? null : HeaderUtils.asStringList(values, clientConfig.getConfiguration()); } @Override
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/ClientConfigTest.java b/core-client/src/test/java/org/glassfish/jersey/client/ClientConfigTest.java index 8f301f2..d2179f6 100644 --- a/core-client/src/test/java/org/glassfish/jersey/client/ClientConfigTest.java +++ b/core-client/src/test/java/org/glassfish/jersey/client/ClientConfigTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -104,6 +104,13 @@ } @Test + public void testHasProperty() { + ClientConfig instance = new ClientConfig().property("name", "value"); + assertTrue(instance.hasProperty("name")); + assertFalse(instance.hasProperty("other")); + } + + @Test public void testGetProperty() { ClientConfig instance = new ClientConfig().property("name", "value"); assertEquals("value", instance.getProperty("name"));
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java b/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java index b432d91..9604438 100644 --- a/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java +++ b/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022 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 @@ -92,6 +92,9 @@ assertFalse(request.getConfiguration().getPropertyNames().contains("name")); assertFalse(request.getPropertyNames().contains("name")); + assertFalse(request.getConfiguration().hasProperty("name")); + assertFalse(request.hasProperty("name")); + assertNull(request.getConfiguration().getProperty("name")); assertNull(request.getProperty("name")); @@ -109,6 +112,9 @@ assertTrue(request.getConfiguration().getPropertyNames().contains("name")); assertFalse(request.getPropertyNames().contains("name")); + assertTrue(request.getConfiguration().hasProperty("name")); + assertFalse(request.hasProperty("name")); + assertEquals("value-global", request.getConfiguration().getProperty("name")); assertNull(request.getProperty("name")); @@ -127,6 +133,9 @@ assertFalse(request.getConfiguration().getPropertyNames().contains("name")); assertTrue(request.getPropertyNames().contains("name")); + assertFalse(request.getConfiguration().hasProperty("name")); + assertTrue(request.hasProperty("name")); + assertNull(request.getConfiguration().getProperty("name")); assertEquals("value-request", request.getProperty("name")); @@ -145,6 +154,9 @@ assertTrue(request.getConfiguration().getPropertyNames().contains("name")); assertTrue(request.getPropertyNames().contains("name")); + assertTrue(request.getConfiguration().hasProperty("name")); + assertTrue(request.hasProperty("name")); + assertEquals("value-global", request.getConfiguration().getProperty("name")); assertEquals("value-request", request.getProperty("name"));
diff --git a/core-common/pom.xml b/core-common/pom.xml index 8b5bf6d..6c4716c 100644 --- a/core-common/pom.xml +++ b/core-common/pom.xml
@@ -230,446 +230,6 @@ <profiles> <profile> - <id>jdk8</id> - <activation> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>src/main/jsr166</source> - <source>src/main/java8</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <artifactId>maven-antrun-plugin</artifactId> - <dependencies> - <dependency> - <groupId>com.sun</groupId> - <artifactId>tools</artifactId> - <version>1.8.0</version> - <scope>system</scope> - <systemPath>${java.home}/../lib/tools.jar</systemPath> - </dependency> - </dependencies> - <executions> - <execution> - <phase>validate</phase> - <goals> - <goal>run</goal> - </goals> - <configuration> - <target> - <echo>Building for JDK8</echo> - </target> - </configuration> - </execution> - <execution> - <!-- need to compile this to be able to compile-2-java8 --> - <id>compile-1-jsr166</id> - <phase>process-resources</phase> - <configuration> - <target> - <javac srcdir="${jsr166.sourceDirectory}" destdir="${project.build.outputDirectory}" - classpath="${project.build.outputDirectory}" includeantruntime="false" /> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - <execution> - <!-- Compile these files with jdk 8 and put them aside to be included in multirelase jar --> - <!-- Multi-release jar is built by jdk 11+, but these classes are buildable by jdk 8 only --> - <id>compile-2-java8</id> - <phase>process-resources</phase> - <configuration> - <target> - <mkdir dir="${java8.build.outputDirectory}" /> - <javac srcdir="${java8.sourceDirectory}" destdir="${java8.build.outputDirectory}" - classpath="${project.build.outputDirectory}" includeantruntime="false" /> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - - <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-antrun-plugin</artifactId> - <executions> - <execution> - <phase>validate</phase> - <goals> - <goal>run</goal> - </goals> - <configuration> - <target> - <echo>Building for JDK 11+</echo> - </target> - </configuration> - </execution> - <execution> - <id>compile-1-jsr166</id> - <phase>process-resources</phase> - <configuration> - <target> - <javac srcdir="${jsr166.sourceDirectory}" destdir="${project.build.outputDirectory}" - classpath="${project.build.outputDirectory}" includeantruntime="false" /> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - <execution> - <!-- build these java 11 specific classes to be put to META-INF/versions/11 later --> - <id>compile-2-java11</id> - <phase>process-resources</phase> - <configuration> - <target> - <mkdir dir="${java11.build.outputDirectory}" /> - <javac srcdir="${java11.sourceDirectory}" destdir="${java11.build.outputDirectory}" - classpath="${project.build.outputDirectory}" includeantruntime="false" release="11" /> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <id>compile-0-addsources</id> - <phase>process-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>src/main/jsr166</source> - <source>src/main/java11</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <executions> - <execution> - <id>default-compile</id> - <configuration> - <!-- compile everything to ensure module-info contains right entries --> - <release>11</release> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>copyJDK11FilesToMultiReleaseJar</id> - <activation> - <file> - <!-- ${java11.build.outputDirectory} does not work here --> - <exists>target/classes-java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.class</exists> - </file> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <inherited>true</inherited> - <extensions>true</extensions> - <configuration> - <instructions> - <Multi-Release>true</Multi-Release> - </instructions> - </configuration> - </plugin> - <plugin> - <artifactId>maven-clean-plugin</artifactId> - <!-- only one file set per execution works --> - <executions> - <execution> - <id>remove-jdk11-generated-sources</id> - <phase>initialize</phase> - <goals> - <goal>clean</goal> - </goals> - <configuration> - <excludeDefaultDirectories>true</excludeDefaultDirectories> - <filesets> - <fileset> - <directory>${project.build.directory}/generated-sources</directory> - </fileset> - </filesets> - </configuration> - </execution> - <execution> - <id>remove-jdk11-classes</id> - <phase>initialize</phase> - <goals> - <goal>clean</goal> - </goals> - <configuration> - <excludeDefaultDirectories>true</excludeDefaultDirectories> - <filesets> - <fileset> - <directory>${project.build.directory}/classes</directory> - </fileset> - </filesets> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-resources-plugin</artifactId> - <inherited>true</inherited> - <executions> - <execution> - <id>copy-jdk11-sources</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/11/org/glassfish/jersey/internal/jsr166</outputDirectory> - <resources> - <resource> - <directory>${java11.sourceDirectory}/org/glassfish/jersey/internal/jsr166</directory> - </resource> - </resources> - </configuration> - </execution> - <execution> - <id>copy-jdk11-classes-to-meta-inf</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${project.build.outputDirectory}/META-INF/versions/11</outputDirectory> - <resources> - <resource> - <directory>${java11.build.outputDirectory}</directory> - </resource> - </resources> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>copyJDK8FilesToMultiReleaseJar</id> - <activation> - <file> - <!-- ${java8.build.outputDirectory} does not work here --> - <exists>target/classes-java8/org/glassfish/jersey/internal/jsr166/UnsafeAccessor.class</exists> - </file> - <jdk>[11,)</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <inherited>true</inherited> - <extensions>true</extensions> - <configuration> - <instructions> - <Multi-Release>true</Multi-Release> - </instructions> - </configuration> - </plugin> - <plugin> - <artifactId>maven-clean-plugin</artifactId> - <!-- only one file set per execution works --> - <executions> - <execution> - <id>remove-jdk11-jsr166-sources</id> - <phase>initialize</phase> - <goals> - <goal>clean</goal> - </goals> - <configuration> - <excludeDefaultDirectories>true</excludeDefaultDirectories> - <filesets> - <fileset> - <directory>${project.build.directory}/generated-sources/rsrc-gen/org/glassfish/jersey/internal/jsr166</directory> - </fileset> - </filesets> - </configuration> - </execution> - <execution> - <id>remove-jdk11-jsr166-META-INF-sources</id> - <phase>initialize</phase> - <goals> - <goal>clean</goal> - </goals> - <configuration> - <excludeDefaultDirectories>true</excludeDefaultDirectories> - <filesets> - <fileset> - <directory>${project.build.directory}/generated-sources/rsrc-gen/META-INF</directory> - </fileset> - </filesets> - </configuration> - </execution> - <execution> - <id>remove-jdk11-jsr166-classes</id> - <phase>prepare-package</phase> - <goals> - <goal>clean</goal> - </goals> - <configuration> - <excludeDefaultDirectories>true</excludeDefaultDirectories> - <filesets> - <fileset> - <directory>${project.build.outputDirectory}/org/glassfish/jersey/internal/jsr166</directory> - <includes> - <include>*.class</include> - </includes> - <excludes> - <exclude>Flow*.class</exclude> - <exclude>SubmittableFlowPublisher.class</exclude> - <exclude>package-info.class</exclude> - </excludes> - </fileset> - </filesets> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-resources-plugin</artifactId> - <inherited>true</inherited> - <executions> - <execution> - <id>copy-jdk8-classes-ouputDirectory</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${project.build.outputDirectory}</outputDirectory> - <resources> - <resource> - <directory>${java8.build.outputDirectory}</directory> - </resource> - </resources> - </configuration> - </execution> - <execution> - <id>copy-jdk8-sources</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen/org/glassfish/jersey/internal/jsr166</outputDirectory> - <resources> - <resource> - <directory>${java8.sourceDirectory}/org/glassfish/jersey/internal/jsr166</directory> - </resource> - </resources> - </configuration> - </execution> - <execution> - <id>copy-jdk11-sources</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/11/org/glassfish/jersey/internal/jsr166</outputDirectory> - <resources> - <resource> - <directory>${java11.sourceDirectory}/org/glassfish/jersey/internal/jsr166</directory> - </resource> - </resources> - </configuration> - </execution> - <execution> - <id>copy-jdk11-classes-to-meta-inf</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${project.build.outputDirectory}/META-INF/versions/11</outputDirectory> - <resources> - <resource> - <directory>${java11.build.outputDirectory}</directory> - </resource> - </resources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-source-plugin</artifactId> - <version>3.0.1</version> - <executions> - <execution> - <id>attach-sources</id> - <phase>package</phase> - <goals> - <goal>jar-no-fork</goal> - </goals> - <configuration> - <excludes> - <exclude>org/glassfish/jersey/internal/jsr166/Jdk9SubmissionPublisher.java</exclude> - </excludes> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - - <profile> <id>securityOff</id> <properties> <surefire.security.argline /> @@ -709,11 +269,6 @@ <properties> <surefire.security.argline>-Djava.security.manager -Djava.security.policy=${project.build.directory}/test-classes/surefire.policy</surefire.security.argline> - <jsr166.sourceDirectory>${project.basedir}/src/main/jsr166</jsr166.sourceDirectory> - <java8.build.outputDirectory>${project.build.directory}/classes-java8</java8.build.outputDirectory> - <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory> - <java11.build.outputDirectory>${project.build.directory}/classes-java11</java11.build.outputDirectory> - <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory> </properties> </project>
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java similarity index 75% rename from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java rename to core-common/src/main/java/org/glassfish/jersey/innate/package-info.java index dd25372..b0648e7 100644 --- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java +++ b/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 */ -package org.glassfish.jersey.internal.jsr166; - -public interface JerseyFlowSubscriber<T> extends Flow.Subscriber<T> { -} +/** + * Jersey innate packages. The innate packages will not be opened by JPMS outside of Jersey. + */ +package org.glassfish.jersey.innate;
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/spi/EntityPartBuilderProvider.java b/core-common/src/main/java/org/glassfish/jersey/innate/spi/EntityPartBuilderProvider.java new file mode 100644 index 0000000..606e212 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/innate/spi/EntityPartBuilderProvider.java
@@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.innate.spi; + +import jakarta.ws.rs.core.EntityPart; + +/** + * Jersey extension of provider of EntityPart.Builder. + * A service meant to be implemented solely by Jersey. + * + * @since 3.1.0 + */ +public interface EntityPartBuilderProvider { + + /** + * @param partName name of the part to create within the multipart entity. + * @return {@link EntityPart.Builder} for building new {@link EntityPart} instances. + */ + public EntityPart.Builder withName(String partName); +}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/core-common/src/main/java/org/glassfish/jersey/innate/spi/package-info.java similarity index 75% copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java copy to core-common/src/main/java/org/glassfish/jersey/innate/spi/package-info.java index dd25372..3d70312 100644 --- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java +++ b/core-common/src/main/java/org/glassfish/jersey/innate/spi/package-info.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 */ -package org.glassfish.jersey.internal.jsr166; - -public interface JerseyFlowSubscriber<T> extends Flow.Subscriber<T> { -} +/** + * Common Jersey innate SPI classes. The innate package will not be opened by JPMS. + */ +package org.glassfish.jersey.innate.spi;
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/AbstractRuntimeDelegate.java b/core-common/src/main/java/org/glassfish/jersey/internal/AbstractRuntimeDelegate.java index aa439db..39940fb 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/AbstractRuntimeDelegate.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/AbstractRuntimeDelegate.java
@@ -25,14 +25,20 @@ import jakarta.ws.rs.core.CacheControl; import jakarta.ws.rs.core.Configuration; import jakarta.ws.rs.core.Cookie; +import jakarta.ws.rs.core.EntityPart; import jakarta.ws.rs.core.EntityTag; import jakarta.ws.rs.core.Link; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.NewCookie; import jakarta.ws.rs.core.Response.ResponseBuilder; import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.ext.ParamConverter; import jakarta.ws.rs.ext.RuntimeDelegate; +import org.glassfish.jersey.innate.spi.EntityPartBuilderProvider; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.message.internal.JerseyLink; import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; import org.glassfish.jersey.message.internal.OutboundMessageContext; @@ -50,6 +56,8 @@ private final Set<HeaderDelegateProvider> hps; private final Map<Class<?>, HeaderDelegate<?>> map; + private LazyValue<EntityPartBuilderProvider> entityPartBuilderProvider = Values.lazy( + (Value<EntityPartBuilderProvider>) () -> findEntityPartBuilderProvider()); /** * Initialization constructor. The injection manager will be shut down. @@ -117,4 +125,24 @@ return null; } + + @Override + public EntityPart.Builder createEntityPartBuilder(String partName) throws IllegalArgumentException { + return entityPartBuilderProvider.get().withName(partName); + } + + /** + * Obtain a {@code RuntimeDelegate} instance using the method described in {@link #getInstance}. + * + * @return an instance of {@code RuntimeDelegate}. + */ + private static EntityPartBuilderProvider findEntityPartBuilderProvider() { + for (final EntityPartBuilderProvider entityPartBuilder : ServiceFinder.find(EntityPartBuilderProvider.class)) { + if (entityPartBuilder != null) { + return entityPartBuilder; + } + } + + throw new IllegalArgumentException(LocalizationMessages.NO_ENTITYPART_BUILDER_FOUND()); + } }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java index 65ac48b..12eb78b 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java
@@ -23,7 +23,6 @@ import jakarta.ws.rs.RuntimeType; import jakarta.ws.rs.container.DynamicFeature; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set;
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java b/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java index 4af235b..b64fef0 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/MapPropertiesDelegate.java b/core-common/src/main/java/org/glassfish/jersey/internal/MapPropertiesDelegate.java index aed1cfe..3cce5bd 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/MapPropertiesDelegate.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/MapPropertiesDelegate.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -64,6 +64,11 @@ } @Override + public boolean hasProperty(final String name) { + return store.containsKey(name); + } + + @Override public Object getProperty(String name) { return store.get(name); }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/PropertiesDelegate.java b/core-common/src/main/java/org/glassfish/jersey/internal/PropertiesDelegate.java index 70833b9..3413de0 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/PropertiesDelegate.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/PropertiesDelegate.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -43,6 +43,22 @@ */ public Object getProperty(String name); + /** + * Returns {@code true} if the property with the given name registered in the current request/response + * exchange context, or {@code false} if there is no property by that name. + * <p> + * Use the {@link #getProperty} method with a property name to get the value of + * a property. + * </p> + * + * @return {@code true} if a property matching the given name exists, or + * {@code false} otherwise. + * @see #getProperty + * @since 3.1.0 + */ + public default boolean hasProperty(String name) { + return getProperty(name) != null; + } /** * Returns an immutable {@link java.util.Collection collection} containing the property
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java index 5f954ba..b21616d 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021 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 @@ -19,16 +19,20 @@ import org.glassfish.jersey.internal.util.PropertiesHelper; import org.glassfish.jersey.spi.HeaderDelegateProvider; +import jakarta.ws.rs.SeBootstrap; import jakarta.ws.rs.core.Application; import jakarta.ws.rs.core.Configuration; +import jakarta.ws.rs.core.EntityPart; import jakarta.ws.rs.core.Link; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.Variant; import jakarta.ws.rs.ext.RuntimeDelegate; + import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.CompletionStage; /** * RuntimeDelegate Decorator that changes behaviour due to provided runtime information. @@ -102,6 +106,27 @@ return runtimeDelegate.createLinkBuilder(); } + @Override + public SeBootstrap.Configuration.Builder createConfigurationBuilder() { + return runtimeDelegate.createConfigurationBuilder(); + } + + @Override + public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) { + return runtimeDelegate.bootstrap(application, configuration); + } + + @Override + public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> applicationClass, + SeBootstrap.Configuration configuration) { + return runtimeDelegate.bootstrap(applicationClass, configuration); + } + + @Override + public EntityPart.Builder createEntityPartBuilder(String partName) throws IllegalArgumentException { + return runtimeDelegate.createEntityPartBuilder(partName); + } + private <T> HeaderDelegate<T> _createHeaderDelegate(final Class<T> type) { for (final HeaderDelegateProvider hp : headerDelegateProviders) { if (hp.supports(type)) {
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java index 80dac78..89e7089 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,11 +16,16 @@ package org.glassfish.jersey.internal; +import jakarta.ws.rs.SeBootstrap; import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.EntityPart; import jakarta.ws.rs.ext.RuntimeDelegate; import org.glassfish.jersey.message.internal.MessagingBinders; +import java.util.Optional; +import java.util.concurrent.CompletionStage; + /** * Default implementation of JAX-RS {@link jakarta.ws.rs.ext.RuntimeDelegate}. * The {@link jakarta.ws.rs.ext.RuntimeDelegate} class looks for the implementations registered @@ -42,13 +47,49 @@ public <T> T createEndpoint(Application application, Class<T> endpointType) throws IllegalArgumentException, UnsupportedOperationException { - // TODO : Do we need multiple RuntimeDelegates? + final RuntimeDelegate runtimeDelegate = findServerDelegate(); + if (runtimeDelegate != null) { + return runtimeDelegate.createEndpoint(application, endpointType); + } + throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE()); + } + + @Override + public SeBootstrap.Configuration.Builder createConfigurationBuilder() { + final RuntimeDelegate runtimeDelegate = findServerDelegate(); + if (runtimeDelegate != null) { + return runtimeDelegate.createConfigurationBuilder(); + } + throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE()); + } + + @Override + public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) { + final RuntimeDelegate runtimeDelegate = findServerDelegate(); + if (runtimeDelegate != null) { + return runtimeDelegate.bootstrap(application, configuration); + } + throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE()); + } + + @Override + public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> applicationClass, + SeBootstrap.Configuration configuration) { + final RuntimeDelegate runtimeDelegate = findServerDelegate(); + if (runtimeDelegate != null) { + return runtimeDelegate.bootstrap(applicationClass, configuration); + } + throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE()); + } + + // TODO : Do we need multiple RuntimeDelegates? + private RuntimeDelegate findServerDelegate() { for (RuntimeDelegate delegate : ServiceFinder.find(RuntimeDelegate.class)) { // try to find runtime delegate from core-server if (delegate.getClass() != RuntimeDelegateImpl.class) { - return delegate.createEndpoint(application, endpointType); + return delegate; } } - throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE()); + return null; } }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java index 13a4baf..23b127a 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModel.java
@@ -86,7 +86,7 @@ } @Override public <T> Optional<T> getOptionalProperty(String name, Class<T> clazz) { - return Optional.of(as(name, clazz)); + return Optional.ofNullable(as(name, clazz)); } @Override @@ -230,9 +230,4 @@ public Set<Object> getInstances() { return null; } - - // Jersey 2.x - private boolean hasProperty(String name) { - return getProperty(name) != null; - } } \ No newline at end of file
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java index 6a234e5..d1b3e9a 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
@@ -17,12 +17,16 @@ package org.glassfish.jersey.internal.inject; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.security.AccessController; import java.text.ParseException; import java.util.Date; @@ -256,6 +260,39 @@ } /** + * Provider of {@link ParamConverter param converter} that convert the supplied string into a Java + * {@link InputStream} instance. + */ + public static class InputStreamProvider implements ParamConverterProvider { + + @Override + public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) { + return rawType != InputStream.class ? null : new ParamConverter<T>() { + + @Override + public T fromString(String value) { + if (value == null) { + throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value")); + } + return rawType.cast(new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8))); + } + + @Override + public String toString(T value) { + if (value == null) { + throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value")); + } + try { + return new String(((InputStream) value).readAllBytes()); + } catch (IOException ioe) { + throw new ExtractorException(ioe); + } + } + }; + } + } + + /** * Provider of {@link ParamConverter param converter} that produce the Optional instance * by invoking {@link ParamConverterProvider}. */ @@ -414,6 +451,7 @@ new TypeFromStringEnum(), new TypeValueOf(), new CharacterProvider(), + new InputStreamProvider(), new TypeFromString(), new StringConstructor(), new OptionalCustomProvider(manager),
diff --git a/core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/Flow.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/Flow.java similarity index 100% rename from core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/Flow.java rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/Flow.java
diff --git a/core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java similarity index 100% rename from core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
diff --git a/core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java similarity index 100% rename from core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java
diff --git a/core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java similarity index 100% rename from core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java
diff --git a/core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/SubmittableFlowPublisher.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmittableFlowPublisher.java similarity index 100% rename from core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/SubmittableFlowPublisher.java rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmittableFlowPublisher.java
diff --git a/core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/package-info.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/package-info.java similarity index 100% rename from core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/package-info.java rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/package-info.java
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java index f8bec0a..bcd7e5f 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 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 @@ -120,6 +120,7 @@ boolean secure = false; boolean httpOnly = false; Date expiry = null; + NewCookie.SameSite sameSite = null; public MutableNewCookie(String name, String value) { this.name = name; @@ -127,7 +128,7 @@ } public NewCookie getImmutableNewCookie() { - return new NewCookie(name, value, path, domain, version, comment, maxAge, expiry, secure, httpOnly); + return new NewCookie(name, value, path, domain, version, comment, maxAge, expiry, secure, httpOnly, sameSite); } } @@ -163,6 +164,8 @@ cookie.version = Integer.parseInt(value); } else if (param.startsWith("httponly")) { cookie.httpOnly = true; + } else if (param.startsWith("samesite")) { + cookie.sameSite = NewCookie.SameSite.valueOf(value.toUpperCase()); } else if (param.startsWith("expires")) { try { cookie.expiry = HttpDateFormat.readDate(value + ", " + bites[++i]);
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InterceptorExecutor.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InterceptorExecutor.java index 3040bf6..9acc012 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InterceptorExecutor.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InterceptorExecutor.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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,11 @@ } @Override + public boolean hasProperty(final String name) { + return propertiesDelegate.hasProperty(name); + } + + @Override public Object getProperty(final String name) { return propertiesDelegate.getProperty(name); }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java index 9c53c74..15ab8d0 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 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 @@ -73,6 +73,10 @@ if (cookie.isHttpOnly()) { b.append(";HttpOnly"); } + if (cookie.getSameSite() != null) { + b.append(";SameSite="); + b.append(cookie.getSameSite()); + } if (cookie.getExpiry() != null) { b.append(";Expires="); b.append(HttpDateFormat.getPreferredDateFormat().format(cookie.getExpiry()));
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/TracingAwarePropertiesDelegate.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/TracingAwarePropertiesDelegate.java index 97afd68..a75e554 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/TracingAwarePropertiesDelegate.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/TracingAwarePropertiesDelegate.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -59,6 +59,14 @@ } @Override + public boolean hasProperty(final String name) { + if (tracingLogger != null && TracingLogger.PROPERTY_NAME.equals(name)) { + return true; + } + return propertiesDelegate.hasProperty(name); + } + + @Override public Object getProperty(String name) { if (tracingLogger != null && TracingLogger.PROPERTY_NAME.equals(name)) { return tracingLogger;
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java b/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java deleted file mode 100644 index 6eb4cb5..0000000 --- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java +++ /dev/null
@@ -1,1626 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -package org.glassfish.jersey.internal.jsr166; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.LockSupport; -import java.util.function.BiConsumer; -import java.util.function.BiPredicate; -import java.util.function.Consumer; - -/** - * A {@link Flow.Publisher} that asynchronously issues submitted - * (non-null) items to current subscribers until it is closed. Each - * current subscriber receives newly submitted items in the same order - * unless drops or exceptions are encountered. Using a - * SubmissionPublisher allows item generators to act as compliant <a - * href="http://www.reactive-streams.org/"> reactive-streams</a> - * Publishers relying on drop handling and/or blocking for flow - * control. - * <p> - * <p>A SubmissionPublisher uses the {@link Executor} supplied in its - * constructor for delivery to subscribers. The best choice of - * Executor depends on expected usage. If the generator(s) of - * submitted items run in separate threads, and the number of - * subscribers can be estimated, consider using a {@link - * Executors#newFixedThreadPool}. Otherwise consider using the - * default, normally the {@link ForkJoinPool#commonPool}. - * <p> - * <p>Buffering allows producers and consumers to transiently operate - * at different rates. Each subscriber uses an independent buffer. - * Buffers are created upon first use and expanded as needed up to the - * given maximum. (The enforced capacity may be rounded up to the - * nearest power of two and/or bounded by the largest value supported - * by this implementation.) Invocations of {@link - * Flow.Subscription#request(long) request} do not directly result in - * buffer expansion, but risk saturation if unfilled requests exceed - * the maximum capacity. The default value of {@link - * Flow#defaultBufferSize()} may provide a useful starting point for - * choosing a capacity based on expected rates, resources, and usages. - * <p> - * <p>Publication methods support different policies about what to do - * when buffers are saturated. Method {@link #submit(Object) submit} - * blocks until resources are available. This is simplest, but least - * responsive. The {@code offer} methods may drop items (either - * immediately or with bounded timeout), but provide an opportunity to - * interpose a handler and then retry. - * <p> - * <p>If any Subscriber method throws an exception, its subscription - * is cancelled. If a handler is supplied as a constructor argument, - * it is invoked before cancellation upon an exception in method - * {@link Flow.Subscriber#onNext onNext}, but exceptions in methods - * {@link Flow.Subscriber#onSubscribe onSubscribe}, - * {@link Flow.Subscriber#onError(Throwable) onError} and - * {@link Flow.Subscriber#onComplete() onComplete} are not recorded or - * handled before cancellation. If the supplied Executor throws - * {@link RejectedExecutionException} (or any other RuntimeException - * or Error) when attempting to execute a task, or a drop handler - * throws an exception when processing a dropped item, then the - * exception is rethrown. In these cases, not all subscribers will - * have been issued the published item. It is usually good practice to - * {@link #closeExceptionally closeExceptionally} in these cases. - * <p> - * <p>Method {@link #consume(Consumer)} simplifies support for a - * common case in which the only action of a subscriber is to request - * and process all items using a supplied function. - * <p> - * <p>This class may also serve as a convenient base for subclasses - * that generate items, and use the methods in this class to publish - * them. For example here is a class that periodically publishes the - * items generated from a supplier. (In practice you might add methods - * to independently start and stop generation, to share Executors - * among publishers, and so on, or use a SubmissionPublisher as a - * component rather than a superclass.) - * <p> - * <pre> {@code - * class PeriodicPublisher<T> extends SubmissionPublisher<T> { - * final ScheduledFuture<?> periodicTask; - * final ScheduledExecutorService scheduler; - * PeriodicPublisher(Executor executor, int maxBufferCapacity, - * Supplier<? extends T> supplier, - * long period, TimeUnit unit) { - * super(executor, maxBufferCapacity); - * scheduler = new ScheduledThreadPoolExecutor(1); - * periodicTask = scheduler.scheduleAtFixedRate( - * () -> submit(supplier.get()), 0, period, unit); - * } - * public void close() { - * periodicTask.cancel(false); - * scheduler.shutdown(); - * super.close(); - * } - * }}</pre> - * <p> - * <p>Here is an example of a {@link Flow.Processor} implementation. - * It uses single-step requests to its publisher for simplicity of - * illustration. A more adaptive version could monitor flow using the - * lag estimate returned from {@code submit}, along with other utility - * methods. - * <p> - * <pre> {@code - * class TransformProcessor<S,T> extends SubmissionPublisher<T> - * implements Flow.Processor<S,T> { - * final Function<? super S, ? extends T> function; - * Flow.Subscription subscription; - * TransformProcessor(Executor executor, int maxBufferCapacity, - * Function<? super S, ? extends T> function) { - * super(executor, maxBufferCapacity); - * this.function = function; - * } - * public void onSubscribe(Flow.Subscription subscription) { - * (this.subscription = subscription).request(1); - * } - * public void onNext(S item) { - * subscription.request(1); - * submit(function.apply(item)); - * } - * public void onError(Throwable ex) { closeExceptionally(ex); } - * public void onComplete() { close(); } - * }}</pre> - * - * @param <T> the published item type - * @author Doug Lea - * @since 9 - */ -public class SubmissionPublisher<T> implements Flow.Publisher<T>, SubmittableFlowPublisher<T>, - AutoCloseable { - /* - * Most mechanics are handled by BufferedSubscription. This class - * mainly tracks subscribers and ensures sequentiality, by using - * built-in synchronization locks across public methods. (Using - * built-in locks works well in the most typical case in which - * only one thread submits items). - */ - - /** - * The largest possible power of two array size. - */ - static final int BUFFER_CAPACITY_LIMIT = 1 << 30; - - /** - * Round capacity to power of 2, at most limit. - */ - static final int roundCapacity(int cap) { - int n = cap - 1; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - return (n <= 0) ? 1 : // at least 1 - (n >= BUFFER_CAPACITY_LIMIT) ? BUFFER_CAPACITY_LIMIT : n + 1; - } - - // default Executor setup; nearly the same as CompletableFuture - - /** - * Default executor -- ForkJoinPool.commonPool() unless it cannot - * support parallelism. - */ - private static final Executor ASYNC_POOL = - (ForkJoinPool.getCommonPoolParallelism() > 1) ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor(); - - /** - * Fallback if ForkJoinPool.commonPool() cannot support parallelism - */ - private static final class ThreadPerTaskExecutor implements Executor { - public void execute(Runnable r) { - new Thread(r).start(); - } - } - - /** - * Clients (BufferedSubscriptions) are maintained in a linked list - * (via their "next" fields). This works well for publish loops. - * It requires O(n) traversal to check for duplicate subscribers, - * but we expect that subscribing is much less common than - * publishing. Unsubscribing occurs only during traversal loops, - * when BufferedSubscription methods return negative values - * signifying that they have been disabled. To reduce - * head-of-line blocking, submit and offer methods first call - * BufferedSubscription.offer on each subscriber, and place - * saturated ones in retries list (using nextRetry field), and - * retry, possibly blocking or dropping. - */ - BufferedSubscription<T> clients; - - /** - * Run status, updated only within locks - */ - volatile boolean closed; - /** - * If non-null, the exception in closeExceptionally - */ - volatile Throwable closedException; - - // Parameters for constructing BufferedSubscriptions - final Executor executor; - final BiConsumer<? super Flow.Subscriber<? super T>, ? super Throwable> onNextHandler; - final int maxBufferCapacity; - - /** - * Creates a new SubmissionPublisher using the given Executor for - * async delivery to subscribers, with the given maximum buffer size - * for each subscriber, and, if non-null, the given handler invoked - * when any Subscriber throws an exception in method {@link - * Flow.Subscriber#onNext(Object) onNext}. - * - * @param executor the executor to use for async delivery, - * supporting creation of at least one independent thread - * @param maxBufferCapacity the maximum capacity for each - * subscriber's buffer (the enforced capacity may be rounded up to - * the nearest power of two and/or bounded by the largest value - * supported by this implementation; method {@link #getMaxBufferCapacity} - * returns the actual value) - * @param handler if non-null, procedure to invoke upon exception - * thrown in method {@code onNext} - * @throws NullPointerException if executor is null - * @throws IllegalArgumentException if maxBufferCapacity not - * positive - */ - public SubmissionPublisher(Executor executor, int maxBufferCapacity, - BiConsumer<? super Flow.Subscriber<? super T>, ? super Throwable> handler) { - if (executor == null) { - throw new NullPointerException(); - } - if (maxBufferCapacity <= 0) { - throw new IllegalArgumentException("capacity must be positive"); - } - this.executor = executor; - this.onNextHandler = handler; - this.maxBufferCapacity = roundCapacity(maxBufferCapacity); - } - - /** - * Creates a new SubmissionPublisher using the given Executor for - * async delivery to subscribers, with the given maximum buffer size - * for each subscriber, and no handler for Subscriber exceptions in - * method {@link Flow.Subscriber#onNext(Object) onNext}. - * - * @param executor the executor to use for async delivery, - * supporting creation of at least one independent thread - * @param maxBufferCapacity the maximum capacity for each - * subscriber's buffer (the enforced capacity may be rounded up to - * the nearest power of two and/or bounded by the largest value - * supported by this implementation; method {@link #getMaxBufferCapacity} - * returns the actual value) - * @throws NullPointerException if executor is null - * @throws IllegalArgumentException if maxBufferCapacity not - * positive - */ - public SubmissionPublisher(Executor executor, int maxBufferCapacity) { - this(executor, maxBufferCapacity, null); - } - - /** - * Creates a new SubmissionPublisher using the {@link - * ForkJoinPool#commonPool()} for async delivery to subscribers - * (unless it does not support a parallelism level of at least two, - * in which case, a new Thread is created to run each task), with - * maximum buffer capacity of {@link Flow#defaultBufferSize}, and no - * handler for Subscriber exceptions in method {@link - * Flow.Subscriber#onNext(Object) onNext}. - */ - public SubmissionPublisher() { - this(ASYNC_POOL, Flow.defaultBufferSize(), null); - } - - /** - * Adds the given Subscriber unless already subscribed. If already - * subscribed, the Subscriber's {@link - * Flow.Subscriber#onError(Throwable) onError} method is invoked on - * the existing subscription with an {@link IllegalStateException}. - * Otherwise, upon success, the Subscriber's {@link - * Flow.Subscriber#onSubscribe onSubscribe} method is invoked - * asynchronously with a new {@link Flow.Subscription}. If {@link - * Flow.Subscriber#onSubscribe onSubscribe} throws an exception, the - * subscription is cancelled. Otherwise, if this SubmissionPublisher - * was closed exceptionally, then the subscriber's {@link - * Flow.Subscriber#onError onError} method is invoked with the - * corresponding exception, or if closed without exception, the - * subscriber's {@link Flow.Subscriber#onComplete() onComplete} - * method is invoked. Subscribers may enable receiving items by - * invoking the {@link Flow.Subscription#request(long) request} - * method of the new Subscription, and may unsubscribe by invoking - * its {@link Flow.Subscription#cancel() cancel} method. - * - * @param subscriber the subscriber - * @throws NullPointerException if subscriber is null - */ - public void subscribe(Flow.Subscriber<? super T> subscriber) { - if (subscriber == null) { - throw new NullPointerException(); - } - BufferedSubscription<T> subscription = - new BufferedSubscription<T>(subscriber, executor, - onNextHandler, maxBufferCapacity); - synchronized (this) { - for (BufferedSubscription<T> b = clients, pred = null;;) { - if (b == null) { - Throwable ex; - subscription.onSubscribe(); - if ((ex = closedException) != null) { - subscription.onError(ex); - } else if (closed) { - subscription.onComplete(); - } else if (pred == null) { - clients = subscription; - } else { - pred.next = subscription; - } - break; - } - BufferedSubscription<T> next = b.next; - if (b.isDisabled()) { // remove - b.next = null; // detach - if (pred == null) { - clients = next; - } else { - pred.next = next; - } - } else if (subscriber.equals(b.subscriber)) { - b.onError(new IllegalStateException("Duplicate subscribe")); - break; - } else { - pred = b; - } - b = next; - } - } - } - - /** - * Publishes the given item to each current subscriber by - * asynchronously invoking its {@link Flow.Subscriber#onNext(Object) - * onNext} method, blocking uninterruptibly while resources for any - * subscriber are unavailable. This method returns an estimate of - * the maximum lag (number of items submitted but not yet consumed) - * among all current subscribers. This value is at least one - * (accounting for this submitted item) if there are any - * subscribers, else zero. - * <p> - * <p>If the Executor for this publisher throws a - * RejectedExecutionException (or any other RuntimeException or - * Error) when attempting to asynchronously notify subscribers, - * then this exception is rethrown, in which case not all - * subscribers will have been issued this item. - * - * @param item the (non-null) item to publish - * @return the estimated maximum lag among subscribers - * @throws IllegalStateException if closed - * @throws NullPointerException if item is null - * @throws RejectedExecutionException if thrown by Executor - */ - public int submit(T item) { - if (item == null) { - throw new NullPointerException(); - } - int lag = 0; - boolean complete; - synchronized (this) { - complete = closed; - BufferedSubscription<T> b = clients; - if (!complete) { - BufferedSubscription<T> pred = null, r = null, rtail = null; - while (b != null) { - BufferedSubscription<T> next = b.next; - int stat = b.offer(item); - if (stat < 0) { // disabled - b.next = null; - if (pred == null) { - clients = next; - } else { - pred.next = next; - } - } else { - if (stat > lag) { - lag = stat; - } else if (stat == 0) { // place on retry list - b.nextRetry = null; - if (rtail == null) { - r = b; - } else { - rtail.nextRetry = b; - } - rtail = b; - } - pred = b; - } - b = next; - } - while (r != null) { - BufferedSubscription<T> nextRetry = r.nextRetry; - r.nextRetry = null; - int stat = r.submit(item); - if (stat > lag) { - lag = stat; - } else if (stat < 0 && clients == r) { - clients = r.next; // postpone internal unsubscribes - } - r = nextRetry; - } - } - } - if (complete) { - throw new IllegalStateException("Closed"); - } else { - return lag; - } - } - - /** - * Publishes the given item, if possible, to each current subscriber - * by asynchronously invoking its {@link - * Flow.Subscriber#onNext(Object) onNext} method. The item may be - * dropped by one or more subscribers if resource limits are - * exceeded, in which case the given handler (if non-null) is - * invoked, and if it returns true, retried once. Other calls to - * methods in this class by other threads are blocked while the - * handler is invoked. Unless recovery is assured, options are - * usually limited to logging the error and/or issuing an {@link - * Flow.Subscriber#onError(Throwable) onError} signal to the - * subscriber. - * <p> - * <p>This method returns a status indicator: If negative, it - * represents the (negative) number of drops (failed attempts to - * issue the item to a subscriber). Otherwise it is an estimate of - * the maximum lag (number of items submitted but not yet - * consumed) among all current subscribers. This value is at least - * one (accounting for this submitted item) if there are any - * subscribers, else zero. - * <p> - * <p>If the Executor for this publisher throws a - * RejectedExecutionException (or any other RuntimeException or - * Error) when attempting to asynchronously notify subscribers, or - * the drop handler throws an exception when processing a dropped - * item, then this exception is rethrown. - * - * @param item the (non-null) item to publish - * @param onDrop if non-null, the handler invoked upon a drop to a - * subscriber, with arguments of the subscriber and item; if it - * returns true, an offer is re-attempted (once) - * @return if negative, the (negative) number of drops; otherwise - * an estimate of maximum lag - * @throws IllegalStateException if closed - * @throws NullPointerException if item is null - * @throws RejectedExecutionException if thrown by Executor - */ - public int offer(T item, - BiPredicate<Flow.Subscriber<? super T>, ? super T> onDrop) { - return doOffer(0L, item, onDrop); - } - - /** - * Publishes the given item, if possible, to each current subscriber - * by asynchronously invoking its {@link - * Flow.Subscriber#onNext(Object) onNext} method, blocking while - * resources for any subscription are unavailable, up to the - * specified timeout or until the caller thread is interrupted, at - * which point the given handler (if non-null) is invoked, and if it - * returns true, retried once. (The drop handler may distinguish - * timeouts from interrupts by checking whether the current thread - * is interrupted.) Other calls to methods in this class by other - * threads are blocked while the handler is invoked. Unless - * recovery is assured, options are usually limited to logging the - * error and/or issuing an {@link Flow.Subscriber#onError(Throwable) - * onError} signal to the subscriber. - * <p> - * <p>This method returns a status indicator: If negative, it - * represents the (negative) number of drops (failed attempts to - * issue the item to a subscriber). Otherwise it is an estimate of - * the maximum lag (number of items submitted but not yet - * consumed) among all current subscribers. This value is at least - * one (accounting for this submitted item) if there are any - * subscribers, else zero. - * <p> - * <p>If the Executor for this publisher throws a - * RejectedExecutionException (or any other RuntimeException or - * Error) when attempting to asynchronously notify subscribers, or - * the drop handler throws an exception when processing a dropped - * item, then this exception is rethrown. - * - * @param item the (non-null) item to publish - * @param timeout how long to wait for resources for any subscriber - * before giving up, in units of {@code unit} - * @param unit a {@code TimeUnit} determining how to interpret the - * {@code timeout} parameter - * @param onDrop if non-null, the handler invoked upon a drop to a - * subscriber, with arguments of the subscriber and item; if it - * returns true, an offer is re-attempted (once) - * @return if negative, the (negative) number of drops; otherwise - * an estimate of maximum lag - * @throws IllegalStateException if closed - * @throws NullPointerException if item is null - * @throws RejectedExecutionException if thrown by Executor - */ - public int offer(T item, long timeout, TimeUnit unit, - BiPredicate<Flow.Subscriber<? super T>, ? super T> onDrop) { - return doOffer(unit.toNanos(timeout), item, onDrop); - } - - /** - * Common implementation for both forms of offer - */ - final int doOffer(long nanos, T item, - BiPredicate<Flow.Subscriber<? super T>, ? super T> onDrop) { - if (item == null) { - throw new NullPointerException(); - } - int lag = 0, drops = 0; - boolean complete; - synchronized (this) { - complete = closed; - BufferedSubscription<T> b = clients; - if (!complete) { - BufferedSubscription<T> pred = null, r = null, rtail = null; - while (b != null) { - BufferedSubscription<T> next = b.next; - int stat = b.offer(item); - if (stat < 0) { - b.next = null; - if (pred == null) { - clients = next; - } else { - pred.next = next; - } - } else { - if (stat > lag) { - lag = stat; - } else if (stat == 0) { - b.nextRetry = null; - if (rtail == null) { - r = b; - } else { - rtail.nextRetry = b; - } - rtail = b; - } else if (stat > lag) { - lag = stat; - } - pred = b; - } - b = next; - } - while (r != null) { - BufferedSubscription<T> nextRetry = r.nextRetry; - r.nextRetry = null; - int stat = (nanos > 0L) - ? r.timedOffer(item, nanos) - : r.offer(item); - if (stat == 0 && onDrop != null && onDrop.test(r.subscriber, item)) { - stat = r.offer(item); - } - if (stat == 0) { - ++drops; - } else if (stat > lag) { - lag = stat; - } else if (stat < 0 && clients == r) { - clients = r.next; - } - r = nextRetry; - } - } - } - if (complete) { - throw new IllegalStateException("Closed"); - } else { - return (drops > 0) ? -drops : lag; - } - } - - /** - * Unless already closed, issues {@link - * Flow.Subscriber#onComplete() onComplete} signals to current - * subscribers, and disallows subsequent attempts to publish. - * Upon return, this method does <em>NOT</em> guarantee that all - * subscribers have yet completed. - */ - public void close() { - if (!closed) { - BufferedSubscription<T> b; - synchronized (this) { - b = clients; - clients = null; - closed = true; - } - while (b != null) { - BufferedSubscription<T> next = b.next; - b.next = null; - b.onComplete(); - b = next; - } - } - } - - /** - * Unless already closed, issues {@link - * Flow.Subscriber#onError(Throwable) onError} signals to current - * subscribers with the given error, and disallows subsequent - * attempts to publish. Future subscribers also receive the given - * error. Upon return, this method does <em>NOT</em> guarantee - * that all subscribers have yet completed. - * - * @param error the {@code onError} argument sent to subscribers - * @throws NullPointerException if error is null - */ - public void closeExceptionally(Throwable error) { - if (error == null) { - throw new NullPointerException(); - } - if (!closed) { - BufferedSubscription<T> b; - synchronized (this) { - b = clients; - clients = null; - closed = true; - closedException = error; - } - while (b != null) { - BufferedSubscription<T> next = b.next; - b.next = null; - b.onError(error); - b = next; - } - } - } - - /** - * Returns true if this publisher is not accepting submissions. - * - * @return true if closed - */ - public boolean isClosed() { - return closed; - } - - /** - * Returns the exception associated with {@link - * #closeExceptionally(Throwable) closeExceptionally}, or null if - * not closed or if closed normally. - * - * @return the exception, or null if none - */ - public Throwable getClosedException() { - return closedException; - } - - /** - * Returns true if this publisher has any subscribers. - * - * @return true if this publisher has any subscribers - */ - public boolean hasSubscribers() { - boolean nonEmpty = false; - if (!closed) { - synchronized (this) { - for (BufferedSubscription<T> b = clients; b != null; ) { - BufferedSubscription<T> next = b.next; - if (b.isDisabled()) { - b.next = null; - b = clients = next; - } else { - nonEmpty = true; - break; - } - } - } - } - return nonEmpty; - } - - /** - * Returns the number of current subscribers. - * - * @return the number of current subscribers - */ - public int getNumberOfSubscribers() { - int count = 0; - if (!closed) { - synchronized (this) { - BufferedSubscription<T> pred = null, next; - for (BufferedSubscription<T> b = clients; b != null; b = next) { - next = b.next; - if (b.isDisabled()) { - b.next = null; - if (pred == null) { - clients = next; - } else { - pred.next = next; - } - } else { - pred = b; - ++count; - } - } - } - } - return count; - } - - /** - * Returns the Executor used for asynchronous delivery. - * - * @return the Executor used for asynchronous delivery - */ - public Executor getExecutor() { - return executor; - } - - /** - * Returns the maximum per-subscriber buffer capacity. - * - * @return the maximum per-subscriber buffer capacity - */ - public int getMaxBufferCapacity() { - return maxBufferCapacity; - } - - /** - * Returns a list of current subscribers for monitoring and - * tracking purposes, not for invoking {@link Flow.Subscriber} - * methods on the subscribers. - * - * @return list of current subscribers - */ - public List<Flow.Subscriber<? super T>> getSubscribers() { - ArrayList<Flow.Subscriber<? super T>> subs = new ArrayList<>(); - synchronized (this) { - BufferedSubscription<T> pred = null, next; - for (BufferedSubscription<T> b = clients; b != null; b = next) { - next = b.next; - if (b.isDisabled()) { - b.next = null; - if (pred == null) { - clients = next; - } else { - pred.next = next; - } - } else { - subs.add(b.subscriber); - } - } - } - return subs; - } - - /** - * Returns true if the given Subscriber is currently subscribed. - * - * @param subscriber the subscriber - * @return true if currently subscribed - * @throws NullPointerException if subscriber is null - */ - public boolean isSubscribed(Flow.Subscriber<? super T> subscriber) { - if (subscriber == null) { - throw new NullPointerException(); - } - if (!closed) { - synchronized (this) { - BufferedSubscription<T> pred = null, next; - for (BufferedSubscription<T> b = clients; b != null; b = next) { - next = b.next; - if (b.isDisabled()) { - b.next = null; - if (pred == null) { - clients = next; - } else { - pred.next = next; - } - } else if (subscriber.equals(b.subscriber)) { - return true; - } else { - pred = b; - } - } - } - } - return false; - } - - /** - * Returns an estimate of the minimum number of items requested - * (via {@link Flow.Subscription#request(long) request}) but not - * yet produced, among all current subscribers. - * - * @return the estimate, or zero if no subscribers - */ - public long estimateMinimumDemand() { - long min = Long.MAX_VALUE; - boolean nonEmpty = false; - synchronized (this) { - BufferedSubscription<T> pred = null, next; - for (BufferedSubscription<T> b = clients; b != null; b = next) { - int n; - long d; - next = b.next; - if ((n = b.estimateLag()) < 0) { - b.next = null; - if (pred == null) { - clients = next; - } else { - pred.next = next; - } - } else { - if ((d = b.demand - n) < min) { - min = d; - } - nonEmpty = true; - pred = b; - } - } - } - return nonEmpty ? min : 0; - } - - /** - * Returns an estimate of the maximum number of items produced but - * not yet consumed among all current subscribers. - * - * @return the estimate - */ - public int estimateMaximumLag() { - int max = 0; - synchronized (this) { - BufferedSubscription<T> pred = null, next; - for (BufferedSubscription<T> b = clients; b != null; b = next) { - int n; - next = b.next; - if ((n = b.estimateLag()) < 0) { - b.next = null; - if (pred == null) { - clients = next; - } else { - pred.next = next; - } - } else { - if (n > max) { - max = n; - } - pred = b; - } - } - } - return max; - } - - /** - * Processes all published items using the given Consumer function. - * Returns a CompletableFuture that is completed normally when this - * publisher signals {@link Flow.Subscriber#onComplete() - * onComplete}, or completed exceptionally upon any error, or an - * exception is thrown by the Consumer, or the returned - * CompletableFuture is cancelled, in which case no further items - * are processed. - * - * @param consumer the function applied to each onNext item - * @return a CompletableFuture that is completed normally - * when the publisher signals onComplete, and exceptionally - * upon any error or cancellation - * @throws NullPointerException if consumer is null - */ - public CompletableFuture<Void> consume(Consumer<? super T> consumer) { - if (consumer == null) { - throw new NullPointerException(); - } - CompletableFuture<Void> status = new CompletableFuture<>(); - subscribe(new ConsumerSubscriber<T>(status, consumer)); - return status; - } - - /** - * Subscriber for method consume - */ - private static final class ConsumerSubscriber<T> - implements Flow.Subscriber<T> { - final CompletableFuture<Void> status; - final Consumer<? super T> consumer; - Flow.Subscription subscription; - - ConsumerSubscriber(CompletableFuture<Void> status, - Consumer<? super T> consumer) { - this.status = status; - this.consumer = consumer; - } - - public final void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - status.whenComplete((v, e) -> subscription.cancel()); - if (!status.isDone()) { - subscription.request(Long.MAX_VALUE); - } - } - - public final void onError(Throwable ex) { - status.completeExceptionally(ex); - } - - public final void onComplete() { - status.complete(null); - } - - public final void onNext(T item) { - try { - consumer.accept(item); - } catch (Throwable ex) { - subscription.cancel(); - status.completeExceptionally(ex); - } - } - } - - /** - * A task for consuming buffer items and signals, created and - * executed whenever they become available. A task consumes as - * many items/signals as possible before terminating, at which - * point another task is created when needed. The dual Runnable - * and ForkJoinTask declaration saves overhead when executed by - * ForkJoinPools, without impacting other kinds of Executors. - */ - @SuppressWarnings("serial") - static final class ConsumerTask<T> extends ForkJoinTask<Void> - implements Runnable, CompletableFuture.AsynchronousCompletionTask { - final BufferedSubscription<T> consumer; - - ConsumerTask(BufferedSubscription<T> consumer) { - this.consumer = consumer; - } - - public final Void getRawResult() { - return null; - } - - public final void setRawResult(Void v) { - } - - public final boolean exec() { - consumer.consume(); - return false; - } - - public final void run() { - consumer.consume(); - } - } - - /** - * A bounded (ring) buffer with integrated control to start a - * consumer task whenever items are available. The buffer - * algorithm is similar to one used inside ForkJoinPool (see its - * internal documentation for details) specialized for the case of - * at most one concurrent producer and consumer, and power of two - * buffer sizes. This allows methods to operate without locks even - * while supporting resizing, blocking, task-triggering, and - * garbage-free buffers (nulling out elements when consumed), - * although supporting these does impose a bit of overhead - * compared to plain fixed-size ring buffers. - * <p> - * The publisher guarantees a single producer via its lock. We - * ensure in this class that there is at most one consumer. The - * request and cancel methods must be fully thread-safe but are - * coded to exploit the most common case in which they are only - * called by consumers (usually within onNext). - * <p> - * Execution control is managed using the ACTIVE ctl bit. We - * ensure that a task is active when consumable items (and - * usually, SUBSCRIBE, ERROR or COMPLETE signals) are present and - * there is demand (unfilled requests). This is complicated on - * the creation side by the possibility of exceptions when trying - * to execute tasks. These eventually force DISABLED state, but - * sometimes not directly. On the task side, termination (clearing - * ACTIVE) that would otherwise race with producers or request() - * calls uses the CONSUME keep-alive bit to force a recheck. - * <p> - * The ctl field also manages run state. When DISABLED, no further - * updates are possible. Disabling may be preceded by setting - * ERROR or COMPLETE (or both -- ERROR has precedence), in which - * case the associated Subscriber methods are invoked, possibly - * synchronously if there is no active consumer task (including - * cases where execute() failed). The cancel() method is supported - * by treating as ERROR but suppressing onError signal. - * <p> - * Support for blocking also exploits the fact that there is only - * one possible waiter. ManagedBlocker-compatible control fields - * are placed in this class itself rather than in wait-nodes. - * Blocking control relies on the "waiter" field. Producers set - * the field before trying to block, but must then recheck (via - * offer) before parking. Signalling then just unparks and clears - * waiter field. If the producer and/or consumer are using a - * ForkJoinPool, the producer attempts to help run consumer tasks - * via ForkJoinPool.helpAsyncBlocker before blocking. - * <p> - * This class uses @Contended and heuristic field declaration - * ordering to reduce false-sharing-based memory contention among - * instances of BufferedSubscription, but it does not currently - * attempt to avoid memory contention among buffers. This field - * and element packing can hurt performance especially when each - * publisher has only one client operating at a high rate. - * Addressing this may require allocating substantially more space - * than users expect. - */ - @SuppressWarnings("serial") - @sun.misc.Contended - private static final class BufferedSubscription<T> - implements Flow.Subscription, ForkJoinPool.ManagedBlocker { - // Order-sensitive field declarations - long timeout; // > 0 if timed wait - volatile long demand; // # unfilled requests - int maxCapacity; // reduced on OOME - int putStat; // offer result for ManagedBlocker - volatile int ctl; // atomic run state flags - volatile int head; // next position to take - int tail; // next position to put - Object[] array; // buffer: null if disabled - Flow.Subscriber<? super T> subscriber; // null if disabled - Executor executor; // null if disabled - BiConsumer<? super Flow.Subscriber<? super T>, ? super Throwable> onNextHandler; - volatile Throwable pendingError; // holds until onError issued - volatile Thread waiter; // blocked producer thread - T putItem; // for offer within ManagedBlocker - BufferedSubscription<T> next; // used only by publisher - BufferedSubscription<T> nextRetry; // used only by publisher - - // ctl values - static final int ACTIVE = 0x01; // consumer task active - static final int CONSUME = 0x02; // keep-alive for consumer task - static final int DISABLED = 0x04; // final state - static final int ERROR = 0x08; // signal onError then disable - static final int SUBSCRIBE = 0x10; // signal onSubscribe - static final int COMPLETE = 0x20; // signal onComplete when done - - static final long INTERRUPTED = -1L; // timeout vs interrupt sentinel - - /** - * Initial buffer capacity used when maxBufferCapacity is - * greater. Must be a power of two. - */ - static final int DEFAULT_INITIAL_CAP = 32; - - BufferedSubscription(Flow.Subscriber<? super T> subscriber, - Executor executor, - BiConsumer<? super Flow.Subscriber<? super T>, - ? super Throwable> onNextHandler, - int maxBufferCapacity) { - this.subscriber = subscriber; - this.executor = executor; - this.onNextHandler = onNextHandler; - this.maxCapacity = maxBufferCapacity; - this.array = new Object[maxBufferCapacity < DEFAULT_INITIAL_CAP - ? (maxBufferCapacity < 2 // at least 2 slots - ? 2 : maxBufferCapacity) - : DEFAULT_INITIAL_CAP]; - } - - @Override - public String toString() { - if (subscriber != null) { - return subscriber.toString(); - } else { - return super.toString(); - } - } - - final boolean isDisabled() { - return ctl == DISABLED; - } - - /** - * Returns estimated number of buffered items, or -1 if - * disabled. - */ - final int estimateLag() { - int n; - return (ctl == DISABLED) ? -1 : ((n = tail - head) > 0) ? n : 0; - } - - /** - * Tries to add item and start consumer task if necessary. - * - * @return -1 if disabled, 0 if dropped, else estimated lag - */ - final int offer(T item) { - int h = head, t = tail, cap, size, stat; - Object[] a = array; - if (a != null && (cap = a.length) > 0 && cap >= (size = t + 1 - h)) { - a[(cap - 1) & t] = item; // relaxed writes OK - tail = t + 1; - stat = size; - } else { - stat = growAndAdd(a, item); - } - return (stat > 0 && (ctl & (ACTIVE | CONSUME)) != (ACTIVE | CONSUME)) ? startOnOffer(stat) : stat; - } - - /** - * Tries to create or expand buffer, then adds item if possible. - */ - private int growAndAdd(Object[] a, T item) { - boolean alloc; - int cap, stat; - if ((ctl & (ERROR | DISABLED)) != 0) { - cap = 0; - stat = -1; - alloc = false; - } else if (a == null || (cap = a.length) <= 0) { - cap = 0; - stat = 1; - alloc = true; - } else { - U.fullFence(); // recheck - int h = head, t = tail, size = t + 1 - h; - if (cap >= size) { - a[(cap - 1) & t] = item; - tail = t + 1; - stat = size; - alloc = false; - } else if (cap >= maxCapacity) { - stat = 0; // cannot grow - alloc = false; - } else { - stat = cap + 1; - alloc = true; - } - } - if (alloc) { - int newCap = (cap > 0) ? cap << 1 : 1; - if (newCap <= cap) { - stat = 0; - } else { - Object[] newArray = null; - try { - newArray = new Object[newCap]; - } catch (Throwable ex) { // try to cope with OOME - } - if (newArray == null) { - if (cap > 0) { - maxCapacity = cap; // avoid continuous failure - } - stat = 0; - } else { - array = newArray; - int t = tail; - int newMask = newCap - 1; - if (a != null && cap > 0) { - int mask = cap - 1; - for (int j = head; j != t; ++j) { - long k = ((long) (j & mask) << ASHIFT) + ABASE; - Object x = U.getObjectVolatile(a, k); - if (x != null && // races with consumer - U.compareAndSwapObject(a, k, x, null)) { - newArray[j & newMask] = x; - } - } - } - newArray[t & newMask] = item; - tail = t + 1; - } - } - } - return stat; - } - - /** - * Spins/helps/blocks while offer returns 0. Called only if - * initial offer return 0. - */ - final int submit(T item) { - int stat; - if ((stat = offer(item)) == 0) { - putItem = item; - timeout = 0L; - putStat = 0; - // safe to comment out when executor != ForkJoinPool (TODO) - // ForkJoinPool.helpAsyncBlocker(executor, this); - if ((stat = putStat) == 0) { - try { - ForkJoinPool.managedBlock(this); - } catch (InterruptedException ie) { - timeout = INTERRUPTED; - } - stat = putStat; - } - if (timeout < 0L) { - Thread.currentThread().interrupt(); - } - } - return stat; - } - - /** - * Timeout version; similar to submit. - */ - final int timedOffer(T item, long nanos) { - int stat; - if ((stat = offer(item)) == 0 && (timeout = nanos) > 0L) { - putItem = item; - putStat = 0; - // safe to comment out when executor != ForkJoinPool (TODO) - // ForkJoinPool.helpAsyncBlocker(executor, this); - if ((stat = putStat) == 0) { - try { - ForkJoinPool.managedBlock(this); - } catch (InterruptedException ie) { - timeout = INTERRUPTED; - } - stat = putStat; - } - if (timeout < 0L) { - Thread.currentThread().interrupt(); - } - } - return stat; - } - - /** - * Tries to start consumer task after offer. - * - * @return -1 if now disabled, else argument - */ - private int startOnOffer(int stat) { - for (;;) { - Executor e; - int c; - if ((c = ctl) == DISABLED || (e = executor) == null) { - stat = -1; - break; - } else if ((c & ACTIVE) != 0) { // ensure keep-alive - if ((c & CONSUME) != 0 || U.compareAndSwapInt(this, CTL, c, c | CONSUME)) { - break; - } - } else if (demand == 0L || tail == head) { - break; - } else if (U.compareAndSwapInt(this, CTL, c, - c | (ACTIVE | CONSUME))) { - try { - e.execute(new ConsumerTask<T>(this)); - break; - } catch (RuntimeException | Error ex) { // back out - do { - } while (((c = ctl) & DISABLED) == 0 - && (c & ACTIVE) != 0 - && !U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)); - throw ex; - } - } - } - return stat; - } - - private void signalWaiter(Thread w) { - waiter = null; - LockSupport.unpark(w); // release producer - } - - /** - * Nulls out most fields, mainly to avoid garbage retention - * until publisher unsubscribes, but also to help cleanly stop - * upon error by nulling required components. - */ - private void detach() { - Thread w = waiter; - executor = null; - subscriber = null; - pendingError = null; - signalWaiter(w); - } - - /** - * Issues error signal, asynchronously if a task is running, - * else synchronously. - */ - final void onError(Throwable ex) { - for (int c;;) { - if (((c = ctl) & (ERROR | DISABLED)) != 0) { - break; - } else if ((c & ACTIVE) != 0) { - pendingError = ex; - if (U.compareAndSwapInt(this, CTL, c, c | ERROR)) { - break; // cause consumer task to exit - } - } else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { - Flow.Subscriber<? super T> s = subscriber; - if (s != null && ex != null) { - try { - s.onError(ex); - } catch (Throwable ignore) { - } - } - detach(); - break; - } - } - } - - /** - * Tries to start consumer task upon a signal or request; - * disables on failure. - */ - private void startOrDisable() { - Executor e; - if ((e = executor) != null) { // skip if already disabled - try { - e.execute(new ConsumerTask<T>(this)); - } catch (Throwable ex) { // back out and force signal - for (int c;;) { - if ((c = ctl) == DISABLED || (c & ACTIVE) == 0) { - break; - } - if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) { - onError(ex); - break; - } - } - } - } - } - - final void onComplete() { - for (int c;;) { - if ((c = ctl) == DISABLED) { - break; - } - if (U.compareAndSwapInt(this, CTL, c, - c | (ACTIVE | CONSUME | COMPLETE))) { - if ((c & ACTIVE) == 0) { - startOrDisable(); - } - break; - } - } - } - - final void onSubscribe() { - for (int c;;) { - if ((c = ctl) == DISABLED) { - break; - } - if (U.compareAndSwapInt(this, CTL, c, - c | (ACTIVE | CONSUME | SUBSCRIBE))) { - if ((c & ACTIVE) == 0) { - startOrDisable(); - } - break; - } - } - } - - /** - * Causes consumer task to exit if active (without reporting - * onError unless there is already a pending error), and - * disables. - */ - public void cancel() { - for (int c;;) { - if ((c = ctl) == DISABLED) { - break; - } else if ((c & ACTIVE) != 0) { - if (U.compareAndSwapInt(this, CTL, c, - c | (CONSUME | ERROR))) { - break; - } - } else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { - detach(); - break; - } - } - } - - /** - * Adds to demand and possibly starts task. - */ - public void request(long n) { - if (n > 0L) { - for (;;) { - long prev = demand, d; - if ((d = prev + n) < prev) { // saturate - d = Long.MAX_VALUE; - } - if (U.compareAndSwapLong(this, DEMAND, prev, d)) { - for (int c, h;; ) { - if ((c = ctl) == DISABLED) { - break; - } else if ((c & ACTIVE) != 0) { - if ((c & CONSUME) != 0 || U.compareAndSwapInt(this, CTL, c, c | CONSUME)) { - break; - } - } else if ((h = head) != tail) { - if (U.compareAndSwapInt(this, CTL, c, - c | (ACTIVE | CONSUME))) { - startOrDisable(); - break; - } - } else if (head == h && tail == h) { - break; // else stale - } - if (demand == 0L) { - break; - } - } - break; - } - } - } else if (n < 0L) { - onError(new IllegalArgumentException( - "negative subscription request")); - } - } - - public final boolean isReleasable() { // for ManagedBlocker - T item = putItem; - if (item != null) { - if ((putStat = offer(item)) == 0) { - return false; - } - putItem = null; - } - return true; - } - - public final boolean block() { // for ManagedBlocker - T item = putItem; - if (item != null) { - putItem = null; - long nanos = timeout; - long deadline = (nanos > 0L) ? System.nanoTime() + nanos : 0L; - while ((putStat = offer(item)) == 0) { - if (Thread.interrupted()) { - timeout = INTERRUPTED; - if (nanos > 0L) { - break; - } - } else if (nanos > 0L && (nanos = deadline - System.nanoTime()) <= 0L) { - break; - } else if (waiter == null) { - waiter = Thread.currentThread(); - } else { - if (nanos > 0L) { - LockSupport.parkNanos(this, nanos); - } else { - LockSupport.park(this); - } - waiter = null; - } - } - } - waiter = null; - return true; - } - - /** - * Consumer loop, called from ConsumerTask, or indirectly - * when helping during submit. - */ - final void consume() { - Flow.Subscriber<? super T> s; - int h = head; - if ((s = subscriber) != null) { // else disabled - for (;;) { - long d = demand; - int c; - Object[] a; - int n; - long i; - Object x; - Thread w; - if (((c = ctl) & (ERROR | SUBSCRIBE | DISABLED)) != 0) { - if (!checkControl(s, c)) { - break; - } - } else if ((a = array) == null || h == tail - || (n = a.length) == 0 - || (x = U.getObjectVolatile(a, (i = ((long) ((n - 1) & h) << ASHIFT) + ABASE))) == null) { - if (!checkEmpty(s, c)) { - break; - } - } else if (d == 0L) { - if (!checkDemand(c)) { - break; - } - } else if (((c & CONSUME) != 0 - || U.compareAndSwapInt(this, CTL, c, c | CONSUME)) - && U.compareAndSwapObject(a, i, x, null)) { - U.putOrderedInt(this, HEAD, ++h); - U.getAndAddLong(this, DEMAND, -1L); - if ((w = waiter) != null) { - signalWaiter(w); - } - try { - @SuppressWarnings("unchecked") T y = (T) x; - s.onNext(y); - } catch (Throwable ex) { - handleOnNext(s, ex); - } - } - } - } - } - - /** - * Responds to control events in consume(). - */ - private boolean checkControl(Flow.Subscriber<? super T> s, int c) { - boolean stat = true; - if ((c & SUBSCRIBE) != 0) { - if (U.compareAndSwapInt(this, CTL, c, c & ~SUBSCRIBE)) { - try { - if (s != null) { - s.onSubscribe(this); - } - } catch (Throwable ex) { - onError(ex); - } - } - } else if ((c & ERROR) != 0) { - Throwable ex = pendingError; - ctl = DISABLED; // no need for CAS - if (ex != null) { // null if errorless cancel - try { - if (s != null) { - s.onError(ex); - } - } catch (Throwable ignore) { - } - } - } else { - detach(); - stat = false; - } - return stat; - } - - /** - * Responds to apparent emptiness in consume(). - */ - private boolean checkEmpty(Flow.Subscriber<? super T> s, int c) { - boolean stat = true; - if (head == tail) { - if ((c & CONSUME) != 0) { - U.compareAndSwapInt(this, CTL, c, c & ~CONSUME); - } else if ((c & COMPLETE) != 0) { - if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { - try { - if (s != null) { - s.onComplete(); - } - } catch (Throwable ignore) { - } - } - } else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) { - stat = false; - } - } - return stat; - } - - /** - * Responds to apparent zero demand in consume(). - */ - private boolean checkDemand(int c) { - boolean stat = true; - if (demand == 0L) { - if ((c & CONSUME) != 0) { - U.compareAndSwapInt(this, CTL, c, c & ~CONSUME); - } else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) { - stat = false; - } - } - return stat; - } - - /** - * Processes exception in Subscriber.onNext. - */ - private void handleOnNext(Flow.Subscriber<? super T> s, Throwable ex) { - BiConsumer<? super Flow.Subscriber<? super T>, ? super Throwable> h; - if ((h = onNextHandler) != null) { - try { - h.accept(s, ex); - } catch (Throwable ignore) { - } - } - onError(ex); - } - - // Unsafe mechanics - private static final sun.misc.Unsafe U = UnsafeAccessor.getUnsafe(); - private static final long CTL; - private static final long TAIL; - private static final long HEAD; - private static final long DEMAND; - private static final int ABASE; - private static final int ASHIFT; - - static { - try { - CTL = U.objectFieldOffset(BufferedSubscription.class.getDeclaredField("ctl")); - TAIL = U.objectFieldOffset(BufferedSubscription.class.getDeclaredField("tail")); - HEAD = U.objectFieldOffset(BufferedSubscription.class.getDeclaredField("head")); - DEMAND = U.objectFieldOffset(BufferedSubscription.class.getDeclaredField("demand")); - - ABASE = U.arrayBaseOffset(Object[].class); - int scale = U.arrayIndexScale(Object[].class); - if ((scale & (scale - 1)) != 0) { - throw new Error("data type scale not a power of two"); - } - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - - // Reduce the risk of rare disastrous classloading in first call to - // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 - Class<?> ensureLoaded = LockSupport.class; - } - } -}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java b/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java deleted file mode 100644 index 3729469..0000000 --- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java +++ /dev/null
@@ -1,53 +0,0 @@ -/* - * Copyright (c) 2019 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.internal.jsr166; - -import java.util.concurrent.Executor; -import java.util.concurrent.ForkJoinPool; -import java.util.function.BiConsumer; - -/** - * Factory creating JDK8 compatible SubmissionPublisher (Jdk8SubmissionPublisher) or JDK 9+ SubmissionPublisher - */ -public class SubmissionPublisherFactory { - - /** - * Creates a new SubmissionPublisher using the {@link - * ForkJoinPool#commonPool()} for async delivery to subscribers - * (unless it does not support a parallelism level of at least two, - * in which case, a new Thread is created to run each task), with - * maximum buffer capacity of {@link Flow#defaultBufferSize}, and no - * handler for Subscriber exceptions in method {@link - * Flow.Subscriber#onNext(Object) onNext}. - */ - public static <T> SubmittableFlowPublisher<T> createSubmissionPublisher() { - return new SubmissionPublisher<T>(); - } - - public static <T> SubmittableFlowPublisher<T> createSubmissionPublisher(Executor executor, - int maxBufferCapacity) { - return new SubmissionPublisher<T>(executor, maxBufferCapacity); - } - - public static <T> SubmittableFlowPublisher<T> createSubmissionPublisher(Executor executor, - int maxBufferCapacity, - BiConsumer<? super Flow.Subscriber<? super T>, - ? super Throwable> handler) { - return new SubmissionPublisher<T>(executor, maxBufferCapacity, handler); - } - -}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/UnsafeAccessor.java b/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/UnsafeAccessor.java deleted file mode 100644 index 19f9887..0000000 --- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/UnsafeAccessor.java +++ /dev/null
@@ -1,53 +0,0 @@ -/* - * Copyright (c) 2017, 2019 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.internal.jsr166; - -import sun.misc.Unsafe; - -import java.lang.reflect.Field; -import java.security.PrivilegedExceptionAction; - -/** - * Helper for classes in the jsr166 package to access the {@code sun.misc.Unsafe} instance. - * - * @author Adam Lindenthal - */ -class UnsafeAccessor { - static sun.misc.Unsafe getUnsafe() { - try { - return sun.misc.Unsafe.getUnsafe(); - } catch (SecurityException tryReflectionInstead) { - } - try { - return java.security.AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> { - Class<Unsafe> k = Unsafe.class; - for (Field f : k.getDeclaredFields()) { - f.setAccessible(true); - Object x = f.get(null); - if (k.isInstance(x)) { - return k.cast(x); - } - } - throw new NoSuchFieldError("the Unsafe"); - }); - } catch (java.security.PrivilegedActionException e) { - throw new RuntimeException("Could not initialize intrinsics", e.getCause()); - } - - - } -}
diff --git a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties index 285b9de..b60bf25 100644 --- a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties +++ b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties
@@ -1,5 +1,5 @@ # -# Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2018 Payara Foundation and/or its affiliates. # # This program and the accompanying materials are made available under the @@ -116,6 +116,7 @@ new.cookie.is.null=New cookie is null. no.container.available=No container available. no.error.processing.in.scope=There is no error processing in scope. +no.entitypart.builder.found="No EntityPart.Builder implementation found. Is jersey-media-multipart on a classpath?"; not.supported.on.outbound.message=Method not supported on an outbound message. osgi.registry.error.opening.resource.stream=Unable to open an input stream for resource {0}. osgi.registry.error.processing.resource.stream=Unexpected error occurred while processing resource stream {0}.
diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/TestRuntimeDelegate.java b/core-common/src/test/java/org/glassfish/jersey/internal/TestRuntimeDelegate.java index 821b62c..2bb7d12 100644 --- a/core-common/src/test/java/org/glassfish/jersey/internal/TestRuntimeDelegate.java +++ b/core-common/src/test/java/org/glassfish/jersey/internal/TestRuntimeDelegate.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 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,8 +16,10 @@ package org.glassfish.jersey.internal; +import jakarta.ws.rs.SeBootstrap; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.EntityPart; import jakarta.ws.rs.core.Link; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -29,6 +31,8 @@ import org.junit.Assert; +import java.util.concurrent.CompletionStage; + /** * Test runtime delegate. * @@ -46,6 +50,22 @@ throw new UnsupportedOperationException("Not supported yet."); } + @Override + public SeBootstrap.Configuration.Builder createConfigurationBuilder() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> aClass, + SeBootstrap.Configuration configuration) { + throw new UnsupportedOperationException("Not supported yet."); + } + public void testMediaType() { MediaType m = new MediaType("text", "plain"); Assert.assertNotNull(m);
diff --git a/core-common/src/test/java/org/glassfish/jersey/message/internal/NewCookieProviderTest.java b/core-common/src/test/java/org/glassfish/jersey/message/internal/NewCookieProviderTest.java new file mode 100644 index 0000000..5a4ba2f --- /dev/null +++ b/core-common/src/test/java/org/glassfish/jersey/message/internal/NewCookieProviderTest.java
@@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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 jakarta.ws.rs.core.NewCookie; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Date; + +public class NewCookieProviderTest { + + private final NewCookie newCookie = new NewCookie( + "test", + "value", + "/", + "localhost", + 1, + "comment", + 60, + new Date(), + true, + true, + NewCookie.SameSite.STRICT + ); + + @Test + public void SameSiteTest() { + final NewCookieProvider provider = new NewCookieProvider(); + final String newCookieString = provider.toString(newCookie); + Assert.assertTrue(newCookieString.contains("SameSite=STRICT")); + Assert.assertEquals(NewCookie.SameSite.STRICT, provider.fromString(newCookieString).getSameSite()); + } + +}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java index 42d11dd..7d75df1 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
@@ -222,7 +222,20 @@ * application handler. */ public ApplicationHandler(final Class<? extends Application> jaxrsApplicationClass) { - initialize(new ApplicationConfigurator(jaxrsApplicationClass), Injections.createInjectionManager(), null); + this(jaxrsApplicationClass, null); + } + + /** + * Create a new Jersey server-side application handler configured by a + * {@link Application JAX-RS Application (sub-)class}. + * + * @param applicationClass JAX-RS {@code Application} (sub-)class that will be + * instantiated and used to configure the new Jersey + * application handler. + * @param customBinder additional custom bindings used to configure the application's. + */ + public ApplicationHandler(final Class<? extends Application> applicationClass, final Binder customBinder) { + initialize(new ApplicationConfigurator(applicationClass), Injections.createInjectionManager(), customBinder); } /**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java index a91521d..c7a8615 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -301,6 +301,11 @@ } @Override + public boolean hasProperty(final String name) { + return propertiesDelegate.hasProperty(name); + } + + @Override public Object getProperty(final String name) { return propertiesDelegate.getProperty(name); } @@ -821,13 +826,13 @@ } /** - * Get the values of a HTTP request header. The returned List is read-only. - * This is a shortcut for {@code getRequestHeaders().get(name)}. + * Get the values of an HTTP request header if the header exists on the current request. The returned value will be + * a read-only List if the specified header exists or {@code null} if it does not. This is a shortcut for + * {@code getRequestHeaders().get(name)}. * * @param name the header name, case insensitive. - * @return a read-only list of header values. - * - * @throws IllegalStateException if called outside the scope of a request. + * @return a read-only list of header values if the specified header exists, otherwise {@code null}. + * @throws java.lang.IllegalStateException if called outside the scope of a request. */ @Override public List<String> getRequestHeader(final String name) {
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/DefaultExceptionMapper.java b/core-server/src/main/java/org/glassfish/jersey/server/DefaultExceptionMapper.java new file mode 100644 index 0000000..b120e69 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/DefaultExceptionMapper.java
@@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.server; + +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; + +class DefaultExceptionMapper implements ExceptionMapper<Throwable> { + @Override + public Response toResponse(Throwable exception) { + return (exception instanceof WebApplicationException) + ? processWebApplicationException((WebApplicationException) exception) + : processDefaultException(exception); + } + + private static Response processWebApplicationException(WebApplicationException exception) { + return (exception.getResponse() == null) + ? processDefaultException(exception) + : exception.getResponse(); + } + + private static Response processDefaultException(Throwable exception) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception.getMessage()).build(); + } +}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java b/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java new file mode 100644 index 0000000..3bfe5db --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java
@@ -0,0 +1,321 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.server; + +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.core.UriBuilder; +import org.glassfish.jersey.internal.config.ExternalPropertiesConfigurationFactory; +import org.glassfish.jersey.internal.config.SystemPropertiesConfigurationModel; +import org.glassfish.jersey.internal.util.PropertiesClass; +import org.glassfish.jersey.server.internal.LocalizationMessages; +import org.glassfish.jersey.server.spi.Container; +import org.glassfish.jersey.server.spi.WebServer; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URI; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.function.BiFunction; +import java.util.logging.Logger; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; + +/** + * Jersey implementation of {@link SeBootstrap.Configuration} implementing arbitrary methods for acquiring + * the configuration settings. + * @since 3.1.0 + */ +public final class JerseySeBootstrapConfiguration implements SeBootstrap.Configuration { + private static final Logger LOGGER = Logger.getLogger(JerseySeBootstrapConfiguration.class.getName()); + protected static final Random RANDOM = new Random(); + private final SeBootstrap.Configuration configuration; + + private JerseySeBootstrapConfiguration(SeBootstrap.Configuration configuration) { + this.configuration = configuration; + } + + @Override + public Object property(String name) { + return configuration.property(name); + } + + /** + * Compose {@link URI} based on properties defined in this configuration. + * @param resolveDefaultPort if {@code true} the port is not set, it is resolved as + * {@link Container#DEFAULT_HTTP_PORT} or {@link Container#DEFAULT_HTTPS_PORT} + * based on the protocol scheme. + * @return Composed {@link URI} based on properties defined in this configuration. + */ + public URI uri(boolean resolveDefaultPort) { + final String protocol = configuration.protocol(); + final String host = configuration.host(); + final int port = resolveDefaultPort ? resolvePort() : configuration.port(); + final String rootPath = configuration.rootPath(); + final URI uri = UriBuilder.newInstance().scheme(protocol.toLowerCase()).host(host).port(port).path(rootPath) + .build(); + return uri; + } + + private int resolvePort() { + final int configPort = configuration.port(); + final int basePort = allowPrivilegedPorts() ? 0 : 8000; + final int port; + switch (configPort) { + case SeBootstrap.Configuration.DEFAULT_PORT: + port = basePort + (isHttps() ? Container.DEFAULT_HTTPS_PORT : Container.DEFAULT_HTTP_PORT); + break; + case SeBootstrap.Configuration.FREE_PORT: + port = _resolvePort(basePort == 0); + break; + default: + port = configPort; + break; + } + return port; + } + + private int _resolvePort(boolean allowPrivilegedPort) { + final int basePort = allowPrivilegedPort ? 0 : 1023; + // Get the initial range parameters + final int lower = basePort; + final int range = 0xFFFF; + + // Select a start point in the range + final int initialOffset = RANDOM.nextInt(range - lower); + + // Loop the offset through all ports in the range and attempt + // to bind to each + int offset = initialOffset; + ServerSocket socket; + do { + final int port = lower + offset; + try { + socket = new ServerSocket(port); + socket.close(); + return port; + } catch (IOException caught) { + // Swallow exceptions until the end + } + offset = (offset + 1) % range; + } while (offset != initialOffset); + + // If a port can't be bound, throw the exception + throw new IllegalArgumentException(LocalizationMessages.COULD_NOT_BIND_TO_ANY_PORT()); + } + + /** + * Return {@link SSLContext} in the configuration if the protocol scheme is {@code HTTPS}. + * @return the SSLContext in the configuration. + */ + @Override + public SSLContext sslContext() { + final SSLContext sslContext = configuration.sslContext(); + return isHttps() ? sslContext : null; + } + + /** + * If the protocol schema is {@code HTTPS}, return {@code true}. + * @return {@code true} when the protocol schema is {@code HTTPS}. + */ + public boolean isHttps() { + return "HTTPS".equalsIgnoreCase(configuration.protocol()); + } + + /** + * Defines if the {@link WebServer} should automatically start. + * @return false if {@link ServerProperties#WEBSERVER_AUTO_START} is {@code false}, {@code true} otherwise. + */ + public boolean autoStart() { + final boolean autoStart = Optional.ofNullable( + (Boolean) configuration.property(ServerProperties.WEBSERVER_AUTO_START)) + .orElse(TRUE); + return autoStart; + } + + /** + * Defines if the {@link WebServer} should start on a privileged port when port is not set. + * @return true if {@link ServerProperties#WEBSERVER_AUTO_START} is {@code true}, {@code false} otherwise. + */ + public boolean allowPrivilegedPorts() { + return Optional.ofNullable( + (Boolean) configuration.property(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS)) + .orElse(FALSE); + } + + /** + * Factory method creating {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}. + * @param configuration wrapped configuration + * @return {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}. + */ + public static JerseySeBootstrapConfiguration from(SeBootstrap.Configuration configuration) { + return JerseySeBootstrapConfiguration.class.isInstance(configuration) + ? (JerseySeBootstrapConfiguration) configuration + : new JerseySeBootstrapConfiguration(configuration); + } + + /** + * Return a Jersey instance of {@link SeBootstrap.Configuration.Builder} with prefilled values. + * @return a Jersey instance of {@link SeBootstrap.Configuration.Builder}. + */ + public static Builder builder() { + return new Builder(); + } + + public static final class Builder implements SeBootstrap.Configuration.Builder { + private static final Map<String, Class<?>> PROPERTY_TYPES = new HashMap<>(); + + static { + PROPERTY_TYPES.put(SeBootstrap.Configuration.PROTOCOL, String.class); + PROPERTY_TYPES.put(SeBootstrap.Configuration.HOST, String.class); + PROPERTY_TYPES.put(SeBootstrap.Configuration.PORT, Integer.class); + PROPERTY_TYPES.put(SeBootstrap.Configuration.ROOT_PATH, String.class); + PROPERTY_TYPES.put(SeBootstrap.Configuration.SSL_CONTEXT, SSLContext.class); + PROPERTY_TYPES.put(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION, SSLClientAuthentication.class); + PROPERTY_TYPES.put(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, Boolean.class); + PROPERTY_TYPES.put(ServerProperties.WEBSERVER_AUTO_START, Boolean.class); + PROPERTY_TYPES.put(ServerProperties.WEBSERVER_CLASS, Class.class); + } + + private final Map<String, Object> properties = new HashMap<>(); + + private Builder() { + this.properties.put(SeBootstrap.Configuration.PROTOCOL, "HTTP"); // upper case mandated by javadoc + this.properties.put(SeBootstrap.Configuration.HOST, "localhost"); + this.properties.put(SeBootstrap.Configuration.PORT, -1); // Auto-select port 8080 for HTTP or 8443 for HTTPS + this.properties.put(SeBootstrap.Configuration.ROOT_PATH, "/"); + this.properties.put(ServerProperties.WEBSERVER_CLASS, WebServer.class); // Auto-select first provider + try { + this.properties.put(SeBootstrap.Configuration.SSL_CONTEXT, SSLContext.getDefault()); + } catch (final NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + this.properties.put(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION, + SeBootstrap.Configuration.SSLClientAuthentication.NONE); + this.properties.put(ServerProperties.WEBSERVER_AUTO_START, TRUE); + this.properties.put(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, FALSE); + + SystemPropertiesConfigurationModel propertiesConfigurationModel = new SystemPropertiesConfigurationModel( + Collections.singletonList(Properties.class.getName()) + ); + from((name, aClass) -> String.class.equals(aClass) || Integer.class.equals(aClass) || Boolean.class.equals(aClass) + ? propertiesConfigurationModel.getOptionalProperty(name, aClass) + : Optional.empty() + ); + } + + @Override + public JerseySeBootstrapConfiguration build() { + return JerseySeBootstrapConfiguration.from(this.properties::get); + } + + @Override + public Builder property(String name, Object value) { + this.properties.put(name, value); + return this; + } + + /** + * Set the the respective {@link WebServer} class to be used by the + * {@link org.glassfish.jersey.server.spi.WebServerProvider}. + * @param webServerClass the class implementing {@link WebServer}. + * @return the updated builder. + */ + public Builder webServerClass(Class<? extends WebServer> webServerClass) { + return property(ServerProperties.WEBSERVER_CLASS, webServerClass); + } + + /** + * Define if the {@link WebServer} should auto-start at bootstrap. + * @param autostart the auto-start flag. + * @return the updated builder. + */ + public Builder autoStart(Boolean autostart) { + return property(ServerProperties.WEBSERVER_AUTO_START, autostart); + } + + @Override + public <T> JerseySeBootstrapConfiguration.Builder from(BiFunction<String, Class<T>, Optional<T>> configProvider) { + PROPERTY_TYPES.forEach( + (propertyName, propertyType) -> configProvider.apply(propertyName, (Class<T>) propertyType) + .ifPresent(propertyValue -> this.properties.put(propertyName, propertyValue))); + return this; + } + + @Override + public JerseySeBootstrapConfiguration.Builder from(Object externalConfig) { + if (SeBootstrap.Configuration.class.isInstance(externalConfig)) { + final SeBootstrap.Configuration other = (SeBootstrap.Configuration) externalConfig; + from((name, clazz) -> { + final Object property = other.property(name); + if (property != null) { + if (clazz.equals(property.getClass())) { + return Optional.of(property); + } else { + LOGGER.warning(LocalizationMessages.IGNORE_SEBOOTSTRAP_CONFIGURATION_PROPERTY(name, clazz)); + } + } + return Optional.empty(); + }); + } + return this; + } + } + + /** + * Name the properties to be internally read from System properties by {@link ExternalPropertiesConfigurationFactory}. + * This is required just when SecurityManager is on, otherwise all system properties are read. + */ + @PropertiesClass + private static class Properties { + /** + * See {@link SeBootstrap.Configuration#PROTOCOL} property. + */ + public static final String SE_BOOTSTRAP_CONFIGURATION_PROTOCOL = SeBootstrap.Configuration.PROTOCOL; + + /** + * See {@link SeBootstrap.Configuration#HOST} property. + */ + public static final String SE_BOOTSTRAP_CONFIGURATION_HOST = SeBootstrap.Configuration.HOST; + + /** + * See {@link SeBootstrap.Configuration#PORT} property. + */ + public static final String SE_BOOTSTRAP_CONFIGURATION_PORT = SeBootstrap.Configuration.PORT; + + /** + * See {@link SeBootstrap.Configuration#ROOT_PATH} property. + */ + public static final String SE_BOOTSTRAP_CONFIGURATION_ROOT_PATH = SeBootstrap.Configuration.ROOT_PATH; + + /** + * See {@link ServerProperties#WEBSERVER_ALLOW_PRIVILEGED_PORTS} property. + */ + public static final String WEBSERVER_ALLOW_PRIVILEGED_PORTS = ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS; + + /** + * See {@link ServerProperties#WEBSERVER_AUTO_START} property. + */ + public static final String WEBSERVER_AUTO_START = ServerProperties.WEBSERVER_AUTO_START; + } +}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java index 291b76f..ae39a68 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -756,6 +756,11 @@ } @Override + public final boolean hasProperty(final String name) { + return state.hasProperty(name); + } + + @Override public final Object getProperty(final String name) { return state.getProperty(name); }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java index 03482f9..4cf65c8 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 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,6 +16,7 @@ package org.glassfish.jersey.server; +import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.MessageBodyWriter; import jakarta.ws.rs.ext.WriterInterceptor; @@ -44,5 +45,8 @@ // JSONP bind(JsonWithPaddingInterceptor.class).to(WriterInterceptor.class).in(Singleton.class); + + //Default exception mapper + bind(DefaultExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class); } }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java index 4c06c5c..099ce70 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -24,6 +24,8 @@ import org.glassfish.jersey.internal.util.PropertiesClass; import org.glassfish.jersey.internal.util.PropertiesHelper; import org.glassfish.jersey.internal.util.PropertyAlias; +import org.glassfish.jersey.server.spi.Container; +import org.glassfish.jersey.server.spi.WebServer; /** @@ -764,6 +766,50 @@ "jersey.config.server.empty.request.media.matches.any.consumes"; /** + * Defines whether to allow privileged ports (0-1023) to be used to start the {@link WebServer} implementation + * to be chosen from the unused ports when the {@link jakarta.ws.rs.SeBootstrap.Configuration#PORT} is set to {@code -1} + * or unset. + * <p> + * The default ports are {@link Container#DEFAULT_HTTP_PORT} for HTTP and {@link Container#DEFAULT_HTTPS_PORT} + * for HTTPS when {@code WEBSERVER_ALLOW_PRIVILEGED_PORTS} is {@code true} or 8080 for HTTP and 8443 for HTTPS when + * {@code WEBSERVER_ALLOW_PRIVILEGED_PORTS} is {@code false}. + * </p> + * <p> + * If {@link jakarta.ws.rs.SeBootstrap.Configuration#PORT} is set to {@code 0}, the implementation chooses random ports + * (0-65535) when {@code WEBSERVER_ALLOW_PRIVILEGED_PORTS} is {@code true}, or (1024-65535) when + * {@code WEBSERVER_ALLOW_PRIVILEGED_PORTS} is {@code false.} + * </p> + * <p> + * The default this is {@code false}. Use {@code true} to allow a restricted port number. The name of the configuration + * property is <tt>{@value}</tt>. + * </p> + * @since 3.1.0 + */ + public static final String WEBSERVER_ALLOW_PRIVILEGED_PORTS = + "jersey.config.server.bootstrap.webserver.allow.privileged.ports"; + + /** + * Whether to automatically startup {@link WebServer} at bootstrap. + * <p> + * By default, servers are immediately listening to connections after bootstrap, + * so no explicit invocation of {@link WebServer#start()} is needed. The name of the configuration + * property is <tt>{@value}</tt>. + * </p> + * @since 3.1.0 + */ + public static final String WEBSERVER_AUTO_START = "jersey.config.server.bootstrap.webserver.autostart"; + + /** + * Defines the implementation of {@link WebServer} to bootstrap. + * <p> + * By default auto-selects the first server provider found. The name of the configuration + * property is <tt>{@value}</tt>. + * </p> + * @since 3.1.0 + */ + public static final String WEBSERVER_CLASS = "jersey.config.server.bootstrap.webserver.class"; + + /** * JVM argument to define the value of * {@link org.glassfish.jersey.server.internal.monitoring.core.ReservoirConstants#COLLISION_BUFFER_POWER}. * Lower values reduce the memory footprint.
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 007cd0a..2e3a263 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
@@ -128,6 +128,11 @@ /** Resolve relative URIs according to RFC7231 (not JAX-RS 2.0 compliant */ private final boolean rfc7231LocationHeaderRelativeUriResolution; + /** + * Default exception mapper (@since 3.1.0 according to JAX-RS 3.1 spec) + */ + private static final ExceptionMapper<Throwable> DEFAULT_EXCEPTION_MAPPER = new DefaultExceptionMapper(); + static ServerRuntime createServerRuntime( InjectionManager injectionManager, ServerBootstrapBag bootstrapBag, @@ -378,6 +383,22 @@ return response; } + private ContainerResponse preProcessResponse(Response exceptionResponse, ContainerRequest request) { + final ContainerResponse response; + try { + response = convertResponse(exceptionResponse); + if (!runtime.disableLocationHeaderRelativeUriResolution) { + ensureAbsolute(response.getLocation(), response.getHeaders(), request, + runtime.rfc7231LocationHeaderRelativeUriResolution); + } + processingContext.monitoringEventBuilder().setContainerResponse(response) + .setResponseSuccessfullyMapped(true); + } finally { + processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPING_FINISHED); + } + return response; + } + /** * Process {@code throwable} by using exception mappers and generating the mapped * response if possible. @@ -401,21 +422,11 @@ processingContext.triggerEvent(RequestEvent.Type.ON_EXCEPTION); ContainerResponse response = null; + ContainerResponse defaultMapperResponse = null; try { final Response exceptionResponse = mapException(throwable); try { - try { - response = convertResponse(exceptionResponse); - if (!runtime.disableLocationHeaderRelativeUriResolution) { - ensureAbsolute(response.getLocation(), response.getHeaders(), request, - runtime.rfc7231LocationHeaderRelativeUriResolution); - } - processingContext.monitoringEventBuilder().setContainerResponse(response) - .setResponseSuccessfullyMapped(true); - } finally { - processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPING_FINISHED); - } - + response = preProcessResponse(exceptionResponse, request); processResponse(response); } catch (final Throwable respError) { LOGGER.log(Level.SEVERE, LocalizationMessages.ERROR_PROCESSING_RESPONSE_FROM_ALREADY_MAPPED_EXCEPTION()); @@ -432,16 +443,20 @@ if (!processResponseError(responseError)) { // Pass the exception to the container. - LOGGER.log(Level.FINE, LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER(), responseError); + LOGGER.log(Level.WARNING, LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER(), responseError); try { request.getResponseWriter().failure(responseError); } finally { completionCallbackRunner.onComplete(responseError); + + defaultMapperResponse = processResponseWithDefaultExceptionMapper(responseError, request); } + } + } finally { - release(response); + release(response, defaultMapperResponse); } } @@ -503,6 +518,7 @@ do { final Throwable throwable = wrap.getCurrent(); + if (wrap.isInMappable() || throwable instanceof WebApplicationException) { // in case ServerProperties.PROCESSING_RESPONSE_ERRORS_ENABLED is true, allow // wrapped MessageBodyProviderNotFoundException to propagate @@ -519,56 +535,22 @@ processingContext.routingContext().setMappedThrowable(throwable); waeResponse = webApplicationException.getResponse(); - if (waeResponse.hasEntity()) { - LOGGER.log(Level.FINE, LocalizationMessages - .EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()), throwable); + if (waeResponse != null && waeResponse.hasEntity()) { + LOGGER.log(Level.FINE, + LocalizationMessages.EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()), + throwable); return waeResponse; } } final long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING); final ExceptionMapper mapper = runtime.exceptionMappers.findMapping(throwable); - if (mapper != null) { - processingContext.monitoringEventBuilder().setExceptionMapper(mapper); - processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPER_FOUND); - try { - final Response mappedResponse = mapper.toResponse(throwable); - - if (tracingLogger.isLogEnabled(ServerTraceEvent.EXCEPTION_MAPPING)) { - tracingLogger.logDuration(ServerTraceEvent.EXCEPTION_MAPPING, - timestamp, mapper, throwable, throwable.getLocalizedMessage(), - mappedResponse != null ? mappedResponse.getStatusInfo() : "-no-response-"); - } - - // set mapped throwable - processingContext.routingContext().setMappedThrowable(throwable); - - if (mappedResponse != null) { - // response successfully mapped - if (LOGGER.isLoggable(Level.FINER)) { - final String message = String.format( - "Exception '%s' has been mapped by '%s' to response '%s' (%s:%s).", - throwable.getLocalizedMessage(), - mapper.getClass().getName(), - mappedResponse.getStatusInfo().getReasonPhrase(), - mappedResponse.getStatusInfo().getStatusCode(), - mappedResponse.getStatusInfo().getFamily()); - LOGGER.log(Level.FINER, message); - } - return mappedResponse; - } else { - return Response.noContent().build(); - } - } catch (final Throwable mapperThrowable) { - // spec: If the exception mapping provider throws an exception while creating a Response - // then return a server error (status code 500) response to the client. - LOGGER.log(Level.SEVERE, LocalizationMessages.EXCEPTION_MAPPER_THROWS_EXCEPTION(mapper.getClass()), - mapperThrowable); - LOGGER.log(Level.SEVERE, LocalizationMessages.EXCEPTION_MAPPER_FAILED_FOR_EXCEPTION(), throwable); - return Response.serverError().build(); - } + if (mapper != null + && !DefaultExceptionMapper.class.getName() + .equals(mapper.getClass().getName()) + ) { + return processExceptionWithMapper(mapper, throwable, timestamp); } - if (waeResponse != null) { LOGGER.log(Level.FINE, LocalizationMessages .EXCEPTION_MAPPING_WAE_NO_ENTITY(waeResponse.getStatus()), throwable); @@ -582,7 +564,6 @@ return Response.status(Response.Status.BAD_REQUEST).build(); } } - if (!wrap.isInMappable() || !wrap.isWrapped()) { // user failures (thrown from Resource methods or provider methods) @@ -598,6 +579,55 @@ throw originalThrowable; } + private Response processExceptionWithMapper(ExceptionMapper mapper, + Throwable throwable, long timestamp) { + processingContext.monitoringEventBuilder().setExceptionMapper(mapper); + processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPER_FOUND); + try { + final Response mappedResponse = mapper.toResponse(throwable); + + if (isTracingLoggingEnabled(mapper, throwable, tracingLogger)) { + tracingLogger.logDuration(ServerTraceEvent.EXCEPTION_MAPPING, + timestamp, mapper, throwable, throwable.getLocalizedMessage(), + mappedResponse != null ? mappedResponse.getStatusInfo() : "-no-response-"); + } + + // set mapped throwable + processingContext.routingContext().setMappedThrowable(throwable); + + if (mappedResponse != null) { + // response successfully mapped + if (LOGGER.isLoggable(Level.FINER)) { + final String message = String.format( + "Exception '%s' has been mapped by '%s' to response '%s' (%s:%s).", + throwable.getLocalizedMessage(), + mapper.getClass().getName(), + mappedResponse.getStatusInfo().getReasonPhrase(), + mappedResponse.getStatusInfo().getStatusCode(), + mappedResponse.getStatusInfo().getFamily()); + LOGGER.log(Level.FINER, message); + } + return mappedResponse; + } else { + return Response.noContent().build(); + } + } catch (final Throwable mapperThrowable) { + // spec: If the exception mapping provider throws an exception while creating a Response + // then return a server error (status code 500) response to the client. + LOGGER.log(Level.SEVERE, LocalizationMessages.EXCEPTION_MAPPER_THROWS_EXCEPTION(mapper.getClass()), + mapperThrowable); + LOGGER.log(Level.SEVERE, LocalizationMessages.EXCEPTION_MAPPER_FAILED_FOR_EXCEPTION(), throwable); + return Response.serverError().build(); + } + } + + private ContainerResponse processResponseWithDefaultExceptionMapper(Throwable exception, + ContainerRequest request) { + long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING); + final Response response = processExceptionWithMapper(DEFAULT_EXCEPTION_MAPPER, exception, timestamp); + return processResponse(preProcessResponse(response, request)); + } + private ContainerResponse writeResponse(final ContainerResponse response) { final ContainerRequest request = processingContext.request(); final ContainerResponseWriter writer = request.getResponseWriter(); @@ -729,16 +759,14 @@ .setResponseWritten(true); } - private void release(final ContainerResponse responseContext) { + private void release(final ContainerResponse... responseContexts) { try { processingContext.closeableService().close(); // Commit the container response writer if not in chunked mode // responseContext may be null in case the request processing was cancelled. - if (responseContext != null && !responseContext.isChunked()) { - // responseContext.commitStream(); - responseContext.close(); - } + Arrays.stream(responseContexts).filter(responseContext -> responseContext != null + && !responseContext.isChunked()).forEach(ContainerResponse::close); } catch (final Throwable throwable) { LOGGER.log(Level.WARNING, LocalizationMessages.RELEASING_REQUEST_PROCESSING_RESOURCES_FAILED(), throwable); @@ -747,6 +775,14 @@ processingContext.triggerEvent(RequestEvent.Type.FINISHED); } } + + private static boolean isTracingLoggingEnabled(ExceptionMapper mapper, Throwable throwable, TracingLogger tracingLogger) { + boolean defaultLoggingState = mapper instanceof DefaultExceptionMapper + && throwable instanceof WebApplicationException; + return !defaultLoggingState + && tracingLogger.isLogEnabled(ServerTraceEvent.EXCEPTION_MAPPING); + + } } private static class AsyncResponder implements AsyncContext, ContainerResponseWriter.TimeoutHandler, CompletionCallback {
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/WebServerFactory.java b/core-server/src/main/java/org/glassfish/jersey/server/WebServerFactory.java new file mode 100644 index 0000000..6454fc1 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/WebServerFactory.java
@@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.server; + +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.internal.ServiceFinder; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; + +/** + * Factory for creating specific HTTP servers. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class WebServerFactory { + + /** + * Prevents instantiation. + */ + private WebServerFactory() { + } + + /** + * Creates a server of a given type which runs the given application using the + * given bootstrap configuration. + * <p> + * The list of service-providers supporting the {@link WebServerProvider} + * service-provider will be iterated over until one returns a non-null server + * instance. + * <p> + * + * @param <T> + * the type of the server. + * @param type + * the type of the server. Providers SHOULD support at least + * {@link WebServer}. + * @param application + * The application to host. + * @param configuration + * The configuration (host, port, etc.) to be used for bootstrapping. + * @return the created server. + * @throws ProcessingException + * if there is an error creating the server. + * @throws IllegalArgumentException + * if no server provider supports the type. + */ + public static <T extends WebServer> T createServer(final Class<T> type, final Application application, + final JerseySeBootstrapConfiguration configuration) { + for (final WebServerProvider webServerProvider : ServiceFinder.find(WebServerProvider.class)) { + final T server = webServerProvider.createServer(type, application, configuration); + if (server != null) { + return server; + } + } + + throw new IllegalArgumentException("No server provider supports the type " + type); + } + + /** + * Creates a server of a given type which runs the given application using the + * given bootstrap configuration. + * <p> + * The list of service-providers supporting the {@link WebServerProvider} + * service-provider will be iterated over until one returns a non-null server + * instance. + * <p> + * + * @param <T> + * the type of the server. + * @param type + * the type of the server. Providers SHOULD support at least + * {@link WebServer}. + * @param application + * The application to host. + * @param configuration + * The configuration (host, port, etc.) to be used for bootstrapping. + * @return the created server. + * @throws ProcessingException + * if there is an error creating the server. + * @throws IllegalArgumentException + * if no server provider supports the type. + */ + public static <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> application, + final JerseySeBootstrapConfiguration configuration) { + for (final WebServerProvider webServerProvider : ServiceFinder.find(WebServerProvider.class)) { + final T server = webServerProvider.createServer(type, application, configuration); + if (server != null) { + return server; + } + } + + throw new IllegalArgumentException("No server provider supports the type " + type); + } + +}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java index ab70a02..1af9a40 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021 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,16 +16,26 @@ package org.glassfish.jersey.server.internal; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import jakarta.ws.rs.SeBootstrap; import jakarta.ws.rs.core.Application; import org.glassfish.jersey.internal.AbstractRuntimeDelegate; import org.glassfish.jersey.message.internal.MessagingBinders; import org.glassfish.jersey.server.ContainerFactory; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.WebServerFactory; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.spi.WebServer; /** * Server-side implementation of JAX-RS {@link jakarta.ws.rs.ext.RuntimeDelegate}. - * This overrides the default implementation of {@link jakarta.ws.rs.ext.RuntimeDelegate} from - * jersey-common which does not implement {@link #createEndpoint(jakarta.ws.rs.core.Application, java.lang.Class)} + * This overrides the default implementation of + * {@link jakarta.ws.rs.ext.RuntimeDelegate} from jersey-common which does not + * implement + * {@link #createEndpoint(jakarta.ws.rs.core.Application, java.lang.Class)} * method. * * @author Jakub Podlesak @@ -39,11 +49,88 @@ } @Override - public <T> T createEndpoint(Application application, Class<T> endpointType) + public <T> T createEndpoint(final Application application, final Class<T> endpointType) throws IllegalArgumentException, UnsupportedOperationException { if (application == null) { throw new IllegalArgumentException("application is null."); } return ContainerFactory.createContainer(endpointType, application); } + + @Override + public JerseySeBootstrapConfiguration.Builder createConfigurationBuilder() { + return JerseySeBootstrapConfiguration.builder(); + } + + @SuppressWarnings("unchecked") + @Override + public CompletableFuture<SeBootstrap.Instance> bootstrap(final Application application, + final SeBootstrap.Configuration configuration) { + + return CompletableFuture.supplyAsync(() -> { + final Class<WebServer> httpServerClass = configuration.hasProperty(ServerProperties.WEBSERVER_CLASS) + ? (Class<WebServer>) configuration.property(ServerProperties.WEBSERVER_CLASS) + : WebServer.class; + + final JerseySeBootstrapConfiguration jerseySeConfiguration + = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer + = WebServerFactory.createServer(httpServerClass, application, jerseySeConfiguration); + return instance(configuration, webServer); + }); + } + + @SuppressWarnings("unchecked") + public CompletableFuture<SeBootstrap.Instance> bootstrap(final Class<? extends Application> applicationClass, + final SeBootstrap.Configuration configuration) { + + return CompletableFuture.supplyAsync(() -> { + final Class<WebServer> httpServerClass = configuration.hasProperty(ServerProperties.WEBSERVER_CLASS) + ? (Class<WebServer>) configuration.property(ServerProperties.WEBSERVER_CLASS) + : WebServer.class; + + final JerseySeBootstrapConfiguration jerseySeConfiguration + = JerseySeBootstrapConfiguration.from(configuration); + final WebServer webServer + = WebServerFactory.createServer(httpServerClass, applicationClass, jerseySeConfiguration); + return instance(configuration, webServer); + }); + } + + private SeBootstrap.Instance instance(final SeBootstrap.Configuration configuration, + final WebServer _webServer) { + return new SeBootstrap.Instance() { + final WebServer webServer = _webServer; + @Override + public final JerseySeBootstrapConfiguration configuration() { + return JerseySeBootstrapConfiguration.from(name -> { + switch (name) { + case SeBootstrap.Configuration.PORT: + return webServer.port(); + case ServerProperties.WEBSERVER_CLASS: + return webServer.getClass(); + default: + return configuration.property(name); + } + }); + } + + @Override + public final CompletionStage<StopResult> stop() { + return this.webServer.stop().thenApply(nativeResult -> new StopResult() { + + @Override + public final <T> T unwrap(final Class<T> nativeClass) { + return nativeClass.cast(nativeResult); + } + }); + } + + @Override + public final <T> T unwrap(final Class<T> nativeClass) { + return nativeClass.isInstance(this.webServer) ? nativeClass.cast(this.webServer) + : this.webServer.unwrap(nativeClass); + } + }; + } }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueParamProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueParamProvider.java index f1fcbeb..b6e428d 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueParamProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueParamProvider.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 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,19 +16,26 @@ package org.glassfish.jersey.server.internal.inject; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.net.URLDecoder; import java.net.URLEncoder; +import java.security.AccessController; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Function; import jakarta.ws.rs.Encoded; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.core.EntityPart; import jakarta.ws.rs.core.Form; +import jakarta.ws.rs.core.GenericType; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MultivaluedMap; @@ -37,7 +44,13 @@ import jakarta.inject.Singleton; import org.glassfish.jersey.internal.inject.ExtractorException; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Providers; +import org.glassfish.jersey.internal.util.ReflectionHelper; +import org.glassfish.jersey.internal.util.collection.LazyValue; import org.glassfish.jersey.internal.util.collection.NullableMultivaluedHashMap; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.message.internal.MediaTypes; import org.glassfish.jersey.message.internal.ReaderWriter; import org.glassfish.jersey.server.ContainerRequest; @@ -45,6 +58,7 @@ import org.glassfish.jersey.server.internal.InternalServerProperties; import org.glassfish.jersey.server.internal.LocalizationMessages; import org.glassfish.jersey.server.model.Parameter; +import org.glassfish.jersey.server.spi.internal.ValueParamProvider; /** * Value factory provider supporting the {@link FormParam} injection annotation. @@ -55,13 +69,17 @@ @Singleton final class FormParamValueParamProvider extends AbstractValueParamProvider { + private final MultipartFormParamValueProvider multipartProvider; /** * Injection constructor. * * @param mpep extractor provider. + * @param injectionManager */ - public FormParamValueParamProvider(Provider<MultivaluedParameterExtractorProvider> mpep) { + public FormParamValueParamProvider(Provider<MultivaluedParameterExtractorProvider> mpep, + InjectionManager injectionManager) { super(mpep, Parameter.Source.FORM); + this.multipartProvider = new MultipartFormParamValueProvider(injectionManager); } @Override @@ -77,18 +95,24 @@ if (e == null) { return null; } - return new FormParamValueProvider(e, !parameter.isEncoded()); + return new FormParamValueProvider(e, multipartProvider, !parameter.isEncoded(), parameter); } private static final class FormParamValueProvider implements Function<ContainerRequest, Object> { private static final Annotation encodedAnnotation = getEncodedAnnotation(); private final MultivaluedParameterExtractor<?> extractor; + private final MultipartFormParamValueProvider multipartProvider; private final boolean decode; + private final Parameter parameter; - FormParamValueProvider(MultivaluedParameterExtractor<?> extractor, boolean decode) { + FormParamValueProvider(MultivaluedParameterExtractor<?> extractor, + MultipartFormParamValueProvider multipartProvider, + boolean decode, Parameter parameter) { this.extractor = extractor; + this.multipartProvider = multipartProvider; this.decode = decode; + this.parameter = parameter; } private static Form getCachedForm(final ContainerRequest request, boolean decode) { @@ -121,24 +145,27 @@ @Override public Object apply(ContainerRequest request) { - Form form = getCachedForm(request, decode); + if (MediaTypes.typeEqual(MediaType.MULTIPART_FORM_DATA_TYPE, request.getMediaType())) { + return multipartProvider.apply(request, parameter); + } else { + Form form = getCachedForm(request, decode); - if (form == null) { - Form otherForm = getCachedForm(request, !decode); - if (otherForm != null) { - form = switchUrlEncoding(request, otherForm); - cacheForm(request, form); - } else { - form = getForm(request); + if (form == null) { + Form otherForm = getCachedForm(request, !decode); + if (otherForm != null) { + form = switchUrlEncoding(request, otherForm); + } else { + form = getForm(request); + } cacheForm(request, form); } - } - try { - return extractor.extract(form.asMap()); - } catch (ExtractorException e) { - throw new ParamException.FormParamException(e.getCause(), - extractor.getName(), extractor.getDefaultValueString()); + try { + return extractor.extract(form.asMap()); + } catch (ExtractorException e) { + throw new ParamException.FormParamException(e.getCause(), + extractor.getName(), extractor.getDefaultValueString()); + } } } @@ -199,4 +226,89 @@ } } } + + @Singleton + private static class MultipartFormParamValueProvider implements BiFunction<ContainerRequest, Parameter, Object> { + private static final class FormParamHolder { + @FormParam("name") + public static final Void dummy = null; // field to get an instance of FormParam annotation + } + private static Parameter entityPartParameter = + Parameter.create( + EntityPart.class, EntityPart.class, false, EntityPart.class, EntityPart.class, + AccessController.doPrivileged(ReflectionHelper.getDeclaredFieldsPA(FormParamHolder.class))[0] + .getAnnotations() + ); + + private final InjectionManager injectionManager; + private final LazyValue<ValueParamProvider> entityPartProvider; + + private MultipartFormParamValueProvider(InjectionManager injectionManager) { + this.injectionManager = injectionManager; + + //Get the provider from jersey-media-multipart + entityPartProvider = Values.lazy((Value<ValueParamProvider>) () -> { + Set<ValueParamProvider> providers = Providers.getProviders(injectionManager, ValueParamProvider.class); + for (ValueParamProvider vfp : providers) { + Function<ContainerRequest, ?> paramValueSupplier = vfp.getValueProvider(entityPartParameter); + if (paramValueSupplier != null && !FormParamValueParamProvider.class.isInstance(vfp)) { + return vfp; + } + } + return null; + }); + } + + + @Override + public Object apply(ContainerRequest containerRequest, Parameter parameter) { + Object entity = null; + if (entityPartProvider.get() != null) { // else jersey-multipart module is missing + final Function<ContainerRequest, ?> valueSupplier = entityPartProvider.get().getValueProvider( + new WrappingFormParamParameter(entityPartParameter, parameter)); + final EntityPart entityPart = (EntityPart) valueSupplier.apply(containerRequest); + try { + entity = parameter.getType() != parameter.getRawType() + ? entityPart.getContent(genericType(parameter.getRawType(), parameter.getType())) + : entityPart.getContent(parameter.getRawType()); + } catch (IOException e) { + throw new ProcessingException(e); + } + } + + return entity; + } + + private GenericType genericType(Type rawType, Type genericType) { + return new GenericType(new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return new Type[]{genericType}; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return null; + } + }); + } + + private static class WrappingFormParamParameter extends Parameter { + protected WrappingFormParamParameter(Parameter entityPartDataParam, Parameter realDataParam) { + super(realDataParam.getAnnotations(), + realDataParam.getSourceAnnotation(), + realDataParam.getSource(), + realDataParam.getSourceName(), + entityPartDataParam.getRawType(), + entityPartDataParam.getType(), + realDataParam.isEncoded(), + realDataParam.getDefaultValue()); + } + } + } }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueParamProviderConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueParamProviderConfigurator.java index b9ddc55..07337eb 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueParamProviderConfigurator.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueParamProviderConfigurator.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021 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 @@ -87,7 +87,7 @@ EntityParamValueParamProvider entityProvider = new EntityParamValueParamProvider(paramExtractor); suppliers.add(entityProvider); - FormParamValueParamProvider formProvider = new FormParamValueParamProvider(paramExtractor); + FormParamValueParamProvider formProvider = new FormParamValueParamProvider(paramExtractor, injectionManager); suppliers.add(formProvider); HeaderParamValueParamProvider headerProvider = new HeaderParamValueParamProvider(paramExtractor);
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServer.java b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServer.java new file mode 100644 index 0000000..731e4eb --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServer.java
@@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.server.spi; + +import java.util.concurrent.CompletionStage; + +import jakarta.ws.rs.ConstrainedTo; +import jakarta.ws.rs.RuntimeType; +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.spi.Contract; + +/** + * Jersey service contract for self-contained servers. + * <p> + * Runs a self-contained {@link Application} in a {@link Container} using a + * Web Server implicitly started and stopped together with the application. + * </p> + * <p> + * The WebServer instance is wrapped by the implementation of {@link jakarta.ws.rs.SeBootstrap.Instance}. + * </p> + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +@Contract +@ConstrainedTo(RuntimeType.SERVER) +public interface WebServer { + + /** + * @return container in which the application lives. + */ + public Container container(); + + /** + * @return IP port the application listens to for requests. + */ + public int port(); + + /** + * Initiates server bootstrap. + * <p> + * Startup happens in background. The completion stage produces a native startup + * result. + * </p> + * <p> + * Portable applications should not expect any particular result type, as it is + * implementation-specific. + * </p> + * + * @return A {@link CompletionStage} providing a native startup result of the + * bootstrap process. The native result MAY be {@code null}. + */ + public CompletionStage<?> start(); + + /** + * Initiates server shutdown. + * <p> + * Shutdown happens in background. The completion stage produces a native + * shutdown result. + * </p> + * </p> + * Portable applications should not expect any particular result type, as it is + * implementation-specific. + * </p> + * + * @return A {@link CompletionStage} providing a native shutdown result of the + * shutdown process. The native result MAY be {@code null}. + */ + public CompletionStage<?> stop(); + + /** + * Provides access to the native handle(s) of the server, if it holds at least + * one. + * <p> + * Implementations MAY use native handles to identify the server instance, and / + * or use those to communicate with and control the instance. Whether or not + * such handles exist, and their respective data types, is + * implementation-specific. + * </p> + * <p> + * Portable applications should not invoke this method, as the types of + * supported handles are implementation-specific. + * </p> + * + * @param nativeClass + * The class of the native handle. + * @return The native handle, or {@code null} if no handle of this type exists. + */ + public <T> T unwrap(Class<T> nativeClass); + +}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServerProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServerProvider.java new file mode 100644 index 0000000..63453ee --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServerProvider.java
@@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.server.spi; + +import jakarta.ws.rs.ConstrainedTo; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.RuntimeType; +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.server.ApplicationHandler; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.spi.Contract; + +/** + * Service-provider interface for creating server instances. + * + * If supported by the provider, a server instance of the requested Java type + * will be created. + * <p> + * The created server uses an internally created {@link Container} which is + * responsible for listening on a communication channel provided by the server + * for new client requests, dispatching these requests to the registered + * {@link ApplicationHandler Jersey application handler} using the handler's + * {@link ApplicationHandler#handle(org.glassfish.jersey.server.ContainerRequest) + * handle(requestContext)} method and sending the responses provided by the + * application back to the client. + * </p> + * <p> + * A provider shall support a one-to-one mapping between a type, provided the + * type is not {@link Object}. A provider may also support mapping of sub-types + * of a type (provided the type is not {@code Object}). It is expected that each + * provider supports mapping for distinct set of types and subtypes so that + * different providers do not conflict with each other. In addition, a provider + * SHOULD support the super type {@link WebServer} to participate in auto-selection + * of providers (in this case the <em>first</em> supporting provider found is + * used). + * </p> + * <p> + * An implementation can identify itself by placing a Java service provider + * configuration file (if not already present) - + * {@code org.glassfish.jersey.server.spi.WebServerProvider} - in the resource + * directory {@code META-INF/services}, and adding the fully qualified + * service-provider-class of the implementation in the file. + * </p> + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +@Contract +@ConstrainedTo(RuntimeType.SERVER) +public interface WebServerProvider { + + /** + * Creates a server of a given type which runs the given application using the + * given bootstrap configuration. + * + * @param <T> + * the type of the web server. + * @param type + * the type of the web server. Providers SHOULD support at least + * {@link WebServer}. + * @param application + * The application to host. + * @param configuration + * The configuration (host, port, etc.) to be used for bootstrapping. + * @return the server, otherwise {@code null} if the provider does not support + * the requested {@code type}. + * @throws ProcessingException + * if there is an error creating the server. + */ + <T extends WebServer> T createServer(Class<T> type, + Application application, + JerseySeBootstrapConfiguration configuration) throws ProcessingException; + + /** + * Creates a server of a given type which runs the given application using the + * given bootstrap configuration. + * + * @param <T> + * the type of the web server. + * @param type + * the type of the web server. Providers SHOULD support at least + * {@link WebServer}. + * @param applicationClass + * The class of application to host. + * @param configuration + * The configuration (host, port, etc.) to be used for bootstrapping. + * @return the server, otherwise {@code null} if the provider does not support + * the requested {@code type}. + * @throws ProcessingException + * if there is an error creating the server. + */ + <T extends WebServer> T createServer(Class<T> type, + Class<? extends Application> applicationClass, + JerseySeBootstrapConfiguration configuration) throws ProcessingException; +}
diff --git a/core-server/src/main/resources/org/glassfish/jersey/server/internal/localization.properties b/core-server/src/main/resources/org/glassfish/jersey/server/internal/localization.properties index 3e0ab6c..7a482f2 100644 --- a/core-server/src/main/resources/org/glassfish/jersey/server/internal/localization.properties +++ b/core-server/src/main/resources/org/glassfish/jersey/server/internal/localization.properties
@@ -33,6 +33,7 @@ closeable.unable.to.close=Error while closing {0}. collection.extractor.type.unsupported=Unsupported collection type. contract.cannot.be.bound.to.resource.method=The given contract ({0}) of {1} provider cannot be bound to a resource method. +could.not.bind.to.any.port=Could not bind to any port. default.could.not.process.method=Default value, {0} could not be processed by method {1}. error.async.callback.failed=Callback {0} invocation failed. error.committing.output.stream=Error while committing the output stream. @@ -40,7 +41,7 @@ error.closing.finder=Error while closing {0} resource finder. error.exception.mapping.original.exception=An exception mapping did not successfully produce and processed a response. Logging the original error. error.exception.mapping.processed.response.error=A response error mapping did not successfully produce and processed a response. -error.exception.mapping.thrown.to.container=An exception mapping did not successfully produce and processed a response. Logging the exception propagated to the container. +error.exception.mapping.thrown.to.container=An exception mapping did not successfully produce and processed a response. Logging the exception propagated to the default exception mapper. error.marshalling.jaxb=Error marshalling JAXB object of type "{0}". error.msg=ERROR: {0} error.monitoring.statistics.generation=Error generating monitoring statistics. @@ -112,6 +113,7 @@ event.sink.returns.type=A HTTP GET method {0} that is being injected with SseEventSink should return void. The output will propagate automatically. multiple.event.sink.injection=A HTTP GET method {0} defines to SseEventSink parameters to be injected. Only one of the injected event sinks will be connected to the output. chunked.output.closed=This chunked output has been closed. +ignore.sebootstrap.configuration.property="SeBootstrap.Configuration property {0} is not of expected type {1} and it is ignored. illegal.client.config.class.property.value="{0}" property value ({1}) does not represent a valid client configuration class. Falling back to "{2}". init.msg=Initiating Jersey application, version {0}... injected.webtarget.uri.invalid="@Uri" annotation value is not a valid URI template: "{0}"
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/ApplicationHandlerTest.java b/core-server/src/test/java/org/glassfish/jersey/server/ApplicationHandlerTest.java index c14dbe3..225073b 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/ApplicationHandlerTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/ApplicationHandlerTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/AsyncCallbackServerTest.java b/core-server/src/test/java/org/glassfish/jersey/server/AsyncCallbackServerTest.java index ad7b02b..b707387 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/AsyncCallbackServerTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/AsyncCallbackServerTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java b/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java index 220a72f..13a7f1f 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/RuntimeDelegateImplTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/RuntimeDelegateImplTest.java index 5a49cbb..3b42c6d 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/internal/RuntimeDelegateImplTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/RuntimeDelegateImplTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,11 +16,19 @@ package org.glassfish.jersey.server.internal; +import jakarta.ws.rs.SeBootstrap; import jakarta.ws.rs.core.Application; import jakarta.ws.rs.ext.RuntimeDelegate; import org.junit.Test; + +import java.security.NoSuchAlgorithmException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** @@ -43,8 +51,64 @@ } } + /** + * Checks that the right RuntimeDelegateImpl is loaded by JAX-RS. + */ @Test public void testRuntimeDelegateInstance() { assertSame(RuntimeDelegateImpl.class, RuntimeDelegate.getInstance().getClass()); } + + @Test + public final void shouldCreateConfigurationBuilder() { + // given + final RuntimeDelegate runtimeDelegate = new RuntimeDelegateImpl(); + // when + final SeBootstrap.Configuration.Builder configurationBuilder = runtimeDelegate.createConfigurationBuilder(); + // then + assertThat(configurationBuilder, is(notNullValue())); + } + + @Test + public final void shouldBuildDefaultConfiguration() throws NoSuchAlgorithmException { + // given + final SeBootstrap.Configuration.Builder configurationBuilder = new RuntimeDelegateImpl().createConfigurationBuilder(); + // when + final SeBootstrap.Configuration configuration = configurationBuilder.build(); + + // then + assertThat(configuration, is(notNullValue())); + assertTrue(configuration.hasProperty(SeBootstrap.Configuration.PROTOCOL)); + assertTrue(configuration.hasProperty(SeBootstrap.Configuration.HOST)); + assertTrue(configuration.hasProperty(SeBootstrap.Configuration.PORT)); + assertTrue(configuration.hasProperty(SeBootstrap.Configuration.ROOT_PATH)); + assertTrue(configuration.hasProperty(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION)); + assertTrue(configuration.hasProperty(SeBootstrap.Configuration.SSL_CONTEXT)); + assertThat(configuration.property(SeBootstrap.Configuration.PROTOCOL), is("HTTP")); + assertThat(configuration.property(SeBootstrap.Configuration.HOST), is("localhost")); + assertThat(configuration.property(SeBootstrap.Configuration.PORT), is(SeBootstrap.Configuration.DEFAULT_PORT)); + assertThat(configuration.property(SeBootstrap.Configuration.ROOT_PATH), is("/")); + assertThat(configuration.property(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION), + is(SeBootstrap.Configuration.SSLClientAuthentication.NONE)); +// assertThat(configuration.property(SeBootstrap.Configuration.SSL_CONTEXT), is(theInstance(SSLContext.getDefault()))); + assertThat(configuration.protocol(), is("HTTP")); + assertThat(configuration.host(), is("localhost")); + assertThat(configuration.port(), is(SeBootstrap.Configuration.DEFAULT_PORT)); + assertThat(configuration.rootPath(), is("/")); + assertThat(configuration.sslClientAuthentication(), is(SeBootstrap.Configuration.SSLClientAuthentication.NONE)); +// assertThat(configuration.sslContext(), is(theInstance(SSLContext.getDefault()))); + } + + @Test + public final void shouldBuildConfigurationContainingCustomProperties() { + // given + final SeBootstrap.Configuration.Builder configurationBuilder = new RuntimeDelegateImpl().createConfigurationBuilder(); + // when + final SeBootstrap.Configuration configuration = configurationBuilder.property("property", "value").build(); + + // then + assertThat(configuration, is(notNullValue())); + assertTrue(configuration.hasProperty("property")); + assertThat(configuration.property("property"), is("value")); + } }
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java index 5cd8568..8ec946a 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
@@ -17,6 +17,8 @@ package org.glassfish.jersey.server.internal.inject; +import java.io.IOException; +import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -48,6 +50,7 @@ import org.glassfish.jersey.server.RequestContextBuilder; import org.glassfish.jersey.server.ResourceConfig; +import org.junit.Assert; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; @@ -300,6 +303,24 @@ } } + @Path("/") + public static class InpuStreamConverterTestResource { + @GET + public String inputStream(@QueryParam("param") InputStream inputStream) throws IOException { + return new String(inputStream.readAllBytes()); + } + } + + @Test + public void inputStreamTest() throws ExecutionException, InterruptedException { + initiateWebApplication(InpuStreamConverterTestResource.class); + + final ContainerResponse responseContext = getResponseContext(UriBuilder.fromPath("/") + .queryParam("param", "Hello").build().toString()); + + Assert.assertEquals("Hello", responseContext.getEntity()); + } + public static class MyEagerParamProvider implements ParamConverterProvider { @Override
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/model/MethodListTest.java b/core-server/src/test/java/org/glassfish/jersey/server/model/MethodListTest.java index 5934f4a..275019e 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/model/MethodListTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/model/MethodListTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -23,6 +23,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @@ -105,7 +106,7 @@ @Test public void testSyntheticMethods() { - assertTrue(CSynthetic.CWithField.class.getDeclaredMethods().length == 2); + // assertEquals(2, CSynthetic.CWithField.class.getDeclaredMethods().length); MethodList ml = new MethodList(CSynthetic.CWithField.class, true); assertTrue(!ml.iterator().hasNext());
diff --git a/docs/src/main/docbook/deployment.xml b/docs/src/main/docbook/deployment.xml index 474fd63..55d8bb7 100644 --- a/docs/src/main/docbook/deployment.xml +++ b/docs/src/main/docbook/deployment.xml
@@ -110,6 +110,9 @@ classpath, unless explicitly stated otherwise in the documentation of each particular extension. Users are expected to explicitly register the extension &jaxrs.core.Feature;s using their &lit.jaxrs.core.Application; subclass. + Since Jersey 3.1.0 all features which extends &jaxrs.core.Feature; and are listed as SPI of the &lit.jaxrs.core.Feature; + interface are automatically registered (not required to be registered within &lit.jaxrs.core.Application; subclass). + For server site Jersey 3.1.0 also registers SPIs for &lit.jaxrs.container.DynamicFeature; interface. For a few Jersey provided modules however there is no need to explicitly register their extension &lit.jaxrs.core.Feature;s as these are discovered and registered in the &jaxrs.core.Configuration; (on client/server) automatically by Jersey runtime whenever the modules implementing these features are present on the classpath @@ -193,6 +196,17 @@ </section> </section> + <section xml:id="deployment.feature.dynamicFeature"> + <title>Feature and Dynamic Feature SPI automatic registration</title> + <para> + Since Jersey 3.1.0 there is a new specification requirement to automatically discover and register classes that + implement &jaxrs.core.Feature; and &jaxrs.container.DynamicFeature; interfaces. This is being done via SPI search + thus all those classes in order to be discovered automatically shall be listed as SPIs for given interfaces. + This automatic registration can also be disabled by <literal>jakarta.ws.rs.loadServices</literal> + (which is new specification property) to be set to false. This is also valid for + &jersey.common.CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE; lookup. + </para> + </section> <section xml:id="deployment.classpath-scanning"> <title>Configuring the Classpath Scanning</title> @@ -207,6 +221,12 @@ <itemizedlist> <title>List of SPIs recognized by Jersey</title> <listitem> + <para>&lit.jaxrs.core.Feature; (server, client) - + Features are being registered similarly to AutoDiscoverable feature. In addition + property jakarta.ws.rs.loadServices is checked before registration. + </para> + </listitem> + <listitem> <para><literal>AutoDiscoverable</literal> (server, client) - it means if you disable service loading the AutoDiscoverable feature is automatically disabled too</para> </listitem> @@ -218,6 +238,10 @@ <para><literal>HeaderDelegateProvider</literal> (server, client)</para> </listitem> <listitem> + <para>&lit.jaxrs.container.DynamicFeature; (server) - + Similar to &lit.jaxrs.core.Feature; registration but only for server.</para> + </listitem> + <listitem> <para><literal>ComponentProvider</literal> (server)</para> </listitem> <listitem>
diff --git a/docs/src/main/docbook/jaxrs-resources.xml b/docs/src/main/docbook/jaxrs-resources.xml index d6cb100..884209d 100644 --- a/docs/src/main/docbook/jaxrs-resources.xml +++ b/docs/src/main/docbook/jaxrs-resources.xml
@@ -480,6 +480,11 @@ </example> </para> + <para> + For quicker getting of values for a known header name there is a shortcut for <literal>hh.getRequestHeaders().get(name)</literal> + which is <literal>hh.getRequestHeader(name)</literal>. + </para> + <para>In general &jaxrs.core.Context; can be used to obtain contextual Java types related to the request or response. </para>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent index 800c3f9..4ffb0df 100644 --- a/docs/src/main/docbook/jersey.ent +++ b/docs/src/main/docbook/jersey.ent
@@ -28,6 +28,7 @@ <!ENTITY jaxb-api-jar.version "$jaxb-api-jar-version"> <!ENTITY jax-rs.version "$jax-rs-api-jar-version"> <!ENTITY jax-rs21.version "2.1.6"> +<!ENTITY jax-rs31.spec.version "3.1"> <!ENTITY jakarta.el.version "$jakarta.el-version"> <!ENTITY jakarta.el-impl.version "$jakarta.el-impl-version"> <!ENTITY jax-rs-api-jar.version "$jax-rs-api-jar-version"> @@ -60,6 +61,7 @@ <!ENTITY jaxrs.release.uri "https://github.com/eclipse-ee4j/jaxrs-api"> <!ENTITY jaxrs.javadoc.uri "https://jakartaee.github.io/rest/apidocs/&jax-rs.version;/jakarta/ws/rs"> <!ENTITY jaxrs21.javadoc.uri "https://jakartaee.github.io/rest/apidocs/&jax-rs21.version;/javax/ws/rs"> +<!ENTITY jaxrs31.spec.uri "https://jakarta.ee/specifications/restful-ws/&jax-rs31.spec.version;/jakarta-restful-ws-spec-&jax-rs31.spec.version;.html"> <!ENTITY jsonb.javadoc.uri "https://javaee.github.io/javaee-spec/javadocs/javax/json/bind"> <!ENTITY jersey.documentation.uri "https://eclipse-ee4j.github.io/jersey.github.io"> @@ -213,6 +215,7 @@ <!ENTITY jaxrs.core.Configuration "<link xlink:href='&jaxrs.javadoc.uri;/core/Configuration.html'>Configuration</link>"> <!ENTITY jaxrs.core.Context "<link xlink:href='&jaxrs.javadoc.uri;/core/Context.html'>@Context</link>"> <!ENTITY jaxrs.core.Cookie "<link xlink:href='&jaxrs.javadoc.uri;/core/Cookie.html'>Cookie</link>"> +<!ENTITY jaxrs.core.EntityPart "<link xlink:href='&jaxrs.javadoc.uri;/core/EntityPart.html'>EntityPart</link>"> <!ENTITY jaxrs.core.EntityTag "<link xlink:href='&jaxrs.javadoc.uri;/core/EntityTag.html'>EntityTag</link>"> <!ENTITY jaxrs.core.Feature "<link xlink:href='&jaxrs.javadoc.uri;/core/Feature.html'>Feature</link>"> <!ENTITY jaxrs.core.Form "<link xlink:href='&jaxrs.javadoc.uri;/core/Form.html'>Form</link>"> @@ -248,6 +251,7 @@ <!ENTITY jaxrs.ext.RuntimeDelegate.HeaderDelegate "<link xlink:href='&jaxrs.javadoc.uri;/ext/RuntimeDelegate.HeaderDelegate.html'>RuntimeDelegate.HeaderDelegate<T></link>"> <!ENTITY jaxrs21.sse.SseEventSink "<link xlink:href='&jaxrs21.javadoc.uri;/sse/SseEventSink.html'>SseEventSink</link>"> <!ENTITY jaxrs21.sse.Sse "<link xlink:href='&jaxrs21.javadoc.uri;/sse/Sse.html'>Sse</link>"> +<!ENTITY jaxrs31.exceptionMapperProvider "<link xlink:href='&jaxrs31.spec.uri;#exceptionmapper'>Exception Mapping Providers</link>"> <!ENTITY jdk6.Boolean "<link xlink:href='&jdk6.javadoc.uri;/java/lang/Boolean.html'>Boolean</link>"> <!ENTITY jdk6.CountDownLatch "<link xlink:href='&jdk6.javadoc.uri;/java/util/concurrent/CountDownLatch.html'>CountDownLatch</link>"> @@ -815,6 +819,7 @@ <!ENTITY lit.jaxrs.core.Context "<literal>@Context</literal>"> <!ENTITY lit.jaxrs.core.Cookie "<literal>Cookie</literal>"> <!ENTITY lit.jaxrs.core.EntityTag "<literal>EntityTag</literal>"> +<!ENTITY lit.jaxrs.core.EntityPart "<literal>EntityPart</literal>"> <!ENTITY lit.jaxrs.core.Feature "<literal>Feature</literal>"> <!ENTITY lit.jaxrs.core.Form "<literal>Form</literal>"> <!ENTITY lit.jaxrs.core.GenericEntity "<literal>GenericEntity<T></literal>"> @@ -860,6 +865,7 @@ <!ENTITY lit.jdk6.InputStream "<literal>InputStream</literal>"> <!ENTITY lit.jdk6.JAXBElement "<literal>JAXBElement</literal>"> <!ENTITY lit.jdk6.KeyStore "<literal>KeyStore</literal>"> +<!ENTITY lit.jdk6.List "<literal>List</literal>"> <!ENTITY lit.jdk6.Number "<literal>Number</literal>"> <!ENTITY lit.jdk6.ObjectName "<literal>ObjectName</literal>"> <!ENTITY lit.jdk6.ParameterizedType "<literal>ParameterizedType</literal>">
diff --git a/docs/src/main/docbook/media.xml b/docs/src/main/docbook/media.xml index 95581df..3bd4013 100644 --- a/docs/src/main/docbook/media.xml +++ b/docs/src/main/docbook/media.xml
@@ -23,6 +23,10 @@ <!ENTITY link.jackson "<link linkend='json.jackson'>Jackson</link>" > <!ENTITY link.jettison "<link linkend='json.jettison'>Jettison</link>" > <!ENTITY link.json-b "<link linkend='json.json-b'>Java API for JSON Binding (JSON-B)</link>" > + <!ENTITY link.multipart.client.jersey "<link linkend='multipart.client.jersey'>Client using Jersey API</link>" > + <!ENTITY link.multipart.client.rest "<link linkend='multipart.client.rest'>Client using Jakarta REST API</link>" > + <!ENTITY link.multipart.server.jersey "<link linkend='multipart.server.jersey'>Server using Jersey API</link>" > + <!ENTITY link.multipart.server.rest "<link linkend='multipart.server.rest'>Server using Jakarta REST API</link>" > <!ENTITY % ents SYSTEM "jersey.ent" > %ents; ]> @@ -1540,25 +1544,11 @@ <title>Registration</title> <para> - Before you can use capabilities of the &lit.jersey-media-multipart; module in your client/server code, you - need to register &jersey.media.multipart.MultiPartFeature;. + Prior to Jersey 3.1.0, before you can use the capabilities of the &lit.jersey-media-multipart; + module in your client/server code, you need to register &jersey.media.multipart.MultiPartFeature;. - <example> - <title>Building client with MultiPart feature enabled.</title> - - <programlisting language="java">final Client client = ClientBuilder.newBuilder() - .register(MultiPartFeature.class) - .build();</programlisting> - </example> - - <example> - <title>Creating JAX-RS application with MultiPart feature enabled.</title> - - <programlisting language="java">// Create JAX-RS application. -final Application application = new ResourceConfig() - .packages("org.glassfish.jersey.examples.multipart") - .register(MultiPartFeature.class)</programlisting> - </example> + The multipart feature is supported by Jakarta RESTful Web Services 3.1 multipart API. From Jersey 3.1.0 on, + the &jersey.media.multipart.MultiPartFeature; is no longer required to be registered and it is registered automatically. </para> </section> @@ -1573,20 +1563,30 @@ <section> <title>Client</title> + <itemizedlist> + <listitem> + <para>&link.multipart.client.jersey;</para> + </listitem> + <listitem> + <para>&link.multipart.client.rest;</para> + </listitem> + </itemizedlist> + <section xml:id="multipart.client.jersey"> + <title>Client using Jersey API</title> - <para> - &jersey.media.multipart.MultiPart; class (or it's subclasses) can be used as an entry point to use - &lit.jersey-media-multipart; module on the client side. This class represents a - <link xlink:href='&wikipedia.uri;MIME#Multipart_messages'>MIME multipart message</link> and is able - to hold an arbitrary number of &jersey.media.multipart.BodyPart;s. Default media type is - <link xlink:href='&wikipedia.uri;MIME#Mixed'>multipart/mixed</link> - for &lit.jersey.media.multipart.MultiPart; entity and <literal>text/plain</literal> for - &lit.jersey.media.multipart.BodyPart;. + <para> + &jersey.media.multipart.MultiPart; class (or it's subclasses) can be used as an entry point to use + &lit.jersey-media-multipart; module on the client side. This class represents a + <link xlink:href='&wikipedia.uri;MIME#Multipart_messages'>MIME multipart message</link> and is able + to hold an arbitrary number of &jersey.media.multipart.BodyPart;s. Default media type is + <link xlink:href='&wikipedia.uri;MIME#Mixed'>multipart/mixed</link> + for &lit.jersey.media.multipart.MultiPart; entity and <literal>text/plain</literal> for + &lit.jersey.media.multipart.BodyPart;. - <example> - <title>&lit.jersey.media.multipart.MultiPart; entity</title> + <example> + <title>&lit.jersey.media.multipart.MultiPart; entity</title> - <programlisting language="java">final MultiPart multiPartEntity = new MultiPart() + <programlisting language="java">final MultiPart multiPartEntity = new MultiPart() .bodyPart(new BodyPart().entity("hello")) .bodyPart(new BodyPart(new JaxbBean("xml"), MediaType.APPLICATION_XML_TYPE)) .bodyPart(new BodyPart(new JaxbBean("json"), MediaType.APPLICATION_JSON_TYPE)); @@ -1595,15 +1595,15 @@ final Response response = target .request() .post(Entity.entity(multiPartEntity, multiPartEntity.getMediaType()));</programlisting> - </example> + </example> - If you send a <literal>multiPartEntity</literal> to the server the entity with <literal>Content-Type</literal> - header in HTTP message would look like (don't forget to register a JSON provider): + If you send a <literal>multiPartEntity</literal> to the server the entity with <literal>Content-Type</literal> + header in HTTP message would look like: - <example> - <title>&lit.jersey.media.multipart.MultiPart; entity in HTTP message.</title> + <example> + <title>&lit.jersey.media.multipart.MultiPart; entity in HTTP message.</title> - <screen language="text" linenumbering="unnumbered"><emphasis>Content-Type: multipart/mixed; boundary=Boundary_1_829077776_1369128119878</emphasis> + <screen language="text" linenumbering="unnumbered"><emphasis>Content-Type: multipart/mixed; boundary=Boundary_1_829077776_1369128119878</emphasis> --Boundary_1_829077776_1369128119878 Content-Type: text/plain @@ -1618,34 +1618,34 @@ {"value":"json"} --Boundary_1_829077776_1369128119878--</screen> - </example> - </para> - <para> - When working with forms (e.g. media type <literal>multipart/form-data</literal>) and various fields in them, - there is a more convenient class to be used - &jersey.media.multipart.FormDataMultiPart;. It automatically sets - the media type for the &lit.jersey.media.multipart.FormDataMultiPart; entity to - <literal>multipart/form-data</literal> and <literal>Content-Disposition</literal> header to - &lit.jersey.media.multipart.FormDataBodyPart; body parts. + </example> + </para> + <para> + When working with forms (e.g. media type <literal>multipart/form-data</literal>) and various fields in them, + there is a more convenient class to be used - &jersey.media.multipart.FormDataMultiPart;. It automatically sets + the media type for the &lit.jersey.media.multipart.FormDataMultiPart; entity to + <literal>multipart/form-data</literal> and <literal>Content-Disposition</literal> header to + &lit.jersey.media.multipart.FormDataBodyPart; body parts. - <example> - <title>&lit.jersey.media.multipart.FormDataMultiPart; entity</title> - <programlisting language="java">final FormDataMultiPart multipart = new FormDataMultiPart() + <example> + <title>&lit.jersey.media.multipart.FormDataMultiPart; entity</title> + <programlisting language="java">final FormDataMultiPart multipart = new FormDataMultiPart() .field("hello", "hello") .field("xml", new JaxbBean("xml")) .field("json", new JaxbBean("json"), MediaType.APPLICATION_JSON_TYPE); final WebTarget target = // Create WebTarget. final Response response = target.request().post(Entity.entity(multipart, multipart.getMediaType()));</programlisting> - </example> + </example> - To illustrate the difference when using &lit.jersey.media.multipart.FormDataMultiPart; instead of - &lit.jersey.media.multipart.FormDataBodyPart; you can take a look at the - &lit.jersey.media.multipart.FormDataMultiPart; entity from HTML message: + To illustrate the difference when using &lit.jersey.media.multipart.FormDataMultiPart; instead of + &lit.jersey.media.multipart.FormDataBodyPart; you can take a look at the + &lit.jersey.media.multipart.FormDataMultiPart; entity from HTML message: - <example> - <title>&lit.jersey.media.multipart.FormDataMultiPart; entity in HTTP message.</title> + <example> + <title>&lit.jersey.media.multipart.FormDataMultiPart; entity in HTTP message.</title> - <screen language="text" linenumbering="unnumbered"><emphasis>Content-Type: multipart/form-data; boundary=Boundary_1_511262261_1369143433608</emphasis> + <screen language="text" linenumbering="unnumbered"><emphasis>Content-Type: multipart/form-data; boundary=Boundary_1_511262261_1369143433608</emphasis> --Boundary_1_511262261_1369143433608 Content-Type: text/plain @@ -1663,17 +1663,17 @@ {"value":"json"} --Boundary_1_511262261_1369143433608--</screen> - </example> - </para> - <para> - A common use-case for many users is sending files from client to server. For this purpose you can use classes from - <literal>org.glassfish.jersey.jersey.media.multipart</literal> package, such as - &jersey.media.multipart.FileDataBodyPart; or &jersey.media.multipart.StreamDataBodyPart;. + </example> + </para> + <para> + A common use-case for many users is sending files from client to server. For this purpose you can use classes from + <literal>org.glassfish.jersey.jersey.media.multipart</literal> package, such as + &jersey.media.multipart.FileDataBodyPart; or &jersey.media.multipart.StreamDataBodyPart;. - <example> - <title>Multipart - sending files.</title> + <example> + <title>Multipart - sending files.</title> - <programlisting language="java">// MediaType of the body part will be derived from the file. + <programlisting language="java">// MediaType of the body part will be derived from the file. final FileDataBodyPart filePart = new FileDataBodyPart("my_pom", new File("pom.xml")); final FormDataMultiPart multipart = new FormDataMultiPart() @@ -1683,19 +1683,76 @@ final WebTarget target = // Create WebTarget. final Response response = target.request() .post(Entity.entity(multipart, multipart.getMediaType()));</programlisting> - </example> - </para> - <warning> - <para> - Do not use &lit.jersey.apache.ApacheConnectorProvider; nor &lit.jersey.grizzly.GrizzlyConnectorProvider; - neither &lit.jersey.jetty.JettyConnectorProvider; connector implementations with Jersey Multipart - features. See <xref linkend="connectors.warning"/> warning for more details. + </example> </para> - </warning> + <warning> + <para> + Do not use &lit.jersey.apache.ApacheConnectorProvider; nor &lit.jersey.grizzly.GrizzlyConnectorProvider; + neither &lit.jersey.jetty.JettyConnectorProvider; connector implementations with Jersey Multipart + features. See <xref linkend="connectors.warning"/> warning for more details. + </para> + </warning> + </section> + + <section xml:id="multipart.client.rest"> + <title>Client using Jakarta REST API</title> + + <para> + &jaxrs.core.EntityPart; interface can be used as an entry point to use + &lit.jersey-media-multipart; module on the client side. This class represents multipart message is able + to hold an arbitrary number of &jaxrs.core.EntityPart;s. Default media type is + <link xlink:href='&wikipedia.uri;MIME#Mixed'>multipart/form-data</link>. + + <example> + <title>Using <literal>EntityPart.Builder</literal> for building an Entity</title> + + <programlisting language="java"> +final List<EntityPart> multiPartEntity = new List<>(); +list.add(EntityPart.withName("part-01").content("hello").build()); +list.add(EntityPart.withName("part-01").content(new JaxbBean("xml")).mediaType(MediaType.APPLICATION_XML_TYPE).build()); //same name +list.add(EntityPart.withName("part-02").content(new JaxbBean("json")).mediaType(MediaType.APPLICATION_JSON_TYPE).build()); //other name +final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; +final Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + +final WebTarget target = // Create WebTarget. +final Response response = target.request().post(entity); + </programlisting> + </example> + </para> + <para> + The common use-case for many users is sending files from client to server. It is also covered by + &jaxrs.core.EntityPart;.Builder. + <example> + <title>EntityPart - sending files.</title> + + <programlisting language="java">// MediaType of the body part will be derived from the file. +final List<EntityPart> multiPartEntity = new List<>(); +list.add(EntityPart.withFileName("file001.txt").content(new FileInputStream("file001.txt")).build()); +list.add(EntityPart.withFileName("mypom.xml").content(new FileInputStream("pom.xml")).build()); + +final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; +final Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + +final WebTarget target = // Create WebTarget. +final Response response = target.request().post(entity); + </programlisting> + </example> + </para> + </section> </section> <section> <title>Server</title> + <itemizedlist> + <listitem> + <para>&link.multipart.server.jersey;</para> + </listitem> + <listitem> + <para>&link.multipart.server.rest;</para> + </listitem> + </itemizedlist> + <section xml:id="multipart.server.jersey"> + <title>Jersey Server API</title> <para> Returning a multipart response from server to client is not much different from the parts described in the client @@ -1852,5 +1909,60 @@ </tip> </section> </section> + <section xml:id="multipart.server.rest"> + <title>Server using Jakarta REST API</title> + <para> + Using &jaxrs.core.EntityPart; on the server side is similar to the client side. + Jakarta REST specification allows for + returning a &lit.jaxrs.core.Response; or a &lit.jdk6.List; of &lit.jaxrs.core.EntityPart;s. + </para> + <para> + Receiving the &jaxrs.core.EntityPart;s can be done either using &lit.jaxrs.FormParam; annotations and + &lit.jaxrs.core.EntityPart;, &lit.jdk6.InputStream; or &lit.jdk6.String; data-types, or using a + &lit.jdk6.List; of &lit.jaxrs.core.EntityPart;s. + </para> + + <example> + <title>Use of &lit.jaxrs.FormParam; annotation with &lit.jaxrs.core.EntityPart; &lit.jdk6.InputStream; + and &lit.jdk6.String; types and returning a &lit.jaxrs.core.Response;</title> + <programlisting language="java">@POST +@Path("/postFormVarious") +public Response postFormVarious(@FormParam("name1") EntityPart part1, + @FormParam("name2") InputStream part2, + @FormParam("name3") String part3) throws IOException { + final List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName(part1.getName()) + .content(part1.getContent(String.class) + new String(part2.readAllBytes()) + part3) + .mediaType(MediaType.TEXT_PLAIN_TYPE) + .build()); + final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build(); +} + </programlisting> + </example> + <example> + <title>Receiving a &lit.jdk6.List; of &lit.jaxrs.core.EntityPart;s</title> + <programlisting language="java">@POST +@Path("/postListForm") +public String postEntityPartForm(@FormParam("part-0x") List<EntityPart> part) throws IOException { + final String entity = part.get(0).getContent(String.class) + part.get(1).getContent(String.class); + return entity; +} + </programlisting> + </example> + <example> + <title>Returning a &lit.jdk6.List; of &lit.jaxrs.core.EntityPart;s</title> + <programlisting language="java">@GET +@Produces(MediaType.MULTIPART_FORM_DATA) +@Path("/getList") +public List<EntityPart> getList() throws IOException { + final List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("name1").content("data1").build()); + return list; +} + </programlisting> + </example> + </section> + </section> </section> </chapter>
diff --git a/docs/src/main/docbook/migration.xml b/docs/src/main/docbook/migration.xml index 3b68582..9389341 100644 --- a/docs/src/main/docbook/migration.xml +++ b/docs/src/main/docbook/migration.xml
@@ -102,6 +102,13 @@ <literal>true</literal>. </para> </listitem> + <listitem> + <para> + Since Jersey 3.1.0+ the <literal>getRequestHeader(String name)</literal> method of the + <literal>ClientRequest</literal> class returns NULL (instead of an empty List) in case if + the specified header does not exist. + </para> + </listitem> </itemizedlist> </para> </section>
diff --git a/docs/src/main/docbook/representations.xml b/docs/src/main/docbook/representations.xml index 50515d6..12ef126 100644 --- a/docs/src/main/docbook/representations.xml +++ b/docs/src/main/docbook/representations.xml
@@ -298,6 +298,19 @@ and let other provider to be chosen for the exception mapping. </para> + <para> + Since Jersey 3.1.0 the default <literal>ExceptionMapper</literal> is implemented. + It is required by JAX-RS 3.1 specification (&jaxrs31.exceptionMapperProvider;). + The default behaviour of the mapper is to return a message from an exception caught and set the response status + to 500 (internal server error). In case of a &jaxrs.WebApplicationException; with a response that response is returned. + If response inside the &lit.jaxrs.WebApplicationException; is NULL the exception is being processed according to the + default behaviour. + + The Default exception mapper is package private and can not be referenced. Its presence is required only by + JAX-RS 3.1 specification. Processing of the default Exception Mapper occurs at the very end of the exception + processing chain. It is invoked only if nothing else in the chain was invoked before. + </para> + </section> <section>
diff --git a/etc/config/copyright-exclude b/etc/config/copyright-exclude index 987175b..d5cff67 100644 --- a/etc/config/copyright-exclude +++ b/etc/config/copyright-exclude
@@ -71,8 +71,6 @@ /tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/xxe.txt /core-server/src/main/java/com/sun/research/ws/wadl /core-common/src/main/java/org/glassfish/jersey/internal/jsr166 -/core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166 -/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166 /tests/performance/etc/data/MEASUREMENT_DATA /core-common/src/main/java/org/glassfish/jersey/internal/guava/ /core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/core/AbstractSlidingWindowTimeReservoir.java
diff --git a/etc/jenkins/Jenkinsfile_ci_build b/etc/jenkins/Jenkinsfile_ci_build index f1b405b..59af68b 100644 --- a/etc/jenkins/Jenkinsfile_ci_build +++ b/etc/jenkins/Jenkinsfile_ci_build
@@ -4,20 +4,6 @@ stages { stage('Jersey build') { parallel { - stage('JDK 8 ') { - agent { - label 'centos-7' - } - tools { - jdk 'oracle-jdk8-latest' - maven 'apache-maven-latest' - } - steps { - sh ''' - bash ${WORKSPACE}/etc/jenkins/jenkins_build.sh - ''' - } - } stage('JDK 11 ') { agent { label 'centos-7'
diff --git a/etc/jenkins/jenkins_build.sh b/etc/jenkins/jenkins_build.sh index dbebc86..9983a05 100644 --- a/etc/jenkins/jenkins_build.sh +++ b/etc/jenkins/jenkins_build.sh
@@ -2,5 +2,4 @@ export DEBUG=true -mvn -V -U -B -e clean install glassfish-copyright:check -Dcopyright.quiet=false - +mvn -V -U -B -e -Pstaging clean install glassfish-copyright:check -Dcopyright.quiet=false \ No newline at end of file
diff --git a/examples/bookstore-webapp/pom.xml b/examples/bookstore-webapp/pom.xml index 101a8dc..52d4199 100644 --- a/examples/bookstore-webapp/pom.xml +++ b/examples/bookstore-webapp/pom.xml
@@ -56,7 +56,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency> @@ -69,6 +68,11 @@ <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> </dependency> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-osgi</artifactId> + <scope>runtime</scope> + </dependency> </dependencies> <build> @@ -88,19 +92,6 @@ <profiles> <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <dependencies> - <dependency> - <groupId>com.sun.xml.bind</groupId> - <artifactId>jaxb-osgi</artifactId> - <scope>runtime</scope> - </dependency> - </dependencies> - </profile> - <profile> <!-- mvn test -Prun-external-tests --> <id>run-external-tests</id> <build>
diff --git a/examples/cdi-webapp/src/main/resources/META-INF/beans.xml b/examples/cdi-webapp/src/main/resources/META-INF/beans.xml index 5d361e9..77e336a 100644 --- a/examples/cdi-webapp/src/main/resources/META-INF/beans.xml +++ b/examples/cdi-webapp/src/main/resources/META-INF/beans.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Distribution License v. 1.0, which is available at @@ -11,4 +11,9 @@ --> -<beans/> +<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + bean-discovery-mode="all"> +</beans>
diff --git a/examples/cdi-webapp/src/main/webapp/WEB-INF/beans.xml b/examples/cdi-webapp/src/main/webapp/WEB-INF/beans.xml index 5d361e9..77e336a 100644 --- a/examples/cdi-webapp/src/main/webapp/WEB-INF/beans.xml +++ b/examples/cdi-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Distribution License v. 1.0, which is available at @@ -11,4 +11,9 @@ --> -<beans/> +<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + bean-discovery-mode="all"> +</beans>
diff --git a/examples/freemarker-webapp/pom.xml b/examples/freemarker-webapp/pom.xml index fb06cc0..cee0d38 100644 --- a/examples/freemarker-webapp/pom.xml +++ b/examples/freemarker-webapp/pom.xml
@@ -40,7 +40,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/examples/helloworld-webapp/pom.xml b/examples/helloworld-webapp/pom.xml index 17d288b..aecb50d 100644 --- a/examples/helloworld-webapp/pom.xml +++ b/examples/helloworld-webapp/pom.xml
@@ -32,7 +32,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <!-- <scope>provided</scope> Make this provided for jetty:run --> </dependency>
diff --git a/examples/helloworld-weld/src/main/resources/META-INF/beans.xml b/examples/helloworld-weld/src/main/resources/META-INF/beans.xml index 5d361e9..77e336a 100644 --- a/examples/helloworld-weld/src/main/resources/META-INF/beans.xml +++ b/examples/helloworld-weld/src/main/resources/META-INF/beans.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Distribution License v. 1.0, which is available at @@ -11,4 +11,9 @@ --> -<beans/> +<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + bean-discovery-mode="all"> +</beans>
diff --git a/examples/https-clientserver-grizzly/pom.xml b/examples/https-clientserver-grizzly/pom.xml index ca1b4d6..14582e1 100644 --- a/examples/https-clientserver-grizzly/pom.xml +++ b/examples/https-clientserver-grizzly/pom.xml
@@ -58,6 +58,12 @@ <artifactId>junit</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-osgi</artifactId> + <scope>runtime</scope> + </dependency> + </dependencies> <build> @@ -74,23 +80,6 @@ <profiles> <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <properties> - <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 --> - <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline> - </properties> - <dependencies> - <dependency> - <groupId>com.sun.xml.bind</groupId> - <artifactId>jaxb-osgi</artifactId> - <scope>runtime</scope> - </dependency> - </dependencies> - </profile> - <profile> <id>pre-release</id> <build> <plugins> @@ -103,4 +92,9 @@ </profile> </profiles> + <properties> + <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 --> + <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline> + </properties> + </project>
diff --git a/examples/osgi-helloworld-webapp/war-bundle/pom.xml b/examples/osgi-helloworld-webapp/war-bundle/pom.xml index ccd3eaf..b789a5b 100644 --- a/examples/osgi-helloworld-webapp/war-bundle/pom.xml +++ b/examples/osgi-helloworld-webapp/war-bundle/pom.xml
@@ -57,7 +57,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version><!-- the current project's ${servlet2.version} is 2.4 and that's not enough --> <scope>provided</scope> </dependency>
diff --git a/examples/osgi-http-service/bundle/pom.xml b/examples/osgi-http-service/bundle/pom.xml index efd62d4..08c440a 100644 --- a/examples/osgi-http-service/bundle/pom.xml +++ b/examples/osgi-http-service/bundle/pom.xml
@@ -36,7 +36,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> </dependency> <dependency> <groupId>org.glassfish.jersey.containers</groupId>
diff --git a/examples/servlet3-webapp/pom.xml b/examples/servlet3-webapp/pom.xml index ea52217..d44fe5a 100644 --- a/examples/servlet3-webapp/pom.xml +++ b/examples/servlet3-webapp/pom.xml
@@ -37,7 +37,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/ext/bean-validation/pom.xml b/ext/bean-validation/pom.xml index 53ac3d1..be3a887 100644 --- a/ext/bean-validation/pom.xml +++ b/ext/bean-validation/pom.xml
@@ -58,6 +58,8 @@ <Import-Package> ${jakarta.annotation.osgi.version}, jakarta.validation.*;resolution:=optional;version="[3,4)", + jakarta.enterprise.*;version="[3.0,5)", + jakarta.decorator.*;version="[3.0,5)", * </Import-Package> </instructions>
diff --git a/ext/cdi/jersey-cdi1x-servlet/pom.xml b/ext/cdi/jersey-cdi1x-servlet/pom.xml index 17b9c91..057251d 100644 --- a/ext/cdi/jersey-cdi1x-servlet/pom.xml +++ b/ext/cdi/jersey-cdi1x-servlet/pom.xml
@@ -86,6 +86,11 @@ <configuration> <instructions> <Export-Package>org.glassfish.jersey.ext.cdi1x.servlet.internal</Export-Package> + <Import-Package> + jakarta.enterprise.*;version="[3.0,5)", + jakarta.decorator.*;version="[3.0,5)", + * + </Import-Package> </instructions> </configuration> </plugin>
diff --git a/ext/cdi/jersey-cdi1x-servlet/src/main/java/org/glassfish/jersey/ext/cdi1x/servlet/internal/CdiExternalRequestScopeExtension.java b/ext/cdi/jersey-cdi1x-servlet/src/main/java/org/glassfish/jersey/ext/cdi1x/servlet/internal/CdiExternalRequestScopeExtension.java index 3a17677..ce367f5 100644 --- a/ext/cdi/jersey-cdi1x-servlet/src/main/java/org/glassfish/jersey/ext/cdi1x/servlet/internal/CdiExternalRequestScopeExtension.java +++ b/ext/cdi/jersey-cdi1x-servlet/src/main/java/org/glassfish/jersey/ext/cdi1x/servlet/internal/CdiExternalRequestScopeExtension.java
@@ -124,7 +124,7 @@ return false; } - @Override + // @Override - Removed in CDI 4 public boolean isNullable() { return false; }
diff --git a/ext/cdi/jersey-cdi1x-transaction/pom.xml b/ext/cdi/jersey-cdi1x-transaction/pom.xml index 4ce8ff6..5c7b4cd 100644 --- a/ext/cdi/jersey-cdi1x-transaction/pom.xml +++ b/ext/cdi/jersey-cdi1x-transaction/pom.xml
@@ -91,7 +91,12 @@ <configuration> <instructions> <Export-Package>org.glassfish.jersey.ext.cdi1x.transaction.internal</Export-Package> - <Import-Package>${jakarta.annotation.osgi.version},*</Import-Package> + <Import-Package> + jakarta.enterprise.*;version="[3.0,5)", + jakarta.decorator.*;version="[3.0,5)", + ${jakarta.annotation.osgi.version}, + * + </Import-Package> </instructions> </configuration> </plugin>
diff --git a/ext/cdi/jersey-cdi1x-validation/pom.xml b/ext/cdi/jersey-cdi1x-validation/pom.xml index f176374..45701d1 100644 --- a/ext/cdi/jersey-cdi1x-validation/pom.xml +++ b/ext/cdi/jersey-cdi1x-validation/pom.xml
@@ -103,7 +103,12 @@ <configuration> <instructions> <Export-Package>org.glassfish.jersey.ext.cdi1x.validation.internal;version=${project.version}</Export-Package> - <Import-Package>${jakarta.annotation.osgi.version},*</Import-Package> + <Import-Package> + jakarta.enterprise.*;version="[3.0,5)", + jakarta.decorator.*;version="[3.0,5)", + ${jakarta.annotation.osgi.version}, + * + </Import-Package> </instructions> </configuration> </plugin>
diff --git a/ext/cdi/jersey-cdi1x-validation/src/main/java/org/glassfish/jersey/ext/cdi1x/validation/internal/CdiInterceptorWrapperExtension.java b/ext/cdi/jersey-cdi1x-validation/src/main/java/org/glassfish/jersey/ext/cdi1x/validation/internal/CdiInterceptorWrapperExtension.java index 6cced30..875f63c 100644 --- a/ext/cdi/jersey-cdi1x-validation/src/main/java/org/glassfish/jersey/ext/cdi1x/validation/internal/CdiInterceptorWrapperExtension.java +++ b/ext/cdi/jersey-cdi1x-validation/src/main/java/org/glassfish/jersey/ext/cdi1x/validation/internal/CdiInterceptorWrapperExtension.java
@@ -155,7 +155,7 @@ return false; } - @Override + // @Override - Removed in CDI 4 public boolean isNullable() { return false; }
diff --git a/ext/cdi/jersey-cdi1x/pom.xml b/ext/cdi/jersey-cdi1x/pom.xml index e31ca92..0c2425d 100644 --- a/ext/cdi/jersey-cdi1x/pom.xml +++ b/ext/cdi/jersey-cdi1x/pom.xml
@@ -87,6 +87,8 @@ <Import-Package> ${jakarta.annotation.osgi.version}, ${hk2.osgi.version}, + jakarta.enterprise.*;version="[3.0,5)", + jakarta.decorator.*;version="[3.0,5)", * </Import-Package> </instructions>
diff --git a/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java b/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java index 4d0eed3..6ee748c 100644 --- a/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java +++ b/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java
@@ -828,7 +828,7 @@ return Collections.emptySet(); } - @Override + // @Override - Removed in CDI 4 public boolean isNullable() { return true; }
diff --git a/ext/microprofile/mp-rest-client/pom.xml b/ext/microprofile/mp-rest-client/pom.xml index 4a60f32..cc11d33 100644 --- a/ext/microprofile/mp-rest-client/pom.xml +++ b/ext/microprofile/mp-rest-client/pom.xml
@@ -72,7 +72,6 @@ <dependency> <groupId>jakarta.json</groupId> <artifactId>jakarta.json-api</artifactId> - <version>${jsonp.ri.version}</version> </dependency> <dependency> <groupId>jakarta.enterprise</groupId> @@ -80,8 +79,8 @@ <scope>provided</scope> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jsonp-jaxrs</artifactId> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson-media</artifactId> </dependency> <dependency> <groupId>org.reactivestreams</groupId>
diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientProducer.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientProducer.java index 48bc397..d583419 100644 --- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientProducer.java +++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientProducer.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022 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 @@ -123,7 +123,7 @@ return Collections.emptySet(); } - @Override + // @Override - Removed in CDI 4 public boolean isNullable() { return false; }
diff --git a/ext/mvc-freemarker/pom.xml b/ext/mvc-freemarker/pom.xml index 214c883..9477666 100644 --- a/ext/mvc-freemarker/pom.xml +++ b/ext/mvc-freemarker/pom.xml
@@ -42,6 +42,7 @@ <extensions>true</extensions> <configuration> <instructions> + <Import-Package>jakarta.servlet.*;version="[5.0,7.0)",*</Import-Package> <Export-Package>org.glassfish.jersey.server.mvc.freemarker.*;version=${project.version}</Export-Package> </instructions> <unpackBundle>true</unpackBundle> @@ -56,17 +57,9 @@ </build> <dependencies> - - <dependency> - <groupId>javax.servlet</groupId> - <artifactId>javax.servlet-api</artifactId> - <version>4.0.1</version> - <scope>provided</scope> - </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/ext/mvc-jsp/pom.xml b/ext/mvc-jsp/pom.xml index ccdea70..6bf47a4 100644 --- a/ext/mvc-jsp/pom.xml +++ b/ext/mvc-jsp/pom.xml
@@ -54,7 +54,7 @@ <instructions> <Import-Package> jakarta.servlet.jsp.*;version="[3.0,4.0)", - jakarta.servlet.*;version="!", + jakarta.servlet.*;version="[5.0,7.0)", * </Import-Package> <Export-Package>org.glassfish.jersey.server.mvc.jsp.*;version=${project.version}</Export-Package> @@ -84,7 +84,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/ext/mvc/pom.xml b/ext/mvc/pom.xml index 68d79ad..3d2f274 100644 --- a/ext/mvc/pom.xml +++ b/ext/mvc/pom.xml
@@ -37,7 +37,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> </dependency> <dependency> @@ -67,7 +66,7 @@ <configuration> <instructions> <Export-Package>org.glassfish.jersey.server.mvc.*;version=${project.version}</Export-Package> - <Import-Package>${jakarta.annotation.osgi.version},*</Import-Package> + <Import-Package> jakarta.servlet.*;version="[5.0,7.0)",${jakarta.annotation.osgi.version},*</Import-Package> </instructions> <unpackBundle>true</unpackBundle> </configuration>
diff --git a/ext/proxy-client/src/main/java/org/glassfish/jersey/client/proxy/RequestParameters.java b/ext/proxy-client/src/main/java/org/glassfish/jersey/client/proxy/RequestParameters.java index 6457dd7..543e1c5 100644 --- a/ext/proxy-client/src/main/java/org/glassfish/jersey/client/proxy/RequestParameters.java +++ b/ext/proxy-client/src/main/java/org/glassfish/jersey/client/proxy/RequestParameters.java
@@ -61,8 +61,7 @@ RequestParameters(final WebTarget newTarget, final MultivaluedMap<String, Object> headers, final List<Cookie> cookies, final Form form) { - this.headers = new MultivaluedHashMap<>(); - this.headers.putAll(headers); + this.headers = new MultivaluedHashMap<>(headers); this.cookies = new LinkedList<>(cookies); this.form = new Form(); this.form.asMap().putAll(form.asMap());
diff --git a/ext/spring6/pom.xml b/ext/spring6/pom.xml index c7817c6..2cfbff6 100644 --- a/ext/spring6/pom.xml +++ b/ext/spring6/pom.xml
@@ -161,7 +161,7 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + <version>${servlet6.version}</version> <scope>provided</scope> </dependency>
diff --git a/incubator/cdi-inject-weld/pom.xml b/incubator/cdi-inject-weld/pom.xml index 999ecbb..2ed9f7d 100644 --- a/incubator/cdi-inject-weld/pom.xml +++ b/incubator/cdi-inject-weld/pom.xml
@@ -59,7 +59,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency> @@ -122,8 +121,11 @@ org.glassfish.jersey.inject.weld.managed.*;version=${project.version} </Export-Package> <Import-Package> + jakarta.servlet.*;version="[5.0,7.0)", sun.misc.*;resolution:=optional, ${jakarta.annotation.osgi.version}, + jakarta.enterprise.*;version="[3.0,5)", + jakarta.decorator.*;version="[3.0,5)", * </Import-Package> </instructions>
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java index c5c4cfe..b653b54 100644 --- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java
@@ -157,7 +157,8 @@ Class<Supplier<T>> supplierClass = (Class<Supplier<T>>) binding.getSupplierClass(); AnnotatedType<Supplier<T>> annotatedType = beanManager.createAnnotatedType(supplierClass); - InjectionTarget<Supplier<T>> injectionTarget = beanManager.createInjectionTarget(annotatedType); + final InjectionTargetFactory<Supplier<T>> injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType); + final InjectionTarget<Supplier<T>> injectionTarget = injectionTargetFactory.createInjectionTarget(null); SupplierClassBean<T> supplierBean = new SupplierClassBean<>(runtimeType, binding); InjectionTarget<Supplier<T>> jit = getJerseyInjectionTarget(supplierClass, injectionTarget, supplierBean, resolvers); @@ -218,7 +219,8 @@ final Class<Supplier<T>> bindingClass = (Class<Supplier<T>>) binding.getSupplierClass(); final AnnotatedType<Supplier<T>> annotatedType = beanManager.createAnnotatedType(bindingClass); - final InjectionTarget<Supplier<T>> injectionTarget = beanManager.createInjectionTarget(annotatedType); + final InjectionTargetFactory<Supplier<T>> injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType); + final InjectionTarget<Supplier<T>> injectionTarget = injectionTargetFactory.createInjectionTarget(null); final CachedConstructorAnalyzer<Supplier<T>> analyzer = new CachedConstructorAnalyzer<>(bindingClass, InjectionUtils.getInjectAnnotations(resolvers)); @@ -239,7 +241,8 @@ final Class<T> bindingClass = binding.getImplementationType(); final AnnotatedType<T> annotatedType = beanManager.createAnnotatedType(bindingClass); - final InjectionTarget<T> injectionTarget = beanManager.createInjectionTarget(annotatedType); + final InjectionTargetFactory<T> injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType); + final InjectionTarget<T> injectionTarget = injectionTargetFactory.createInjectionTarget(null); final CachedConstructorAnalyzer<T> analyzer = new CachedConstructorAnalyzer<>(bindingClass, InjectionUtils.getInjectAnnotations(resolvers));
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/JerseyBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/JerseyBean.java index 2f4da94..a56f23b 100644 --- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/JerseyBean.java +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/JerseyBean.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -142,7 +142,7 @@ return false; } - @Override + // @Override - Removed in CDI 4 public boolean isNullable() { return false; }
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ServerBootstrapPreinitialization.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ServerBootstrapPreinitialization.java index c995f48..826c52f 100644 --- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ServerBootstrapPreinitialization.java +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ServerBootstrapPreinitialization.java
@@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 Contributors to the Eclipse Foundation * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -46,7 +47,6 @@ import org.glassfish.jersey.internal.inject.Binding; import org.glassfish.jersey.internal.inject.ClassBinding; import org.glassfish.jersey.internal.inject.ReferencingFactory; -import org.glassfish.jersey.internal.spi.AutoDiscoverable; import org.glassfish.jersey.internal.util.collection.Ref; import org.glassfish.jersey.model.internal.CommonConfig; import org.glassfish.jersey.model.internal.ComponentBag; @@ -231,31 +231,11 @@ } @Override - public Servlet getServlet(String name) throws ServletException { - return null; - } - - @Override - public Enumeration<Servlet> getServlets() { - return null; - } - - @Override - public Enumeration<String> getServletNames() { - return null; - } - - @Override public void log(String msg) { } @Override - public void log(Exception exception, String msg) { - - } - - @Override public void log(String message, Throwable throwable) { }
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/RequestScopeBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/RequestScopeBean.java index eff570f..7dd99c8 100644 --- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/RequestScopeBean.java +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/RequestScopeBean.java
@@ -54,7 +54,7 @@ this.injectionTarget = injectionTargetFactory.createInjectionTarget(null); } - @Override + // @Override - Removed in CDI 4 public boolean isNullable() { return false; }
diff --git a/incubator/declarative-linking/pom.xml b/incubator/declarative-linking/pom.xml index 1c2d642..2a6c8a5 100644 --- a/incubator/declarative-linking/pom.xml +++ b/incubator/declarative-linking/pom.xml
@@ -74,12 +74,12 @@ <version>${project.version}</version> <scope>test</scope> </dependency> -<!-- <dependency>--> -<!-- <groupId>org.glassfish.jersey.media</groupId>--> -<!-- <artifactId>jersey-media-json-jackson</artifactId>--> -<!-- <version>${jersey.version}</version>--> -<!-- <scope>test</scope>--> -<!-- </dependency>--> + <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-json-jackson</artifactId> + <version>${jersey.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> @@ -174,22 +174,6 @@ </execution> </executions> </plugin> - <plugin> - <!-- TODO remove after jakartification --> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <executions> - <execution> - <id>default-test</id> <!-- jakartification-excluded-tests --> - <configuration> - <excludes> - <exclude>org/glassfish/jersey/linking/integration/LinkingTest.java</exclude> - <exclude>org/glassfish/jersey/linking/integration/LinkingManualTest.java</exclude> - </excludes> - </configuration> - </execution> - </executions> - </plugin> </plugins> </build> </project>
diff --git a/inject/cdi2-se/pom.xml b/inject/cdi2-se/pom.xml index f01855d..d4d2c26 100644 --- a/inject/cdi2-se/pom.xml +++ b/inject/cdi2-se/pom.xml
@@ -91,6 +91,8 @@ </Export-Package> <Import-Package> sun.misc.*;resolution:=optional, + jakarta.enterprise.*;version="[3.0,5)", + jakarta.decorator.*;version="[3.0,5)", ${jakarta.annotation.osgi.version}, * </Import-Package>
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/RequestScopeBean.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/RequestScopeBean.java index 4bf816f..86e97ab 100644 --- a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/RequestScopeBean.java +++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/RequestScopeBean.java
@@ -54,7 +54,7 @@ this.injectionTarget = injectionTargetFactory.createInjectionTarget(null); } - @Override + // @Override - Removed in CDI 4 public boolean isNullable() { return false; }
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java index 2ae0ebc..584193f 100644 --- a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java +++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java
@@ -147,9 +147,10 @@ public static <T> void registerSupplier(SupplierClassBinding<T> binding, AfterBeanDiscovery abd, Collection<InjectionResolver> resolvers, BeanManager beanManager) { - Class<Supplier<T>> supplierClass = (Class<Supplier<T>>) binding.getSupplierClass(); - AnnotatedType<Supplier<T>> annotatedType = beanManager.createAnnotatedType(supplierClass); - InjectionTarget<Supplier<T>> injectionTarget = beanManager.createInjectionTarget(annotatedType); + final Class<Supplier<T>> supplierClass = (Class<Supplier<T>>) binding.getSupplierClass(); + final AnnotatedType<Supplier<T>> annotatedType = beanManager.createAnnotatedType(supplierClass); + final InjectionTargetFactory<Supplier<T>> injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType); + final InjectionTarget<Supplier<T>> injectionTarget = injectionTargetFactory.createInjectionTarget(null); SupplierClassBean<T> supplierBean = new SupplierClassBean<>(binding); InjectionTarget<Supplier<T>> jit = getJerseyInjectionTarget(supplierClass, injectionTarget, supplierBean, resolvers);
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/JerseyBean.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/JerseyBean.java index 9b94750..c6bd602 100644 --- a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/JerseyBean.java +++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/JerseyBean.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022 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 @@ -133,7 +133,7 @@ return false; } - @Override + // @Override - Removed in CDI 4 public boolean isNullable() { return false; }
diff --git a/media/json-binding/pom.xml b/media/json-binding/pom.xml index f5395b1..3ae3b87 100644 --- a/media/json-binding/pom.xml +++ b/media/json-binding/pom.xml
@@ -78,9 +78,8 @@ <artifactId>jakarta.json.bind-api</artifactId> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jakarta.json</artifactId> - <classifier>module</classifier> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson</artifactId> </dependency> <dependency>
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 c383a09..1e93043 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.11.3 +Jackson JAX-RS Providers version 2.13.0 * License: Apache License, 2.0 * Project: https://github.com/FasterXML/jackson-jaxrs-providers * Copyright: (c) 2009-2011 FasterXML, LLC. All rights reserved unless otherwise indicated. \ No newline at end of file
diff --git a/media/json-jettison/pom.xml b/media/json-jettison/pom.xml index 9e69d15..6ff9a21 100644 --- a/media/json-jettison/pom.xml +++ b/media/json-jettison/pom.xml
@@ -53,7 +53,14 @@ <extensions>true</extensions> <configuration> <instructions> + <!-- Explicitly set versions for packages from GlassFish to allow future uptake of GlassFish 7.x--> + <Import-Package> + jakarta.xml.bind.*;version="[3.0,5)", + * + </Import-Package> + <Export-Package>org.glassfish.jersey.jettison.*</Export-Package> + <!--<Import-Package> com.sun.xml.bind.annotation;resolution:=optional,com.sun.xml.bind.v2.*;resolution:=optional, * </Import-Package>-->
diff --git a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/JettisonJaxbContext.java b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/JettisonJaxbContext.java index f76ab53..6d6b039 100644 --- a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/JettisonJaxbContext.java +++ b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/JettisonJaxbContext.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 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,6 @@ import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Marshaller; import jakarta.xml.bind.Unmarshaller; -import jakarta.xml.bind.Validator; import org.glassfish.jersey.jettison.internal.BaseJsonMarshaller; import org.glassfish.jersey.jettison.internal.BaseJsonUnmarshaller; @@ -300,15 +299,4 @@ public Marshaller createMarshaller() throws JAXBException { return new JettisonJaxbMarshaller(jaxbContext, getJSONConfiguration()); } - - /** - * Simply delegates to underlying JAXBContext implementation. - * - * @return what underlying JAXBContext returns - * @throws jakarta.xml.bind.JAXBException - */ - @Override - public Validator createValidator() throws JAXBException { - return jaxbContext.createValidator(); - } }
diff --git a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/JettisonJaxbMarshaller.java b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/JettisonJaxbMarshaller.java index cd78fc9..4603572 100644 --- a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/JettisonJaxbMarshaller.java +++ b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/JettisonJaxbMarshaller.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 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 @@ -128,17 +128,17 @@ } @Override - public void setAdapter(XmlAdapter adapter) { + public <A extends XmlAdapter<?, ?>> void setAdapter(A adapter) { jaxbMarshaller.setAdapter(adapter); } @Override - public <A extends XmlAdapter> void setAdapter(Class<A> type, A adapter) { + public <A extends XmlAdapter<?, ?>> void setAdapter(Class<A> type, A adapter) { jaxbMarshaller.setAdapter(type, adapter); } @Override - public <A extends XmlAdapter> A getAdapter(Class<A> type) { + public <A extends XmlAdapter<?, ?>> A getAdapter(Class<A> type) { return jaxbMarshaller.getAdapter(type); }
diff --git a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/JettisonJaxbUnmarshaller.java b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/JettisonJaxbUnmarshaller.java index 3d24d50..377e37a 100644 --- a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/JettisonJaxbUnmarshaller.java +++ b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/JettisonJaxbUnmarshaller.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 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 @@ -123,16 +123,6 @@ } @Override - public void setValidating(boolean validating) throws JAXBException { - this.jaxbUnmarshaller.setValidating(validating); - } - - @Override - public boolean isValidating() throws JAXBException { - return this.jaxbUnmarshaller.isValidating(); - } - - @Override public void setEventHandler(ValidationEventHandler validationEventHandler) throws JAXBException { this.jaxbUnmarshaller.setEventHandler(validationEventHandler); } @@ -163,17 +153,17 @@ } @Override - public void setAdapter(XmlAdapter xmlAdapter) { + public <A extends XmlAdapter<?, ?>> void setAdapter(A xmlAdapter) { this.jaxbUnmarshaller.setAdapter(xmlAdapter); } @Override - public <A extends XmlAdapter> void setAdapter(Class<A> type, A adapter) { + public <A extends XmlAdapter<?, ?>> void setAdapter(Class<A> type, A adapter) { this.jaxbUnmarshaller.setAdapter(type, adapter); } @Override - public <A extends XmlAdapter> A getAdapter(Class<A> type) { + public <A extends XmlAdapter<?, ?>> A getAdapter(Class<A> type) { return this.jaxbUnmarshaller.getAdapter(type); }
diff --git a/media/json-processing/pom.xml b/media/json-processing/pom.xml index 10dee3a..373b174 100644 --- a/media/json-processing/pom.xml +++ b/media/json-processing/pom.xml
@@ -57,8 +57,7 @@ <!-- TODO: change to ${range;[==,+);${jsonp.api.version}} once the version is final--> <Import-Package> ${jakarta.annotation.osgi.version}, - jakarta.json.*;version="[0,3)", - org.glassfish.json.*;version="[0,3)", + jakarta.json.*;version="[2,3)", * </Import-Package> </instructions> @@ -81,13 +80,12 @@ <artifactId>jakarta.json-api</artifactId> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jakarta.json</artifactId> - <classifier>module</classifier> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson</artifactId> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jsonp-jaxrs</artifactId> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson-media</artifactId> </dependency> <dependency>
diff --git a/media/json-processing/src/main/java/org/glassfish/jersey/jsonp/JsonProcessingFeature.java b/media/json-processing/src/main/java/org/glassfish/jersey/jsonp/JsonProcessingFeature.java index cc1c913..3f23a73 100644 --- a/media/json-processing/src/main/java/org/glassfish/jersey/jsonp/JsonProcessingFeature.java +++ b/media/json-processing/src/main/java/org/glassfish/jersey/jsonp/JsonProcessingFeature.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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,10 +20,11 @@ import jakarta.ws.rs.core.Feature; import jakarta.ws.rs.core.FeatureContext; +import org.eclipse.parsson.media.JsonValueBodyReader; +import org.eclipse.parsson.media.JsonValueBodyWriter; + import org.glassfish.jersey.CommonProperties; -import org.glassfish.json.jaxrs.JsonValueBodyReader; -import org.glassfish.json.jaxrs.JsonValueBodyWriter; /** * {@link Feature} used to register JSON-P providers.
diff --git a/media/moxy/pom.xml b/media/moxy/pom.xml index 2a36b2c..f590550 100644 --- a/media/moxy/pom.xml +++ b/media/moxy/pom.xml
@@ -101,9 +101,8 @@ <artifactId>jakarta.json.bind-api</artifactId> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jakarta.json</artifactId> - <classifier>module</classifier> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson</artifactId> </dependency> <dependency>
diff --git a/media/multipart/pom.xml b/media/multipart/pom.xml index be32746..bb340e5 100644 --- a/media/multipart/pom.xml +++ b/media/multipart/pom.xml
@@ -92,6 +92,12 @@ <version>${project.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.glassfish.jersey.connectors</groupId> + <artifactId>jersey-jetty-connector</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>junit</groupId> @@ -99,40 +105,4 @@ </dependency> </dependencies> - <profiles> - <profile> - <id>JettyExclude</id> - <activation> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <testExcludes> - <testExclude>org/glassfish/jersey/media/multipart/internal/MultiPartHeaderModificationTest.java</testExclude> - </testExcludes> - </configuration> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>Jetty11</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <dependencies> - <dependency> - <groupId>org.glassfish.jersey.connectors</groupId> - <artifactId>jersey-jetty-connector</artifactId> - <version>${project.version}</version> - <scope>test</scope> - </dependency> - </dependencies> - </profile> - </profiles> - </project>
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/BodyPart.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/BodyPart.java index 0e20e25..7287759 100644 --- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/BodyPart.java +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/BodyPart.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -18,9 +18,11 @@ import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.reflect.Type; import java.text.ParseException; import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.core.GenericType; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.ext.MessageBodyReader; @@ -263,21 +265,42 @@ * entity instance is not the unconverted content of the body part entity. */ public <T> T getEntityAs(final Class<T> clazz) { + return getEntityAs(clazz, clazz); + } + + /** + * Returns the entity after appropriate conversion to the requested type. This is useful only when the containing + * {@link MultiPart} instance has been received, which causes the {@code providers} property to have been set. + * + * @param genericEntity desired entity type into which the entity should be converted. + * @return entity after appropriate conversion to the requested type. + * + * @throws ProcessingException if an IO error arises during reading an entity. + * @throws IllegalArgumentException if no {@link MessageBodyReader} can be found to perform the requested conversion. + * @throws IllegalStateException if this method is called when the {@code providers} property has not been set or when the + * entity instance is not the unconverted content of the body part entity. + */ + <T> T getEntityAs(final GenericType<T> genericEntity) { + return (T) getEntityAs(genericEntity.getRawType(), genericEntity.getType()); + } + + <T> T getEntityAs(final Class<T> type, Type genericType) { if (entity == null || !(entity instanceof BodyPartEntity)) { throw new IllegalStateException(LocalizationMessages.ENTITY_HAS_WRONG_TYPE()); } - if (clazz == BodyPartEntity.class) { - return clazz.cast(entity); + if (type == BodyPartEntity.class) { + return type.cast(entity); } final Annotation[] annotations = new Annotation[0]; - final MessageBodyReader<T> reader = messageBodyWorkers.getMessageBodyReader(clazz, clazz, annotations, mediaType); + final MessageBodyReader<T> reader = messageBodyWorkers.getMessageBodyReader(type, genericType, annotations, mediaType); if (reader == null) { - throw new IllegalArgumentException(LocalizationMessages.NO_AVAILABLE_MBR(clazz, mediaType)); + throw new IllegalArgumentException(LocalizationMessages.NO_AVAILABLE_MBR(type, mediaType)); } try { - return reader.readFrom(clazz, clazz, annotations, mediaType, headers, ((BodyPartEntity) entity).getInputStream()); + return reader.readFrom(type, genericType, annotations, mediaType, headers, + ((BodyPartEntity) entity).getInputStream()); } catch (final IOException ioe) { throw new ProcessingException(LocalizationMessages.ERROR_READING_ENTITY(String.class), ioe); }
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/FormDataBodyPart.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/FormDataBodyPart.java index 2380c44..802cc02 100644 --- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/FormDataBodyPart.java +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/FormDataBodyPart.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 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,9 +16,14 @@ package org.glassfish.jersey.media.multipart; +import java.io.InputStream; import java.text.ParseException; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.core.EntityPart; +import jakarta.ws.rs.core.GenericType; import jakarta.ws.rs.core.MediaType; import org.glassfish.jersey.media.multipart.internal.LocalizationMessages; @@ -59,9 +64,10 @@ * @author Paul Sandoz * @author Michal Gajdos */ -public class FormDataBodyPart extends BodyPart { +public class FormDataBodyPart extends BodyPart implements EntityPart { private final boolean fileNameFix; + protected final AtomicBoolean contentRead = new AtomicBoolean(false); /** * Instantiates an unnamed new {@link FormDataBodyPart} with a @@ -231,6 +237,34 @@ return formDataContentDisposition.getName(); } + @Override + public Optional<String> getFileName() { + return Optional.ofNullable(getFormDataContentDisposition().getFileName()); + } + + @Override + public InputStream getContent() { + return getContent(InputStream.class); + } + + @Override + public <T> T getContent(Class<T> type) { + if (contentRead.compareAndExchange(false, true)) { + throw new IllegalStateException(LocalizationMessages.CONTENT_HAS_BEEN_ALREADY_READ()); + } + final Object entity = getEntity(); + return type.isInstance(entity) ? type.cast(entity) : getEntityAs(type); + } + + @Override + public <T> T getContent(GenericType<T> type) { + if (contentRead.compareAndExchange(false, true)) { + throw new IllegalStateException(LocalizationMessages.CONTENT_HAS_BEEN_ALREADY_READ()); + } + final Object entity = getEntity(); + return type.getRawType().isInstance(entity) ? (T) entity : getEntityAs(type); + } + /** * Sets the control name. *
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/JerseyEntityPartBuilderProvider.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/JerseyEntityPartBuilderProvider.java new file mode 100644 index 0000000..61583d1 --- /dev/null +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/JerseyEntityPartBuilderProvider.java
@@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.media.multipart; + +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.EntityPart; +import jakarta.ws.rs.core.GenericEntity; +import jakarta.ws.rs.core.GenericType; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import org.glassfish.jersey.innate.spi.EntityPartBuilderProvider; +import org.glassfish.jersey.media.multipart.file.FileDataBodyPart; +import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart; +import org.glassfish.jersey.media.multipart.internal.LocalizationMessages; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +/** + * Jersey implementation of {@link EntityPart.Builder}. + * @since 3.1.0 + */ +public class JerseyEntityPartBuilderProvider implements EntityPartBuilderProvider { + + @Override + public EntityPart.Builder withName(String partName) { + return new EnityPartBuilder().withName(partName); + } + + private static class EnityPartBuilder implements EntityPart.Builder { + + private String partName; + private String fileName = null; + private MultivaluedMap<String, String> headers = new MultivaluedHashMap<>(); + private MediaType mediaType = null; + private MethodData methodData; + + private EntityPart.Builder withName(String partName) { + this.partName = partName; + return this; + } + + @Override + public EntityPart.Builder mediaType(MediaType mediaType) throws IllegalArgumentException { + this.mediaType = mediaType; + return this; + } + + @Override + public EntityPart.Builder mediaType(String mediaTypeString) throws IllegalArgumentException { + this.mediaType = MediaType.valueOf(mediaTypeString); + return this; + } + + @Override + public EntityPart.Builder header(String headerName, String... headerValues) throws IllegalArgumentException { + this.headers.addAll(headerName, headerValues); + return this; + } + + @Override + public EntityPart.Builder headers(MultivaluedMap<String, String> newHeaders) throws IllegalArgumentException { + for (Map.Entry<String, List<String>> entry : newHeaders.entrySet()) { + header(entry.getKey(), entry.getValue().toArray(new String[0])); + } + return this; + } + + @Override + public EntityPart.Builder fileName(String fileName) throws IllegalArgumentException { + this.fileName = fileName; + return this; + } + + @Override + public EntityPart.Builder content(InputStream content) throws IllegalArgumentException { + methodData = new InputStreamMethodData(content); + return this; + } + + @Override + public <T> EntityPart.Builder content(T content, Class<? extends T> type) throws IllegalArgumentException { + if (File.class.equals(type)) { + methodData = new FileMethodData((File) content); + } else if (InputStream.class.equals(type)) { + methodData = new InputStreamMethodData((InputStream) content); + } else { + methodData = new GenericData(content, null); + } + return this; + } + + @Override + public <T> EntityPart.Builder content(T content, GenericType<T> type) throws IllegalArgumentException { + if (File.class.equals(type.getRawType())) { + methodData = new FileMethodData((File) content); + } else if (InputStream.class.equals(type.getRawType())) { + methodData = new InputStreamMethodData((InputStream) content); + } else { + methodData = new GenericData(content, type); + } + return this; + } + + @Override + public EntityPart build() throws IllegalStateException, IOException, WebApplicationException { + if (methodData == null) { + throw new IllegalStateException(LocalizationMessages.ENTITY_CONTENT_NOT_SET()); + } + final FormDataBodyPart bodyPart = methodData.build(); + return bodyPart; + } + + + private abstract class MethodData<T> { + protected MethodData(T content) { + this.content = content; + } + protected final T content; + protected abstract FormDataBodyPart build(); + protected void fillFormData(FormDataBodyPart bodyPart) { + FormDataContentDisposition contentDisposition = + FormDataContentDisposition.name(partName).fileName(fileName).build(); + bodyPart.setContentDisposition(contentDisposition); + if (mediaType != null) { + bodyPart.setMediaType(mediaType); + } + for (Map.Entry<String, List<String>> entry : headers.entrySet()) { + bodyPart.getHeaders().addAll(entry.getKey(), entry.getValue().toArray(new String[0])); + } + } + } + + private class InputStreamMethodData extends MethodData<InputStream> { + protected InputStreamMethodData(InputStream content) { + super(content); + } + + @Override + protected FormDataBodyPart build() { + final StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart(); + streamDataBodyPart.setFilename(fileName); + fillFormData(streamDataBodyPart); + streamDataBodyPart.setStreamEntity(content); + return streamDataBodyPart; + } + } + + private class FileMethodData extends MethodData<File> { + protected FileMethodData(File content) { + super(content); + } + + @Override + protected FormDataBodyPart build() { + final FileDataBodyPart fileDataBodyPart = new FileDataBodyPart(); + fillFormData(fileDataBodyPart); + fileDataBodyPart.setFileEntity(content); + return fileDataBodyPart; + } + } + + private class GenericData extends MethodData<Object> { + private final GenericType<?> genericEntity; + + protected GenericData(Object content, GenericType<?> genericEntity) { + super(content); + this.genericEntity = genericEntity; + } + + @Override + protected FormDataBodyPart build() { + final FormDataBodyPart formDataBodyPart = new FormDataBodyPart(); + fillFormData(formDataBodyPart); + if (genericEntity != null && !GenericEntity.class.isInstance(content)) { + GenericEntity entity = new GenericEntity(content, genericEntity.getType()); + formDataBodyPart.setEntity(entity); + } else { + formDataBodyPart.setEntity(content); + } + + return formDataBodyPart; + } + } + } +}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeature.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeature.java index 3224ec5..03bf661 100644 --- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeature.java +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeature.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -20,6 +20,8 @@ import jakarta.ws.rs.core.Feature; import jakarta.ws.rs.core.FeatureContext; +import org.glassfish.jersey.media.multipart.internal.EntityPartReader; +import org.glassfish.jersey.media.multipart.internal.EntityPartWriter; import org.glassfish.jersey.media.multipart.internal.FormDataParamInjectionFeature; import org.glassfish.jersey.media.multipart.internal.MultiPartReaderClientSide; import org.glassfish.jersey.media.multipart.internal.MultiPartReaderServerSide; @@ -45,6 +47,9 @@ context.register(MultiPartWriter.class); + context.register(EntityPartReader.class); + context.register(EntityPartWriter.class); + return true; } }
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeatureAutodiscoverable.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeatureAutodiscoverable.java new file mode 100644 index 0000000..f9cfb9d --- /dev/null +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeatureAutodiscoverable.java
@@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.media.multipart; + +import jakarta.ws.rs.core.FeatureContext; +import org.glassfish.jersey.internal.spi.AutoDiscoverable; + +/** + * Automatic registration of {@link MultiPartFeature}. + * @since 3.1.0 + */ +public class MultiPartFeatureAutodiscoverable implements AutoDiscoverable { + @Override + public void configure(FeatureContext context) { + context.register(MultiPartFeature.class); + } +}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/DefaultMediaTypePredictor.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/DefaultMediaTypePredictor.java index f91c723..1493e8f 100644 --- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/DefaultMediaTypePredictor.java +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/DefaultMediaTypePredictor.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 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 @@ -38,6 +38,7 @@ * Currently supported file extension and MIME Types are - * <ul> * <li>".xml" - application/xml</li> + * <li>".json - application/json</li> * <li>".txt" - text/plain</li> * <li>".pdf" - application/pdf</li> * <li>".htm" - text/html</li> @@ -59,6 +60,7 @@ public enum CommonMediaTypes { XML(".xml", MediaType.APPLICATION_XML_TYPE), + JSON(".json", MediaType.APPLICATION_JSON_TYPE), TXT(".txt", MediaType.TEXT_PLAIN_TYPE), HTM(".htm", MediaType.TEXT_HTML_TYPE), HTML(".html", MediaType.TEXT_HTML_TYPE),
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.java index 3ddedf2..707bedf 100644 --- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.java +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -18,6 +18,7 @@ import java.io.InputStream; import java.text.MessageFormat; +import java.util.Optional; import jakarta.ws.rs.core.MediaType; @@ -275,4 +276,8 @@ return filename; } + @Override + public Optional<String> getFileName() { + return Optional.ofNullable(getFilename()); + } }
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartReader.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartReader.java new file mode 100644 index 0000000..864cec4 --- /dev/null +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartReader.java
@@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.media.multipart.internal; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.EntityPart; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.ext.MessageBodyReader; +import jakarta.ws.rs.ext.Providers; +import org.glassfish.jersey.internal.util.ReflectionHelper; +import org.glassfish.jersey.media.multipart.BodyPart; +import org.glassfish.jersey.media.multipart.FormDataBodyPart; +import org.glassfish.jersey.media.multipart.JerseyEntityPartBuilderProvider; +import org.glassfish.jersey.media.multipart.MultiPart; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.LinkedList; +import java.util.List; + +/** + * Reader supporting List<EntityPart> Make sure {@code GenericEntity} class is used when reading the + * {@link EntityPart}s. + * @since 3.1.0 + */ +@Consumes(MediaType.MULTIPART_FORM_DATA) +@Singleton +public class EntityPartReader implements MessageBodyReader<List<EntityPart>> { + + private MultiPartReaderClientSide multiPartReaderClientSide; + + @Inject + Providers providers; + + @Override + public boolean isReadable(Class<?> type, Type generic, Annotation[] annotations, MediaType mediaType) { + return List.class.isAssignableFrom(type) + && ParameterizedType.class.isInstance(generic) + && EntityPart.class.isAssignableFrom(ReflectionHelper.getGenericTypeArgumentClasses(generic).get(0)); + } + + @Override + public List<EntityPart> readFrom(Class<List<EntityPart>> type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap<String, String> httpHeaders, + InputStream entityStream) throws IOException, WebApplicationException { + + if (multiPartReaderClientSide == null) { + multiPartReaderClientSide = (MultiPartReaderClientSide) providers.getMessageBodyReader( + MultiPart.class, MultiPart.class, new Annotation[0], MediaType.MULTIPART_FORM_DATA_TYPE); + } + + final MultiPart multiPart = multiPartReaderClientSide.readFrom( + MultiPart.class, MultiPart.class, annotations, mediaType, httpHeaders, entityStream); + final List<BodyPart> bodyParts = multiPart.getBodyParts(); + final List<EntityPart> entityParts = new LinkedList<>(); + + for (BodyPart bp : bodyParts) { + if (FormDataBodyPart.class.isInstance(bp)) { + entityParts.add((EntityPart) bp); + } else { + final EntityPart ep = new JerseyEntityPartBuilderProvider().withName("") + .mediaType(bp.getMediaType()).content(bp.getEntity()).headers(bp.getHeaders()).build(); + entityParts.add(ep); + } + } + + return entityParts; + } +}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartWriter.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartWriter.java new file mode 100644 index 0000000..f5c3472 --- /dev/null +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartWriter.java
@@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.media.multipart.internal; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.EntityPart; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.ext.MessageBodyWriter; +import jakarta.ws.rs.ext.Providers; +import org.glassfish.jersey.internal.util.ReflectionHelper; +import org.glassfish.jersey.media.multipart.BodyPart; +import org.glassfish.jersey.media.multipart.MultiPart; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; + +/** + * Writer supporting List<EntityPart> Make sure {@code GenericEntity} class is used when sending the + * {@link EntityPart}s. + * @since 3.1.0 + */ +@Produces(MediaType.MULTIPART_FORM_DATA) +@Singleton +public class EntityPartWriter implements MessageBodyWriter<List<EntityPart>> { + + private MultiPartWriter multiPartWriter; + + @Inject + Providers providers; + + @Override + public boolean isWriteable(Class<?> type, Type generic, Annotation[] annotations, MediaType mediaType) { + return List.class.isAssignableFrom(type) + && ParameterizedType.class.isInstance(generic) + && EntityPart.class.isAssignableFrom(ReflectionHelper.getGenericTypeArgumentClasses(generic).get(0)); + } + + @Override + public void writeTo(List<EntityPart> entityParts, Class<?> type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) + throws IOException, WebApplicationException { + final MultiPart multiPart = new MultiPart(); + multiPart.setMediaType(mediaType); + for (EntityPart ep : entityParts) { + multiPart.bodyPart((BodyPart) ep); + } + + if (multiPartWriter == null) { + multiPartWriter = (MultiPartWriter) providers.getMessageBodyWriter( + MultiPart.class, MultiPart.class, new Annotation[0], MediaType.MULTIPART_FORM_DATA_TYPE); + } + + multiPartWriter.writeTo(multiPart, MultiPart.class, MultiPart.class, + annotations, multiPart.getMediaType(), httpHeaders, entityStream); + } +}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java index d95b320..6c834d9 100644 --- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -31,6 +31,8 @@ import java.util.stream.Collectors; import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.core.EntityPart; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.ext.MessageBodyReader; @@ -59,7 +61,7 @@ /** * Value supplier provider supporting the {@link FormDataParam} injection annotation and entity ({@link FormDataMultiPart}) - * injection. + * injection. Also supports {@link FormParam} {@code EntityPart} annotation injection. * * @author Craig McClanahan * @author Paul Sandoz @@ -311,6 +313,43 @@ } } + /** + * Provider supplier for list of {@link EntityPart} types injected via + * {@link jakarta.ws.rs.FormParam} annotation. + */ + private final class ListEntityPartValueProvider extends ValueProvider<List<EntityPart>> { + + private final String name; + + public ListEntityPartValueProvider(final String name) { + this.name = name; + } + + @Override + public List<EntityPart> apply(ContainerRequest request) { + return (List<EntityPart>) (List<?>) getEntity(request).getFields(name); + } + } + + /** + * Provider supplier for list of {@link EntityPart} types injected via + * {@link jakarta.ws.rs.FormParam} annotation. + */ + private final class EntityPartValueProvider extends ValueProvider<EntityPart> { + + private final String name; + + public EntityPartValueProvider(final String name) { + this.name = name; + } + + @Override + public EntityPart apply(ContainerRequest request) { + List<FormDataBodyPart> bodyParts = getEntity(request).getFields(name); + return bodyParts.size() != 0 ? bodyParts.get(0) : null; + } + } + private static final Set<Class<?>> TYPES = initializeTypes(); private static Set<Class<?>> initializeTypes() { @@ -344,7 +383,7 @@ * @param extractorProvider multi-valued map parameter extractor provider. */ public FormDataParamValueParamProvider(Provider<MultivaluedParameterExtractorProvider> extractorProvider) { - super(extractorProvider, Parameter.Source.ENTITY, Parameter.Source.UNKNOWN); + super(extractorProvider, Parameter.Source.ENTITY, Parameter.Source.FORM, Parameter.Source.UNKNOWN); } @Override @@ -386,6 +425,16 @@ } else { return new FormDataParamValueProvider(parameter, get(parameter)); } + } else if (FormParam.class.equals(parameter.getSourceAnnotation().annotationType())) { + final String paramName = parameter.getSourceName(); + if (Collection.class == rawType || List.class == rawType) { + final Class clazz = ReflectionHelper.getGenericTypeArgumentClasses(parameter.getType()).get(0); + if (EntityPart.class.equals(clazz)) { + return new ListEntityPartValueProvider(paramName); + } + } else if (EntityPart.class.equals(rawType)) { + return new EntityPartValueProvider(paramName); + } } return null;
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartWriter.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartWriter.java index 4e155f8..011096a 100644 --- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartWriter.java +++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartWriter.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 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.Produces; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.GenericEntity; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MultivaluedMap; @@ -195,14 +196,21 @@ bodyEntity = ((BodyPartEntity) bodyEntity).getInputStream(); } + Type bodyType = bodyClass; + if (GenericEntity.class.isInstance(bodyEntity)) { + bodyClass = ((GenericEntity<?>) bodyEntity).getRawType(); + bodyType = ((GenericEntity) bodyEntity).getType(); + bodyEntity = ((GenericEntity<?>) bodyEntity).getEntity(); + } + final MessageBodyWriter bodyWriter = providers.getMessageBodyWriter( bodyClass, - bodyClass, + bodyType, EMPTY_ANNOTATIONS, bodyMediaType); if (bodyWriter == null) { - throw new IllegalArgumentException(LocalizationMessages.NO_AVAILABLE_MBW(bodyClass, mediaType)); + throw new IllegalArgumentException(LocalizationMessages.NO_AVAILABLE_MBW(bodyClass, bodyMediaType)); } bodyWriter.writeTo(
diff --git a/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.innate.spi.EntityPartBuilderProvider b/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.innate.spi.EntityPartBuilderProvider new file mode 100644 index 0000000..32738ac --- /dev/null +++ b/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.innate.spi.EntityPartBuilderProvider
@@ -0,0 +1 @@ +org.glassfish.jersey.media.multipart.JerseyEntityPartBuilderProvider \ No newline at end of file
diff --git a/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable b/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable new file mode 100644 index 0000000..04a6776 --- /dev/null +++ b/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
@@ -0,0 +1 @@ +org.glassfish.jersey.media.multipart.MultiPartFeatureAutodiscoverable \ No newline at end of file
diff --git a/media/multipart/src/main/resources/org/glassfish/jersey/media/multipart/internal/localization.properties b/media/multipart/src/main/resources/org/glassfish/jersey/media/multipart/internal/localization.properties index 851c026..904214d 100644 --- a/media/multipart/src/main/resources/org/glassfish/jersey/media/multipart/internal/localization.properties +++ b/media/multipart/src/main/resources/org/glassfish/jersey/media/multipart/internal/localization.properties
@@ -1,5 +1,5 @@ # -# Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2021 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 @@ -15,6 +15,8 @@ # cannot.inject.file=Cannot provide file for an entity body part. +content.has.been.already.read=Content method has already been invoked. +entity.content.not.set=EntityPart content is not set. entity.has.wrong.type=Entity instance does not contain the unconverted content. error.parsing.content.disposition=Error parsing content disposition: {0} error.reading.entity=Error reading entity as {0}.
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/file/FileDataBodyPartTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/file/FileDataBodyPartTest.java index d2acefb..ca040cb 100644 --- a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/file/FileDataBodyPartTest.java +++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/file/FileDataBodyPartTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 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 @@ -86,6 +86,12 @@ fdbp.setFileEntity(file, expectedType); checkEntityAttributes(name, fdbp, file, expectedType); + file = new File("pom.json"); + name = "json"; + fdbp = new FileDataBodyPart(name, file); + expectedType = DefaultMediaTypePredictor.CommonMediaTypes.JSON.getMediaType(); + checkEntityAttributes(name, fdbp, file, expectedType); + file = new File("pom.png"); name = "png"; fdbp = new FileDataBodyPart("png", file);
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartTest.java new file mode 100644 index 0000000..91e3368 --- /dev/null +++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartTest.java
@@ -0,0 +1,372 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.media.multipart.internal; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.EntityPart; +import jakarta.ws.rs.core.GenericEntity; +import jakarta.ws.rs.core.GenericType; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.MessageBodyReader; +import jakarta.ws.rs.ext.MessageBodyWriter; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.internal.util.ReflectionHelper; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Assert; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +public class EntityPartTest extends JerseyTest { + + private static final GenericType<List<EntityPart>> LIST_ENTITY_PART_TYPE = new GenericType<List<EntityPart>>(){}; + private static final GenericType<AtomicReference<String>> ATOMIC_REFERENCE_GENERIC_TYPE = new GenericType<>(){}; + + @Path("/") + public static class EntityPartTestResource { + @GET + public Response getResponse() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("part-01").content("TEST1").build()); + list.add(EntityPart.withName("part-02").content("TEST2").build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build(); + } + + @POST + @Path("/postList") + public String postEntityPartList(List<EntityPart> list) throws IOException { + String entity = list.get(0).getContent(String.class) + list.get(1).getContent(String.class); + return entity; + } + + @POST + @Path("/postForm") + public String postEntityPartForm(@FormParam("part-01") EntityPart part1, @FormParam("part-02") EntityPart part2) + throws IOException { + String entity = part1.getContent(String.class) + part2.getContent(String.class); + return entity; + } + + @POST + @Path("/postListForm") + public String postEntityPartForm(@FormParam("part-0x") List<EntityPart> part) + throws IOException { + String entity = part.get(0).getContent(String.class) + part.get(1).getContent(String.class); + return entity; + } + + @POST + @Path("/postStreams") + public Response postEntityStreams(@FormParam("name1") EntityPart part1, + @FormParam("name2") EntityPart part2, + @FormParam("name3") EntityPart part3) throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName(part1.getName()).fileName(part1.getFileName().get()).content( + new ByteArrayInputStream(part1.getContent(String.class).getBytes(StandardCharsets.UTF_8))).build()); + list.add(EntityPart.withName(part2.getName()).fileName(part2.getFileName().get()).content( + new ByteArrayInputStream(part2.getContent(String.class).getBytes(StandardCharsets.UTF_8))).build()); + list.add(EntityPart.withName(part3.getName()).fileName(part3.getFileName().get()) + .content(part3.getContent(StringHolder.class), StringHolder.class) + .mediaType(part3.getMediaType()).build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build(); + } + + @POST + @Path("/postHeaders") + public Response postEntityStreams(@FormParam("name1") EntityPart part1) throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName(part1.getName()).content(part1.getContent(String.class)) + .headers(part1.getHeaders()).build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build(); + } + + @POST + @Path("/postGeneric") + public Response postGeneric(@FormParam("name1") EntityPart part1) throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName(part1.getName()) + .content(part1.getContent(ATOMIC_REFERENCE_GENERIC_TYPE), ATOMIC_REFERENCE_GENERIC_TYPE) + .mediaType(MediaType.TEXT_PLAIN_TYPE) + .build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build(); + } + + @POST + @Path("/postFormVarious") + public Response postFormVarious(@FormParam("name1") EntityPart part1, + @FormParam("name2") InputStream part2, + @FormParam("name3") String part3) throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName(part1.getName()) + .content(part1.getContent(String.class) + new String(part2.readAllBytes()) + part3) + .mediaType(MediaType.TEXT_PLAIN_TYPE) + .build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build(); + } + + @GET + @Produces(MediaType.MULTIPART_FORM_DATA) + @Path("/getList") + public List<EntityPart> getList() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("name1").content("data1").build()); + return list; + } + } + + public static class StringHolder extends AtomicReference<String> { + StringHolder(String name) { + set(name); + } + } + + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + public static class StringHolderWorker implements MessageBodyReader<StringHolder>, MessageBodyWriter<StringHolder> { + + @Override + public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return type == StringHolder.class; + } + + @Override + public StringHolder readFrom(Class<StringHolder> type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap<String, String> httpHeaders, + InputStream entityStream) throws IOException, WebApplicationException { + final StringHolder holder = new StringHolder(new String(entityStream.readAllBytes())); + return holder; + } + + @Override + public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return type == StringHolder.class; + } + + @Override + public void writeTo(StringHolder s, Class<?> type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, + OutputStream entityStream) throws IOException, WebApplicationException { + entityStream.write(s.get().getBytes(StandardCharsets.UTF_8)); + } + } + + @Override + protected Application configure() { + return new ResourceConfig(EntityPartTestResource.class, + StringHolderWorker.class, AtomicReferenceProvider.class) + .property(ServerProperties.WADL_FEATURE_DISABLE, true); + } + + @Override + protected void configureClient(ClientConfig config) { + config.register(StringHolderWorker.class).register(AtomicReferenceProvider.class); + } + + @Test + public void getEntityPartListTest() throws IOException { + try (Response response = target().request().get()) { + List<EntityPart> list = response.readEntity(LIST_ENTITY_PART_TYPE); + Assert.assertEquals("TEST1", list.get(0).getContent(String.class)); + Assert.assertEquals("TEST2", list.get(1).getContent(String.class)); + } + } + + @Test + public void postEntityPartListTest() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("part-01").content("TEST").build()); + list.add(EntityPart.withName("part-02").content("1").build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + try (Response response = target("/postList").request().post(entity)) { + Assert.assertEquals("TEST1", response.readEntity(String.class)); + } + } + + @Test + public void postEntityPartFormParamTest() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("part-01").content("TEST").build()); + list.add(EntityPart.withName("part-02").content("1").build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + try (Response response = target("/postForm").request().post(entity)) { + Assert.assertEquals("TEST1", response.readEntity(String.class)); + } + } + + @Test + public void postListEntityPartFormParamTest() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("part-0x").content("TEST").build()); + list.add(EntityPart.withName("part-0x").content("1").build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + try (Response response = target("/postListForm").request().post(entity)) { + Assert.assertEquals("TEST1", response.readEntity(String.class)); + } + } + + @Test + public void postEntityPartStreamsTest() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("name1").fileName("file1.doc").content( + new ByteArrayInputStream("data1".getBytes(StandardCharsets.UTF_8))).build()); + list.add(EntityPart.withName("name2").fileName("file2.doc").content( + new ByteArrayInputStream("data2".getBytes(StandardCharsets.UTF_8))).build()); + list.add(EntityPart.withName("name3").fileName("file3.xml") + .content(new StringHolder("data3"), StringHolder.class) + .mediaType(MediaType.TEXT_PLAIN_TYPE).build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) { + }; + Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + + try (Response response = target("/postStreams").request().post(entity)) { + List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE); + + EntityPart part1 = result.get(0); + Assert.assertEquals("name1", part1.getName()); + Assert.assertEquals("file1.doc", part1.getFileName().get()); + Assert.assertEquals("data1", part1.getContent(String.class)); + + EntityPart part2 = result.get(1); + Assert.assertEquals("name2", part2.getName()); + Assert.assertEquals("file2.doc", part2.getFileName().get()); + Assert.assertEquals("data2", part2.getContent(String.class)); + + EntityPart part3 = result.get(2); + Assert.assertEquals("name3", part3.getName()); + Assert.assertEquals("file3.xml", part3.getFileName().get()); + Assert.assertEquals("data3", part3.getContent(String.class)); + Assert.assertEquals(MediaType.TEXT_PLAIN_TYPE, part3.getMediaType()); + } + } + + @Test + public void postHeaderTest() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("name1").content("data1") + .header("header-01", "value-01").build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + try (Response response = target("/postHeaders").request().post(entity)) { + List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE); + Assert.assertEquals("value-01", result.get(0).getHeaders().getFirst("header-01")); + Assert.assertEquals("data1", result.get(0).getContent(String.class)); + } + } + + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.TEXT_PLAIN) + public static class AtomicReferenceProvider implements + MessageBodyReader<AtomicReference<String>>, + MessageBodyWriter<AtomicReference<String>> { + + @Override + public boolean isReadable(Class<?> type, Type generic, Annotation[] annotations, MediaType mediaType) { + return type == AtomicReference.class + && ParameterizedType.class.isInstance(generic) + && String.class.isAssignableFrom(ReflectionHelper.getGenericTypeArgumentClasses(generic).get(0)); + } + + @Override + public AtomicReference<String> readFrom(Class<AtomicReference<String>> type, Type genericType, + Annotation[] annotations, MediaType mediaType, + MultivaluedMap<String, String> httpHeaders, InputStream entityStream) + throws IOException, WebApplicationException { + return new AtomicReference<String>(new String(entityStream.readAllBytes(), StandardCharsets.UTF_8)); + } + + @Override + public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return isReadable(type, genericType, annotations, mediaType); + } + + @Override + public void writeTo(AtomicReference<String> stringAtomicReference, Class<?> type, Type genericType, + Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, + OutputStream entityStream) throws IOException, WebApplicationException { + entityStream.write(stringAtomicReference.get().getBytes(StandardCharsets.UTF_8)); + } + } + + @Test + public void genericEntityTest() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("name1") + .content(new AtomicReference<String>("data1"), ATOMIC_REFERENCE_GENERIC_TYPE) + .mediaType(MediaType.TEXT_PLAIN_TYPE) + .build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; + Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + try (Response response = target("/postGeneric").request().post(entity)) { + List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE); + Assert.assertEquals("data1", result.get(0).getContent(String.class)); + } + } + + @Test + public void postVariousTest() throws IOException { + List<EntityPart> list = new LinkedList<>(); + list.add(EntityPart.withName("name1").content("Hello ").build()); + list.add(EntityPart.withName("name2").content("world").build()); + list.add(EntityPart.withName("name3").content("!").build()); + GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) { + }; + Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE); + + try (Response response = target("/postFormVarious").request().post(entity)) { + List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE); + Assert.assertEquals("Hello world!", result.get(0).getContent(String.class)); + } + } + + @Test + public void getListTest() throws IOException { + try (Response response = target("/getList").request().get()) { + List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE); + Assert.assertEquals("data1", result.get(0).getContent(String.class)); + } + } +}
diff --git a/pom.xml b/pom.xml index 5305302..20535cc 100644 --- a/pom.xml +++ b/pom.xml
@@ -453,7 +453,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> - <version>3.2.0</version> + <version>3.3.2</version> <configuration> <doctitle>Jersey ${jersey.version} API Documentation</doctitle> <windowtitle>Jersey ${jersey.version} API</windowtitle> @@ -672,7 +672,7 @@ <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> - <version>3.5.0</version> + <version>5.1.4</version> <extensions>true</extensions> <configuration> <instructions> @@ -1531,6 +1531,11 @@ <artifactId>jakarta.activation-api</artifactId> <version>${jakarta.activation-api.version}</version> </dependency> + <dependency> + <groupId>jakarta.servlet</groupId> + <artifactId>jakarta.servlet-api</artifactId> + <version>${servlet6.version}</version> + </dependency> <dependency> <groupId>com.sun.activation</groupId> @@ -1841,14 +1846,13 @@ <version>${jakarta.jsonp.version}</version> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jakarta.json</artifactId> - <classifier>module</classifier> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson</artifactId> <version>${jsonp.ri.version}</version> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jsonp-jaxrs</artifactId> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson-media</artifactId> <version>${jsonp.jaxrs.version}</version> </dependency> @@ -1974,6 +1978,19 @@ <version>6.9.6</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>3.21.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <version>4.1.1</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.hamcrest</groupId> @@ -2071,7 +2088,7 @@ <findbugs.glassfish.logging.validLoggerPrefixes> javax.enterprise </findbugs.glassfish.logging.validLoggerPrefixes> - <java.version>1.8</java.version> + <java.version>11</java.version> <!-- <jersey.repackaged.prefix>jersey.repackaged</jersey.repackaged.prefix>--> <!-- <netbeans.hint.license>gf-cddl-gpl</netbeans.hint.license>--> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> @@ -2120,13 +2137,10 @@ <hamcrest.version>2.2</hamcrest.version> <!--<helidon.version>1.0.3</helidon.version>--> <xmlunit.version>1.6</xmlunit.version> - <hk2.osgi.version>org.glassfish.hk2.*;version="[2.5,4)"</hk2.osgi.version> - <hk2.jvnet.osgi.version>org.jvnet.hk2.*;version="[2.5,4)"</hk2.jvnet.osgi.version> - <hk2.config.version>6.0.0</hk2.config.version> <httpclient.version>4.5.13</httpclient.version> <httpclient5.version>5.1.2</httpclient5.version> <jackson.version>2.13.0</jackson.version> - <javassist.version>3.25.0-GA</javassist.version> + <javassist.version>3.28.0-GA</javassist.version> <jboss.logging.version>3.3.0.Final</jboss.logging.version> <jersey1.version>1.19.3</jersey1.version> <jersey1.last.final.version>${jersey1.version}</jersey1.last.final.version> @@ -2149,19 +2163,20 @@ <paxexam.mvn.plugin.version>1.2.4</paxexam.mvn.plugin.version> <rxjava.version>1.2.5</rxjava.version> <rxjava2.version>2.0.4</rxjava2.version> - <servlet3.version>3.0.1</servlet3.version> + <servlet4.version>4.0.3</servlet4.version> + <servlet6.version>6.0.0</servlet6.version> + <simple.version>6.0.1</simple.version> <skip.e2e>false</skip.e2e> <slf4j.version>1.7.21</slf4j.version> <spring6.version>6.0.0-M3</spring6.version> <surefire.version>3.0.0-M6</surefire.version> - <validation.impl.version>6.2.0.Final</validation.impl.version> <!-- Jakartified, eligible for CQ --> - <weld.version>4.0.2.Final</weld.version> + <weld.version>5.0.0.CR2</weld.version> <weld3.version>3.1.7.SP1</weld3.version> - <validation.impl.version>7.0.1.Final</validation.impl.version> + <validation.impl.version>8.0.0.Alpha1</validation.impl.version> <!-- END of Jakartified, eligible for CQ --> <xerces.version>2.12.2</xerces.version> @@ -2169,41 +2184,43 @@ <graalvm.version>20.3.2</graalvm.version> <!-- do not need CQs (below this line till the end of version properties)--> - <gf.impl.version>6.0.0</gf.impl.version> + <gf.impl.version>7.0.0-M2</gf.impl.version> <!-- Jakartified --> - <cdi.api.version>3.0.0</cdi.api.version> + <cdi.api.version>4.0.0</cdi.api.version> <ejb.version>4.0.0</ejb.version> - <grizzly2.version>3.0.1</grizzly2.version> + <grizzly2.version>4.0.0-M1</grizzly2.version> <grizzly.npn.version>2.0.0</grizzly.npn.version> - <hk2.version>3.0.2</hk2.version> - <jsp.version>3.0.0</jsp.version> - <jstl.version>2.0.0</jstl.version> + <hk2.version>3.0.3</hk2.version> + <hk2.osgi.version>org.glassfish.hk2.*;version="[3.0,4)"</hk2.osgi.version> + <hk2.jvnet.osgi.version>org.jvnet.hk2.*;version="[3.0,4)"</hk2.jvnet.osgi.version> + <hk2.config.version>7.0.0-M2</hk2.config.version> + <jsp.version>3.1.0</jsp.version> + <jstl.version>3.0.0</jstl.version> <jta.api.version>2.0.0</jta.api.version> - <servlet5.version>5.0.0</servlet5.version> <istack.commons.runtime.version>4.0.0</istack.commons.runtime.version> - <jakarta.activation-api.version>2.0.1</jakarta.activation-api.version> + <jakarta.activation-api.version>2.1.0</jakarta.activation-api.version> <jakarta.activation.version>2.0.1</jakarta.activation.version> - <jakarta.el.version>4.0.0</jakarta.el.version> - <jakarta.el.impl.version>4.0.2</jakarta.el.impl.version> + <jakarta.el.version>5.0.0</jakarta.el.version> + <jakarta.el.impl.version>5.0.0-M1</jakarta.el.impl.version> <jakarta.annotation.osgi.version>jakarta.annotation.*;version="[2.0,3)"</jakarta.annotation.osgi.version> - <jakarta.annotation.version>2.0.0</jakarta.annotation.version> + <jakarta.annotation.version>2.1.0</jakarta.annotation.version> <jakarta.inject.version>2.0.1</jakarta.inject.version> - <jakarta.interceptor.version>2.0.0</jakarta.interceptor.version> - <jakarta.jsonp.version>2.0.1</jakarta.jsonp.version> - <jakarta.persistence.version>3.0.0</jakarta.persistence.version> - <jakarta.validation.api.version>3.0.0</jakarta.validation.api.version> <!--Can't be updated to 3.0.1 /OSGi incompatibility with JDK 1.8 --> - <jakarta.jaxb.api.version>3.0.1</jakarta.jaxb.api.version> - <jaxb.ri.version>3.0.2</jaxb.ri.version> - <jaxrs.api.spec.version>3.0</jaxrs.api.spec.version> - <jaxrs.api.impl.version>3.0.0</jaxrs.api.impl.version> + <jakarta.interceptor.version>2.1.0</jakarta.interceptor.version> + <jakarta.jsonp.version>2.1.0</jakarta.jsonp.version> + <jakarta.persistence.version>3.1.0</jakarta.persistence.version> + <jakarta.validation.api.version>3.0.1</jakarta.validation.api.version> + <jakarta.jaxb.api.version>4.0.0</jakarta.jaxb.api.version> + <jaxb.ri.version>4.0.0-M3</jaxb.ri.version> + <jaxrs.api.spec.version>3.1</jaxrs.api.spec.version> + <jaxrs.api.impl.version>3.1.0</jaxrs.api.impl.version> <jetty.version>11.0.7</jetty.version> <jetty.plugin.version>11.0.7</jetty.plugin.version> <jetty.servlet.api.25.version>6.1.14</jetty.servlet.api.25.version> - <jsonb.api.version>2.0.0</jsonb.api.version> - <jsonp.ri.version>2.0.1</jsonp.ri.version> - <jsonp.jaxrs.version>2.0.1</jsonp.jaxrs.version> - <moxy.version>3.0.2</moxy.version> - <yasson.version>2.0.3</yasson.version> + <jsonb.api.version>3.0.0</jsonb.api.version> + <jsonp.ri.version>1.1.0</jsonp.ri.version> + <jsonp.jaxrs.version>1.1.0</jsonp.jaxrs.version> + <moxy.version>4.0.0-M3</moxy.version> + <yasson.version>3.0.0-RC2</yasson.version> <!-- END of Jakartified --> <javax.annotation.version>1.3.2</javax.annotation.version> <!--Deprecated, used only for @generated annotation in perf tests -->
diff --git a/test-framework/core/pom.xml b/test-framework/core/pom.xml index eeb186a..cf14a94 100644 --- a/test-framework/core/pom.xml +++ b/test-framework/core/pom.xml
@@ -40,7 +40,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> </dependency> <dependency> <groupId>org.glassfish.jersey.core</groupId>
diff --git a/test-framework/providers/grizzly2/pom.xml b/test-framework/providers/grizzly2/pom.xml index 75ce20d..b610451 100644 --- a/test-framework/providers/grizzly2/pom.xml +++ b/test-framework/providers/grizzly2/pom.xml
@@ -36,7 +36,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> </dependency> <dependency>
diff --git a/test-framework/providers/jetty/pom.xml b/test-framework/providers/jetty/pom.xml index 399d3de..e2c1af2 100644 --- a/test-framework/providers/jetty/pom.xml +++ b/test-framework/providers/jetty/pom.xml
@@ -44,148 +44,4 @@ </dependency> </dependencies> - <properties> - <java8.build.outputDirectory>${project.basedir}/target</java8.build.outputDirectory> - <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory> - <java11.build.outputDirectory>${project.basedir}/target11</java11.build.outputDirectory> - <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory> - </properties> - - <profiles> - <profile> - <id>JettyExclude</id> - <activation> - <jdk>1.8</jdk> - </activation> - <build> - <directory>${java8.build.outputDirectory}</directory> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>${java8.sourceDirectory}</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <testExcludes> - <testExclude>org/glassfish/jersey/test/jetty/*.java</testExclude> - </testExcludes> - </configuration> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>Jetty11</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <build> - <directory>${java11.build.outputDirectory}</directory> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>${java11.sourceDirectory}</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - <profile> - <id>copyJDK11FilesToMultiReleaseJar</id> - <activation> - <file> - <!-- ${java11.build.outputDirectory} does not work here --> - <exists>target11/classes/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.class</exists> - </file> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <inherited>true</inherited> - <extensions>true</extensions> - <configuration> - <instructions> - <Multi-Release>true</Multi-Release> - </instructions> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-resources-plugin</artifactId> - <inherited>true</inherited> - <executions> - <execution> - <id>copy-jdk11-classes</id> - <phase>prepare-package</phase> - <goals> - <goal>copy-resources</goal> - </goals> - <configuration> - <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory> - <resources> - <resource> - <directory>${java11.build.outputDirectory}/classes</directory> - </resource> - </resources> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-antrun-plugin</artifactId> - <executions> - <execution> - <id>copy-jdk11-sources</id> - <phase>package</phase> - <configuration> - <target> - <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/> - <echo>sources-jar: ${sources-jar}</echo> - <zip destfile="${sources-jar}" update="true"> - <zipfileset dir="${java11.sourceDirectory}" prefix="META-INF/versions/11"/> - </zip> - </target> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> - </project>
diff --git a/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java similarity index 98% rename from test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java rename to test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java index e5dba36..2ce4577 100644 --- a/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java +++ b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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/test-framework/providers/jetty/src/main/java8/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java8/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java deleted file mode 100644 index cd2e332..0000000 --- a/test-framework/providers/jetty/src/main/java8/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java +++ /dev/null
@@ -1,38 +0,0 @@ -/* - * Copyright (c) 2020 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.test.jetty; - -import jakarta.ws.rs.ProcessingException; -import org.glassfish.jersey.jetty.internal.LocalizationMessages; -import org.glassfish.jersey.test.DeploymentContext; -import org.glassfish.jersey.test.spi.TestContainer; -import org.glassfish.jersey.test.spi.TestContainerFactory; - -import java.net.URI; - -/** - * Jetty test factory stub for JDK 1.8 only - * - * since Jetty 11+ does not support JDKs below 11 - */ -public class JettyTestContainerFactory implements TestContainerFactory { - - @Override - public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException { - throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED()); - } -}
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml index 3890a59..b52fcd7 100644 --- a/tests/e2e-client/pom.xml +++ b/tests/e2e-client/pom.xml
@@ -186,16 +186,6 @@ <profiles> <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <properties> - <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 --> - <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline> - </properties> - </profile> - <profile> <id>xdk</id> <properties> <!-- do not use security manager for xdk --> @@ -222,4 +212,9 @@ </profile> </profiles> + <properties> + <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 --> + <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline> + </properties> + </project>
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java index 5a506a2..02343f8 100644 --- a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java +++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021 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,8 +16,10 @@ package org.glassfish.jersey.tests.e2e.common; +import jakarta.ws.rs.SeBootstrap; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.EntityPart; import jakarta.ws.rs.core.Link; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -30,6 +32,8 @@ import org.junit.Assert; +import java.util.concurrent.CompletionStage; + /** * Test runtime delegate. * @@ -47,6 +51,22 @@ throw new UnsupportedOperationException("Not supported yet."); } + @Override + public SeBootstrap.Configuration.Builder createConfigurationBuilder() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> aClass, + SeBootstrap.Configuration configuration) { + throw new UnsupportedOperationException("Not supported yet."); + } + public void testMediaType() { MediaType m = new MediaType("text", "plain"); Assert.assertNotNull(m);
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java index 767b644..80b03e0 100644 --- a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java +++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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 @@ -37,14 +37,18 @@ @Test public void testCaseInsensitiveNewCookieParams() throws Exception { - _testCaseInsensitiveNewCookieParams("expires", "max-age", "path", "domain", "comment", "version", "secure", "httponly"); - _testCaseInsensitiveNewCookieParams("Expires", "Max-Age", "Path", "Domain", "Comment", "Version", "Secure", "HttpOnly"); - _testCaseInsensitiveNewCookieParams("exPires", "max-aGe", "patH", "doMAin", "Comment", "vErsion", "secuRe", "httPonly"); + _testCaseInsensitiveNewCookieParams("expires", "max-age", "path", "domain", + "comment", "version", "secure", "httponly", "samesite"); + _testCaseInsensitiveNewCookieParams("Expires", "Max-Age", "Path", "Domain", + "Comment", "Version", "Secure", "HttpOnly", "SameSite"); + _testCaseInsensitiveNewCookieParams("exPires", "max-aGe", "patH", "doMAin", + "Comment", "vErsion", "secuRe", "httPonly", "samEsite"); } private void _testCaseInsensitiveNewCookieParams(final String expires, final String maxAge, final String path, final String domain, final String comment, final String version, - final String secure, final String httpOnly) throws Exception { + final String secure, final String httpOnly, final String sameSite) + throws Exception { final String header = "foo=bar;" + expires + "=Tue, 15 Jan 2013 21:47:38 GMT;" @@ -54,7 +58,8 @@ + comment + "=Testing;" + version + "=1;" + secure + ";" - + httpOnly; + + httpOnly + ";" + + sameSite + "=STRICT"; final NewCookie cookie = CookiesParser.parseNewCookie(header); @@ -69,5 +74,6 @@ assertThat(cookie.getVersion(), equalTo(1)); assertThat(cookie.isSecure(), is(true)); assertThat(cookie.isHttpOnly(), is(true)); + assertThat(cookie.getSameSite(), equalTo(NewCookie.SameSite.STRICT)); } }
diff --git a/tests/e2e-inject/cdi2-se/src/main/resources/META-INF/beans.xml b/tests/e2e-inject/cdi2-se/src/main/resources/META-INF/beans.xml index ae84224..c2146b4 100644 --- a/tests/e2e-inject/cdi2-se/src/main/resources/META-INF/beans.xml +++ b/tests/e2e-inject/cdi2-se/src/main/resources/META-INF/beans.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v. 2.0, which is available at @@ -17,7 +17,11 @@ --> -<beans> +<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + bean-discovery-mode="all"> <interceptors> <class>org.glassfish.jersey.tests.e2e.inject.cdi.se.SecurityInterceptor</class> </interceptors>
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java index 5d129ae..b58da21 100644 --- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -46,6 +46,7 @@ import org.glassfish.jersey.test.JerseyTest; import org.glassfish.jersey.test.TestProperties; +import org.junit.Ignore; import org.junit.Test; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; @@ -105,6 +106,7 @@ } @Test + @Ignore("since 3.1 nothing is being thrown") public void testResumeRuntimeException() throws Exception { testResumeException("resumeRuntimeException", null); @@ -112,6 +114,7 @@ } @Test + @Ignore("since 3.1 nothing is being thrown") public void testResumeCheckedException() throws Exception { testResumeException("resumeCheckedException", null);
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java index 6b50666..2e93185 100644 --- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 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 @@ -76,14 +76,12 @@ public void testRuntime() throws Exception { final Response response = target().path("runtime").request().get(); assertEquals(500, response.getStatus()); - assertEquals(getLastLoggedRecord().getThrown().getClass(), MyRuntimeException.class); } @Test public void testChecked() throws Exception { final Response response = target().path("checked").request().get(); assertEquals(500, response.getStatus()); - assertEquals(getLastLoggedRecord().getThrown().getClass(), MyCheckedException.class); } @Provider @@ -123,8 +121,6 @@ public void testReaderFails() throws Exception { final Response response = target().path("resource/entity").request().get(); assertEquals(500, response.getStatus()); - - assertEquals(getLastLoggedRecord().getThrown().getMessage(), "test"); } static class ExceptionLoggingTestPOJO {
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java index 6e0389f..eecbfdb 100644 --- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 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 @@ -137,6 +137,13 @@ assertEquals(500, res.getStatus()); } + @Test + public void testDefaultExceptionMapper() { + Response res = target().path("test/defaultExceptionMapper") + .request("test/test").get(); + assertEquals(200, res.getStatus()); + } + @Path("test") public static class Resource { @@ -174,6 +181,14 @@ new RuntimeException("runtime-exception", new ClientErrorException("client-error", 499))); } + + @GET + @Path("defaultExceptionMapper") + public Response isRegisteredDefaultExceptionTest(@Context Providers providers) { + ExceptionMapper<Throwable> em = providers + .getExceptionMapper(Throwable.class); + return Response.status((em == null) ? 500 : 200).build(); + } } public static class ClientErrorExceptionMapper implements ExceptionMapper<ClientErrorException> {
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java index 1d840e3..2b53c60 100644 --- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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 @@ -278,8 +278,8 @@ LocalizationMessages.ERROR_EXCEPTION_MAPPING_ORIGINAL_EXCEPTION(), LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER()}) { - if (logRecord.getMessage().contains(message) && logRecord.getLevel().intValue() > Level.FINE.intValue()) { - fail("Log message should be logged at lower (FINE) level: " + message); + if (logRecord.getMessage().contains(message) && logRecord.getLevel().intValue() > Level.WARNING.intValue()) { + fail("Log message should be logged at lower (WARNING) level: " + message); } } }
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapPropertiesTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapPropertiesTest.java new file mode 100644 index 0000000..089994f --- /dev/null +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapPropertiesTest.java
@@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.server.sebootstrap; + +import jakarta.ws.rs.SeBootstrap; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.internal.RuntimeDelegateImpl; +import org.glassfish.jersey.server.spi.Container; +import org.junit.Assert; +import org.junit.Test; + +import java.net.URI; + +public class SeBootstrapPropertiesTest { + @Test + public void testRandomPortScanning() { + JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance() + .createConfigurationBuilder().port(SeBootstrap.Configuration.FREE_PORT).build(); + + URI uri = configuration.uri(true); + Assert.assertTrue(uri.getPort() > 0); + } + + @Test + public void testDefaultUnprivilegedPort() { + JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance() + .createConfigurationBuilder().port(SeBootstrap.Configuration.DEFAULT_PORT).build(); + + URI uri = configuration.uri(true); + Assert.assertEquals(Container.DEFAULT_HTTP_PORT + 8000, uri.getPort()); + } + + @Test + public void testDefaultPrivilegedPort() { + JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance() + .createConfigurationBuilder() + .property(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, Boolean.TRUE) + .port(SeBootstrap.Configuration.DEFAULT_PORT).build(); + + URI uri = configuration.uri(true); + Assert.assertEquals(Container.DEFAULT_HTTP_PORT, uri.getPort()); + } + + @Test + public void testDefaultUnprivilegedSecuredPort() { + JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance() + .createConfigurationBuilder().protocol("HTTPS").port(SeBootstrap.Configuration.DEFAULT_PORT).build(); + + URI uri = configuration.uri(true); + Assert.assertEquals(Container.DEFAULT_HTTPS_PORT + 8000, uri.getPort()); + } + + @Test + public void testDefaultPrivilegedSecuredPort() { + JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance() + .createConfigurationBuilder() + .protocol("HTTPS") + .property(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, Boolean.TRUE) + .port(SeBootstrap.Configuration.DEFAULT_PORT).build(); + + URI uri = configuration.uri(true); + Assert.assertEquals(Container.DEFAULT_HTTPS_PORT, uri.getPort()); + } +}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapSystemPropertiesTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapSystemPropertiesTest.java new file mode 100644 index 0000000..1cb540a --- /dev/null +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapSystemPropertiesTest.java
@@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.server.sebootstrap; + +import jakarta.ws.rs.SeBootstrap; +import org.glassfish.jersey.CommonProperties; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.internal.RuntimeDelegateImpl; +import org.glassfish.jersey.server.spi.Container; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.net.URI; + +public class SeBootstrapSystemPropertiesTest { + + @BeforeClass + public static void setUp() { + System.setProperty(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.TRUE.toString()); + System.getProperties().put(SeBootstrap.Configuration.PORT, "9998"); + System.getProperties().put(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, Boolean.TRUE.toString()); + } + + @AfterClass + public static void tearDown() { + System.clearProperty(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER); + System.clearProperty(SeBootstrap.Configuration.PORT); + System.clearProperty(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS); + } + + @Test + public void testDefaultPrivilegedPortSystemProperty() { + JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance() + .createConfigurationBuilder() + .port(SeBootstrap.Configuration.DEFAULT_PORT).build(); + + URI uri = configuration.uri(true); + Assert.assertEquals(Container.DEFAULT_HTTP_PORT, uri.getPort()); + } + + @Test + public void testPortSystemProperty() { + JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance() + .createConfigurationBuilder() + .build(); + + URI uri = configuration.uri(true); + Assert.assertEquals(9998, uri.getPort()); + } +}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapTest.java new file mode 100644 index 0000000..18b15e7 --- /dev/null +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapTest.java
@@ -0,0 +1,367 @@ +/* + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 Markus Karg. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package org.glassfish.jersey.tests.e2e.server.sebootstrap; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.SeBootstrap; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.UriBuilder; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Compliance Test for Java SE Bootstrap API of Jakarta REST API + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1 + */ +public class SeBootstrapTest { + /** + * Verifies that an instance will boot using default configuration. + * + * @throws ExecutionException if the instance didn't boot correctly + * @throws InterruptedException if the test took much longer than usually + * expected + */ + @Test + public final void shouldBootInstanceUsingDefaults() throws InterruptedException, ExecutionException { + // given + final int expectedResponse = mockInt(); + final Application application = new StaticApplication(expectedResponse); + final SeBootstrap.Configuration.Builder bootstrapConfigurationBuilder = SeBootstrap.Configuration.builder(); + final SeBootstrap.Configuration requestedConfiguration = bootstrapConfigurationBuilder.build(); + + // when + final CompletionStage<SeBootstrap.Instance> completionStage = SeBootstrap.start(application, requestedConfiguration); + final SeBootstrap.Instance instance = completionStage.toCompletableFuture().get(); + final SeBootstrap.Configuration actualConfiguration = instance.configuration(); + final int actualResponse = client.target(UriBuilder.newInstance().scheme(actualConfiguration.protocol()) + .host(actualConfiguration.host()).port(actualConfiguration.port()).path(actualConfiguration.rootPath()) + .path("application/resource")).request().get(int.class); + + // then + assertThat(actualResponse, is(expectedResponse)); + assertThat(actualConfiguration.protocol(), is("HTTP")); + assertThat(actualConfiguration.host(), is("localhost")); + assertThat(actualConfiguration.port(), is(greaterThan(0))); + assertThat(actualConfiguration.rootPath(), is("/")); + instance.stop().toCompletableFuture().get(); + } + + /** + * Verifies that an instance will boot using explicit configuration given by + * properties. + * + * @throws ExecutionException if the instance didn't boot correctly + * @throws InterruptedException if the test took much longer than usually + * expected + * @throws IOException if no IP port was free + */ + @Test + public final void shouldBootInstanceUsingProperties() throws InterruptedException, ExecutionException, IOException { + // given + final int expectedResponse = mockInt(); + final Application application = new StaticApplication(expectedResponse); + final SeBootstrap.Configuration.Builder bootstrapConfigurationBuilder = SeBootstrap.Configuration.builder(); + final SeBootstrap.Configuration requestedConfiguration = bootstrapConfigurationBuilder + .property(SeBootstrap.Configuration.PROTOCOL, "HTTP") + .property(SeBootstrap.Configuration.HOST, "localhost") + .property(SeBootstrap.Configuration.PORT, someFreeIpPort()) + .property(SeBootstrap.Configuration.ROOT_PATH, "/root/path").build(); + + // when + final CompletionStage<SeBootstrap.Instance> completionStage = SeBootstrap.start(application, requestedConfiguration); + final SeBootstrap.Instance instance = completionStage.toCompletableFuture().get(); + final SeBootstrap.Configuration actualConfiguration = instance.configuration(); + final int actualResponse = client.target(UriBuilder.newInstance().scheme(actualConfiguration.protocol()) + .host(actualConfiguration.host()).port(actualConfiguration.port()).path(actualConfiguration.rootPath()) + .path("application/resource")).request().get(int.class); + + // then + assertThat(actualResponse, is(expectedResponse)); + assertThat(actualConfiguration.protocol(), is(requestedConfiguration.protocol())); + assertThat(actualConfiguration.host(), is(requestedConfiguration.host())); + assertThat(actualConfiguration.port(), is(requestedConfiguration.port())); + assertThat(actualConfiguration.rootPath(), is(requestedConfiguration.rootPath())); + instance.stop().toCompletableFuture().get(); + } + + /** + * Verifies that an instance will boot using explicit configuration given by + * convenience methods. + * + * @throws ExecutionException if the instance didn't boot correctly + * @throws InterruptedException if the test took much longer than usually + * expected + * @throws IOException if no IP port was free + */ + @Test + public final void shouldBootInstanceUsingConvenienceMethods() throws InterruptedException, ExecutionException, IOException { + // given + final int expectedResponse = mockInt(); + final Application application = new StaticApplication(expectedResponse); + final SeBootstrap.Configuration.Builder bootstrapConfigurationBuilder = SeBootstrap.Configuration.builder(); + final SeBootstrap.Configuration requestedConfiguration = bootstrapConfigurationBuilder.protocol("HTTP").host("localhost") + .port(someFreeIpPort()).rootPath("/root/path").build(); + + // when + final CompletionStage<SeBootstrap.Instance> completionStage = SeBootstrap.start(application, requestedConfiguration); + final SeBootstrap.Instance instance = completionStage.toCompletableFuture().get(); + final SeBootstrap.Configuration actualConfiguration = instance.configuration(); + final int actualResponse = client.target(UriBuilder.newInstance().scheme(actualConfiguration.protocol()) + .host(actualConfiguration.host()).port(actualConfiguration.port()).path(actualConfiguration.rootPath()) + .path("application/resource")).request().get(int.class); + + // then + assertThat(actualResponse, is(expectedResponse)); + assertThat(actualConfiguration.protocol(), is(requestedConfiguration.protocol())); + assertThat(actualConfiguration.host(), is(requestedConfiguration.host())); + assertThat(actualConfiguration.port(), is(requestedConfiguration.port())); + assertThat(actualConfiguration.rootPath(), is(requestedConfiguration.rootPath())); + instance.stop().toCompletableFuture().get(); + } + + /** + * Verifies that an instance will boot using external configuration. + * + * @throws ExecutionException if the instance didn't boot correctly + * @throws InterruptedException if the test took much longer than usually + * expected + * @throws IOException if no IP port was free + */ + @Test + public final void shouldBootInstanceUsingExternalConfiguration() throws Exception { + // given + final int someFreeIpPort = someFreeIpPort(); + final int expectedResponse = mockInt(); + final Application application = new StaticApplication(expectedResponse); + final SeBootstrap.Configuration.Builder bootstrapConfigurationBuilder = SeBootstrap.Configuration.builder(); + final SeBootstrap.Configuration requestedConfiguration = bootstrapConfigurationBuilder.from((property, type) -> { + switch (property) { + case SeBootstrap.Configuration.PROTOCOL: + return Optional.of("HTTP"); + case SeBootstrap.Configuration.HOST: + return Optional.of("localhost"); + case SeBootstrap.Configuration.PORT: + return Optional.of(someFreeIpPort); + case SeBootstrap.Configuration.ROOT_PATH: + return Optional.of("/root/path"); + default: + return Optional.empty(); + } + }).build(); + + // when + final CompletionStage<SeBootstrap.Instance> completionStage = SeBootstrap.start(application, requestedConfiguration); + final SeBootstrap.Instance instance = completionStage.toCompletableFuture().get(); + final SeBootstrap.Configuration actualConfiguration = instance.configuration(); + final int actualResponse = client.target(UriBuilder.newInstance().scheme(actualConfiguration.protocol()) + .host(actualConfiguration.host()).port(actualConfiguration.port()).path(actualConfiguration.rootPath()) + .path("application/resource")).request().get(int.class); + + // then + assertThat(actualResponse, is(expectedResponse)); + assertThat(actualConfiguration.protocol(), is(requestedConfiguration.protocol())); + assertThat(actualConfiguration.host(), is(requestedConfiguration.host())); + assertThat(actualConfiguration.port(), is(requestedConfiguration.port())); + assertThat(actualConfiguration.rootPath(), is(requestedConfiguration.rootPath())); + instance.stop().toCompletableFuture().get(); + } + + /** + * Verifies that an instance will ignore unknown configuration parameters. + * + * @throws ExecutionException if the instance didn't boot correctly + * @throws InterruptedException if the test took much longer than usually + * expected + * @throws IOException if no IP port was free + */ + @Test + public final void shouldBootInstanceDespiteUnknownConfigurationParameters() throws Exception { + // given + final int expectedResponse = mockInt(); + final Application application = new StaticApplication(expectedResponse); + final SeBootstrap.Configuration.Builder bootstrapConfigurationBuilder = SeBootstrap.Configuration.builder(); + final SeBootstrap.Configuration requestedConfiguration = bootstrapConfigurationBuilder.protocol("HTTP").host("localhost") + .port(someFreeIpPort()).rootPath("/root/path").from((property, type) -> { + switch (property) { + case "jakarta.ws.rs.tck.sebootstrap.SeBootstrapIT$Unknown_1": + return Optional.of("Silently ignored value A"); + default: + return Optional.empty(); + } + }).property("jakarta.ws.rs.tck.sebootstrap.SeBootstrapIT$Unknown_2", "Silently ignored value B") + .from(new Object()).build(); + + // when + final CompletionStage<SeBootstrap.Instance> completionStage = SeBootstrap.start(application, requestedConfiguration); + final SeBootstrap.Instance instance = completionStage.toCompletableFuture().get(); + final SeBootstrap.Configuration actualConfiguration = instance.configuration(); + final int actualResponse = client.target(UriBuilder.newInstance().scheme(actualConfiguration.protocol()) + .host(actualConfiguration.host()).port(actualConfiguration.port()).path(actualConfiguration.rootPath()) + .path("application/resource")).request().get(int.class); + + // then + assertThat(actualResponse, is(expectedResponse)); + assertThat(actualConfiguration.protocol(), is(requestedConfiguration.protocol())); + assertThat(actualConfiguration.host(), is(requestedConfiguration.host())); + assertThat(actualConfiguration.port(), is(requestedConfiguration.port())); + assertThat(actualConfiguration.rootPath(), is(requestedConfiguration.rootPath())); + instance.stop().toCompletableFuture().get(); + } + + /** + * Verifies that an instance will boot using a self-detected free IP port. + * + * @throws ExecutionException if the instance didn't boot correctly + * @throws InterruptedException if the test took much longer than usually + * expected + */ + @Test + public final void shouldBootInstanceUsingSelfDetectedFreeIpPort() throws InterruptedException, ExecutionException { + // given + final int expectedResponse = mockInt(); + final Application application = new StaticApplication(expectedResponse); + final SeBootstrap.Configuration.Builder bootstrapConfigurationBuilder = SeBootstrap.Configuration.builder(); + final SeBootstrap.Configuration requestedConfiguration = bootstrapConfigurationBuilder.protocol("HTTP").host("localhost") + .port(SeBootstrap.Configuration.FREE_PORT).rootPath("/root/path").build(); + + // when + final CompletionStage<SeBootstrap.Instance> completionStage = SeBootstrap.start(application, requestedConfiguration); + final SeBootstrap.Instance instance = completionStage.toCompletableFuture().get(); + final SeBootstrap.Configuration actualConfiguration = instance.configuration(); + final int actualResponse = client.target(UriBuilder.newInstance().scheme(actualConfiguration.protocol()) + .host(actualConfiguration.host()).port(actualConfiguration.port()).path(actualConfiguration.rootPath()) + .path("application/resource")).request().get(int.class); + + // then + assertThat(actualResponse, is(expectedResponse)); + assertThat(actualConfiguration.protocol(), is(requestedConfiguration.protocol())); + assertThat(actualConfiguration.host(), is(requestedConfiguration.host())); + assertThat(actualConfiguration.port(), is(greaterThan(0))); + assertThat(actualConfiguration.rootPath(), is(requestedConfiguration.rootPath())); + instance.stop().toCompletableFuture().get(); + } + + /** + * Verifies that an instance will boot using the implementation's default IP + * port. + * + * @throws ExecutionException if the instance didn't boot correctly + * @throws InterruptedException if the test took much longer than usually + * expected + */ + @Test + public final void shouldBootInstanceUsingImplementationsDefaultIpPort() throws InterruptedException, ExecutionException { + // given + final int expectedResponse = mockInt(); + final Application application = new StaticApplication(expectedResponse); + final SeBootstrap.Configuration.Builder bootstrapConfigurationBuilder = SeBootstrap.Configuration.builder(); + final SeBootstrap.Configuration requestedConfiguration = bootstrapConfigurationBuilder.protocol("HTTP").host("localhost") + .port(SeBootstrap.Configuration.DEFAULT_PORT).rootPath("/root/path").build(); + + // when + final CompletionStage<SeBootstrap.Instance> completionStage = SeBootstrap.start(application, requestedConfiguration); + final SeBootstrap.Instance instance = completionStage.toCompletableFuture().get(); + final SeBootstrap.Configuration actualConfiguration = instance.configuration(); + final int actualResponse = client.target(UriBuilder.newInstance().scheme(actualConfiguration.protocol()) + .host(actualConfiguration.host()).port(actualConfiguration.port()).path(actualConfiguration.rootPath()) + .path("application/resource")).request().get(int.class); + + // then + assertThat(actualResponse, is(expectedResponse)); + assertThat(actualConfiguration.protocol(), is(requestedConfiguration.protocol())); + assertThat(actualConfiguration.host(), is(requestedConfiguration.host())); + assertThat(actualConfiguration.port(), is(greaterThan(0))); + assertThat(actualConfiguration.rootPath(), is(requestedConfiguration.rootPath())); + instance.stop().toCompletableFuture().get(); + } + + private static Client client; + + @BeforeClass + public static void createClient() { + SeBootstrapTest.client = ClientBuilder.newClient(); + } + + @AfterClass + public static void disposeClient() { + SeBootstrapTest.client.close(); + } + + @ApplicationPath("application") + public static final class StaticApplication extends Application { + + private final StaticResource staticResource; + + private StaticApplication(final long staticResponse) { + this.staticResource = new StaticResource(staticResponse); + } + + @Override + public final Set<Object> getSingletons() { + return Collections.<Object>singleton(staticResource); + } + + @Path("resource") + public static final class StaticResource { + + private final long staticResponse; + + private StaticResource(final long staticResponse) { + this.staticResponse = staticResponse; + } + + @GET + public final long staticResponse() { + return this.staticResponse; + } + } + } + + private static final int someFreeIpPort() throws IOException { + int port = 0; + int cnt = 0; + while (port < 1024 && cnt++ < 1025) { + try (final ServerSocket serverSocket = new ServerSocket(0)) { + port = serverSocket.getLocalPort(); + } + } + return port; + } + + private static final int mockInt() { + return (int) Math.round(Integer.MAX_VALUE * Math.random()); + } +}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/AnotherContactBean.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/AnotherContactBean.java index fa65eae..2d88b34 100644 --- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/AnotherContactBean.java +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/AnotherContactBean.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -18,6 +18,8 @@ import java.io.Serializable; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.executable.ExecutableType; @@ -26,9 +28,6 @@ import org.glassfish.jersey.tests.e2e.server.validation.Extended; -import org.hibernate.validator.constraints.Email; -import org.hibernate.validator.constraints.NotBlank; - /** * @author Michal Gajdos */
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java index 3c3c946..f3ecf18 100644 --- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022 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,12 +32,22 @@ import jakarta.validation.executable.ExecutableType; import jakarta.validation.executable.ValidateOnExecution; +import jakarta.ws.rs.ext.ContextResolver; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBException; +import org.eclipse.persistence.jaxb.BeanValidationMode; +import org.eclipse.persistence.jaxb.MarshallerProperties; +import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ServerProperties; import org.glassfish.jersey.test.TestProperties; import org.glassfish.jersey.test.util.runner.RunSeparately; import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; @@ -289,6 +299,28 @@ } } + /** + * Do not validate the bean by Moxy, validate just by Jersey + */ + public static class MoxyNotValidateContextResolver implements ContextResolver<JAXBContext> { + @Override + public JAXBContext getContext(Class<?> type) { + Map<String, Object> properties = new HashMap<>(); + properties.put(MarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE); + try { + return JAXBContext.newInstance(new Class[]{type}, properties); + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + } + + @Override + protected void configureClient(ClientConfig config) { + config.register(MoxyNotValidateContextResolver.class); + super.configureClient(config); + } + @Override protected Application configure() { enable(TestProperties.LOG_TRAFFIC); @@ -309,7 +341,8 @@ ValidateGetterExecutableOnTypeMatch.class, ValidateGetterExecutableOnBeans.class, ValidateGetterResourceMethod.class, - ValidateExecutableResource.class) + ValidateExecutableResource.class, + MoxyNotValidateContextResolver.class) .property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true); }
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlBeanParamTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlBeanParamTest.java index 195c675..481c20c 100644 --- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlBeanParamTest.java +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlBeanParamTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -22,6 +22,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.Properties; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.CookieParam; @@ -38,9 +39,13 @@ import jakarta.ws.rs.core.Request; import jakarta.ws.rs.core.Response; +import javax.xml.transform.ErrorListener; +import javax.xml.transform.Result; +import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; +import javax.xml.transform.URIResolver; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; @@ -89,8 +94,9 @@ */ @Override public boolean qualifyForComparison(final Element control, final Element test) { - if (test != null && !"param".equals(test.getNodeName())) { - return super.qualifyForComparison(control, test); + if (test != null && !"param".equals(test.getNodeName()) && !"ns0:param".equals(test.getNodeName())) { + boolean spr = super.qualifyForComparison(control, test); + return spr; } if (!(control != null && test != null && equalsNamespace(control, test)
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java index 7237128..da2d787 100644 --- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java +++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 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 @@ -1009,7 +1009,12 @@ final String document = response.readEntity(String.class); // check that the resulting document contains a method element with id="fooX" - assertTrue(document.replaceAll("\n", " ").matches(".*<method[^>]+id=\"foo" + i + "\"[^>]*>.*")); + assertTrue(document + .replaceAll("\n", " ") + .replaceAll("\r", "") + .replaceAll("ns0:", "") + .matches(".*<method[^>]+id=\"foo" + i + "\"[^>]*>.*") + ); } } }
diff --git a/tests/e2e/pom.xml b/tests/e2e/pom.xml index 567044b..ec78efa 100644 --- a/tests/e2e/pom.xml +++ b/tests/e2e/pom.xml
@@ -198,26 +198,6 @@ </properties> </profile> <profile> - <id>JettyExclude</id> - <activation> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <testExcludes> - <testExclude>org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java</testExclude> - <testExclude>org/glassfish/jersey/tests/e2e/container/JettyEmptyHeaderParamTest.java</testExclude> - </testExcludes> - </configuration> - </plugin> - </plugins> - </build> - </profile> - <profile> <id>sonar</id> <build> <pluginManagement>
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java index 1d8c7de..a5f04a7 100644 --- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java +++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021 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 @@ -187,6 +187,12 @@ cookie = new NewCookie("fred", "flintstone", null, null, "a modern stonage family", 60, false); expResult = "fred=flintstone;Version=1;Comment=\"a modern stonage family\";Max-Age=60"; assertEquals(expResult, cookie.toString()); + + cookie = new NewCookie("fred", "flintstone", null, null, 1, + "a modern stonage family", 60, null, false, false, + NewCookie.SameSite.STRICT); + expResult = "fred=flintstone;Version=1;Comment=\"a modern stonage family\";Max-Age=60;SameSite=STRICT"; + assertEquals(expResult, cookie.toString()); } @Test @@ -209,6 +215,16 @@ assertEquals(1, cookie.getVersion()); assertEquals(60, cookie.getMaxAge()); assertTrue(cookie.isSecure()); + + cookie = NewCookie.valueOf( + "fred=flintstone;Version=1;Comment=\"a modern stonage family\";Max-Age=60;Secure;SameSite=NONE"); + assertEquals("fred", cookie.getName()); + assertEquals("flintstone", cookie.getValue()); + assertEquals("a modern stonage family", cookie.getComment()); + assertEquals(1, cookie.getVersion()); + assertEquals(60, cookie.getMaxAge()); + assertTrue(cookie.isSecure()); + assertEquals(NewCookie.SameSite.NONE, cookie.getSameSite()); } }
diff --git a/tests/integration/async-jersey-filter/pom.xml b/tests/integration/async-jersey-filter/pom.xml index 1c46a01..4db6da5 100644 --- a/tests/integration/async-jersey-filter/pom.xml +++ b/tests/integration/async-jersey-filter/pom.xml
@@ -49,7 +49,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> </dependencies>
diff --git a/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml b/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml index e5a76ea..e4769b2 100644 --- a/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml +++ b/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml
@@ -40,7 +40,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml b/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml index 190ca83..4ebaa21 100644 --- a/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml +++ b/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml
@@ -51,7 +51,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/tests/integration/cdi-integration/cdi-test-webapp/src/main/resources/META-INF/beans.xml b/tests/integration/cdi-integration/cdi-test-webapp/src/main/resources/META-INF/beans.xml index 14ea61e..8d3e4b2 100644 --- a/tests/integration/cdi-integration/cdi-test-webapp/src/main/resources/META-INF/beans.xml +++ b/tests/integration/cdi-integration/cdi-test-webapp/src/main/resources/META-INF/beans.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v. 2.0, which is available at @@ -17,4 +17,9 @@ --> -<beans/> +<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + bean-discovery-mode="all"> +</beans>
diff --git a/tests/integration/cdi-integration/cdi-test-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-integration/cdi-test-webapp/src/main/webapp/WEB-INF/beans.xml index 14ea61e..8d3e4b2 100644 --- a/tests/integration/cdi-integration/cdi-test-webapp/src/main/webapp/WEB-INF/beans.xml +++ b/tests/integration/cdi-integration/cdi-test-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v. 2.0, which is available at @@ -17,4 +17,9 @@ --> -<beans/> +<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + bean-discovery-mode="all"> +</beans>
diff --git a/tests/integration/cdi-integration/context-inject-on-server/pom.xml b/tests/integration/cdi-integration/context-inject-on-server/pom.xml index 07b0452..5fd8561 100644 --- a/tests/integration/cdi-integration/context-inject-on-server/pom.xml +++ b/tests/integration/cdi-integration/context-inject-on-server/pom.xml
@@ -49,7 +49,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/tests/integration/ejb-test-webapp/pom.xml b/tests/integration/ejb-test-webapp/pom.xml index 5597e20..cb1ca1c 100644 --- a/tests/integration/ejb-test-webapp/pom.xml +++ b/tests/integration/ejb-test-webapp/pom.xml
@@ -40,7 +40,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/tests/integration/j-441/war1/pom.xml b/tests/integration/j-441/war1/pom.xml index 822b8a6..5a1ebb5 100644 --- a/tests/integration/j-441/war1/pom.xml +++ b/tests/integration/j-441/war1/pom.xml
@@ -42,7 +42,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/tests/integration/j-441/war2/pom.xml b/tests/integration/j-441/war2/pom.xml index f13ec2f..fc0e19a 100644 --- a/tests/integration/j-441/war2/pom.xml +++ b/tests/integration/j-441/war2/pom.xml
@@ -42,7 +42,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/tests/integration/j-59/war/pom.xml b/tests/integration/j-59/war/pom.xml index 9b77f25..0870851 100644 --- a/tests/integration/j-59/war/pom.xml +++ b/tests/integration/j-59/war/pom.xml
@@ -65,6 +65,11 @@ <artifactId>hamcrest</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>jakarta.jws</groupId> + <artifactId>jakarta.jws-api</artifactId> + <version>1.1.1</version> + </dependency> </dependencies> <build> @@ -96,22 +101,6 @@ </plugins> </build> - <profiles> - <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <dependencies> - <dependency> - <groupId>jakarta.jws</groupId> - <artifactId>jakarta.jws-api</artifactId> - <version>1.1.1</version> - </dependency> - </dependencies> - </profile> - </profiles> - <properties> <failOnMissingWebXml>false</failOnMissingWebXml> <skipTests>true</skipTests>
diff --git a/tests/integration/jersey-1960/pom.xml b/tests/integration/jersey-1960/pom.xml index 9a1124f..73c7cf2 100644 --- a/tests/integration/jersey-1960/pom.xml +++ b/tests/integration/jersey-1960/pom.xml
@@ -36,7 +36,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/jersey-1964/pom.xml b/tests/integration/jersey-1964/pom.xml index e639fb9..d54819d 100644 --- a/tests/integration/jersey-1964/pom.xml +++ b/tests/integration/jersey-1964/pom.xml
@@ -48,7 +48,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/jersey-2031/pom.xml b/tests/integration/jersey-2031/pom.xml index 684455a..1776468 100644 --- a/tests/integration/jersey-2031/pom.xml +++ b/tests/integration/jersey-2031/pom.xml
@@ -46,7 +46,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/tests/integration/jersey-2160/pom.xml b/tests/integration/jersey-2160/pom.xml index e22af06..5d2cc16 100644 --- a/tests/integration/jersey-2160/pom.xml +++ b/tests/integration/jersey-2160/pom.xml
@@ -36,7 +36,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/jersey-2176/pom.xml b/tests/integration/jersey-2176/pom.xml index 84d8735..ce80a2e 100644 --- a/tests/integration/jersey-2176/pom.xml +++ b/tests/integration/jersey-2176/pom.xml
@@ -41,7 +41,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java index 4bc3f0c..bff0fe2 100644 --- a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java +++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -34,6 +34,7 @@ property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, setStatusOverSendError); register(MyWriterInterceptor.class); register(Issue2176ReproducerResource.class); + register(Jersey2176ExceptionMapper.class); } public boolean isSetStatusOverSendError() {
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ExceptionMapper.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ExceptionMapper.java new file mode 100644 index 0000000..3a97c3d --- /dev/null +++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ExceptionMapper.java
@@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.integration.jersey2176; + +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; + +public class Jersey2176ExceptionMapper implements ExceptionMapper<Throwable> { + @Override + public Response toResponse(Throwable exception) { + if (exception instanceof WebApplicationException) { + return ((WebApplicationException) exception).getResponse(); + } + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception).build(); + } +}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java index ddb6b60..dadc0cd 100644 --- a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java +++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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 @@ -61,6 +61,9 @@ } catch (final Throwable th) { status = "FAIL"; } finally { + if (((HttpServletResponse) response).getStatus() == 500) { + status = "FAIL"; + } final long duration = System.nanoTime() - startTime; ((HttpServletResponse) response).addHeader(X_SERVER_DURATION_HEADER, String.valueOf(duration)); ((HttpServletResponse) response).addHeader(X_STATUS_HEADER, status);
diff --git a/tests/integration/jersey-2184/pom.xml b/tests/integration/jersey-2184/pom.xml index 5e635ea..0599984 100644 --- a/tests/integration/jersey-2184/pom.xml +++ b/tests/integration/jersey-2184/pom.xml
@@ -42,7 +42,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/jersey-2322/pom.xml b/tests/integration/jersey-2322/pom.xml index 4b9713d..309f71f 100644 --- a/tests/integration/jersey-2322/pom.xml +++ b/tests/integration/jersey-2322/pom.xml
@@ -58,14 +58,6 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <excludes> - <exclude>**/*</exclude> - </excludes> - <testExcludes> - <testExclude>**/*</testExclude> - </testExcludes> - </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId>
diff --git a/tests/integration/jersey-2637/pom.xml b/tests/integration/jersey-2637/pom.xml index c20582d..5d6d2b9 100644 --- a/tests/integration/jersey-2637/pom.xml +++ b/tests/integration/jersey-2637/pom.xml
@@ -38,7 +38,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/jersey-2673/pom.xml b/tests/integration/jersey-2673/pom.xml index 8c23d23..bcd11d5 100644 --- a/tests/integration/jersey-2673/pom.xml +++ b/tests/integration/jersey-2673/pom.xml
@@ -53,7 +53,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/jersey-2689/pom.xml b/tests/integration/jersey-2689/pom.xml index 1780f96..cf66de8 100644 --- a/tests/integration/jersey-2689/pom.xml +++ b/tests/integration/jersey-2689/pom.xml
@@ -81,7 +81,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> </dependencies>
diff --git a/tests/integration/jersey-2704/pom.xml b/tests/integration/jersey-2704/pom.xml index cb57dc8..c804bee 100644 --- a/tests/integration/jersey-2704/pom.xml +++ b/tests/integration/jersey-2704/pom.xml
@@ -48,7 +48,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> </dependencies>
diff --git a/tests/integration/jersey-3796/pom.xml b/tests/integration/jersey-3796/pom.xml index 4bbaf5f..5eca63f 100644 --- a/tests/integration/jersey-3796/pom.xml +++ b/tests/integration/jersey-3796/pom.xml
@@ -47,7 +47,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/jersey-4542/src/test/resources/META-INF/beans.xml b/tests/integration/jersey-4542/src/test/resources/META-INF/beans.xml index d773c46..d6b4b48 100644 --- a/tests/integration/jersey-4542/src/test/resources/META-INF/beans.xml +++ b/tests/integration/jersey-4542/src/test/resources/META-INF/beans.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v. 2.0, which is available at @@ -17,4 +17,9 @@ --> -<beans/> \ No newline at end of file +<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" + bean-discovery-mode="all"> +</beans> \ No newline at end of file
diff --git a/tests/integration/jersey-4722/pom.xml b/tests/integration/jersey-4722/pom.xml index f13e71c..43fabeb 100644 --- a/tests/integration/jersey-4722/pom.xml +++ b/tests/integration/jersey-4722/pom.xml
@@ -39,13 +39,11 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> </dependency> <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId> <artifactId>jersey-test-framework-provider-grizzly2</artifactId> - <version>${project.version}</version> <exclusions> <exclusion> <groupId>org.glassfish.jersey.media</groupId>
diff --git a/tests/integration/pom.xml b/tests/integration/pom.xml index 6406f8d..dc72108 100644 --- a/tests/integration/pom.xml +++ b/tests/integration/pom.xml
@@ -34,21 +34,55 @@ <modules> <module>asm</module> + <module>async-jersey-filter</module> <module>cdi-integration</module> <module>client-connector-provider</module> <module>ejb-multimodule</module> <module>ejb-multimodule-reload</module> <module>ejb-test-webapp</module> + <module>externalproperties</module> + <module>jaxrs-component-inject</module> <module>j-376</module> <module>j-441</module> <module>j-59</module> + <module>jersey-780</module> + <module>jersey-1107</module> + <module>jersey-1223</module> + <module>jersey-1604</module> + <module>jersey-1667</module> + <!-- <module>jersey-1829</module> Jakartification--> + <module>jersey-1883</module> + <module>jersey-1928</module> + <module>jersey-1960</module> + <module>jersey-1964</module> + <module>jersey-2031</module> <module>jersey-2136</module> <module>jersey-2137</module> <module>jersey-2154</module> + <module>jersey-2160</module> + <module>jersey-2164</module> + <module>jersey-2167</module> + <module>jersey-2176</module> + <module>jersey-2184</module> + <module>jersey-2255</module> + <module>jersey-2322</module> + <module>jersey-2335</module> <module>jersey-2421</module> + <module>jersey-2551</module> + <module>jersey-2612</module> + <module>jersey-2637</module> + <module>jersey-2654</module> + <module>jersey-2673</module> + <module>jersey-2689</module> + <module>jersey-2704</module> <module>jersey-2776</module> + <module>jersey-2794</module> + <module>jersey-2846</module> + <module>jersey-2878</module> + <module>jersey-2892</module> <module>jersey-3662</module> <module>jersey-3670</module> + <module>jersey-3796</module> <module>jersey-3992</module> <module>jersey-4003</module> <module>jersey-4099</module> @@ -57,29 +91,64 @@ <module>jersey-4542</module> <module>jersey-4697</module> <module>jersey-4722</module> + <module>jersey-4949</module> <module>microprofile</module> + <module>property-check</module> <module>reactive-streams</module> + <module>security-digest</module> + <module>servlet-2.5-autodiscovery-1</module> + <module>servlet-2.5-autodiscovery-2</module> + <module>servlet-2.5-filter</module> + <module>servlet-2.5-inflector-1</module> + <module>servlet-2.5-init-1</module> + <module>servlet-2.5-init-2</module> + <module>servlet-2.5-init-3</module> + <module>servlet-2.5-init-4</module> + <module>servlet-2.5-init-5</module> + <module>servlet-2.5-init-6</module> + <module>servlet-2.5-init-7</module> + <module>servlet-2.5-init-8</module> + <module>servlet-2.5-mvc-1</module> + <module>servlet-2.5-mvc-2</module> + <module>servlet-2.5-mvc-3</module> + <module>servlet-2.5-reload</module> + <module>servlet-3-async</module> + <module>servlet-3-chunked-io</module> + <module>servlet-3-filter</module> + <module>servlet-3-gf-async</module> + <module>servlet-3-inflector-1</module> + <module>servlet-3-init-1</module> + <module>servlet-3-init-2</module> + <module>servlet-3-init-3</module> + <module>servlet-3-init-4</module> + <module>servlet-3-init-5</module> + <module>servlet-3-init-6</module> + <module>servlet-3-init-7</module> + <module>servlet-3-init-8</module> + <module>servlet-3-init-9</module> + <module>servlet-3-init-provider</module> + <module>servlet-3-params</module> + <module>servlet-3-sse-1</module> + <module>servlet-4.0-mvc-1</module> + <module>servlet-tests</module> + <module>servlet-request-wrapper-binding</module> + <module>servlet-request-wrapper-binding-2</module> + <!-- <module>spring4</module>--> + <!-- <module>spring5</module>--> + <module>sonar-test</module> + <module>thin-server</module> + <module>tracing-support</module> </modules> + <properties> + <env>default</env> + <jersey.config.test.container.port>9998</jersey.config.test.container.port> + <jetty.log.file>${project.build.directory}/jetty-out.log</jetty.log.file> + </properties> + <profiles> <profile> - <id>default</id> - <properties> - <env>default</env> - <jersey.config.test.container.port>9998</jersey.config.test.container.port> - </properties> - <activation> - <jdk>[1.8,)</jdk> -<!-- <activeByDefault>true</activeByDefault> does not work ?!--> - </activation> - </profile> - <profile> <id>sonar</id> - <properties> - <env>default</env> - <jersey.config.test.container.port>9998</jersey.config.test.container.port> - <jetty.log.file>${project.build.directory}/jetty-out.log</jetty.log.file> - </properties> <build> <pluginManagement> <plugins> @@ -120,99 +189,6 @@ </pluginManagement> </build> </profile> - <profile> - <id>Jetty11</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <modules> - <module>async-jersey-filter</module> - <module>externalproperties</module> - <module>jaxrs-component-inject</module> - <module>jersey-780</module> - <module>jersey-1107</module> - <module>jersey-1223</module> - <module>jersey-1604</module> - <module>jersey-1667</module> -<!-- <module>jersey-1829</module> Jakartification--> - <module>jersey-1883</module> - <module>jersey-1928</module> - <module>jersey-1960</module> - <module>jersey-1964</module> - <module>jersey-2031</module> - <module>jersey-2160</module> - <module>jersey-2164</module> - <module>jersey-2167</module> - <module>jersey-2176</module> - <module>jersey-2184</module> - <module>jersey-2255</module> - <module>jersey-2322</module> - <module>jersey-2335</module> - <module>jersey-2551</module> - <module>jersey-2612</module> - <module>jersey-2637</module> - <module>jersey-2654</module> - <module>jersey-2673</module> - <module>jersey-2689</module> - <module>jersey-2704</module> - <module>jersey-2794</module> - <module>jersey-2846</module> - <module>jersey-2878</module> - <module>jersey-2892</module> - <module>jersey-3796</module> - <module>jersey-4949</module> - <module>property-check</module> - <module>security-digest</module> - <module>servlet-2.5-autodiscovery-1</module> - <module>servlet-2.5-autodiscovery-2</module> - <module>servlet-2.5-filter</module> - <module>servlet-2.5-inflector-1</module> - <module>servlet-2.5-init-1</module> - <module>servlet-2.5-init-2</module> - <module>servlet-2.5-init-3</module> - <module>servlet-2.5-init-4</module> - <module>servlet-2.5-init-5</module> - <module>servlet-2.5-init-6</module> - <module>servlet-2.5-init-7</module> - <module>servlet-2.5-init-8</module> - <module>servlet-2.5-mvc-1</module> - <module>servlet-2.5-mvc-2</module> - <module>servlet-2.5-mvc-3</module> - <module>servlet-2.5-reload</module> - <module>servlet-3-async</module> - <module>servlet-3-chunked-io</module> - <module>servlet-3-filter</module> - <module>servlet-3-gf-async</module> - <module>servlet-3-inflector-1</module> - <module>servlet-3-init-1</module> - <module>servlet-3-init-2</module> - <module>servlet-3-init-3</module> - <module>servlet-3-init-4</module> - <module>servlet-3-init-5</module> - <module>servlet-3-init-6</module> - <module>servlet-3-init-7</module> - <module>servlet-3-init-8</module> - <module>servlet-3-init-9</module> - <module>servlet-3-init-provider</module> - <module>servlet-3-params</module> - <module>servlet-3-sse-1</module> - <module>servlet-4.0-mvc-1</module> - <module>servlet-tests</module> - <module>servlet-request-wrapper-binding</module> - <module>servlet-request-wrapper-binding-2</module> - <module>sonar-test</module> - <module>tracing-support</module> - </modules> - </profile> - <profile> - <id>spring6-jdk17</id> - <activation> - <jdk>[17,)</jdk> - </activation> - <modules> - <module>spring6</module> - </modules> - </profile> </profiles> <build>
diff --git a/tests/integration/servlet-2.5-inflector-1/pom.xml b/tests/integration/servlet-2.5-inflector-1/pom.xml index 0c4a305..b8af766 100644 --- a/tests/integration/servlet-2.5-inflector-1/pom.xml +++ b/tests/integration/servlet-2.5-inflector-1/pom.xml
@@ -43,7 +43,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-2.5-init-1/pom.xml b/tests/integration/servlet-2.5-init-1/pom.xml index 54f437b..04e2cc9 100644 --- a/tests/integration/servlet-2.5-init-1/pom.xml +++ b/tests/integration/servlet-2.5-init-1/pom.xml
@@ -36,7 +36,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-2.5-mvc-1/pom.xml b/tests/integration/servlet-2.5-mvc-1/pom.xml index ddf4d53..63892cd 100644 --- a/tests/integration/servlet-2.5-mvc-1/pom.xml +++ b/tests/integration/servlet-2.5-mvc-1/pom.xml
@@ -57,7 +57,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-2.5-mvc-2/pom.xml b/tests/integration/servlet-2.5-mvc-2/pom.xml index 7e8cd21..cf3515d 100644 --- a/tests/integration/servlet-2.5-mvc-2/pom.xml +++ b/tests/integration/servlet-2.5-mvc-2/pom.xml
@@ -57,7 +57,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-2.5-mvc-3/pom.xml b/tests/integration/servlet-2.5-mvc-3/pom.xml index 83bf515..32d989d 100644 --- a/tests/integration/servlet-2.5-mvc-3/pom.xml +++ b/tests/integration/servlet-2.5-mvc-3/pom.xml
@@ -57,7 +57,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-3-inflector-1/pom.xml b/tests/integration/servlet-3-inflector-1/pom.xml index af1d929..95bb384 100644 --- a/tests/integration/servlet-3-inflector-1/pom.xml +++ b/tests/integration/servlet-3-inflector-1/pom.xml
@@ -43,7 +43,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-3-init-9/pom.xml b/tests/integration/servlet-3-init-9/pom.xml index 209e822..bddea9f 100644 --- a/tests/integration/servlet-3-init-9/pom.xml +++ b/tests/integration/servlet-3-init-9/pom.xml
@@ -42,7 +42,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-3-init-provider/pom.xml b/tests/integration/servlet-3-init-provider/pom.xml index 0406650..07459d7 100644 --- a/tests/integration/servlet-3-init-provider/pom.xml +++ b/tests/integration/servlet-3-init-provider/pom.xml
@@ -40,7 +40,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-3-params/pom.xml b/tests/integration/servlet-3-params/pom.xml index 48177f9..967c2d0 100644 --- a/tests/integration/servlet-3-params/pom.xml +++ b/tests/integration/servlet-3-params/pom.xml
@@ -40,7 +40,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-4.0-mvc-1/pom.xml b/tests/integration/servlet-4.0-mvc-1/pom.xml index 7cca8ac..2aa2491 100644 --- a/tests/integration/servlet-4.0-mvc-1/pom.xml +++ b/tests/integration/servlet-4.0-mvc-1/pom.xml
@@ -49,7 +49,14 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + </dependency> + <dependency> + <groupId>jakarta.xml.bind</groupId> + <artifactId>jakarta.xml.bind-api</artifactId> + </dependency> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-osgi</artifactId> </dependency> <dependency> @@ -77,12 +84,11 @@ <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> - <!--TODO jakartify this --> <dependencies> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + <version>${servlet6.version}</version> </dependency> </dependencies> <configuration> @@ -91,24 +97,4 @@ </plugin> </plugins> </build> - - <profiles> - <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <dependencies> - <dependency> - <groupId>jakarta.xml.bind</groupId> - <artifactId>jakarta.xml.bind-api</artifactId> - </dependency> - <dependency> - <groupId>com.sun.xml.bind</groupId> - <artifactId>jaxb-osgi</artifactId> - </dependency> - </dependencies> - </profile> - </profiles> - </project>
diff --git a/tests/integration/servlet-request-wrapper-binding-2/pom.xml b/tests/integration/servlet-request-wrapper-binding-2/pom.xml index 3437dbd..52761de 100644 --- a/tests/integration/servlet-request-wrapper-binding-2/pom.xml +++ b/tests/integration/servlet-request-wrapper-binding-2/pom.xml
@@ -40,7 +40,7 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + <version>${servlet6.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseWrapperProvider.java b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseWrapperProvider.java index 86017ec..e152f64 100644 --- a/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseWrapperProvider.java +++ b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseWrapperProvider.java
@@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 Contributors to the Eclipse Foundation * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -39,6 +40,7 @@ import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConnection; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletInputStream; @@ -291,16 +293,6 @@ } @Override - public String encodeUrl(String s) { - return getHttpServletResponse().encodeUrl(s); - } - - @Override - public String encodeRedirectUrl(String s) { - return getHttpServletResponse().encodeRedirectUrl(s); - } - - @Override public void sendError(int i, String s) throws IOException { getHttpServletResponse().sendError(i, s); } @@ -330,14 +322,17 @@ getHttpServletResponse().setHeader(h, v); } + @Override public Collection<String> getHeaderNames() { return getHttpServletResponse().getHeaderNames(); } + @Override public Collection<String> getHeaders(String s) { return getHttpServletResponse().getHeaders(s); } + @Override public String getHeader(String s) { return getHttpServletResponse().getHeader(s); } @@ -368,11 +363,6 @@ } @Override - public void setStatus(int i, String s) { - getHttpServletResponse().setStatus(i, s); - } - - @Override public String getCharacterEncoding() { return getHttpServletResponse().getCharacterEncoding(); } @@ -501,6 +491,22 @@ private abstract static class MyHttpServletRequestImpl implements HttpServletRequest { @Override + public String getRequestId() { + return getHttpServletRequest().getRequestId(); + } + + @Override + public String getProtocolRequestId() { + return getHttpServletRequest().getProtocolRequestId(); + } + + @Override + public ServletConnection getServletConnection() { + return getHttpServletRequest().getServletConnection(); + } + + + @Override public String getAuthType() { return getHttpServletRequest().getAuthType(); } @@ -658,11 +664,6 @@ } @Override - public boolean isRequestedSessionIdFromUrl() { - return getHttpServletRequest().isRequestedSessionIdFromUrl(); - } - - @Override public Object getAttribute(String s) { return getHttpServletRequest().getAttribute(s); } @@ -783,11 +784,6 @@ } @Override - public String getRealPath(String s) { - return getHttpServletRequest().getRealPath(s); - } - - @Override public int getRemotePort() { return getHttpServletRequest().getRemotePort(); }
diff --git a/tests/integration/servlet-request-wrapper-binding/pom.xml b/tests/integration/servlet-request-wrapper-binding/pom.xml index 2ff1a47..1d0a483 100644 --- a/tests/integration/servlet-request-wrapper-binding/pom.xml +++ b/tests/integration/servlet-request-wrapper-binding/pom.xml
@@ -40,7 +40,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/servlet-tests/pom.xml b/tests/integration/servlet-tests/pom.xml index b7a2eb5..329b1b5 100644 --- a/tests/integration/servlet-tests/pom.xml +++ b/tests/integration/servlet-tests/pom.xml
@@ -36,7 +36,7 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + <version>${servlet6.version}</version> <scope>provided</scope> </dependency>
diff --git a/tests/integration/thin-server/pom.xml b/tests/integration/thin-server/pom.xml new file mode 100644 index 0000000..8f9f511 --- /dev/null +++ b/tests/integration/thin-server/pom.xml
@@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 2022 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 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>project</artifactId> + <groupId>org.glassfish.jersey.tests.integration</groupId> + <version>3.1.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>thin-server</artifactId> + <name>jersey-thin-server</name> + <description> + Run server without HTTP stack in tests. + </description> + + <dependencies> + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-common</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-server</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-client</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.test-framework</groupId> + <artifactId>jersey-test-framework-util</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project>
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/tests/integration/thin-server/src/main/java/org.glassfish.jersey.integration.thinserver/ThinServerResource.java similarity index 65% copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java copy to tests/integration/thin-server/src/main/java/org.glassfish.jersey.integration.thinserver/ThinServerResource.java index dd25372..9cb3cb7 100644 --- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java +++ b/tests/integration/thin-server/src/main/java/org.glassfish.jersey.integration.thinserver/ThinServerResource.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -14,7 +14,16 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 */ -package org.glassfish.jersey.internal.jsr166; +package org.glassfish.jersey.integration.thinserver; -public interface JerseyFlowSubscriber<T> extends Flow.Subscriber<T> { +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/") +public class ThinServerResource { + @GET + @Path("someget") + public String get() { + return ThinServerResource.class.getName(); + } }
diff --git a/tests/integration/thin-server/src/test/java/org/glassfish/jersey/integration/thinserver/ThinServerTest.java b/tests/integration/thin-server/src/test/java/org/glassfish/jersey/integration/thinserver/ThinServerTest.java new file mode 100644 index 0000000..c64bce5 --- /dev/null +++ b/tests/integration/thin-server/src/test/java/org/glassfish/jersey/integration/thinserver/ThinServerTest.java
@@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 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.integration.thinserver; + +import jakarta.ws.rs.HttpMethod; +import jakarta.ws.rs.core.Response; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; +import org.glassfish.jersey.message.internal.OutboundMessageContext; +import org.glassfish.jersey.server.ApplicationHandler; +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ContainerResponse; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.util.server.ContainerRequestBuilder; +import org.junit.Assert; +import org.junit.Test; + +import java.net.URI; +import java.util.concurrent.ExecutionException; + +public class ThinServerTest { + @Test + public void testGet() throws ExecutionException, InterruptedException { + ContainerRequest request = + ContainerRequestBuilder.from(URI.create("/someget"), HttpMethod.GET, new ClientConfig()).build(); + + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(ThinServerResource.class)); + ContainerResponse containerResponse = applicationHandler.apply(request).get(); + OutboundMessageContext outboundMessageContext = containerResponse.getWrappedMessageContext(); + Response response = new OutboundJaxrsResponse(containerResponse.getStatusInfo(), outboundMessageContext); + Assert.assertEquals(ThinServerResource.class.getName(), response.getEntity()); + } +}
diff --git a/tests/jersey-tck/README.md b/tests/jersey-tck/README.md new file mode 100644 index 0000000..2457e01 --- /dev/null +++ b/tests/jersey-tck/README.md
@@ -0,0 +1,15 @@ +# Jakarta REST TCK + +The **Jakarta REST TCK** is a standalone kit for testing compliance of an implementation with the Jakarta REST specification. + + +## Performing Test + +While the test kit *is not dependent* of Maven, the most easy way to perform the tests is to create a copy of the sample Maven project found in the `jersey-tck` folder and adjust it to the actual needs of the implementation to be actually tested: +* Replace the dependency to Jersey by a dependency to the implementation to be actually tested. +* Execute `mvn verify`. +* Find the test result as part of Maven's output on the console or refer to the surefire reports. + +**Note:** Certainly the same can be performed using *other* build tools, like Gradle, or even by running a standalone Jupiter API compatible test runner (e. g. JUnit 5 Console Runner), as long as the Jakarta REST TCK JAR file and the implementation to test are both found on the classpath. + +**Hint:** The test project can safely get stored as part of the implementation, so it can be easily executed as part of the QA process.
diff --git a/tests/jersey-tck/j2ee.pass b/tests/jersey-tck/j2ee.pass new file mode 100644 index 0000000..7cd0e34 --- /dev/null +++ b/tests/jersey-tck/j2ee.pass
@@ -0,0 +1,16 @@ +# +# Copyright (c) 2022 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 +# +AS_ADMIN_USERPASSWORD=j2ee \ No newline at end of file
diff --git a/tests/jersey-tck/javajoe.pass b/tests/jersey-tck/javajoe.pass new file mode 100644 index 0000000..f06be72 --- /dev/null +++ b/tests/jersey-tck/javajoe.pass
@@ -0,0 +1,16 @@ +# +# Copyright (c) 2022 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 +# +AS_ADMIN_USERPASSWORD=javajoe \ No newline at end of file
diff --git a/tests/jersey-tck/pom.xml b/tests/jersey-tck/pom.xml new file mode 100644 index 0000000..3a4f946 --- /dev/null +++ b/tests/jersey-tck/pom.xml
@@ -0,0 +1,544 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + + Copyright (c) 2022 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 + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-tck</artifactId> + <version>3.1.0</version> + <packaging>jar</packaging> + + <name>Jakarta RESTful WS Compliance for Jersey</name> + <description>This test verifies the compliance of Eclipse Jersey with Jakarta REST</description> + + <properties> + <maven.compiler.source>11</maven.compiler.source> + <maven.compiler.target>11</maven.compiler.target> + <jersey.version>3.1.0-M3</jersey.version> <!-- the public version that pass the tck --> + <glassfish.container.version>6.2.5</glassfish.container.version> + <glassfish.home>${project.build.directory}/glassfish6</glassfish.home> + <jakarta.platform.version>10.0.0-RC1</jakarta.platform.version> + <junit.jupiter.version>5.7.2</junit.jupiter.version> + <jakarta.rest.version>3.1.0</jakarta.rest.version> + <tck.artifactId>jakarta-restful-ws-tck</tck.artifactId> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.junit</groupId> + <artifactId>junit-bom</artifactId> + <version>${junit.jupiter.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey</groupId> + <artifactId>jersey-bom</artifactId> + <version>${jersey.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <repositories> + <repository> + <id>jakarta-snapshots</id> + <url>https://jakarta.oss.sonatype.org/content/repositories/staging/</url> + </repository> + </repositories> + + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <version>${junit.jupiter.version}</version> + </dependency> + + <dependency> + <groupId>org.glassfish.hk2</groupId> + <artifactId>hk2-locator</artifactId> + <version>3.0.0</version> + </dependency> + + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-impl</artifactId> + <version>3.0.0</version> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>org.jboss.arquillian.container</groupId> + <artifactId>arquillian-glassfish-managed-6</artifactId> + <version>1.0.0.Alpha1</version> + </dependency> + + <dependency> + <groupId>jakarta.ws.rs</groupId> + <artifactId>${tck.artifactId}</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>jakarta.ws.rs</groupId> + <artifactId>jakarta.ws.rs-api</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <version>2.2</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.glassfish.main.common</groupId> + <artifactId>simple-glassfish-api</artifactId> + <version>${glassfish.container.version}</version> + </dependency> + + <dependency> + <groupId>org.jboss.arquillian.junit5</groupId> + <artifactId>arquillian-junit5-container</artifactId> + <version>1.7.0.Alpha10</version> + </dependency> + + <dependency> + <groupId>jakarta.platform</groupId> + <artifactId>jakarta.jakartaee-api</artifactId> + <version>${jakarta.platform.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>commons-httpclient</groupId> + <artifactId>commons-httpclient</artifactId> + <version>3.1</version> + </dependency> + + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-server</artifactId> + <version>${jersey.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-grizzly2-http</artifactId> + <version>${jersey.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.netbeans.tools</groupId> + <artifactId>sigtest-maven-plugin</artifactId> + <version>1.4</version> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-json-binding</artifactId> + <version>${jersey.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-jaxb</artifactId> + <version>${jersey.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-sse</artifactId> + <version>${jersey.version}</version> + <scope>test</scope> + </dependency> + + </dependencies> + + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>3.2.0</version> + <executions> + <execution> + <id>unpack</id> + <phase>pre-integration-test</phase> + <goals> + <goal>unpack</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.glassfish.main.distributions</groupId> + <artifactId>glassfish</artifactId> + <version>${glassfish.container.version}</version> + <type>zip</type> + <overWrite>false</overWrite> + <outputDirectory>${project.build.directory}</outputDirectory> + </artifactItem> + </artifactItems> + </configuration> + </execution> + <execution> + <id>copy</id> + <phase>pre-integration-test</phase> + <goals> + <goal>copy</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-client</artifactId> + <version>${jersey.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jersey-client.jar</destFileName> + </artifactItem> + <artifactItem> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-server</artifactId> + <version>${jersey.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jersey-server.jar</destFileName> + </artifactItem> + <artifactItem> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-common</artifactId> + <version>${jersey.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jersey-common.jar</destFileName> + </artifactItem> + <artifactItem> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-grizzly2-http</artifactId> + <version>${jersey.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jersey-container-grizzly2-http.jar</destFileName> + </artifactItem> + <artifactItem> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-servlet-core</artifactId> + <version>${jersey.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jersey-container-servlet-core.jar</destFileName> + </artifactItem> + <artifactItem> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-servlet</artifactId> + <version>${jersey.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jersey-container-servlet.jar</destFileName> + </artifactItem> + <artifactItem> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-sse</artifactId> + <version>${jersey.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jersey-media-sse.jar</destFileName> + </artifactItem> + <artifactItem> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-json-binding</artifactId> + <version>${jersey.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jersey-media-json-binding.jar</destFileName> + </artifactItem> + <artifactItem> + <groupId>jakarta.ws.rs</groupId> + <artifactId>jakarta.ws.rs-api</artifactId> + <version>${jakarta.rest.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${glassfish.home}/glassfish/modules</outputDirectory> + <destFileName>jakarta.ws.rs-api.jar</destFileName> + </artifactItem> + </artifactItems> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>exec-maven-plugin</artifactId> + <groupId>org.codehaus.mojo</groupId> + <executions> + <execution> + <id>StopDomain1</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>stop-domain</argument> + </arguments> + </configuration> + </execution> + <execution> + <id>StartDomain1</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>start-domain</argument> + </arguments> + </configuration> + </execution> + <execution> + <id>Enable Trace requests</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>set</argument> + <argument>server-config.network-config.protocols.protocol.http-listener-1.http.trace-enabled=true</argument> + </arguments> + </configuration> + </execution> + <execution> + <id>Delete User j2ee</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>--passwordfile</argument> + <argument>${project.basedir}/j2ee.pass</argument> + <argument>delete-file-user</argument> + <argument>j2ee</argument> + </arguments> + <successCodes> + <successCode>0</successCode> + <successCode>1</successCode> + </successCodes> + </configuration> + </execution> + <execution> + <id>Add User j2ee</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>--passwordfile</argument> + <argument>${project.basedir}/j2ee.pass</argument> + <argument>create-file-user</argument> + <argument>--groups</argument> + <argument>staff:mgr</argument> + <argument>j2ee</argument> + </arguments> + </configuration> + </execution> + <execution> + <id>Delete User javajoe</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>--passwordfile</argument> + <argument>${project.basedir}/javajoe.pass</argument> + <argument>delete-file-user</argument> + <argument>javajoe</argument> + </arguments> + <successCodes> + <successCode>0</successCode> + <successCode>1</successCode> + </successCodes> + </configuration> + </execution> + <execution> + <id>Add User javajoe</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>--passwordfile</argument> + <argument>${project.basedir}/javajoe.pass</argument> + <argument>create-file-user</argument> + <argument>--groups</argument> + <argument>guest</argument> + <argument>javajoe</argument> + </arguments> + </configuration> + </execution> + <execution> + <id>list users</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>list-file-users</argument> + </arguments> + </configuration> + </execution> + <execution> + <id>StopDomain</id> + <phase>pre-integration-test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>${asadmin.home}</workingDirectory> + <executable>${asadmin}</executable> + <arguments> + <argument>stop-domain</argument> + </arguments> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-failsafe-plugin</artifactId> + <version>3.0.0-M5</version> + <executions> + <execution> + <id>gf-tests</id> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + <configuration> + <excludes> + <exclude>**/SeBootstrapIT.java</exclude> + </excludes> + <skipTests>false</skipTests> <!-- Do not skip when the jersey-tck profile is on --> + <dependenciesToScan>jakarta.ws.rs:${tck.artifactId}</dependenciesToScan> + <systemPropertyVariables> + <GLASSFISH_HOME>${glassfish.home}</GLASSFISH_HOME> + <servlet_adaptor>org.glassfish.jersey.servlet.ServletContainer</servlet_adaptor> + <webServerHost>localhost</webServerHost> + <webServerPort>8080</webServerPort> + <junit.log.traceflag>true</junit.log.traceflag> + <user>j2ee</user> + <password>j2ee</password> + <authuser>javajoe</authuser> + <authpassword>javajoe</authpassword> + <porting.ts.url.class.1>ee.jakarta.tck.ws.rs.lib.implementation.sun.common.SunRIURL</porting.ts.url.class.1> + <jimage.dir>${project.build.directory}/jdk11-bundle</jimage.dir> + <optional.tech.packages.to.ignore>jakarta.xml.bind</optional.tech.packages.to.ignore> + <signature.sigTestClasspath>${glassfish.home}/glassfish/modules/jakarta.ws.rs-api.jar:${glassfish.home}/glassfish/modules/jakarta.xml.bind-api.jar:${project.build.directory}/jdk11-bundle/java.base:${project.build.directory}/jdk11-bundle/java.rmi:${project.build.directory}/jdk11-bundle/java.sql:${project.build.directory}/jdk11-bundle/java.naming</signature.sigTestClasspath> + </systemPropertyVariables> + <environmentVariables> + <GLASSFISH_HOME>${glassfish.home}</GLASSFISH_HOME> + </environmentVariables> + </configuration> + </execution> + <execution> + <id>se-tests</id> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + <configuration> + <skipTests>false</skipTests> <!-- Do not skip when the jersey-tck profile is on --> + <includes> + <include>**/SeBootstrapIT.java</include> + </includes> + <dependenciesToScan>jakarta.ws.rs:${tck.artifactId}</dependenciesToScan> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>onLinux</id> + <activation> + <os> + <family>unix</family> + </os> + </activation> + <properties> + <asadmin.home>${basedir}</asadmin.home> + <asadmin>${project.build.directory}/glassfish6/glassfish/bin/asadmin</asadmin> + </properties> + </profile> + <profile> + <id>onWindows</id> + <activation> + <os> + <family>Windows</family> + </os> + </activation> + <properties> + <asadmin.home>${glassfish.home}/glassfish/bin</asadmin.home> + <asadmin>asadmin</asadmin> + </properties> + </profile> + <profile> + <id>jersey-tck</id> + <properties> + <jersey.version>3.1.0-SNAPSHOT</jersey.version> <!-- When running the profile, use SNAPSHOT --> + </properties> + </profile> + </profiles> +</project>
diff --git a/tests/jersey-tck/src/main/java/org/glassfish/jersey/core/tck/JerseyApplicationArchiveProcessor.java b/tests/jersey-tck/src/main/java/org/glassfish/jersey/core/tck/JerseyApplicationArchiveProcessor.java new file mode 100644 index 0000000..c5597e5 --- /dev/null +++ b/tests/jersey-tck/src/main/java/org/glassfish/jersey/core/tck/JerseyApplicationArchiveProcessor.java
@@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 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.core.tck; + +import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor; +import org.jboss.arquillian.test.spi.TestClass; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.spec.WebArchive; + +public class JerseyApplicationArchiveProcessor implements ApplicationArchiveProcessor { + @Override + public void process(Archive<?> archive, TestClass testClass) { + if ("jaxrs_ee_rs_container_requestcontext_security_web.war".equals(archive.getName())) { + WebArchive webArchive = (WebArchive) archive; + webArchive.addAsWebInfResource("jaxrs_ee_rs_container_requestcontext_security_web.war.sun-web.xml", "sun-web.xml"); + } else if ("jaxrs_ee_core_securitycontext_basic_web.war".equals(archive.getName())) { + WebArchive webArchive = (WebArchive) archive; + webArchive.addAsWebInfResource("jaxrs_ee_core_securitycontext_basic_web.war.sun-web.xml", "sun-web.xml"); + } + } +}
diff --git a/tests/jersey-tck/src/main/java/org/glassfish/jersey/core/tck/JerseyLoadableExtension.java b/tests/jersey-tck/src/main/java/org/glassfish/jersey/core/tck/JerseyLoadableExtension.java new file mode 100644 index 0000000..d4da355 --- /dev/null +++ b/tests/jersey-tck/src/main/java/org/glassfish/jersey/core/tck/JerseyLoadableExtension.java
@@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 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.core.tck; + +import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor; +import org.jboss.arquillian.core.spi.LoadableExtension; + +public class JerseyLoadableExtension implements LoadableExtension { + @Override + public void register(ExtensionBuilder extensionBuilder) { + extensionBuilder.service(ApplicationArchiveProcessor.class, JerseyApplicationArchiveProcessor.class); + } +}
diff --git a/tests/jersey-tck/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/tests/jersey-tck/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension new file mode 100644 index 0000000..577c00f --- /dev/null +++ b/tests/jersey-tck/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
@@ -0,0 +1 @@ +org.glassfish.jersey.core.tck.JerseyLoadableExtension \ No newline at end of file
diff --git a/tests/jersey-tck/src/main/resources/jaxrs_ee_core_securitycontext_basic_web.war.sun-web.xml b/tests/jersey-tck/src/main/resources/jaxrs_ee_core_securitycontext_basic_web.war.sun-web.xml new file mode 100644 index 0000000..8e2a251 --- /dev/null +++ b/tests/jersey-tck/src/main/resources/jaxrs_ee_core_securitycontext_basic_web.war.sun-web.xml
@@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 2022 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 + +--> + +<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Sun ONE Application Server 8.0 Servlet 2.4//EN" "http://www.sun.com/software/sunone/appserver/dtds/sun-web-app_2_5-0.dtd"> +<sun-web-app> + <context-root>jaxrs_ee_core_securitycontext_basic_web</context-root> + <security-role-mapping> + <role-name>DIRECTOR</role-name> + <principal-name>j2ee</principal-name> + </security-role-mapping> + <security-role-mapping> + <role-name>OTHERROLE</role-name> + <principal-name>javajoe</principal-name> + </security-role-mapping> +</sun-web-app>
diff --git a/tests/jersey-tck/src/main/resources/jaxrs_ee_rs_container_requestcontext_security_web.war.sun-web.xml b/tests/jersey-tck/src/main/resources/jaxrs_ee_rs_container_requestcontext_security_web.war.sun-web.xml new file mode 100644 index 0000000..a5efdc5 --- /dev/null +++ b/tests/jersey-tck/src/main/resources/jaxrs_ee_rs_container_requestcontext_security_web.war.sun-web.xml
@@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 2022 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 + +--> + +<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Sun ONE Application Server 8.0 Servlet 2.4//EN" "http://www.sun.com/software/sunone/appserver/dtds/sun-web-app_2_5-0.dtd"> +<sun-web-app> + <context-root>jaxrs_ee_rs_container_requestcontext_security_web</context-root> + <security-role-mapping> + <role-name>DIRECTOR</role-name> + <principal-name>j2ee</principal-name> + </security-role-mapping> +</sun-web-app>
diff --git a/tests/jmockit/src/test/java/org/glassfish/jersey/tests/jmockit/server/WebServerFactoryTest.java b/tests/jmockit/src/test/java/org/glassfish/jersey/tests/jmockit/server/WebServerFactoryTest.java new file mode 100644 index 0000000..2831134 --- /dev/null +++ b/tests/jmockit/src/test/java/org/glassfish/jersey/tests/jmockit/server/WebServerFactoryTest.java
@@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018 Markus KARG. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.jmockit.server; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.theInstance; +import static org.junit.Assert.assertThat; + +import java.util.Iterator; + +import jakarta.ws.rs.core.Application; + +import org.glassfish.jersey.internal.ServiceFinder; +import org.glassfish.jersey.internal.ServiceFinder.ServiceIteratorProvider; +import org.glassfish.jersey.internal.guava.Iterators; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InjectionManagerFactory; +import org.glassfish.jersey.server.JerseySeBootstrapConfiguration; +import org.glassfish.jersey.server.WebServerFactory; +import org.glassfish.jersey.server.spi.WebServer; +import org.glassfish.jersey.server.spi.WebServerProvider; +import org.junit.After; +import org.junit.Test; + +import mockit.Mocked; + +/** + * Unit tests for {@link WebServerFactory}. + * + * @author Markus KARG (markus@headcrashing.eu) + * @since 3.1.0 + */ +public final class WebServerFactoryTest { + + @Test + public final void shouldBuildServer(@Mocked final Application mockApplication, + @Mocked final WebServer mockServer, + @Mocked final JerseySeBootstrapConfiguration mockConfiguration, + @Mocked final InjectionManager mockInjectionManager) { + // given + ServiceFinder.setIteratorProvider(new ServiceIteratorProvider() { + @Override + public final <T> Iterator<T> createIterator(final Class<T> service, final String serviceName, + final ClassLoader loader, final boolean ignoreOnClassNotFound) { + return Iterators.singletonIterator(service.cast( + service == WebServerProvider.class ? new WebServerProvider() { + @Override + public final <U extends WebServer> U createServer( + final Class<U> type, + final Application application, + final JerseySeBootstrapConfiguration configuration) { + return application == mockApplication && configuration == mockConfiguration + ? type.cast(mockServer) + : null; + } + + @Override + public <T extends WebServer> T createServer( + final Class<T> type, + final Class<? extends Application> applicationClass, + final JerseySeBootstrapConfiguration configuration) { + return null; + } + } + : service == InjectionManagerFactory.class ? new InjectionManagerFactory() { + @Override + public final InjectionManager create(final Object parent) { + return mockInjectionManager; + } + } + : null)); + } + + @Override + public final <T> Iterator<Class<T>> createClassIterator(final Class<T> service, final String serviceName, + final ClassLoader loader, final boolean ignoreOnClassNotFound) { + return null; + } + }); + + // when + final WebServer server = WebServerFactory.createServer(WebServer.class, mockApplication, mockConfiguration); + + // then + assertThat(server, is(theInstance(mockServer))); + } + + @After + public final void resetServiceFinder() { + ServiceFinder.setIteratorProvider(null); + } + +}
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml index c92c5bf..77337b8 100644 --- a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml +++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml
@@ -125,7 +125,7 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + <version>${servlet6.version}</version> <scope>provided</scope> </dependency> <dependency>
diff --git a/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml index af0c7e3..459badb 100644 --- a/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml +++ b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml
@@ -106,7 +106,6 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> <scope>provided</scope> </dependency> </dependencies>
diff --git a/tests/osgi/functional/pom.xml b/tests/osgi/functional/pom.xml index 23317ba..eb31c6b 100644 --- a/tests/osgi/functional/pom.xml +++ b/tests/osgi/functional/pom.xml
@@ -207,7 +207,6 @@ <scope>test</scope> <version>${httpclient.version}</version> </dependency> - <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> @@ -276,7 +275,7 @@ <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <version>${servlet5.version}</version> + <version>${servlet6.version}</version> <scope>test</scope> </dependency> <dependency> @@ -309,14 +308,13 @@ <artifactId>jakarta.json-api</artifactId> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jakarta.json</artifactId> - <classifier>module</classifier> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>org.glassfish</groupId> - <artifactId>jsonp-jaxrs</artifactId> + <groupId>org.eclipse.parsson</groupId> + <artifactId>parsson-media</artifactId> <scope>test</scope> </dependency> <dependency>
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJacksonTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJacksonTest.java index 7d06f02..7aebd55 100644 --- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJacksonTest.java +++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJacksonTest.java
@@ -41,6 +41,9 @@ options.addAll(Helper.expandedList( // vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"), + // TODO - remove when jackson-module-jakarta-xmlbind-annotations supports JAX-B/4 + mavenBundle().groupId("jakarta.xml.bind").artifactId("jakarta.xml.bind-api").version("3.0.1"), + mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-json-jackson").versionAsInProject(), mavenBundle().groupId("org.glassfish.jersey.ext").artifactId("jersey-entity-filtering").versionAsInProject(),
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java index 0f7eb6f..c4558a8 100644 --- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java +++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -57,7 +57,7 @@ mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.moxy").versionAsInProject(), mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.core").versionAsInProject(), mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.asm").versionAsInProject(), - mavenBundle().groupId("org.glassfish").artifactId("jakarta.json").versionAsInProject(), + mavenBundle().groupId("jakarta.json").artifactId("jakarta.json-api").versionAsInProject(), // validation mavenBundle().groupId("org.hibernate.validator").artifactId("hibernate-validator").versionAsInProject(),
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java index ca3a7a0..f40cafe 100644 --- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java +++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021 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 @@ -69,8 +69,9 @@ // JSON processing. mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-json-processing") .versionAsInProject(), - mavenBundle().groupId("org.glassfish").artifactId("jakarta.json").versionAsInProject(), - mavenBundle().groupId("org.glassfish").artifactId("jsonp-jaxrs").versionAsInProject() + mavenBundle().groupId("jakarta.json").artifactId("jakarta.json-api").versionAsInProject(), + mavenBundle().groupId("org.eclipse.parsson").artifactId("parsson").versionAsInProject(), + mavenBundle().groupId("org.eclipse.parsson").artifactId("parsson-media").versionAsInProject() )); options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java index 424b1ba..c3a2fca 100644 --- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java +++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
@@ -79,7 +79,7 @@ .versionAsInProject(), // MBR/MBW for JSON-P is on the classpath. - mavenBundle().groupId("org.glassfish").artifactId("jakarta.json").versionAsInProject() + mavenBundle().groupId("jakarta.json").artifactId("jakarta.json-api").versionAsInProject() )); options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/util/Helper.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/util/Helper.java index 76a1515..4be49dd 100644 --- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/util/Helper.java +++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/util/Helper.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022 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 @@ -183,9 +183,9 @@ mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-client").versionAsInProject(), // Jersey Injection provider - mavenBundle().groupId("org.glassfish.jersey.inject").artifactId("jersey-hk2").versionAsInProject(), + mavenBundle().groupId("org.glassfish.jersey.inject").artifactId("jersey-hk2").versionAsInProject() // Jaxb - api - getActivationBundle() +// getActivationBundle() )); }
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/pom.xml b/tests/performance/test-cases/mbw-xml-jaxb/pom.xml index 9f94dd8..b046d56 100644 --- a/tests/performance/test-cases/mbw-xml-jaxb/pom.xml +++ b/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
@@ -46,6 +46,11 @@ <artifactId>jakarta.ws.rs-api</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>com.sun.xml.bind</groupId> + <artifactId>jaxb-osgi</artifactId> + </dependency> + <dependency> <groupId>org.glassfish.jersey.test-framework.providers</groupId> @@ -63,42 +68,4 @@ </plugins> </build> - <profiles> - <profile> - <id>jdk11+</id> - <activation> - <jdk>[11,)</jdk> - </activation> - <dependencies> - <dependency> - <groupId>jakarta.xml.bind</groupId> - <artifactId>jakarta.xml.bind-api</artifactId> - </dependency> - <dependency> - <groupId>com.sun.xml.bind</groupId> - <artifactId>jaxb-osgi</artifactId> - </dependency> - </dependencies> - </profile> - <profile> - <id>jdk8</id> - <activation> - <jdk>1.8</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <excludes> - <exclude>org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityTest.java</exclude> - </excludes> - </configuration> - </plugin> - </plugins> - </build> - </profile> - </profiles> - </project>
diff --git a/tests/pom.xml b/tests/pom.xml index 0d1a499..700f42e 100644 --- a/tests/pom.xml +++ b/tests/pom.xml
@@ -105,5 +105,13 @@ </plugins> </build> </profile> + <profile> + <id>jersey-tck</id> + <modules> + <module>jersey-tck</module> + </modules> + <build> + </build> + </profile> </profiles> </project>