This is an automated email from the ASF dual-hosted git repository.

merlimat pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new 2624383b7c7 [feat][pip] PIP-472: Migrate from javax.* to jakarta.* 
APIs (#25700)
2624383b7c7 is described below

commit 2624383b7c79ed944aece4e5f1a2915a558c671d
Author: Lari Hotari <[email protected]>
AuthorDate: Mon May 11 19:26:18 2026 +0300

    [feat][pip] PIP-472: Migrate from javax.* to jakarta.* APIs (#25700)
---
 pip/pip-472.md | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 318 insertions(+)

diff --git a/pip/pip-472.md b/pip/pip-472.md
new file mode 100644
index 00000000000..19d68eec819
--- /dev/null
+++ b/pip/pip-472.md
@@ -0,0 +1,318 @@
+# PIP-472: Migrate from javax.* to jakarta.* APIs
+
+## Background Knowledge
+
+In 2017, Oracle transferred Java EE to the Eclipse Foundation, where it was 
renamed Jakarta EE. As part of that transfer, Oracle did not grant Eclipse the 
right to evolve the `javax.*` namespace; from Jakarta EE 9 onward (released 
2020), every Jakarta EE specification moved its package prefix from `javax.*` 
to `jakarta.*`. The two namespaces are source- and binary-incompatible: an 
application can use either, but not both, for the same specification.
+
+Concretely, the following Pulsar-relevant specs moved from `javax.*` (Jakarta 
EE 8 and earlier) to `jakarta.*` (Jakarta EE 9+):
+
+| Specification | Old namespace | New namespace |
+|---|---|---|
+| Common Annotations (JSR 250) | `javax.annotation` | `jakarta.annotation` |
+| Servlet | `javax.servlet` | `jakarta.servlet` |
+| RESTful Web Services (JAX-RS) | `javax.ws.rs` | `jakarta.ws.rs` |
+| Bean Validation | `javax.validation` | `jakarta.validation` |
+| XML Binding (JAXB) | `javax.xml.bind` | `jakarta.xml.bind` |
+| Activation Framework | `javax.activation` | `jakarta.activation` |
+| Dependency Injection (JSR 330) | `javax.inject` | `jakarta.inject` |
+| WebSocket | `javax.websocket` | `jakarta.websocket` |
+
+Independently of Jakarta EE, several `javax.*` packages are part of the JDK 
itself (not Jakarta EE specifications) and **stay in `javax.*` permanently**. 
They are out of scope for any "migrate to jakarta" effort:
+
+- `javax.crypto` (JCE)
+- `javax.naming` (JNDI)
+- `javax.net`, `javax.net.ssl` (JSSE)
+- `javax.management` (JMX)
+- `javax.security.auth.*` (JAAS — except for a few callbacks moved into 
Jakarta Security)
+- `javax.sql` (DataSource APIs)
+- `javax.tools` (Compiler API)
+- `javax.xml.parsers`, `javax.xml.transform`, `javax.xml.xpath`, 
`javax.xml.stream` (JAXP — separate from JAXB)
+- `javax.transaction.xa`
+
+### Apache Pulsar's current state
+
+Pulsar's build is Gradle, with all dependency versions managed centrally in 
`gradle/libs.versions.toml`. The minimum runtime is Java 21 (JDK 25 is also 
supported). The latest snapshot version is `5.0.0-M1-SNAPSHOT` and Pulsar 5.0 
is the next LTS line per PIP-162.
+
+Today the codebase imports `javax.*` from approximately 1,620 Java files. 
Removing the JDK packages listed above leaves three real migration surfaces:
+
+| Package | Approx. files | Status |
+|---|---|---|
+| `javax.ws.rs` | 660 | Migrate to `jakarta.ws.rs` |
+| `javax.servlet` | 368 | Pulsar's own code migrates to `jakarta.servlet`; 
usage in the `AdditionalServlet` plugin SPI surface is intentionally retained 
on `javax.servlet` (see *High-Level Design — Phase 1*) |
+| `javax.annotation` (JSR 250 portion) | 48 | Migrate to `jakarta.annotation` |
+
+Pulsar's existing usage of `javax.security.*` (in `pulsar-broker-auth-sasl` 
and similar) is exclusively `javax.security.auth.callback`, 
`javax.security.auth.login`, `javax.security.auth.Subject`, and 
`javax.security.sasl.*` — all JDK-resident JAAS / SASL APIs that stay in 
`javax.*` and have no Jakarta counterpart. They are therefore out of scope.
+
+A partial migration is already visible in git history (e.g. commits replacing 
`javax.ws.rs-api` with `jakarta.ws.rs-api`, replacing `javax.annotation-api` 
with `jakarta.annotation-api`, and shading `jakarta.annotation` into shaded 
client JARs), and `gradle/libs.versions.toml` already declares Jakarta-named 
API artifacts. However, those Jakarta-named artifacts are pinned to Jakarta EE 
8 versions, which still use the `javax.*` namespace — so the artifact rename 
has happened but the namespa [...]
+
+## Motivation
+
+1. **Upstream ecosystem has moved**. Jersey 3.x, Jetty 11+, Swagger Core 2.x, 
Hibernate Validator 7+, Spring 6, MicroProfile 5+ — all of them require 
`jakarta.*`. Pulsar can no longer pick up upstream bug fixes, performance 
improvements, security advisories, or compatibility with newer Java releases 
without leaving the `javax.*` namespace.
+
+2. **Pulsar is already partially mid-migration**. The Jakarta-named API 
artifacts are declared but pinned to javax-namespace versions, the partial 
replacement of individual artifacts is happening one PR at a time, and Jetty 12 
is in use but configured with its `ee8-*` (javax-servlet compat) modules rather 
than `ee10-*`. The result is a half-migrated state that is harder to reason 
about and harder to extend than either endpoint. A coordinated migration ends 
this drift.
+
+3. **Pulsar 5.0 LTS is the right window**. Migration touches public surfaces 
(admin client REST DTOs, plugin/SDK annotations, function/connector base 
contexts). Once 5.0 ships and is supported as LTS, breaking those surfaces 
becomes very expensive: a major-version bump is the only legitimate path. Doing 
the migration before 5.0 cuts means the LTS line carries clean Jakarta APIs for 
its full lifecycle.
+
+4. **`javax.*` is functionally frozen**. Reference implementations in the 
`javax.*` namespace receive only critical security fixes; new features, 
performance work, and dependency updates land only in `jakarta.*`. Carrying 
`javax.*` through a multi-year LTS window means accumulating risk.
+
+5. **Direct `javax.servlet:javax.servlet-api:3.1.0` dependency**. Pulsar 
declares this dependency directly, which constrains every downstream module to 
Servlet 3.1 even though the deployed Jetty 12 runtime supports Servlet 6.0. 
Replacing the dependency unlocks newer Servlet APIs.
+
+## Goals
+
+### In Scope
+
+- **Source code namespace migration** for the Jakarta-EE-derived `javax.*` 
packages used in Pulsar's own code (`javax.ws.rs`, `javax.annotation` JSR-250 
portion, `javax.validation`, `javax.xml.bind`, `javax.activation`, 
`javax.inject` where used, `javax.websocket` where used). `javax.servlet` is 
intentionally retained in the `AdditionalServlet` plugin SPI surface (see 
*High-Level Design*) and therefore not eliminated codebase-wide. Approximate 
scope: ~1,100 files (1,620 total `javax.*` i [...]
+- **Extend the `AdditionalServlet` SPI** so plugins can register 
`jakarta.servlet` handlers (preferred, routed to Jetty's ee10 environment) in 
addition to the existing `javax.servlet` registration path (legacy, routed to 
ee8). Both registration styles coexist; existing plugins keep working without 
recompilation.
+- **Dependency upgrades** in `gradle/libs.versions.toml` to Jakarta-namespace 
versions of every affected library (Jersey 2.42 → 3.1.10, Swagger Core 1.6.2 → 
2.2.27, add `jetty-ee10-*` alongside the retained `jetty-ee8-*`, add 
`jakarta.servlet:jakarta.servlet-api:6.0.0` alongside the retained 
`javax.servlet:javax.servlet-api`, bump the `jakarta-*` API versions to their 
Jakarta EE 10 namespace versions, swap `jackson-jaxrs-json-provider` for 
`jackson-jakarta-rs-json-provider`).
+- **Shading and packaging cleanup** for shaded client/proxy/function JARs that 
currently include `javax.*` API classes (`pulsar-client-shaded`, 
`pulsar-client-admin-shaded`, `pulsar-client-all-shaded`, 
`pulsar-functions/localrun-shaded`, etc.).
+- **LICENSE and NOTICE updates** to reflect any artifact replacements.
+- **Migration guide** for plugin, connector, function, and embedding-API 
authors describing what changes they must make to recompile against Pulsar 5.0.
+- **Land in Pulsar 5.0** before the LTS branch is cut.
+
+### Out of Scope
+
+- **JDK `javax.*` packages**: `javax.crypto`, `javax.naming`, `javax.net`, 
`javax.net.ssl`, `javax.management`, `javax.security.auth.*` (the JAAS 
portions), `javax.sql`, `javax.tools`, `javax.xml.parsers`, 
`javax.xml.transform`, `javax.xml.xpath`, `javax.xml.stream`, 
`javax.transaction.xa`. These are not Jakarta EE specifications and have no 
`jakarta.*` counterpart.
+- **Removing `javax.servlet` support from the `AdditionalServlet` plugin 
SPI**. The SPI is extended to accept `jakarta.servlet` handlers (the preferred 
path going forward), but the existing `javax.servlet` handler registration 
continues to be supported so that existing AdditionalServlet plugins keep 
working without recompilation. Removing the `javax.servlet` registration path 
is a separate decision for a future PIP if desired.
+- **Apache BookKeeper's own migration**. Pulsar embeds BookKeeper 
transitively. The Pulsar community should drive a Jakarta migration in 
BookKeeper 4.18 through the BookKeeper community (see *Alternatives*); the work 
itself happens in the BookKeeper repository, not as part of this PIP.
+- **Wire-protocol or REST/HTTP shape changes**. The migration is purely a Java 
namespace change. HTTP request/response bodies, status codes, and admin REST 
API URLs are unchanged.
+- **Functional behavioural changes** in any feature. Anything that changes 
broker, client, or function behaviour beyond the namespace move is out of scope 
and tracked separately.
+- **A `javax.*` ↔ `jakarta.*` runtime compatibility shim** (e.g. Eclipse 
Transformer at JAR load time). See *Alternatives* for why this is rejected.
+
+## High Level Design
+
+The migration is executed in four phases. Each phase is mergeable on its own; 
the codebase remains buildable and shippable between phases.
+
+### Phase 0 — Preparation
+
+- Confirm the BookKeeper community's plans for jakarta migration; document any 
constraint this places on Pulsar's classpath.
+- Add an OpenRewrite (or equivalent) automation tool to `build-logic` for the 
mechanical import-rename portion of the migration. The recipe of choice is 
`org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta` from 
`rewrite-migrate-java`. Eclipse Transformer's source-mode is an alternative.
+- Add a CI lint that prevents new `javax.ws.rs`, `javax.servlet`, 
`javax.annotation` (JSR 250 portion), or `javax.validation` imports from being 
introduced after Phase 1 lands. The lint is feature-flagged so it can be 
enabled per-module as the migration progresses.
+- Inventory shaded client modules (`pulsar-client-shaded`, 
`pulsar-client-admin-shaded`, `pulsar-client-all-shaded`, 
`pulsar-functions/localrun-shaded`, `jclouds-shaded`, etc.) and document their 
current `javax.*` shading rules so Phase 3 has a checklist.
+
+### Phase 1 — Web stack migration with ee8/ee10 coexistence
+
+The web stack has two interlocked layers, each with its own coexistence story:
+
+1. **Servlet container (Jetty 12).** Jetty 12 supports running multiple 
Servlet API generations side by side, each in its own *environment*. Pulsar's 
broker, proxy, and websocket components move from `jetty-ee8-*` (Servlet 4 / 
`javax.servlet`) to `jetty-ee10-*` (Servlet 6 / `jakarta.servlet`). The 
`jetty-ee8-*` artifacts are **retained** alongside ee10 so that both 
environments share the same Jetty 12 server instance and connector pool. The 
`AdditionalServlet` SPI (`org.apache.pulsar.bro [...]
+
+2. **JAX-RS provider (Jersey).** Jersey 2 (`javax.ws.rs`) and Jersey 3 
(`jakarta.ws.rs`) cannot share a single REST tier. The broker REST tier moves 
atomically to Jersey 3. Every JAX-RS resource class registered with the broker 
REST application — across `pulsar-broker`, `pulsar-broker-common`, 
`pulsar-client-admin`, `pulsar-websocket`, and `pulsar-proxy` — is updated in 
the same coordinated change to import `jakarta.ws.rs.*`. Modules outside the 
broker REST tier are unaffected by the Jer [...]
+
+In `gradle/libs.versions.toml`:
+
+| Variable | Before | After | Note |
+|---|---|---|---|
+| `jersey` | `2.42` | `3.1.10` | |
+| `swagger` | `1.6.2` | `2.2.27` | Group changes to 
`io.swagger.core.v3:swagger-*`; annotations move from 
`io.swagger.annotations.*` to `io.swagger.v3.oas.annotations.*` |
+| `jakarta-ws-rs` | `2.1.6` | `3.1.0` | Jakarta EE 10 |
+| `jakarta-annotation` | `1.3.5` | `2.1.1` | Jakarta EE 10 |
+| `jakarta-validation` | `2.0.2` | `3.0.2` | Jakarta EE 10 |
+| `jakarta-xml-bind` | `2.3.3` | `4.0.2` | Jakarta EE 10 |
+| `jakarta-activation` | `1.2.2` | `2.1.3` | Jakarta EE 10 |
+| `javax-servlet` | `3.1.0` | unchanged | **Retained** for the ee8 environment 
used by AdditionalServlet plugins |
+| *new* `jakarta-servlet` | — | `6.0.0` | Servlet 6 / Jakarta EE 10 |
+
+The PIP targets **Jakarta EE 10** consistently across these specs (rather than 
mixing in EE 11) because EE 10 has the broadest tooling and runtime support 
today and matches the maturity of the third-party libraries Pulsar consumes. EE 
11 is a candidate for a future follow-up.
+
+Plus aliases:
+
+- **Add** `jetty-ee10-*` library aliases for every `jetty-ee8-*` artifact 
currently in use. The `jetty-ee8-*` aliases are retained alongside ee10 so 
AdditionalServlet plugins keep working. Pulsar's own broker/proxy/websocket 
Jetty wiring uses ee10.
+- **Replace** `jackson-jaxrs-json-provider` 
(`com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider`) with 
`jackson-jakarta-rs-json-provider` 
(`com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider`) at the 
existing Jackson 2.21.3 version (Jackson publishes both variants under the same 
release).
+- **Add** `jakarta.servlet:jakarta.servlet-api:6.0.0` as a new servlet-api 
alias **alongside** the retained `javax.servlet:javax.servlet-api:3.1.0`.
+
+Phase 1 lands as one coordinated PR that:
+
+- Updates `gradle/libs.versions.toml` per the table above.
+- Updates Jetty wiring in `WebService`, `ProxyServer`, and `WebSocketService` 
to start the ee10 environment alongside ee8.
+- Extends the `AdditionalServlet` SPI so plugins can register 
`jakarta.servlet` handlers in addition to existing `javax.servlet` handlers; 
existing plugin entry points stay binary-compatible.
+- Migrates every JAX-RS resource class participating in the broker REST tier 
(~239 files: ~140 in `pulsar-broker` + 34 in `pulsar-broker-common` + 30 in 
`pulsar-client-admin` + 17 in `pulsar-websocket` + 18 in `pulsar-proxy`). The 
mechanical `javax.ws.rs.*` → `jakarta.ws.rs.*` and Swagger 1 → 2 annotation 
renames in those files are driven by OpenRewrite.
+
+Phase 1 is large but atomic — Jersey cannot be half-migrated.
+
+### Phase 2 — Per-module sweep of non-REST-tier `javax.*`
+
+After Phase 1, the broker REST tier (Jersey, Swagger, the `javax.ws.rs` 
resource classes in `pulsar-broker`, `pulsar-broker-common`, 
`pulsar-client-admin`, `pulsar-websocket`, and `pulsar-proxy`) is on jakarta. 
What remains is the non-REST `javax.*` usage scattered across the rest of the 
codebase: `javax.annotation` (JSR 250), `javax.validation`, `javax.xml.bind`, 
`javax.activation`, `javax.inject`, and `javax.websocket` where used.
+
+These are independent of the JAX-RS / Jersey switch and can be migrated one 
module at a time. A series of mechanical PRs runs the OpenRewrite recipe over 
each module:
+
+- Test trees of all modules already swept in Phase 1 
(`pulsar-broker/src/test`, `pulsar-broker-common/src/test`, etc.) — JAX-RS test 
code follows the production code.
+- `pulsar-client-api` and remaining client modules (small file counts; mostly 
`@Generated`, `@PostConstruct`, validation annotations).
+- Function and connector modules whose source code uses `javax.annotation` / 
`javax.validation` (NAR runtime is unaffected; this is a source rename only).
+- Documentation tooling (`pulsar-docs-tools`).
+
+Each PR:
+
+- Runs the OpenRewrite recipe for that module's sources and tests.
+- Manually reviews any non-mechanical adjustments.
+- Enables the per-module Checkstyle import check that bans new `javax.*` 
imports for that module.
+
+### Phase 3 — Cleanup
+
+- Update shading rules: remove `javax.ws.rs`, `javax.annotation-api`, 
`javax.validation`, `javax.xml.bind`, `javax.activation` entries (all now 
Jakarta-namespaced); add equivalent `jakarta.*` entries where shading is still 
required. The `javax.servlet` shading rules remain because the ee8 environment 
is retained for `AdditionalServlet` plugin support.
+- Update LICENSE and NOTICE files in shaded client distributions.
+- Remove now-unused exclusions (e.g. `Exclude javax.annotation` rules in build 
files).
+- Enable the codebase-wide Checkstyle import check.
+- Land a migration guide for plugin authors in the Pulsar website docs 
(apache/pulsar-site).
+
+## Detailed Design
+
+### Dependency upgrade table
+
+| Group:Artifact | Current | Target | Notes |
+|---|---|---|---|
+| `org.glassfish.jersey.core:jersey-server` (and all `org.glassfish.jersey.*` 
artifacts) | 2.42 | 3.1.10 | Group ID unchanged; major bump moves namespace to 
`jakarta.ws.rs` |
+| `io.swagger:swagger-annotations`, `io.swagger:swagger-core` | 1.6.2 | — | 
**Replace with** `io.swagger.core.v3:swagger-annotations`, 
`io.swagger.core.v3:swagger-jaxrs2` at 2.2.27. Annotation package renames from 
`io.swagger.annotations.*` to `io.swagger.v3.oas.annotations.*` |
+| `org.eclipse.jetty.ee8:jetty-ee8-*` | 12.1.8 | unchanged | **Retained** 
alongside `jetty-ee10-*` to preserve `AdditionalServlet` plugin support |
+| *new* `org.eclipse.jetty.ee10:jetty-ee10-*` | — | 12.1.8 | Pulsar's own 
broker/proxy/websocket Jetty wiring uses ee10 |
+| `javax.servlet:javax.servlet-api` | 3.1.0 | unchanged | **Retained** for the 
ee8 environment used by AdditionalServlet plugins |
+| *new* `jakarta.servlet:jakarta.servlet-api` | — | 6.0.0 | Servlet 6 / 
Jakarta EE 10 |
+| `jakarta.ws.rs:jakarta.ws.rs-api` | 2.1.6 | 3.1.0 | 2.1.x is Jakarta EE 8 
(javax namespace); 3.1.x is Jakarta EE 10 (jakarta namespace) |
+| `jakarta.annotation:jakarta.annotation-api` | 1.3.5 | 2.1.1 | 2.x is Jakarta 
EE 10 namespace |
+| `jakarta.validation:jakarta.validation-api` | 2.0.2 | 3.0.2 | 3.x is Jakarta 
EE 10 namespace |
+| `jakarta.xml.bind:jakarta.xml.bind-api` | 2.3.3 | 4.0.2 | 4.x is Jakarta EE 
10 namespace |
+| `jakarta.activation:jakarta.activation-api` | 1.2.2 | 2.1.3 | 2.x is Jakarta 
EE 10 namespace |
+| `com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider` | 2.21.3 | — | 
**Replace with** 
`com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:2.21.3` 
(same Jackson version) |
+| `org.springframework:*` | 6.2.12 | unchanged | Already Jakarta-native |
+| `org.apache.bookkeeper:*` | 4.17.3 | track | Pulsar community to drive a 
Jakarta migration in BookKeeper 4.18 (see *Alternatives*) |
+
+### Source code automation
+
+OpenRewrite recipe:
+
+```
+org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta
+```
+
+This recipe handles the bulk of the namespace renames in source files, 
including imports and fully-qualified references. Manual review is required for:
+
+- Reflection / dynamic class loading using string class names (e.g. 
`Class.forName("javax.ws.rs.core.Response")`).
+- Code generation templates and resource files (e.g. service-loader files 
under `META-INF/services/`).
+- XML configuration files referencing fully-qualified class names.
+- Comments and documentation strings that still mention `javax.*`.
+
+A grep pass at the end of Phase 2 finds any residual `javax.*` outside the 
JDK-allowed list.
+
+### Shaded clients
+
+Shaded modules currently rewrite some `javax.*` classes into Pulsar's shaded 
namespace to avoid classpath conflicts. After Phase 1 the shading rules need to:
+
+- Add equivalent rules for `jakarta.*` API classes that are bundled.
+- Remove `javax.*` rules that no longer apply.
+- Keep JDK-resident `javax.*` classes (`javax.crypto`, `javax.net.ssl`, etc.) 
un-shaded — they were never bundled and remain on the JDK.
+
+### CI lint
+
+Pulsar already runs Checkstyle in CI. The migration uses Checkstyle's 
`ImportControl` (or `IllegalImport`) module to prevent regressions, since 
extending an existing tool is cheaper than introducing a new Gradle plugin. The 
Jakarta-EE-spec packages below are added to the existing Checkstyle 
configuration and disallowed across the codebase, with two well-defined 
exceptions:
+
+- `javax.servlet` is allowed only in code paths that implement the 
`AdditionalServlet` SPI (where javax-servlet support is intentionally retained).
+- `javax.annotation.processing` is always allowed (it is a JDK package, not 
Jakarta EE).
+
+Disallowed packages for Pulsar's own code:
+
+- `javax.ws.rs`
+- `javax.servlet` (except in the `AdditionalServlet` plugin SPI implementation 
classes)
+- `javax.annotation` (excluding `javax.annotation.processing`)
+- `javax.validation`
+- `javax.xml.bind`
+- `javax.activation`
+- `javax.inject`
+- `javax.websocket`
+
+The check is enabled per-module as that module is swept (Phase 2), then turned 
on codebase-wide in Phase 3.
+
+## Public-facing Changes
+
+### Public API
+
+The migration's compile-time impact on downstream Java code is narrower than 
the file count suggests, because most extension points live in their own 
classloaders or are explicitly preserved.
+
+**Affected — these consumers must change imports and recompile:**
+
+- **Embedding consumers of `pulsar-client-admin`** that import REST DTOs and 
any of their JAX-RS / Bean Validation annotations directly from Pulsar's 
published JARs. Annotations like `@PathParam`, `@QueryParam`, `@Encoded`, the 
`Response` builder, and Bean Validation `@NotNull` / `@Size` / `@Min` / `@Max` 
move from `javax.*` to `jakarta.*`.
+- **Authentication / Authorization plugins** (`AuthenticationProvider`, 
`AuthorizationProvider`) and any other plugin that contributes JAX-RS resources 
or sub-resources to the broker REST tier — these are loaded into the broker's 
REST classpath and must use `jakarta.ws.rs.*`.
+- **OpenAPI / Swagger annotation consumers**: any plugin that uses Swagger 
annotations on its REST classes must move from `io.swagger.annotations.*` 
(Swagger 1.x) to `io.swagger.v3.oas.annotations.*` (Swagger 2.x).
+
+**Unaffected — these continue to work without recompilation:**
+
+- **`AdditionalServlet` plugins**: existing plugins that register 
`javax.servlet` handlers continue to work unchanged — the SPI's `javax.servlet` 
registration path is retained and routed to Jetty's ee8 environment. New 
plugins (and existing plugins that want to migrate) can register 
`jakarta.servlet` handlers via the extended SPI; those go to the ee10 
environment. Both styles coexist.
+- **Connectors and Functions**: NAR archives execute in isolated classloaders 
(`NarClassLoader`). The connector / function base contexts (`BaseContext`, 
`FunctionContext`, `SinkContext`, `SourceContext`) currently do not expose 
Jakarta-EE-spec types in their signatures, so existing 4.x NARs continue to 
work on a 5.0 broker as long as they don't directly call the affected Pulsar 
REST/auth SPIs. A connector / function NAR that internally uses `javax.ws.rs` 
for its own HTTP client is also u [...]
+
+**Wire protocol and REST API are unchanged.** Pulsar's binary protocol, REST 
API URLs, request bodies, response bodies, and status codes are identical 
before and after the migration. A 4.x client speaking to a 5.0 broker, or vice 
versa, behaves the same as it does today.
+
+### Binary Protocol
+
+No changes.
+
+### Configuration
+
+No new or removed configuration. A small number of Jersey/Jetty internal 
configuration keys may differ between Jersey 2.42/Jetty `ee8` and Jersey 
3.1/Jetty `ee10`; any user-visible difference will be documented in the upgrade 
guide.
+
+### CLI
+
+No CLI changes.
+
+### Metrics
+
+No metric changes.
+
+## Monitoring
+
+No new monitoring is required. Existing health checks, latency metrics, and 
request-rate metrics on the broker, proxy, and WebSocket components continue to 
function unchanged.
+
+During the migration window, operators are advised to watch for any classpath 
errors of the form `NoClassDefFoundError: javax.ws.rs.*` or 
`NoClassDefFoundError: jakarta.ws.rs.*` in broker logs after upgrade — these 
indicate a third-party plugin compiled against the wrong namespace.
+
+## Security Considerations
+
+- Jersey 3.1 and Jetty 12 (with `ee10` modules) both receive active security 
maintenance; Jersey 2.x and Jetty's `ee8` family are increasingly only fixed 
for serious CVEs. Migration improves Pulsar's exposure to security fixes.
+- Swagger Core 2.x receives active maintenance; 1.6.x is end-of-life.
+- No new authentication, authorisation, or transport-security surfaces are 
introduced. JAAS (`javax.security.auth.*`) and JSSE (`javax.net.ssl`) remain on 
`javax.*` (they are JDK SPIs, not Jakarta EE).
+- Plugin authors recompiling for the new Pulsar 5.0 must use updated 
dependencies; the migration guide explicitly calls out that plugins compiled 
against the old `javax.*` namespace will not load on a 5.0 broker, which is the 
desired (failing-loud) behaviour rather than a silent classpath conflict.
+
+## Backward & Forward Compatibility
+
+### Upgrade
+
+- Operators upgrading a broker from 4.x → 5.0: no special steps for the broker 
itself. Broker configuration is unchanged.
+- **`AdditionalServlet` plugin authors**: existing `javax.servlet`-based 
plugins keep working without recompilation. New or migrating plugins can target 
`jakarta.servlet` through the extended SPI, which is the preferred path going 
forward.
+- **Connector and function authors**: unaffected. NAR-isolated classloaders 
mean existing 4.x NARs continue to work on 5.0.
+- **Authentication / authorization plugin authors and other authors of plugins 
that contribute JAX-RS resources to the broker REST tier**: must update 
`javax.ws.rs.*` / `javax.annotation.*` / `javax.validation.*` / 
`io.swagger.annotations.*` imports to the corresponding `jakarta.*` / 
`io.swagger.v3.oas.annotations.*` packages and recompile against Pulsar 5.0.
+- **Embedding consumers of `pulsar-client-admin`**: Java applications that 
import REST DTOs / JAX-RS annotations from Pulsar's published JARs in-process 
must update their `javax.*` imports to `jakarta.*` and recompile.
+- A migration guide for plugin authors will be published in the Pulsar website 
docs (apache/pulsar-site repo) covering the import-rename recipes 
(admin-client, REST plugin, Swagger annotation rename, OpenRewrite usage).
+
+### Downgrade / Rollback
+
+Wire protocol and REST API are unchanged, so a 5.0 broker can be replaced with 
a 4.x broker without rollback steps.
+
+### Pulsar Geo-Replication Upgrade & Downgrade/Rollback Considerations
+
+Geo-replication runs broker-to-broker over Pulsar's binary protocol, which 
this PIP does not change. A mixed-version geo-replicated cluster (some sites on 
4.x, others on 5.0) replicates correctly. There are no special steps.
+
+## Alternatives
+
+1. **Stay on `javax.*` indefinitely.**
+   *Rejected.* The upstream ecosystem has moved; staying on `javax.*` means no 
new Jersey, Jetty, Swagger, Spring, Hibernate Validator features, performance 
work, or security fixes can be picked up. The cost grows over time.
+
+2. **Defer the migration to Pulsar 6.0.**
+   *Rejected.* Pulsar 5.0 is LTS. Carrying `javax.*` through 5.0 means the LTS 
line will require a major-version bump just to migrate. Doing it now keeps the 
LTS line clean for its full lifecycle.
+
+3. **Run-time `javax.*` ↔ `jakarta.*` bytecode transformation (Eclipse 
Transformer at load time).**
+   *Rejected.* Adds startup cost and operational complexity, masks classpath 
problems behind a translation layer that is itself a class of bugs, and does 
not help downstream consumers (their compile-time signatures still resolve 
against whichever side of the namespace the Pulsar artifacts publish). It also 
doesn't simplify the build.
+
+4. **Big-bang migration in a single feature branch.**
+   *Rejected.* The scope is ~1,100 files. A long-lived feature branch against 
master, which evolves daily, becomes unmaintainable to rebase. Phasing keeps 
master always-shippable and lets the work parallelise across contributors.
+
+5. **A `javax.*` → `jakarta.*` source compatibility shim layer.**
+   *Rejected.* Maintaining a permanent shim that re-exports `jakarta.*` types 
as `javax.*` (or vice versa) defeats the goal of removing the dual classpath, 
doubles the public surface, and is itself a source of subtle bugs.
+
+6. **Migrate Apache BookKeeper as part of this PIP.**
+   *Out of scope, but actively coordinated.* BookKeeper is a separate Apache 
project with its own community and release cadence, and a Jakarta migration in 
BookKeeper cannot land via a Pulsar PIP. The Pulsar and BookKeeper communities 
are closely linked, however, and the Pulsar community should drive a Jakarta 
migration in BookKeeper 4.18 through the BookKeeper community's normal process 
so that Pulsar 5.0 can ship on a BookKeeper release whose own classpath is also 
Jakarta-clean. If Boo [...]
+
+## General Notes
+
+The migration is heavily mechanical — most of the source-code change is 
import-rename driven by an automated recipe — but the dependency-upgrade and 
JAX-RS namespace switch in Phase 1 require careful coordination because Jersey 
2 and Jersey 3 cannot coexist in the same REST tier. Phase 1 should be executed 
by contributors familiar with Jersey, Jetty, and Swagger initialisation in 
`pulsar-broker`. The Jetty 12 ee8/ee10 dual-environment configuration that 
preserves the `AdditionalServlet`  [...]
+
+## Links
+
+* Mailing List discussion thread: 
https://lists.apache.org/thread/1crbw2y2zg89qsyyq7qnv3ldh640lpsy
+* Mailing List voting thread: *(populate after vote opens)*
+* Related: [PIP-421: Require Java 17 as the minimum for Pulsar Java client 
SDK](pip-421.md)
+* Related: [PR 25100: Upgrade to Jetty 
12.1.x](https://github.com/apache/pulsar/pull/25100)
\ No newline at end of file

Reply via email to