Skip to content

Jetty12 (Jakarta EE8) upgrade#18424

Merged
capistrant merged 47 commits intoapache:masterfrom
capistrant:jetty12-ee8-upgrade
Sep 20, 2025
Merged

Jetty12 (Jakarta EE8) upgrade#18424
capistrant merged 47 commits intoapache:masterfrom
capistrant:jetty12-ee8-upgrade

Conversation

@capistrant
Copy link
Copy Markdown
Contributor

@capistrant capistrant commented Aug 21, 2025

Description

Upgrade to Jetty 12

Upgrades our jetty dependency to jetty 12.0.x (12.0.25 at time of writing). Uses Jakarta EE8 compatible jetty 12 to enable a less intrusive upgrade.

Avatica Changes

Calcite Avatica has a hard dependency on jetty9 still and there is no clear timeline on their upgrade plans. To avoid this being a blocker we have reduced our dependency footprint on Avatica in this PR by bringing the Jetty handler implementations for Druid JDBC while retaining compatibility with the Avatica JDBC client.

Hadoop Compatibility

Hadoop ecosystem also comes with lots of dependencies on jetty9 and java11 support. This section lays out implications for hadoop support

Deep storage

Confirmed functionality using Docker IT env

index_hadoop

Confirmed functionality using Docker IT env. We will want more expansive testing here. I tried to slim down the code that sets up the injector, but since the task needs to load all of the extensions, I ended up falling back to an ugly duplication of the service injector creation, just without jetty. I'm sure it can be improved if we want to.

Java 11 Support Implications

Jetty12 deps are in java 17 class file version, so running druid services on java11 seems out of the question. If this is the case, java 11 support will have to be dropped in 35.

Future Work

  • Explore jetty 12 features and integrate them where appropriate

Release note

Upgrading Druid to Jetty 12 requires dropping Java 11 runtime support for Druid. The one exception to this is that Hadoop Ingestion can continue to run on Java 11 YARN clusters because the Jetty 12 dependency can be excluded from that runtime classpath safely.

A new server configuration option has been added with this PR: druid.server.http.uriCompliance. Jetty 12 by default has strict enforcement of RFC3986 URI format. This is a change from Jetty 9. To retain compatibility with legacy Druid, we default this config to LEGACY, which uses the more permissive URI format enforcement that jetty9 used. If the cluster you operate does not require LEGACY compatibility, it is recommended to use the upstream jetty default of RFC3986 in your Druid deployment. Please see jetty documentation for more info.

Upgrade Note

Java 11 runtime no longer supported. Java 17 is the only officially supported and recommended runtime environment for Druid 35


Key changed/added classes in this PR

PR is mostly a lot of tiny changes and tweaks. But some places to get close look could be:

  • JDBC related files
    • sql/src/main/java/org/apache/druid/sql/avatica/DruidAvaticaJsonHandler.java
    • sql/src/main/java/org/apache/druid/sql/avatica/DruidAvaticaProtobufHandler.java
  • Hadoop Indexing related files
    • indexing-hadoop/src/main/java/org/apache/druid/indexer/HadoopDruidIndexerConfig.java
    • indexing-hadoop/src/main/java/org/apache/druid/indexer/JobHelper.java
    • indexing-hadoop/src/main/java/org/apache/druid/indexer/Utils.java
    • server/src/main/java/org/apache/druid/initialization/CoreInjectorBuilder.java
    • server/src/main/java/org/apache/druid/initialization/Initialization.java
    • server/src/main/java/org/apache/druid/initialization/ServerInjectorBuilder.java
  • server/src/main/java/org/apache/druid/guice/http/JettyHttpClientModule.java
  • server/src/main/java/org/apache/druid/server/initialization/jetty/JettyServerInitUtils.java
  • server/src/main/java/org/apache/druid/server/initialization/jetty/JettyServerModule.java

This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added or updated version, license, or notice information in licenses.yaml
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

