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-73258] Migrate Stapler from EE 8 to EE 9 #482

Merged
merged 11 commits into from
Sep 3, 2024
Merged

[JENKINS-73258] Migrate Stapler from EE 8 to EE 9 #482

merged 11 commits into from
Sep 3, 2024

Conversation

basil
Copy link
Member

@basil basil commented Aug 2, 2023

Context

Goes with three other pull requests:

Problem

Stapler only supports EE 8, precluding an upgrade to Spring Security 6.x in Jenkins core (which requires Spring Framework 6.x, which requires EE 9).

Solution

  • Update dependencies in pom.xml to align with Jetty 12 (EE 9)
  • Migrate imports
  • Add compatibility layer for EE 8 to support Jenkins plugins

At a high level, we add StaplerRequest2 and StaplerResponse2 classes, the Jakarta equivalents of the old versions (now deprecated). The RequestImpl and ResponseImpl implementations are now based on EE 9 rather than EE 8, but support for plugins is maintained by converting to and from the interfaces as needed.

Testing done

Full PCT and ATH (Jenkins public version and CloudBees internal version)

@basil basil force-pushed the jakarta branch 3 times, most recently from 9818bb9 to 28b6ce2 Compare May 15, 2024 21:25
@basil basil changed the title Jakarta EE 9 transition Jetty 12 EE 8 to Jetty 12 EE 9 May 16, 2024
@basil basil changed the title Jetty 12 EE 8 to Jetty 12 EE 9 Upgrade Jetty from 10.0.20 to 12.0.9 (EE 8) May 16, 2024
@basil basil force-pushed the jakarta branch 2 times, most recently from 4996efc to 3f566c2 Compare May 16, 2024 00:11
@basil basil changed the title Upgrade Jetty from 10.0.20 to 12.0.9 (EE 8) EE 8 to EE 9 May 16, 2024
@basil basil force-pushed the jakarta branch 13 times, most recently from 7dd5d97 to 4721447 Compare May 23, 2024 16:07
@basil basil force-pushed the jakarta branch 6 times, most recently from b2a0128 to 7ab2633 Compare May 29, 2024 01:09
@basil basil force-pushed the jakarta branch 2 times, most recently from 8d92a23 to c2378c0 Compare June 3, 2024 18:48
@basil basil force-pushed the jakarta branch 2 times, most recently from 6299e57 to 2d50895 Compare August 27, 2024 19:40
Comment on lines +82 to +86
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-servlet-api</artifactId>
<version>4.0.6</version>
</dependency>
Copy link
Member Author

Choose a reason for hiding this comment

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

A dependency of the compatibility layer. Not part of the servlet container (Jetty) classpath, but part of the web application classpath in order to handle calls made from Jenkins plugins that were compiled against EE 8.

public class ServletRequestWrapper {
public static jakarta.servlet.ServletRequest toJakartaServletRequest(ServletRequest from) {
if (from instanceof JavaxServletRequestWrapper javax) {
return javax.toJakartaServletRequest();
Copy link
Member Author

Choose a reason for hiding this comment

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

Here and elsewhere, prevent infinite recursion by returning the original request.

import java.util.Objects;
import javax.servlet.http.Cookie;

public class CookieWrapper {
Copy link
Member Author

Choose a reason for hiding this comment

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

Does a deep copy rather than wrapping the Cookie object (which is a POJO rather than an interface), but an audit of usages shows that this should not be a problem for existing callers.

import jakarta.servlet.ServletResponse;
import java.io.IOException;

public interface CompatibleFilter extends Filter {
Copy link
Member Author

Choose a reason for hiding this comment

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

A convenience class that allows existing Filter subclasses in Stapler and Jenkins core to be used as-is with either EE 8 (i.e., old plugins) or EE 9 (modern Stapler and core). I sprinkled this liberally wherever a class extends Filter in Stapler or core. Perhaps not all of them are necessary, and this can be simplified in the future when usages of Filter in plugins have been migrated to EE 9.

* @throws IllegalArgumentException When {@code derived} does not derive from {@code base}, or when {@code base}
* does not contain the specified method.
*/
public static boolean isOverridden(
Copy link
Member Author

Choose a reason for hiding this comment

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

Copypasta of the corresponding utilities from core. Unfortunately I can't easily mark them as @Restricted(DoNotUse.class) at present because this repository isn't set up for lib-access-modifier. Perhaps someone else will find the energy to add both lib-access-modifier and the annotation. Unfortunately I can't make it package-private either, as it needs to be used in a lot of places in Stapler.

*
* @author Kohsuke Kawaguchi
*/
public class RequestImpl extends HttpServletRequestWrapper implements StaplerRequest {
public class RequestImpl extends HttpServletRequestWrapper implements StaplerRequest2 {
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 implementation class is changing incompatibly to implement StaplerRequest2. With the exception of a single proprietary plugin, no plugins care about the implementation class, just the interface.

* @see Stapler#getCurrentRequest2()
* @author Kohsuke Kawaguchi
*/
public interface StaplerRequest2 extends HttpServletRequest {
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 Jakarta version of StaplerRequest (which is now deprecated).

Comment on lines +45 to +57
String paramNames = m.getParameters().stream()
.map(VariableElement::getSimpleName)
.collect(Collectors.joining(","));
String existing = output.get(m.getSimpleName().toString());
/*
* Allow multiple methods to have the same name but different argument types as long as the arguments
* have the same names. This allows deprecated StaplerRequest/StaplerResponse methods to coexist
* alongside non-deprecated StaplerRequest2/StaplerResponse2 methods.
*/
if (existing == null || !existing.equals(paramNames)) {
write(paramNames, m);
output.put(m.getSimpleName().toString(), paramNames);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Needed to fix a compilation error in core. Comes with an associated unit test that demonstrates the problem.

Comment on lines +155 to +158
context.setVariable("request", StaplerRequest.fromStaplerRequest2(req));
context.setVariable("response", StaplerResponse.fromStaplerResponse2(rsp));
context.setVariable("request2", req);
context.setVariable("response2", rsp);
Copy link
Member Author

Choose a reason for hiding this comment

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

I had briefly considered changing request and response into Jakarta versions, but this broke plugins with Jelly views that called plugin methods whose parameters were requests or responses.

Copy link
Member

@timja timja left a comment

Choose a reason for hiding this comment

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

Thanks!

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AsyncContextWrapper {

Choose a reason for hiding this comment

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

@basil Just curious why don't we have the documentation for any of the class here. At least 1-2 liner description would have given the clarity that the purpose of that 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.

This would not hurt, but the most important aspect of Javadoc in this PR is linking to non-deprecated versions of newly-deprecated functionality, which I have done consistently.

Copy link

@vwagh-dev vwagh-dev left a comment

Choose a reason for hiding this comment

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

LGTM

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

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.

Looks fine, security-wise

Copy link
Member

@daniel-beck daniel-beck left a comment

Choose a reason for hiding this comment

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

Looks good as far as I can evaluate it 👍

Some *Wrapper methods seem to not handle documented null values well, but I'm not sure how relevant that really is.

StaplerResponse*#getCompressed* methods were accidentally undeprecated.

I don't think any of my comments need to be addressed for the weekly.

@basil basil merged commit 994adb3 into master Sep 3, 2024
3 of 5 checks passed
@basil basil deleted the jakarta branch September 3, 2024 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants