Skip to content

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
- [x] The Jira issue, if it exists, is well-described.
- [x] The changelog entries and upgrade guidelines are appropriate for the audience affected by the change (users or developers, depending on the change) and are in the imperative mood (see [examples](https://github.com/jenkins-infra/jenkins.io/blob/master/content/_data/changelogs/weekly.yml)). Fill in the **Proposed upgrade guidelines** section only if there are breaking changes or changes that may require extra steps from users during upgrade.
- [x] There is automated testing or an explanation as to why this change has no tests.
- [x] New public classes, fields, and methods are annotated with `@Restricted` or have `@since TODO` Javadocs, as appropriate.
- [x] New deprecations are annotated with `@Deprecated(since = "TODO")` or `@Deprecated(forRemoval = true, since = "TODO")`, if applicable.
- [x] New or substantially changed JavaScript is not defined inline and does not call `eval` to ease future introduction of Content Security Policy (CSP) directives (see [documentation](https://www.jenkins.io/doc/developer/security/csp/)).
- [x] For dependency updates, there are links to external changelogs and, if possible, full differentials.
- [x] For new APIs and extension points, there is a link to at least one consumer.

Desired reviewers

@mention

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

### Maintainer checklist
- [ ] There are at least two (2) approvals for the pull request and no outstanding requests for change.
- [ ] Conversations in the pull request are over, or it is explicit that a reviewer is not blocking the change.
- [x] Changelog entries in the pull request title and/or **Proposed changelog entries** are accurate, human-readable, and in the imperative mood.
- [x] Proper changelog labels are set so that the changelog can be generated automatically.
- [x] If the change needs additional upgrade steps from users, the `upgrade-guide-needed` label is set and there is a **Proposed upgrade guidelines** section in the pull request title (see [example](https://github.com/jenkinsci/jenkins/pull/4387)).
- [x] If it would make sense to backport the change to LTS, a Jira issue must exist, be a _Bug_ or _Improvement_, and be labeled as `lts-candidate` to be considered (see [query](https://issues.jenkins.io/issues/?filter=12146)).

@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
<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
<!-- 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.

<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.

}

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.

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.

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
@github-actions
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
@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.

8 participants