@github-actions github-actions Bot added the GHA label Aug 22, 2025
Comment on lines -112 to -119
public static Handler getJettyRequestLogHandler()
{
// Ref: http://www.eclipse.org/jetty/documentation/9.2.6.v20141205/configuring-jetty-request-logs.html
RequestLogHandler requestLogHandler = new RequestLogHandler();
requestLogHandler.setRequestLog(new JettyRequestLog());

return requestLogHandler;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it looks like this is replaced with Server.setRequestLog in jetty 12, https://jetty.org/docs/jetty/12.1/programming-guide/migration/11-to-12.html#api-changes-handler-requestlog, do we need to add that somewhere like the server initializers class or something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

💯 definitely a miss by not properly migrating this. I think initialization in JettyServerModule is the right spot? Confirmed that it was wired up across all druid services in my Docker env after moving it in latest commits.

then
DOCKER_COMPOSE='docker-compose'
else
DOCKER_COMPOSE='docker compose'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

shouldn't we prefer docker compose? i think docker-compose is pretty old now and hasn't been included in docker distribution for a while now

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

heh, ya. I probably just need to fix my local setup and not use docker-compose. If nothing else I'll flip it to only fall back to docker-compose.

Comment thread docs/operations/java.md
Comment on lines -58 to -65
On Java 11, if these parameters are not included, you will see warnings like the following:

```
WARNING: An illegal reflective access operation has occurred
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you still see these if you don't add the stuff, are we just assuming that people are used to it by now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ehh that was probably me just seeing Java 11 and not Java 11+ and hitting delete. I'll confirm it is still there and add back without the explicit java 11 part

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

actually maybe removing them is right if java 17 is actually throwing exceptions without them like we indicate on line 66

@capistrant capistrant requested a review from kfaraz September 15, 2025 14:32
@capistrant
Copy link
Copy Markdown
Contributor Author

@kfaraz Thank you for the initial review! I made some modifications to the initialization code and avatica handler per your review. Let me know if this is going down the lines of what you were thinking.

Copy link
Copy Markdown
Contributor

@kfaraz kfaraz left a comment

Choose a reason for hiding this comment

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

Thanks for addressing the feedback, @capistrant !
I have left minor suggestions/queries in some places.
But none of those should block this PR.

Comment thread integration-tests/stop_cluster.sh Outdated
# See the License for the specific language governing permissions and
# limitations under the License.

# Determine if docker compose is available. If not, assume Docker supports
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this change need to be included in this PR? Or can it be done separately too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I ended up removing it and will not PR it separately. My machine just had a bad setup for docker, I assume most people use docker compose > docker-compose these days so supporting legacy is not needed. Especially since we are phasing out legacy ITs

Comment thread pom.xml Outdated
<groupId>org.eclipse.jetty.ee8</groupId>
<artifactId>jetty-ee8-servlet</artifactId>
<version>${jetty.version}</version>
<exclusions>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: indentation seems off here.

Comment on lines +158 to +159
// Keep Druid jetty jars out of the classpath. They are not runnable in the < 17 hadoop java runtime
if (jarFile.getName().endsWith(".jar") && !jarFile.getName().contains("jetty")) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we use jetty-ee8-* instead?

/**
* Needed for Hadoop indexing that needs server-like Injector but can't run jetty 12
*/
@VisibleForTesting
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this method is used by HadoopDruidIndexerConfig too. If that is the case, let's remove this annotation.

return this.build(false);
}

public static Module registerNodeRoleModule(Set<NodeRole> nodeRoles)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It seems that this method is unchanged. Can we move it back to its original position so that we can avoid it from showing up in the diff?

import org.apache.calcite.avatica.util.UnsynchronizedBuffer;
import org.eclipse.jetty.server.Handler;

public abstract class DruidAvaticaHandler extends Handler.Abstract implements MetricsAwareAvaticaHandler
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please add a short javadoc, if possible.

public static final String AVATICA_PATH_NO_TRAILING_SLASH = "/druid/v2/sql/avatica-protobuf";
public static final String AVATICA_PATH = AVATICA_PATH_NO_TRAILING_SLASH + "/";

private final ProtobufHandler pbHandler;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
private final ProtobufHandler pbHandler;
private final ProtobufHandler protobufHandler;

Comment on lines +72 to +104
try (Timer.Context ctx = this.requestTimer.start()) {
if (!"POST".equals(request.getMethod())) {
response.setStatus(405);
response.write(
true,
ByteBuffer.wrap("This server expects only POST calls.".getBytes(StandardCharsets.UTF_8)), callback
);
return true;
}
final byte[] requestBytes;
// Avoid a new buffer creation for every HTTP request
final UnsynchronizedBuffer buffer = threadLocalBuffer.get();
try (InputStream inputStream = Content.Source.asInputStream(request)) {
requestBytes = AvaticaUtils.readFullyToBytes(inputStream, buffer);
}
finally {
buffer.reset();
}

response.getHeaders().put("Content-Type", "application/octet-stream;charset=utf-8");

org.apache.calcite.avatica.remote.Handler.HandlerResponse<byte[]> handlerResponse;
try {
handlerResponse = pbHandler.apply(requestBytes);
}
catch (Exception e) {
LOG.debug(e, "Error invoking request");
handlerResponse = pbHandler.convertToErrorResponse(e);
}

response.setStatus(handlerResponse.getStatusCode());
response.write(true, ByteBuffer.wrap(handlerResponse.getResponse()), callback);
return true;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if parts of this logic can also live in the super class DruidAvaticaHandler. Not a blocker for this PR though.


// Default implementation is for JSON to allow debugging of tests.
protected AbstractAvaticaHandler getAvaticaHandler(final DruidMeta druidMeta)
protected Handler.Abstract getAvaticaHandler(final DruidMeta druidMeta)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
protected Handler.Abstract getAvaticaHandler(final DruidMeta druidMeta)
protected DruidAvaticaHandler getAvaticaHandler(final DruidMeta druidMeta)


@Override
protected AbstractAvaticaHandler getAvaticaHandler(final DruidMeta druidMeta)
protected Handler.Abstract getAvaticaHandler(final DruidMeta druidMeta)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
protected Handler.Abstract getAvaticaHandler(final DruidMeta druidMeta)
protected DruidAvaticaHandler getAvaticaHandler(final DruidMeta druidMeta)

@capistrant capistrant merged commit 7a084e6 into apache:master Sep 20, 2025
255 of 260 checks passed
@cecemei cecemei added this to the 35.0.0 milestone Oct 21, 2025
airlock-confluentinc Bot pushed a commit to confluentinc/druid that referenced this pull request Dec 4, 2025
* jetty 10 upgrade

* checkpoint

* compiling but broken cuz of at least one dep calcite depending on jetty9

* Fix breakage in router

* Compilation fixes after merging master in. plus getting checkstyle fixed up

* Remove some of the avatica deps blocking jetty12

* bump jetty to latest and fixup licenses.yaml

* fixup after merging master

* Fixup some licenses reporting issues

* remove jetty dependency from indexing hadoop in attempt to keep it alive

* stop doing static checks and build with jdk11

* Modifications to index_hadoop to try and keep it alive

* Modify duplicate header removal strategy

* Remove dependency on non public jetty nested packages

* Revert back to verbose and dirty Injector setup to get hadoop index fully working

* Fix http trailers for QueryResultPusher

* Fix trailer related tests in QueryResourceTest

* add some UTs

* prevent banned dep from getting pulled in

* Run rewrite rules mvn goal to fixup test files

* stop accounting for buggy mock impl in sql test

* Add new http server config for Jetty UriCompliance mode. Default to allow backwards compatibility

* fix import order

* fix spell checker flag

* Fixup java docs links to use java17

* Unify approach to jetty handler for avatica json and protobuf

* use 405 response code for avatica jdbc non post requests

* fix checkstyle

* Add link for new jetty related config

* fix grammar in java support doc

* Make the legacy ITs easier to run in a wider variety of docker steps

* Cleanup some of the Injector initialization code for hadoop tasks, based on review

* Starting point for creating a DruidAvaticaHandler base class

* Share a little bit of the code between the avatica handlers

* Work to fix AvaticaHandler constructor issues

* Fix codeql flag

* Revert "Make the legacy ITs easier to run in a wider variety of docker steps"

This reverts commit bafb2fc.

* Add RequestLogger configuration back using proper jetty12 mechanism

* Fix checkstyle

* fix import order

* Address review comments

* fix serialization

(cherry picked from commit 7a084e6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants