Repository navigation
Modularize the HTTP transport layer: independent Jetty 9, Jetty 12, servlet, and HttpService bundles - #365
Modularize the HTTP transport layer: independent Jetty 9, Jetty 12, servlet, and HttpService bundles#365holgerfriedrich wants to merge 47 commits into
Conversation
|
Great I'll have a look! I was also thinking about giving it a go with AI soon. I'll use them to review instead now. 😄 |
There was a problem hiding this comment.
Pull request overview
This PR modularizes jUPnP’s network transport layer so that transport implementations live in separate artifacts, enabling Jetty 9.4 and (on Java 17+) Jetty 12 to coexist while keeping the project’s baseline Java release at 11.
Changes:
- Introduces
TransportConfigurationProvidertransport discovery viaServiceLoader, with a reflective fallback for environments like OSGi. - Splits transports into new modules/artifacts:
org.jupnp.transport.jetty9(Servlet-based) andorg.jupnp.transport.jetty12(Jetty core API, Java 17+). - Updates build/BOM/docs/CI to account for transport modularization and to add an additional Java 17 CI build.
Reviewed changes
Copilot reviewed 30 out of 34 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| website/docs/getting-started.md | Updates getting-started dependency guidance to mention separate transport artifacts. |
| website/docs/android.md | Documents Android requirement for Jetty 9 transport and adds Maven dependency example. |
| website/docs/advanced.md | Updates transport documentation to describe modular transports and new implementation classes. |
| tools/org.jupnp.tool/pom.xml | Adds an explicit dependency on the Jetty 9 transport module. |
| pom.xml | Introduces a separate jetty12.version property while keeping Java 11 baseline. |
| itests/org.jupnp.osgi.tests/pom.xml | Adds Jetty 9 transport dependency for OSGi integration tests. |
| itests/org.jupnp.osgi.tests/itest.bndrun | Adds org.jupnp.transport.jetty9 bundle to the OSGi test runtime. |
| bundles/pom.xml | Adds Jetty 9 transport module and activates Jetty 12 transport module only on JDK 17+. |
| bundles/org.jupnp/src/main/java/org/jupnp/transport/TransportConfigurationProvider.java | Replaces hard-wired Jetty transport selection with discovery logic. |
| bundles/org.jupnp/src/main/java/org/jupnp/transport/impl/StreamClientConfigurationImpl.java | Moves “default” stream client configuration to a transport-independent package. |
| bundles/org.jupnp/src/main/java/org/jupnp/OSGiUpnpServiceConfiguration.java | Lazily discovers the transport implementation and adjusts optional HttpService handling. |
| bundles/org.jupnp/src/main/java/org/jupnp/DefaultUpnpServiceConfiguration.java | Lazily discovers the transport implementation instead of constructing it directly. |
| bundles/org.jupnp/bnd.bnd | Adjusts OSGi import strategy for transport implementation packages. |
| bundles/org.jupnp.transport.jetty9/src/test/java/org/jupnp/transport/JettyServerJettyClientTest.java | Updates tests to use the relocated transport-independent client configuration class. |
| bundles/org.jupnp.transport.jetty9/src/main/resources/META-INF/services/org.jupnp.transport.TransportConfiguration | Registers Jetty 9 transport via ServiceLoader. |
| bundles/org.jupnp.transport.jetty9/src/main/java/org/jupnp/transport/impl/jetty/StreamClientConfigurationImpl.java | Adds a compatibility wrapper for the old Jetty-specific configuration class name. |
| bundles/org.jupnp.transport.jetty9/src/main/java/org/jupnp/transport/impl/jetty/JettyTransportConfiguration.java | Defines the Jetty 9 transport implementation entrypoint. |
| bundles/org.jupnp.transport.jetty9/src/main/java/org/jupnp/transport/impl/jetty/JettyStreamClientImpl.java | Adds the Jetty 9 HTTP client implementation. |
| bundles/org.jupnp.transport.jetty9/src/main/java/org/jupnp/transport/impl/jetty/JettyServletContainer.java | Adds a standalone Jetty 9 servlet container adapter. |
| bundles/org.jupnp.transport.jetty9/src/main/java/org/jupnp/transport/impl/jetty/HeaderUtil.java | Adds Jetty 9 header/content conversion helpers. |
| bundles/org.jupnp.transport.jetty9/pom.xml | Introduces the Jetty 9 transport module build configuration. |
| bundles/org.jupnp.transport.jetty12/src/test/java/org/jupnp/transport/Jetty12ServerJetty12ClientTest.java | Adds Jetty 12 transport integration test coverage. |
| bundles/org.jupnp.transport.jetty12/src/main/resources/META-INF/services/org.jupnp.transport.TransportConfiguration | Registers Jetty 12 transport via ServiceLoader. |
| bundles/org.jupnp.transport.jetty12/src/main/java/org/jupnp/transport/impl/jetty12/Jetty12UpnpStream.java | Adds Jetty 12 server-side request/response bridging to UPnP streams. |
| bundles/org.jupnp.transport.jetty12/src/main/java/org/jupnp/transport/impl/jetty12/Jetty12TransportConfiguration.java | Defines the Jetty 12 transport implementation entrypoint. |
| bundles/org.jupnp.transport.jetty12/src/main/java/org/jupnp/transport/impl/jetty12/Jetty12StreamServerImpl.java | Adds a Jetty 12 core-API stream server (non-servlet). |
| bundles/org.jupnp.transport.jetty12/src/main/java/org/jupnp/transport/impl/jetty12/Jetty12StreamServerConfigurationImpl.java | Adds Jetty 12 server configuration implementation. |
| bundles/org.jupnp.transport.jetty12/src/main/java/org/jupnp/transport/impl/jetty12/Jetty12StreamClientImpl.java | Adds the Jetty 12 HTTP client implementation. |
| bundles/org.jupnp.transport.jetty12/src/main/java/org/jupnp/transport/impl/jetty12/HeaderUtil.java | Adds Jetty 12 header conversion helpers. |
| bundles/org.jupnp.transport.jetty12/pom.xml | Introduces the Jetty 12 transport module build configuration (Java 17 release). |
| bundles/org.jupnp.android/pom.xml | Ensures Android bundle depends on the Jetty 9 transport module. |
| bom/jupnp/pom.xml | Adds transport artifacts to the BOM, with Jetty 12 included only on JDK 17+. |
| bom/compile/pom.xml | Removes Jetty dependencies from the compile BOM since they move to transport modules. |
| .github/workflows/ci-build.yml | Adds an extra Java 17 CI build to cover the optional Jetty 12 transport. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@kaikreuzer perhaps you can enable the "ChatGPT Codex Connector" for this repo so I can also ask Codex for a review in this repo as well. |
|
@wborn I pushed the AI to get this done, and it turned out there really was a bug in the ServiceLoader-based discovery: the code called the two-argument ServiceLoader.load(Class, ClassLoader) overload, which silently bypasses SPI Fly's weaving (SPI Fly only intercepts the one-argument overload). Fixed that. Getting a reliable itest was still complicated: the transport bundle only registers itself with SPI Fly after jUPnP's own bundle already attempts discovery, so the two race, and a short startup retry doesn't reliably close that gap. Pinning bundle start levels in the bnd file did work, but isn't a good way — bnd's resolver silently drops those attributes every time it recomputes -runbundles (e.g. -DwithResolver), which is a routine dev step. So instead, OSGiUpnpServiceConfiguration now consumes the transport via a mandatory Declarative Services @reference instead of through TransportConfigurationProvider. DS won't activate the component until the transport bundle has actually registered, so it's deterministic rather than a race. The ServiceLoader/SPI Fly path is kept for non-DS consumers (e.g. plain DefaultUpnpServiceConfiguration use). WDYT? |
ba06f23 to
1966817
Compare
|
Back to draft, last commit needs some rework... |
|
After having a first look the changes are quite straightforward and seem pretty nice overall. 👍 |
|
Is it also possible to move the classes using |
This is likely a change bigger than this one. Everything still relays on We can try to modularize the the code around I will give this a try - the transport modularization itself is only half of the story. |
|
@wborn If I see it correctly, we use the fallback with a separate See last commit. I will give it a try with OH patch. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 41 out of 43 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
bundles/org.jupnp/src/main/java/org/jupnp/transport/TransportConfigurationProvider.java:87
- The thrown message says "No transport implementation found", but discovery can also fail when a provider is present yet can’t be loaded (e.g. linkage/config errors). Adjusting the message to "No usable transport implementation" makes it accurate for both cases.
throw new InitializationException("No transport implementation found via ServiceLoader. "
+ "Add a jUPnP transport bundle (e.g. org.jupnp:org.jupnp.transport.jetty9) as a dependency. "
+ "In OSGi, also ensure an OSGi Service Loader Mediator (e.g. Apache Aries SPI Fly) is "
+ "installed and started.");
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 41 out of 43 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
bundles/org.jupnp/src/main/java/org/jupnp/transport/TransportConfigurationProvider.java:128
- The PR description says an application can depend on Jetty 9 and Jetty 12 transports "(or both)", but
TransportConfigurationProviderhard-fails when more than one ServiceLoader provider is present. Either update the stated behavior, or add a deterministic selection mechanism (e.g., system property / service ranking) so "both present" is actually supported.
// ServiceLoader iteration order is unspecified, so silently picking the first of several
// candidates would make transport selection non-deterministic across runs. Fail loudly instead,
// the same way the no-provider-found case does, rather than risk a random pick.
if (foundClassNames.size() > 1) {
throw new InitializationException("Multiple transport implementations found via ServiceLoader: "
+ foundClassNames + ". Make sure only one jUPnP transport bundle (e.g. "
+ "org.jupnp.transport.jetty9 or org.jupnp.transport.jetty12) is on the class path.");
}
* Upgrade jetty9 from 9.4.53 to 9.4.58 * Upgrade pax-web8 from 8.0.25 to 8.0.36 * Upgrade jetty12 to 12.1.12 * Upgrade pax-web11 to 11.1.3 * Upgrade pax-logging to 2.3.5 * Upgrade slf4j from 2.0.11 to 2.0.18 * Upgrade logback from 1.5.35 to 1.6.2 Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
Manage all public jUPnP artifacts, including both transports and the servlet/HttpService bridges, in dependencyManagement instead of listing a subset as plain dependencies, so importing it with scope=import actually works the standard way. Consumers now declare exactly the artifacts they want, with no version needed on any of them. This also removes the need for the org.jupnp.android/jetty9 exclusion, since dependencyManagement entries don't add anything to the classpath by themselves. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
Align artifact, bundle/module, and Java package names across all six transport bundles before they become part of the 4.0 API surface: - org.jupnp.transport.servlet -> org.jupnp.transport.javax.servlet (package org.jupnp.transport.impl.servlet -> org.jupnp.transport.javax.servlet) - org.jupnp.transport.httpservice -> org.jupnp.transport.javax.httpservice (package org.jupnp.transport.impl.osgi -> org.jupnp.transport.javax.httpservice) - org.jupnp.transport.jakarta.servlet: artifact unchanged, package org.jupnp.transport.impl.servlet.jakarta -> org.jupnp.transport.jakarta.servlet - org.jupnp.transport.jakarta.httpservice: artifact unchanged (kept, not renamed to "whiteboard", to preserve symmetry with javax.httpservice), package org.jupnp.transport.impl.osgi.jakarta -> org.jupnp.transport.jakarta.httpservice - org.jupnp.transport.jetty12: artifact unchanged, package org.jupnp.transport.impl.jetty12 -> org.jupnp.transport.jetty12 - org.jupnp.transport.jetty9: artifact unchanged, package org.jupnp.transport.impl.jetty -> org.jupnp.transport.jetty9, and its classes renamed for symmetry with Jetty 12's Jetty12* prefix: JettyTransportConfiguration -> Jetty9TransportConfiguration, JettyStreamClientImpl -> Jetty9StreamClientImpl, JettyServletContainer -> Jetty9ServletContainer, StreamClientConfigurationImpl -> Jetty9StreamClientConfigurationImpl Also renames the itest module org.jupnp.osgi.tests to org.jupnp.osgi.tests.jetty9 (it specifically exercises Jetty 9 now that jetty12/jetty12.jakarta are separate modules), sorts bundles/pom.xml's module lists, updates bom/jupnp/pom.xml's managed artifacts to match, and regenerates every itest's -runbundles via -DwithResolver. Adds the two newer itest modules (org.jupnp.osgi.tests.jetty12 and org.jupnp.osgi.tests.jetty12.jakarta) to the Central-publishing excludeArtifacts list, which had only ever listed the original module -- a pre-existing gap unrelated to this rename, found while regenerating that list's entries. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
…ace in its itest The Jakarta bridge implements the OSGi Servlet Whiteboard, not a Jakarta port of the classic HttpService, so name it accordingly and free up javax.whiteboard for a possible future javax-based Whiteboard bridge: - org.jupnp.transport.jakarta.httpservice -> org.jupnp.transport.jakarta.whiteboard (package org.jupnp.transport.jakarta.httpservice -> org.jupnp.transport.jakarta.whiteboard) - JakartaHttpServiceServletContainerAdapter -> JakartaWhiteboardServletContainerAdapter - JakartaHttpServiceSharedStreamServerProvider -> JakartaWhiteboardSharedStreamServerProvider - itest module org.jupnp.osgi.tests.jetty12.jakarta -> org.jupnp.osgi.tests.jetty12.jakarta.whiteboard, matching what it actually exercises (Jetty 12 + the Jakarta Whiteboard bridge) Updates every cross-reference: bundles/pom.xml and bom/jupnp/pom.xml module/dependency lists (re-sorted), itests/pom.xml, root pom.xml's Central-publishing excludeArtifacts (which still had the itest's old name), and regenerates -runbundles via -DwithResolver. Also fixes the DS lifecycle race behind the intermittent Java 17 CI failure in that itest's RealSharedServerActivationTest: enabling OSGiUpnpServiceConfiguration before the Jakarta Whiteboard bridge's SharedStreamServerProvider was available let the component activate without it, then reactivate once the provider showed up -- UpnpServiceImpl.unsetUpnpServiceConfiguration() briefly nulls the configuration mid-rebind, which a UpnpService reference obtained just before that reactivation could observe as null. Waiting for SharedStreamServerProvider first means activation always happens with the provider already bound, so no later reactivation. Verified with repeated itest runs (previously intermittent, now consistently green). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
6855e55 to
bff1a92
Compare
|
I rebased to resolve recent merge conflicts. |
Felix SCR 2.2.10 (Jan 2024) never activates delayed Declarative Services components under Java 25/26: bind methods and constructors are simply never invoked, even though all mandatory @reference dependencies are registered and available the whole time. This broke all three OSGi itests (jetty9, jetty12, jetty12.jakarta.whiteboard) on Java 25 and 26 in CI, while Java 11/17/21 were unaffected. Confirmed via direct reproduction: instrumenting the DS component's constructor and bind method showed both firing correctly under Java 21 but never firing under Java 25, with an otherwise identical bndrun/classpath. Bumping org.eclipse.osgi (the OSGi framework itself) alone did not fix it; the fix is specifically in Felix SCR. org.apache.felix.scr 2.2.18 requires org.osgi.util.promise/function >=1.1.0, but org.osgi.service.component (1.5.0/1.5.1) still transitively pulls 1.0.0, so both are pinned explicitly to win over that transitive resolution. The three affected itest.bndrun files were regenerated via -DwithResolver. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
The felix.scr bump alone did not fully fix the OSGi itests on Java 25/26: they still failed intermittently in CI and in a full local reactor build, even though a repeated isolated single-module build kept passing. The real, separate cause: org.apache.aries.spifly's WeavingHook uses a bundled ASM 9.6, whose class file parser doesn't recognize Java 25/26 (major versions 69/70). It throws IllegalArgumentException whenever it processes a JVM-synthesized lambda proxy class, which is generated with the running JVM's own class file version regardless of --release used at compile time. That throw is caught and logged as a FrameworkEvent.ERROR rather than crashing, but depending on exactly when in the bundle startup sequence it hits (a genuinely racy timing), it can corrupt spifly's TransportConfiguration provider registration for that run, leaving the DS component that depends on it permanently unsatisfied. Pinned the whole ASM family (asm, asm-commons, asm-tree, asm-analysis, asm-util) to 9.10.1 in itests/pom.xml's dependencyManagement, inherited by all three OSGi itest modules, overriding the transitive 9.6 pulled in by aries-spifly-dynamic-bundle:1.3.7 (the latest available spifly release, which itself has not been updated for newer JDKs). Verified with 5 consecutive clean runs of jetty9 and 3 of jetty12/jetty12.jakarta.whiteboard under Java 25 (previously flaky), a full `clean verify` reactor build under Java 25, and re-verified Java 21 still passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
wborn
left a comment
There was a problem hiding this comment.
Thanks for addressing all the comments. 👍 After another pass it all looks very nice. Here are a few minor comments I found along the way. I also still want to give it a small test.
Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
Thanks, @wborn. |
|
@wborn did you find some time to test this a bit? We could test the new modularization on Jetty9/Karaf 4.4.12 in the next OH milestone.... |
|
Maybe @kaikreuzer ? Would be good to have this prior to the big karaf upgrade in openHAB. |
|
I would leave it with @wborn unless he states that he won't be able to do it and ask me instead. |
|
Yes, it's still on my radar. I'm hoping to find some time to test it this coming weekend. 🙂 |
During a Karaf bundle refresh (e.g. installing an openHAB add-on that pulls in GraalVM), jUPnP was activated on the refresh thread before Pax Web was restarted. JakartaWhiteboardServletContainerAdapter then polled for up to 60s for HttpServiceRuntime's endpoint on that same thread, so the endpoint could never appear: the refresh stalled for the full timeout and jUPnP ended up without a stream server. - Gate JakartaWhiteboardSharedStreamServerProvider on an HttpServiceRuntime advertising an http endpoint, so DS (re)starts jUPnP on the shared server once Pax Web is actually up. - Read the port once in addConnector(), using the same filter, instead of polling. - Confirm the Whiteboard picked up the servlet on a background thread, cancelled by stopIfRunning(), instead of blocking registerServlet(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
7bb0ecb to
e0b9f5b
Compare
Signed-off-by: Wouter Born <github@maindrain.net>
Preserve the existing documentation as a 3.x snapshot before jupnp#365 updates the main docs for 4.x. Keep 3.x as the default and expose 4.x through a version selector. Enable versioned search and make edit links target the correct docs. Signed-off-by: Wouter Born <github@maindrain.net>

Use this as the commit message if GitHub squash-merges this PR (paste into the squash commit message box):
This PR was generated with significant use of AI.
What this does
This started as the transport split requested in #297 (comment) and grew into a full modularization of jUPnP's HTTP layer. Six bundles now do what core used to do internally:
org.jupnp.transport.jetty9org.jupnp.transport.jetty12org.jupnp.transport.javax.servletjavax.servlettransport infra, used by jetty9 and Androidorg.jupnp.transport.javax.httpserviceorg.jupnp.transport.jakarta.servletjakarta.servlet6.1 transport infra, async-onlyorg.jupnp.transport.jakarta.whiteboardCore (
org.jupnp) no longer has any compile-time or OSGi-manifest dependency onjavax.servlet,jakarta.servlet,org.osgi.service.http, ororg.osgi.service.servlet-- not even an optional one. It talks to transports throughTransportConfigurationand to an optional shared HTTP server through a newSharedStreamServerProviderSPI; neither interface mentions servlet or HttpService/Whiteboard types.Discovery is fail-fast on ambiguity in both the ServiceLoader path (non-OSGi) and the Declarative Services path (OSGi), so a misconfigured classpath/bundle-set with two transport bundles -- or two shared-server providers -- throws a clear error instead of picking one non-deterministically. Which transport to use is always an explicit choice the consumer makes:
org.jupnp.bommanages versions fororg.jupnp.transport.jetty9andorg.jupnp.transport.jetty12side by side, but importing it doesn't add either (or any other artifact) as a dependency by itself -- the consumer still declares exactly the one they want. Port sharing (viaSharedStreamServerProvider) is a separate, orthogonal choice from which transport is in use --org.jupnp.transport.jetty12plusorg.jupnp.transport.jakarta.whiteboardshares a port with a Jakarta HTTP Whiteboard runtime (e.g. Pax Web 11+) the same wayorg.jupnp.transport.jetty9plusorg.jupnp.transport.javax.httpserviceshares a port with a classicHttpService.Along the way, dependencies exercised by the new itests got a pass too: Jetty 9 (9.4.53 -> 9.4.58) and its matching Pax Web 8.x (8.0.25 -> 8.0.36), Jetty 12 (12.1.10 -> 12.1.12) and Pax Web 11.x (11.1.2 -> 11.1.3), Pax Logging (-> 2.3.5), logback (1.5.35 -> 1.6.2), and slf4j (2.0.11 -> 2.0.18).
Compatibility
TransportConfiguration/UpnpServiceConfiguration-- with one exception: subclasses ofOSGiUpnpServiceConfiguration. No API changes toStreamServer,StreamClient,Router, or the discovery entry points.OSGiUpnpServiceConfigurationitself moved from manually bindingHttpServiceto Declarative Services, which removes or changes several protected/public extension points a subclass may have relied on: theprotected BundleContext context,protected TransportConfiguration transportConfiguration, andprotected HttpService httpServicefields are gone, as are theactivate(BundleContext, Map<String, Object>),setHttpService(HttpService), andunsetHttpService(HttpService)methods (activate(Map<String, Object>), without theBundleContextparameter, replaces the oldactivateoverride point). A subclass no longer bindsHttpServiceitself at all -- installorg.jupnp.transport.javax.httpserviceororg.jupnp.transport.jakarta.whiteboard(steps 3/4 below) instead, and DS injects aSharedStreamServerProviderautomatically. See step 6 below.org.jupnp.transport.impl.ServletStreamServerImpl,ServletContainerAdapter,HttpServiceServletContainerAdapter, etc. fromorg.jupnpwill get compile errors -- those classes moved to new packages in the new bundles (see migration steps). This also coversorg.jupnp.http.RequestInfo'sHttpServletRequest-based methods --reportRequest,reportParameters,reportHeaders,reportCookies,reportClient,dumpRequestHeaders,dumpRequestString,getRequestInfoString,getRequestFullURL, and theHttpServletRequest-typed overloads ofisPS3Request/isJRiverRequest/isXbox360Request/isXbox360AlbumArtRequest-- which moved unchanged to the neworg.jupnp.transport.javax.servlet.ServletRequestInfoinorg.jupnp.transport.javax.servlet(step 3 below already covers adding that dependency).RequestInfostays in core with only itsString-based overloads (isPS3Request(String, String),isAndroidBubbleUPnPRequest,isJRiverRequest(String),isWMPRequest,isXbox360Request(String, String)) -- nothing was dropped, it was only split by whether it needsjavax.servlet.org.jupnp'sImport-Packageno longer listsjavax.servlet,javax.servlet.http,org.osgi.service.http,jakarta.servlet, ororg.osgi.service.servletat all (previously optional for the javax/HttpService ones, now absent everywhere). Runtimes that don't have those packages will now resolveorg.jupnpexactly as before (it never needed them to resolve); runtimes that do have them no longer get core wired to them implicitly.org.jupnp.transport.impl.jetty.JettyTransportConfiguration->org.jupnp.transport.jetty9.Jetty9TransportConfiguration(same package/name change forJettyStreamClientImpl->Jetty9StreamClientImpl,JettyServletContainer->Jetty9ServletContainer, andStreamClientConfigurationImpl->Jetty9StreamClientConfigurationImpl). Only relevant if you referenced these directly (e.g. a customcreateStreamClient()/createStreamServer()override, per the docs); consumers going throughTransportConfiguration/UpnpServiceConfigurationare unaffected.Steps for existing consumers of the Jetty 9 implementation
org.jupnp.bomis now a conventional Maven BOM (previously a convenience aggregator that pulled inorg.jupnp/org.jupnp.osgi/org.jupnp.supporttransitively). Import it viadependencyManagement/scope=import, then declare exactly the jUPnP artifacts you want as plain dependencies with no<version>(every example below omits it accordingly):org.jupnpcore (or a transport) to your classpath by itself -- list every artifact you need explicitly, same as if you weren't using the BOM at all (you can also skip the BOM entirely and pin each artifact's<version>directly, as before). Skipping the transport dependency doesn't break your build, it compiles fine and then fails at runtime with no transport found; skippingorg.jupnpitself is a compile error, so that one's harder to miss. Nothing is auto-selected based on your build's JDK version.UpnpServiceConfiguration.createStreamServer()override usingServletStreamServerImpl/ServletContainerAdapter, per the docs), add the new servlet dependency:org.jupnp.transport.impl.ServletStreamServerImpl->org.jupnp.transport.javax.servlet.ServletStreamServerImpl(same rename forServletStreamServerConfigurationImpl,ServletContainerAdapter,ServletUpnpStream,ServletConnection).OSGiUpnpServiceConfigurationwhere a classicHttpServiceis present and you want jUPnP to share its port), add the new HttpService bridge bundle to your runtime:OSGiUpnpServiceConfigurationactivates anyway and falls through to the transport's own native StreamServer on its own port -- unlike before this PR, where a missing HttpService simply meant the component never activated. If that fallback port collides with an already-running server (e.g. openHAB/openhab-addons commonly do, since their web UI's HttpService and jUPnP's default streamListenPort are both 8080), stream server init fails with a BindException and the whole router stays disabled. See openhab/openhab-core#5740 for a worked example of the required consumer-side change.HttpService; this is the runtime you'll have if you've migrated to Jetty 12/Jakarta EE 10), use the jakarta bridge instead -- pair it withorg.jupnp.transport.jetty12:OSGiUpnpServiceConfigurationfalls through to jetty12's own standalone port.org.jupnp.transport.jetty9andorg.jupnp.transport.javax.servlet); no action needed forAndroidUpnpServiceConfigurationconsumers.OSGiUpnpServiceConfiguration: drop any override ofsetHttpService/unsetHttpService/the removedhttpService/contextfields -- port sharing is now handled entirely by installing a bridge bundle (steps 3/4), with no code on your side. If you overrodeactivate(BundleContext, Map<String, Object>), overrideactivate(Map<String, Object>)instead (theBundleContextparameter is gone, since this class no longer needs it directly).This PR was generated with significant use of AI.