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

Jakarta EE 9 transition #6085

Closed
wants to merge 2 commits into from
Closed

Jakarta EE 9 transition #6085

wants to merge 2 commits into from

Conversation

basil
Copy link
Member

@basil basil commented Dec 19, 2021

Do not review. This is an experimental PR purely for the purposes of getting incremental builds and running tests on CI.

@basil basil added the rfe For changelog: Minor enhancement. use `major-rfe` for changes to be highlighted label Dec 19, 2021
Comment on lines 516 to 537
private static void transform(String... args) {
Transformer jTrans = new Transformer(System.out, System.err);
jTrans.setOptionDefaults(ClassicPluginStrategy.class, getOptionDefaults());
jTrans.setArgs(args);

@SuppressWarnings("unused")
int rc = jTrans.run();
if (rc != 0) {
throw new RuntimeException("Transformer failed with exit code: " + rc);
}
}

private static Map<AppOption, String> getOptionDefaults() {
Map<AppOption, String> optionDefaults = new HashMap<>();
optionDefaults.put(AppOption.RULES_RENAMES, "jakarta-renames.properties");
optionDefaults.put(AppOption.RULES_VERSIONS, "jakarta-versions.properties");
optionDefaults.put(AppOption.RULES_BUNDLES, "jakarta-bundles.properties");
optionDefaults.put(AppOption.RULES_DIRECT, "jakarta-direct.properties");
optionDefaults.put(AppOption.RULES_MASTER_TEXT, "jakarta-text-master.properties");
optionDefaults.put(AppOption.RULES_PER_CLASS_CONSTANT, "jakarta-per-class-constant-master.properties");
return Collections.unmodifiableMap(optionDefaults);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Beginnings of a compatibility layer for plugins. This initial prototype uses Eclipse Transformer to transform the extracted plugin to a Jakarta EE 9 compatible version after exploding the plugin JPI. Some possible ideas for how to productize this:

  • Perhaps we might update plugin-pom/maven-hpi-plugin to apply this transformation when the plugin is built and then mark the plugin as having been transformed in MANIFEST.MF. Then we can skip over the transformation for that plugin at runtime.
  • Perhaps it's too heavyweight to rewrite all the plugins' contents at startup. The Eclipse Transformer README lists a "possible secondary use" of dynamic rewriting at class loading time. Perhaps we want to lazily do the transformation at class loading time rather than eagerly transforming all plugins when Jenkins starts up.

Copy link
Member

@daniel-beck daniel-beck Aug 2, 2022

Choose a reason for hiding this comment

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

Perhaps we might update plugin-pom/maven-hpi-plugin to apply this transformation when the plugin is built and then mark the plugin as having been transformed in MANIFEST.MF. Then we can skip over the transformation for that plugin at runtime.

Is the Servlet API available to plugins not tied to the core version the plugin depends on? If it is, I imagine at some point a plugin maintainer raises the Jenkins core dependency to whichever release includes this change, and then the plugin should no longer build unless imports are fixed. Do you want maintainers to not have to do that? Because declaring a lesser Jenkins core dependency, and then unconditionally rewriting imports would be incompatible with that core.

Comment on lines 73 to 75
// TODO needs triage
//return new Hudson(home, createWebServer(), pluginManager);
return new Hudson(home, null, pluginManager);
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 haven't even begun to try and get the test harness up and running, but needless to say there will be a lot of changes required there as well.

@basil
Copy link
Member Author

basil commented Dec 19, 2021

Another problem is that Guice currently doesn't support Jakarta EE 9.

@basil basil force-pushed the jakarta branch 2 times, most recently from 1b49351 to 4065205 Compare December 19, 2021 21:58
@oleg-nenashev
Copy link
Member

The BouncyCastle API and Git-related plugins all failed to load, but everything else seems to barely work.

Good start though :P

@basil
Copy link
Member Author

basil commented Dec 19, 2021

Even though I called infra.runWithMaven with '17' as the second argument, I still got a Java 11 runtime. Seems like an infrastructure bug.

@basil
Copy link
Member Author

basil commented Dec 19, 2021

Looks like despite jenkins-infra/docker-inbound-agents#7 there is no maven-17 label either.

@basil basil force-pushed the jakarta branch 9 times, most recently from 2a69788 to f17d65f Compare December 22, 2021 19:24
@basil
Copy link
Member Author

basil commented Dec 23, 2021

First CI build: 849 tests failed, 28261 tests passed. It can only get better from here!

@basil
Copy link
Member Author

basil commented Jan 10, 2022

Did a big update on this PR today. I updated the Transformer logic so that it transforms the classes dynamically at class loading time. The resulting design is much like the old Bytecode Compatibility Transformer that I got rid of a while back. :-) You can see the result in JakartaCompatibilityTransformer, JenkinsClassLoader, AntClassLoader, and URLClassLoader2.

The result is that java -jar jenkins.war starts up in a reasonable amount of time and without screaming about anything, and I haven't been able to notice any issues using the result. Pipeline jobs build successfully and just about everything else I've tried works. This is giving me confidence that a class transformation design is at least viable, even if it confuses tools. Performance isn't too bad with the current set of changes, though if we wanted to improve it even further we could scan bytecode at build time in maven-hpi-plugin and mark the Servlet API version in MANIFEST.MF, later using that information at runtime to decide whether the plugin needs transformation or not.

Tests still don't work, because they don't go through the same class loader hierarchy as production code does. I haven't yet thought about how we can apply the compatibility transformation in test context.

@jglick
Copy link
Member

jglick commented Jan 10, 2022

it transforms the classes dynamically at class loading time. The resulting design is much like the old Bytecode Compatibility Transformer

Not something we want to do if we can possibly avoid it IMO.

Is it fair to say that while many plugins would be broken by a change of Servlet API package, the number using other stuff like JavaMail are pretty small and could be reasonably updated manually, as I think you have already started to do? If so, we should be able to avoid any bytecode / class loader tricks. Just include javax.servlet in the classpath alongside the Jakarta version, and patch Stapler to tolerate web methods using the old interfaces. (Plus a handful of core APIs directly referring to Servlet types.)

@basil
Copy link
Member Author

basil commented Jan 10, 2022

Just include javax.servlet in the classpath alongside the Jakarta version, and patch Stapler to tolerate web methods using the old interfaces. (Plus a handful of core APIs directly referring to Servlet types.)

"Just" is an understatement. This isn't as easy as it sounds.

@jglick
Copy link
Member

jglick commented Jan 10, 2022

I would not expect it to be trivial, but probably a lot easier than the Acegi Security → Spring Security stubs from JEP-227?

@basil
Copy link
Member Author

basil commented Jan 10, 2022

I would not expect it to be trivial, but probably a lot easier than the Acegi Security → Spring Security stubs from JEP-227?

Probably as hard if not harder I think.

@jglick
Copy link
Member

jglick commented Jan 10, 2022

Why would you say that? It is mostly about package renames, right? And most code deals with interfaces rather than classes? Those things are typically straightforward to delegate. The complex delegation work in JEP-227 revolved around APIs that had fundamentally changed in the intervening major versions, abstract base classes, and weird stuff like subtly different exception hierarchies (where calling code actually caught and handled specific types).

@basil
Copy link
Member Author

basil commented Jan 10, 2022

Why would you say that?

Try it and you'll find out. 😄

@jglick
Copy link
Member

jglick commented Jan 11, 2022

OK…just trying to understand if this is something you have already tried to prototype, and ran into difficulties, or if you have only attempted the bytecode manipulation approach.

@basil
Copy link
Member Author

basil commented Jan 11, 2022

OK…just trying to understand if this is something you have already tried to prototype, and ran into difficulties, or if you have only attempted the bytecode manipulation approach.

I spent a couple hours on it before realizing I could get a lot further a lot faster with the bytecode manipulation approach.

@jglick
Copy link
Member

jglick commented Jan 14, 2022

Right, makes sense. This is a long way out (right?) so we should have time to make a serious attempt to do a compatibility shim in plain old Java code, which would be easier to debug for regular mortals and should not require changes to tooling. The shims in #4848 were somewhat tedious to write but I learned some tricks along the way; much of the time spent was actually setting up infrastructure to get adequate test coverage.

@basil
Copy link
Member Author

basil commented Jan 14, 2022

we should

Please speak for yourself (e.g. "I think") rather than for others ("we should"). I have a different opinion. Writing the shims you describe is time-consuming and difficult. I think that time would be better spent migrating callers to the new APIs. Once most callers have been migrated to the new APIs, we can go back and reëvaluate the bytecode manipulation approach. By that point, writing shims for the small number of remaining callers would likely be much easier, and it should be possible to rip out the bytecode manipulation. True, the transition period would be messier, but on the other hand the end result would be cleaner with fewer shims (and it would take less time to get there).

@jglick
Copy link
Member

jglick commented Jan 14, 2022

Perhaps, but ever shipping the bytecode library will probably force a fair amount of tricky tooling work, and I find this sort of work much harder and riskier than just writing Java code to delegate interfaces, which can be understood and tested in an IDE. I would rather avoid ever putting bytecode manipulation or class loader tricks in trunk. Definitely it is a good idea to be looking for outlier plugins which use exotic parts of the API surface and simplify them in advance so that we do not waste time working on shims for difficult cases that are rarely used to begin with. In the case of #4848 I did just that—only wrote a shim after reviewing usages of the deprecated API and concluding that there were too many to reasonably rewrite in every plugin before shipping the core change.

@basil
Copy link
Member Author

basil commented Aug 2, 2022

I have edited the PR description to read "do not review" to express several things:

  • I am not taking requests for change
  • I am not committing to completing this project

This is an experimental PR purely for the purposes of getting incremental builds and running tests on CI.

@basil
Copy link
Member Author

basil commented Aug 4, 2022

I ripped out the bytecode rewriting logic and implemented some basic Java compatibility shims in Stapler: nothing exhaustive, but enough to get test runs off the ground. With that we've gone 849 failing tests to only 8 failing tests. This I call progress.

Interactive testing shows that many things are working, but more plugin compatibility is needed. StaplerRequest/RequestImpl/StaplerResponse/ResponseImpl are particularly troublesome: there is no obvious way to support compatibility with both old and new plugins in cases like

2022-08-04 02:54:02.584+0000 [id=77]    WARNING h.i.i.InstallUncaughtExceptionHandler#handleException: Caught unhandled exception with ID a8a3680a-646c-47e2-8b61-4777394893df
java.lang.NoSuchMethodError: 'javax.servlet.ServletOutputStream org.kohsuke.stapler.StaplerResponse.getOutputStream()'
        at com.cloudbees.workflow.util.JsonResponse.generateResponse(JsonResponse.java:28)
        at org.kohsuke.stapler.HttpResponseRenderer$Default.handleHttpResponse(HttpResponseRenderer.java:124)
        at org.kohsuke.stapler.HttpResponseRenderer$Default.generateResponse(HttpResponseRenderer.java:69)
        at org.kohsuke.stapler.Function.renderResponse(Function.java:159)
        at org.kohsuke.stapler.Function.bindAndInvokeAndServeResponse(Function.java:142)
        at org.kohsuke.stapler.MetaClass$11.doDispatch(MetaClass.java:558)
        at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.MetaClass$9.dispatch(MetaClass.java:475)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.MetaClass$4.doDispatch(MetaClass.java:289)
        at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:690)
        at org.kohsuke.stapler.Stapler.service(Stapler.java:240)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
        at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:182)
        at javax.servlet.FilterChain$2.doFilter(FilterChain.java:51)
        at hudson.plugins.locale.LocaleFilter.doFilter(LocaleFilter.java:42)
        at javax.servlet.Filter$1.doFilter(Filter.java:50)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:179)
        at jenkins.telemetry.impl.UserLanguages$AcceptLanguageFilter.doFilter(UserLanguages.java:130)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:179)
        at jenkins.security.ResourceDomainFilter.doFilter(ResourceDomainFilter.java:81)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:179)
        at hudson.util.PluginServletFilter.doFilter(PluginServletFilter.java:185)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.security.csrf.CrumbFilter.doFilter(CrumbFilter.java:160)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:95)
        at jenkins.security.AcegiSecurityExceptionFilter.doFilter(AcegiSecurityExceptionFilter.java:52)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at hudson.security.UnwrapSecurityExceptionFilter.doFilter(UnwrapSecurityExceptionFilter.java:54)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:98)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:145)
        at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at jenkins.security.BasicHeaderProcessor.doFilter(BasicHeaderProcessor.java:98)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:117)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
        at hudson.security.HttpSessionContextIntegrationFilter2.doFilter(HttpSessionContextIntegrationFilter2.java:63)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at hudson.security.ChainedServletFilter.doFilter(ChainedServletFilter.java:112)
        at hudson.security.HudsonFilter.doFilter(HudsonFilter.java:173)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.kohsuke.stapler.compression.CompressionFilter.doFilter(CompressionFilter.java:53)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.util.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:86)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.kohsuke.stapler.DiagnosticThreadNameFilter.doFilter(DiagnosticThreadNameFilter.java:30)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at jenkins.security.SuspiciousRequestFilter.doFilter(SuspiciousRequestFilter.java:38)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:549)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1375)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1297)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
        at org.eclipse.jetty.server.Server.handle(Server.java:562)
        at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505)
        at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
        at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:138)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:407)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038)
        at java.base/java.lang.Thread.run(Thread.java:833)

and

2022-08-04 02:55:42.435+0000 [id=21]    WARNING h.i.i.InstallUncaughtExceptionHandler#handleException: Caught unhandled exception with ID c2cfb454-f58e-48fc-abfb-0e2f2ff1b398
java.lang.IncompatibleClassChangeError: Class org.kohsuke.stapler.RequestImpl does not implement the requested interface javax.servlet.http.HttpServletRequest
        at hudson.plugins.timestamper.format.TimestampFormatProvider.get(TimestampFormatProvider.java:75)
        at hudson.plugins.timestamper.format.TimestampFormatProvider.lambda$static$0(TimestampFormatProvider.java:49)
        at hudson.plugins.timestamper.format.TimestampFormatProvider.get(TimestampFormatProvider.java:62)
        at hudson.plugins.timestamper.annotator.TimestampAnnotator.markup(TimestampAnnotator.java:107)
        at hudson.plugins.timestamper.annotator.TimestampAnnotator.annotate(TimestampAnnotator.java:87)
        at hudson.plugins.timestamper.annotator.TimestampAnnotator.annotate(TimestampAnnotator.java:46)
        at hudson.console.ConsoleAnnotator$ConsoleAnnotatorAggregator.annotate(ConsoleAnnotator.java:108)
        at hudson.console.ConsoleAnnotationOutputStream.eol(ConsoleAnnotationOutputStream.java:147)
        at hudson.console.LineTransformationOutputStream.eol(LineTransformationOutputStream.java:61)
        at hudson.console.LineTransformationOutputStream.write(LineTransformationOutputStream.java:57)
        at hudson.console.LineTransformationOutputStream.write(LineTransformationOutputStream.java:75)
        at org.apache.commons.io.output.ProxyOutputStream.write(ProxyOutputStream.java:92)
        at org.kohsuke.stapler.framework.io.LargeText$HeadMark.moveTo(LargeText.java:329)
        at org.kohsuke.stapler.framework.io.LargeText.writeLogTo(LargeText.java:244)
        at hudson.console.AnnotatedLargeText.writeHtmlTo(AnnotatedLargeText.java:180)
        at hudson.console.AnnotatedLargeText.writeLogTo(AnnotatedLargeText.java:152)
        at org.kohsuke.stapler.framework.io.LargeText.doProgressText(LargeText.java:279)
        at hudson.console.AnnotatedLargeText.doProgressiveHtml(AnnotatedLargeText.java:95)
        at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:732)
        at org.kohsuke.stapler.Function$MethodFunction.invoke(Function.java:397)
        at org.kohsuke.stapler.Function$InstanceFunction.invoke(Function.java:409)
        at org.kohsuke.stapler.Function.bindAndInvoke(Function.java:207)
        at org.kohsuke.stapler.Function.bindAndInvokeAndServeResponse(Function.java:140)
        at org.kohsuke.stapler.MetaClass$11.doDispatch(MetaClass.java:558)
        at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
Caused: jakarta.servlet.ServletException
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:812)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.MetaClass$2.doDispatch(MetaClass.java:224)
        at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.MetaClass$9.dispatch(MetaClass.java:475)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.MetaClass$4.doDispatch(MetaClass.java:289)
        at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
        at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:762)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:894)
        at org.kohsuke.stapler.Stapler.invoke(Stapler.java:690)
        at org.kohsuke.stapler.Stapler.service(Stapler.java:240)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
        at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:182)
        at javax.servlet.FilterChain$2.doFilter(FilterChain.java:51)
Caused: javax.servlet.ServletException
        at javax.servlet.FilterChain$2.doFilter(FilterChain.java:54)
        at hudson.plugins.locale.LocaleFilter.doFilter(LocaleFilter.java:42)
        at javax.servlet.Filter$1.doFilter(Filter.java:50)
Caused: jakarta.servlet.ServletException
        at javax.servlet.Filter$1.doFilter(Filter.java:55)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:179)
        at jenkins.security.ResourceDomainFilter.doFilter(ResourceDomainFilter.java:81)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:179)
        at jenkins.telemetry.impl.UserLanguages$AcceptLanguageFilter.doFilter(UserLanguages.java:130)
        at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:179)
        at hudson.util.PluginServletFilter.doFilter(PluginServletFilter.java:185)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.security.csrf.CrumbFilter.doFilter(CrumbFilter.java:154)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:95)
        at jenkins.security.AcegiSecurityExceptionFilter.doFilter(AcegiSecurityExceptionFilter.java:52)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at hudson.security.UnwrapSecurityExceptionFilter.doFilter(UnwrapSecurityExceptionFilter.java:54)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:98)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:145)
        at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at jenkins.security.BasicHeaderProcessor.doFilter(BasicHeaderProcessor.java:98)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:117)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
        at hudson.security.HttpSessionContextIntegrationFilter2.doFilter(HttpSessionContextIntegrationFilter2.java:63)
        at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:100)
        at hudson.security.ChainedServletFilter.doFilter(ChainedServletFilter.java:112)
        at hudson.security.HudsonFilter.doFilter(HudsonFilter.java:173)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.kohsuke.stapler.compression.CompressionFilter.doFilter(CompressionFilter.java:53)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at hudson.util.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:86)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.kohsuke.stapler.DiagnosticThreadNameFilter.doFilter(DiagnosticThreadNameFilter.java:30)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at jenkins.security.SuspiciousRequestFilter.doFilter(SuspiciousRequestFilter.java:38)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:549)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1375)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544)
        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1297)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
        at org.eclipse.jetty.server.Server.handle(Server.java:562)
        at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505)
        at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
        at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268)
        at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:138)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:407)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038)
        at java.base/java.lang.Thread.run(Thread.java:833)

@jglick
Copy link
Member

jglick commented Aug 4, 2022

https://github.com/jenkinsci/timestamper-plugin/blob/1f98c5153e4742e1926674664294126052ec4ba2/src/main/java/hudson/plugins/timestamper/format/TimestampFormatProvider.java#L68 the type could just be changed to StaplerRequest right? But in general jenkinsci/stapler#301 could be made more compatible, as I did for Spring: for example, deprecate StaplerRequest in favor of StaplerRequest2 implementing the Jakarta variants. Of course this means correspondingly deprecating anything taking or returning this type, and so on transitively. Tedious, obviously, but not usually mentally challenging except when it comes to:

  • Code implementing/extending, rather than simply calling, APIs. Or more precisely, cases where some type is both called by some plugins, and implemented by some plugins. This forces you to have bidirectional bridges.
  • classes from a library layer referred to directly from plugins, particularly ones with fields. interfaces are easier to deal with. In particular, Exceptions can be troublesome because these must be classes and are frequently exposed. This was a major source of minor incompatibilities in [JEP-227] Replace Acegi Security with Spring Security & upgrade Spring Framework #4848.
  • Idioms involving reflection, which I take pretty broadly to mean not just java.lang.reflect but anything not referentially transparent: getClass, instanceof, == (rather than equals), and so on.

https://github.com/jenkinsci/pipeline-stage-view-plugin/blob/35de9f668c0aec5bd4560884b61f94f335579987/rest-api/src/main/java/com/cloudbees/workflow/util/JsonResponse.java#L12-L16 is a bit unusual—normally you would just return JSONObject from your method, but I guess here the idea was to use a different library. Anyway, implementing HttpResponse is not particularly exotic (I think—have not looked up usage numbers), so that also would better be kept compatible (deprecate in favor of HttpResponse2).

Will be interesting to see the extent of breakage in bom testing after the 8 failures here are taken care of.

@basil
Copy link
Member Author

basil commented Aug 4, 2022

Please refer to the PR description:

Do not review

@basil
Copy link
Member Author

basil commented Aug 4, 2022

Changes have been requested when I specifically wrote:

Do not review
I am not taking requests for change
I am not committing to completing this project

Therefore, I am closing this PR.

@basil basil closed this Aug 4, 2022
@jglick
Copy link
Member

jglick commented Aug 4, 2022

Hmm? I did not “request changes”. I did not review at all. You mentioned some test failures and I was offering some tips based on prior experience doing something analogous which may or may not be applicable.

@basil
Copy link
Member Author

basil commented Aug 4, 2022

I am not going to do this project alone. Keeping this PR open invites people to leave GitHub comments with advice rather than writing code, which I am not going to encourage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rfe For changelog: Minor enhancement. use `major-rfe` for changes to be highlighted
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants