Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JENKINS-73130] Upgrade core from Jetty 10.x to 12.x (EE 8) #9590

Merged
merged 5 commits into from
Aug 12, 2024
Merged

Conversation

basil
Copy link
Member

@basil basil commented Aug 8, 2024

Context

See JENKINS-73130 and its linked epic.

Problem

January 1, 2024 marked the end of community support for the Jetty 10 and 11 releases. Jenkins currently delivers Jetty 10.

Solution

Upgrade to Jetty 12 (EE 8).

This release does not have any significant compatibility implications for plugins—the javax Servlet API packages remain in use. A small number of plugins rely on Jetty implementation details in their test suites; these test suites must be adapted to the new Jetty 12 APIs, but the production code does not. The main API differences between Jetty 10 and Jetty 12 (EE 8) are that the Jetty codebase has been turned into a modular architecture that can support multiple Servlet API versions. Previous versions of Jetty were tied to a particular version of the Servlet API.

Implementation

In general, replace dependencies with EE 8 versions in pom.xml and update all relevant imports. More notes are left in the self-review below.

Testing done

Full PCT/ATH

Future work

A forthcoming PR will migrate from EE 8 to EE 9.

Proposed changelog entries

Upgrade Jetty from 10.0.22 to 12.0.12.

Proposed upgrade guidelines

N/A

Submitter checklist

Desired reviewers

@mention

Before the changes are marked as ready-for-merge:

Maintainer checklist

@basil basil added rfe For changelog: Minor enhancement. use `major-rfe` for changes to be highlighted ath-successful This PR has successfully passed the full acceptance-test-harness suite pct-successful This PR has successfully passed the full plugin-compatibility-test suite labels Aug 8, 2024
],
"enabled": false,
"matchPackageNames": [
"jakarta.servlet:jakarta.servlet-api",
"jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exclusion remains valid, but not for the reason currently given. The exclusion remains valid for the reason given in the previous exclusion for jakarta.servlet:jakarta.servlet-api. Indeed, that reason applies to both jakarta.servlet:jakarta.servlet-api and jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api, so move these exclusions to the same section.

Comment on lines +35 to +37
<file url="file://$PROJECT_DIR$/websocket/jetty12-ee8/src/filter/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/websocket/jetty12-ee8/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/websocket/jetty12-ee8/src/main/resources" charset="UTF-8" />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a new Jetty 12 (EE 8) WebSocket implementation. Both the Jetty 10 and Jetty 12 (EE 8) implementations will be dropped in a forthcoming PR when we add support for Jetty 12 (EE 9).

bom/pom.xml Outdated
@@ -40,7 +40,8 @@ THE SOFTWARE.
<properties>
<commons-fileupload2.version>2.0.0-M2</commons-fileupload2.version>
<slf4jVersion>2.0.15</slf4jVersion>
<stapler.version>1892.v73465f3d074d</stapler.version>
<!-- TODO JENKINS-73122 https://github.com/jenkinsci/stapler/pull/537 -->
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pom.xml Outdated
@@ -97,7 +98,8 @@ THE SOFTWARE.
<bridge-method-injector.version>1.29</bridge-method-injector.version>
<spotless.check.skip>false</spotless.check.skip>
<!-- Make sure to keep the jetty-maven-plugin version in war/pom.xml in sync with the Jetty release in Winstone: -->
<winstone.version>6.21</winstone.version>
<!-- TODO JENKINS-73126 https://github.com/jenkinsci/winstone/pull/383 -->
<winstone.version>7.0-rc921.b_dc6422f61b_6</winstone.version>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -149,7 +149,7 @@ THE SOFTWARE.
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jenkins-test-harness</artifactId>
<version>2225.2230.v6210cb_b_827f9</version>
<version>2250.v03a_1295b_0a_30</version>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest version of Jenkins Test Harness (JTH), which requires Java 17 and is based on Jetty 12, using reflection to choose between EE 8 and EE 9.

@@ -336,7 +338,7 @@ protected JSONSignatureValidator getJsonSignatureValidator(String name) {
}
}

private static class RemoteUpdateSiteHandler extends AbstractHandler {
private static class RemoteUpdateSiteHandler extends Handler.Abstract {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

Comment on lines +276 to +281
// The client might still be sending us more of the request, but we have had enough of it already and
// have decided to stop processing it. Drain the read end of the socket so that the client can finish
// sending its request in order to read the response we are about to provide.
try (OutputStream os = OutputStream.nullOutputStream()) {
req.getInputStream().transferTo(os);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See JENKINS-73127 for an explanation of this change.

@@ -103,7 +103,7 @@ public void shouldReturnWebAppPropertyIfSystemPropertyNotSetAndDefaultIsSet() th
* @param value value of the property
*/
protected void setWebAppInitParameter(String property, String value) {
Assume.assumeThat(j.jenkins.servletContext, Matchers.instanceOf(ContextHandler.Context.class));
((ContextHandler.Context) j.jenkins.servletContext).getContextHandler().getInitParams().put(property, value);
Assume.assumeThat(j.jenkins.servletContext, Matchers.instanceOf(WebAppContext.Context.class));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adapting to new Jetty 12 APIs as per the migration guide.

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To ease reviewing, a diff from Jetty 10:

 The MIT License
 
-Copyright (c) 2019, CloudBees, Inc.
+Copyright (c) 2019, 2024 CloudBees, Inc.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -32,9 +32,9 @@ THE SOFTWARE.
     <relativePath>../..</relativePath>
   </parent>
 
-  <artifactId>websocket-jetty10</artifactId>
-  <name>Jetty 10 implementation for WebSocket</name>
-  <description>An implementation of the WebSocket handler that works with Jetty 10.</description>
+  <artifactId>websocket-jetty12-ee8</artifactId>
+  <name>Jetty 12 (EE 8) implementation for WebSocket</name>
+  <description>An implementation of the WebSocket handler that works with Jetty 12 (EE 8).</description>
 
   <dependencyManagement>
     <dependencies>
@@ -52,7 +52,7 @@ THE SOFTWARE.
     <dependency>
       <groupId>org.jenkins-ci</groupId>
       <artifactId>winstone</artifactId>
-      <version>6.21</version>
+      <version>${winstone.version}</version>
       <optional>true</optional>
     </dependency>
     <dependency>

@@ -0,0 +1,178 @@
/*
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To ease reviewing, a diff from Jetty 10:

diff --git a/websocket/jetty10/src/main/java/jenkins/websocket/Jetty10Provider.java b/websocket/jetty10/src/main/java/jenkins/websocket/Jetty10Provider.java
index b5243e627f..cc006b180f 100644
--- a/websocket/jetty10/src/main/java/jenkins/websocket/Jetty10Provider.java
+++ b/websocket/jetty10/src/main/java/jenkins/websocket/Jetty10Provider.java
@@ -1,7 +1,7 @@
  * The MIT License
  *
- * Copyright 2022 CloudBees, Inc.
+ * Copyright 2022, 2024 CloudBees, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -31,35 +31,35 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.websocket.api.Session;
-import org.eclipse.jetty.websocket.api.WebSocketListener;
-import org.eclipse.jetty.websocket.api.WriteCallback;
-import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest;
-import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse;
-import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
+import org.eclipse.jetty.ee8.websocket.api.Session;
+import org.eclipse.jetty.ee8.websocket.api.WebSocketListener;
+import org.eclipse.jetty.ee8.websocket.api.WriteCallback;
+import org.eclipse.jetty.ee8.websocket.server.JettyServerUpgradeRequest;
+import org.eclipse.jetty.ee8.websocket.server.JettyServerUpgradeResponse;
+import org.eclipse.jetty.ee8.websocket.server.JettyWebSocketServerContainer;
 import org.kohsuke.MetaInfServices;
 import org.kohsuke.accmod.Restricted;
 import org.kohsuke.accmod.restrictions.NoExternalUse;
 
 @Restricted(NoExternalUse.class)
 @MetaInfServices(Provider.class)
-public class Jetty10Provider implements Provider {
+public class Jetty12EE8Provider implements Provider {
 
     /**
      * Number of seconds a WebsocketConnection may stay idle until it expires.
      * Zero to disable.
      * This value must be higher than the <code>jenkins.websocket.pingInterval</code>.
-     * Per <a href=https://www.eclipse.org/jetty/documentation/jetty-10/programming-guide/index.html#pg-websocket-session-ping>Jetty 10 documentation</a>
+     * Per <a href=https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-websocket-session-ping>Jetty 12 documentation</a>
      * a ping mechanism should keep the websocket active. Therefore, the idle timeout must be higher than the ping
      * interval to avoid timeout issues.
      */
     private static long IDLE_TIMEOUT_SECONDS = Long.getLong("jenkins.websocket.idleTimeout", 60L);
 
-    private static final String ATTR_LISTENER = Jetty10Provider.class.getName() + ".listener";
+    private static final String ATTR_LISTENER = Jetty12EE8Provider.class.getName() + ".listener";
 
     private boolean initialized = false;
 
-    public Jetty10Provider() {
+    public Jetty12EE8Provider() {
         JettyWebSocketServerContainer.class.hashCode();
     }
 
@@ -74,12 +74,12 @@ public class Jetty10Provider implements Provider {
     public Handler handle(HttpServletRequest req, HttpServletResponse rsp, Listener listener) throws Exception {
         init(req);
         req.setAttribute(ATTR_LISTENER, listener);
-        // TODO Jetty 10 has no obvious equivalent to WebSocketServerFactory.isUpgradeRequest; RFC6455Negotiation?
+        // TODO Jetty 10+ has no obvious equivalent to WebSocketServerFactory.isUpgradeRequest; RFC6455Negotiation?
         if (!"websocket".equalsIgnoreCase(req.getHeader("Upgrade"))) {
             rsp.sendError(HttpServletResponse.SC_BAD_REQUEST, "only WS connections accepted here");
             return null;
         }
-        if (!JettyWebSocketServerContainer.getContainer(req.getServletContext()).upgrade(Jetty10Provider::createWebSocket, req, rsp)) {
+        if (!JettyWebSocketServerContainer.getContainer(req.getServletContext()).upgrade(Jetty12EE8Provider::createWebSocket, req, rsp)) {
             rsp.sendError(HttpServletResponse.SC_BAD_REQUEST, "did not manage to upgrade");
             return null;
         }
@@ -175,5 +175,4 @@ public class Jetty10Provider implements Provider {
             }
         };
     }
-
 }

@basil basil added the needs-security-review Awaiting review by a security team member label Aug 8, 2024
@basil basil requested a review from a team August 8, 2024 21:45
Comment on lines +654 to +656
<config implementation="org.eclipse.jetty.maven.MavenResource">
<resourceAsString>${basedir}/src/realm.properties</resourceAsString>
</config>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<configuration>
<!--
Reload webapp when you hit ENTER. (See JETTY-282 for more)
-->
<reload>manual</reload>
<scan>0</scan>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@MarkEWaite MarkEWaite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good to me.

I see that the websocket/jetty10 module is retained in the pom.xml at the root of the repository. Is that needed for API compatibility so that the jenkins.websocket.Jetty10Provider class is not removed from the Jenkins API or is there something else that I'm not understanding about the Jetty 10 websocket provider being built and packaged in the Jenkins war file along with the Jetty 12 websocket provider?

@basil
Copy link
Member Author

basil commented Aug 9, 2024

I see that the websocket/jetty10 module is retained in the pom.xml at the root of the repository. Is that needed for API compatibility so that the jenkins.websocket.Jetty10Provider class is not removed from the Jenkins API or is there something else that I'm not understanding about the Jetty 10 websocket provider being built and packaged in the Jenkins war file along with the Jetty 12 websocket provider?

Historically we have retained older WebSocket Jetty modules in core to allow for older test harnesses (with older Jetty versions) to be used with newer cores, and this PR follows past precedent. In any case this state of affairs will only last for a few weeks, as we'll be removing both the Jetty 10 and Jetty 12 EE 8 WebSocket modules when adding support for Jetty 12 EE 9 in core. I haven't made any attempt to get a Jetty 12 EE 9 core to work with an older test harness. Instead I have updated PCT to dynamically upgrade the test harness to one that supports Jetty 12 EE 8 and EE 9.

Copy link

@BorisYaoA BorisYaoA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@MarkEWaite
Copy link
Contributor

Historically we have retained older WebSocket Jetty modules in core to allow for older test harnesses (with older Jetty versions) to be used with newer cores, and this PR follows past precedent. In any case this state of affairs will only last for a few weeks, as we'll be removing both the Jetty 10 and Jetty 12 EE 8 WebSocket modules when adding support for Jetty 12 EE 9 in core. I haven't made any attempt to get a Jetty 12 EE 9 core to work with an older test harness. Instead I have updated PCT to dynamically upgrade the test harness to one that supports Jetty 12 EE 8 and EE 9.

Thanks! I think you had even said that in your earlier comments on the change. I must have missed that earlier comment when I reviewed the code.

Copy link
Member

@alecharp alecharp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we have release stapler, winstone and JTH we can update the versions in the poms here.

@github-actions github-actions bot added the unresolved-merge-conflict There is a merge conflict with the target branch. label Aug 10, 2024
Copy link
Contributor

Please take a moment and address the merge conflicts of your pull request. Thanks!

@github-actions github-actions bot removed the unresolved-merge-conflict There is a merge conflict with the target branch. label Aug 10, 2024
Copy link
Contributor

@Kevin-CB Kevin-CB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security wise, it looks good to me!

@Kevin-CB Kevin-CB added security-approved @jenkinsci/core-security-review reviewed this PR for security issues and removed needs-security-review Awaiting review by a security team member labels Aug 12, 2024
@MarkEWaite
Copy link
Contributor

MarkEWaite commented Aug 12, 2024

This PR is ready for merge once the versions are updated for the upcoming winstone and stapler releases:

@basil
Copy link
Member Author

basil commented Aug 12, 2024

Thanks all! I am merging this to do one final set of tests before it is released tomorrow.

@basil basil merged commit 121c0ae into master Aug 12, 2024
8 of 9 checks passed
@basil basil deleted the prototype branch August 12, 2024 14:42
@daniel-beck
Copy link
Member

daniel-beck commented Aug 12, 2024

I'm trying to mvn -pl war jetty:run and it doesn't work in 121c0ae (but does in d176756).

HTTP ERROR 500 javax.servlet.ServletException: org.apache.commons.jelly.JellyTagException: file:/…/core/src/main/resources/lib/layout/icon.jelly:71:93: <j:invokeStatic> method getSymbol threw exception: org/apache/xml/serializer/OutputPropertiesFactory

Caused by: java.lang.ClassNotFoundException: org.apache.xml.serializer.OutputPropertiesFactory
    at org.apache.xalan.templates.OutputProperties.<init> (OutputProperties.java:84)
    at org.apache.xalan.transformer.TransformerIdentityImpl.<init> (TransformerIdentityImpl.java:93)
    at org.apache.xalan.processor.TransformerFactoryImpl.newTransformer (TransformerFactoryImpl.java:818)
    at org.jenkins.ui.symbol.Symbol.loadSymbol (Symbol.java:129)

Is this happening for anyone else?

@MarkEWaite
Copy link
Contributor

MarkEWaite commented Aug 12, 2024

Is this happening for anyone else?

I'm seeing the same thing when I run with mvn -pl war jetty:run after first running mvn -am -pl war,bom -Pquick-build clean install

I don't see that when I run with:

JENKINS_HOME=war/work java -jar war/target/jenkins.war

@basil
Copy link
Member Author

basil commented Aug 12, 2024

Is this happening for anyone else?

It is happening for me as well.

@daniel-beck
Copy link
Member

@basil Is this easily followed up with a fix, or should we revert the PR? Any idea?

@basil
Copy link
Member Author

basil commented Aug 12, 2024

@daniel-beck Not sure offhand. Have you done anything to debug the issue?

@MarkEWaite
Copy link
Contributor

My attempts in the debugger have not shown me anything yet, but I'll continue after lunch to see if I can discover something

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ath-successful This PR has successfully passed the full acceptance-test-harness suite pct-successful This PR has successfully passed the full plugin-compatibility-test suite rfe For changelog: Minor enhancement. use `major-rfe` for changes to be highlighted security-approved @jenkinsci/core-security-review reviewed this PR for security issues
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants