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

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new 0444ac4f73 FELIX-6692 jetty 12.x websockets new approach (based on 
#309) (#310)
0444ac4f73 is described below

commit 0444ac4f73a3cb876511b00a2df49e7f16efee0c
Author: Paul <p...@blueconic.com>
AuthorDate: Wed May 1 09:04:05 2024 +0200

    FELIX-6692 jetty 12.x websockets new approach (based on #309) (#310)
    
    * Revert "Add jetty websocket support to Jetty12  (#298)"
    
    This reverts commit 6d95c936dfc7767f1cea23d1db98a8c7d7810378.
    
    * FELIX-6692 Add Jetty WebSocket support for jetty 12.x
    - Apply 11.x approach to jetty12 bundle
    - Add two new classifiers 'with-jetty-ee10-websockets' and 
'with-jakarta-ee10-websockets' to have a fat jar containing the appropriate 
websocket classes
    
    * Working example base on previous code.
    Do note that the workaround in FelixJettyWebSocketServlet are still 
required; the initialization code in the Jetty12 bundle doesn't seem to work
    
    * * Enable cross context support to allow WebSockets to be registered in 
Jetty 12.
    See 
https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java#L510
 and https://github.com/jetty/jetty.project/issues/9774
    
    * Add example based on registering the websocket to the main servlet 
context instead of the per-bundle one
    -
    
    * Added paxweb unit tests based on the Jetty11 work from #309
    
    * Fix version
    
    * Fix message
    
    * Test casing
    
    * Update README.md
    
    * Make client optional
    
    * Add Jetty12 bundle to pom
    
    * Store the WebSocket container reference and set it on the shared servlet 
context once available
    This removes the need to set setCrossContextDispatchSupported, as the WS 
container is available on the proper servlet context itself
    
    * Update example to no longer use the root context.
    Removed other WebSocket example, as this is no longer needed with the new 
approach.
    Updated documentation.
    
    * Add servlet based example again, as it shows another example of how to 
register a WebSocket endpoint that abides to the servlet context it's 
registered to.
    
    * Rename class
    
    * Comment
    
    * Comments
    
    * Remove classloader code as it now also works without
    
    * Rename test class to EE10
    
    * Small changes to README.md
---
 http/README.md                                     | 123 +++----
 http/jetty12/pom.xml                               | 377 +++++++++++++++++++--
 .../jetty/internal/ConfigMetaTypeProvider.java     |  11 +
 .../felix/http/jetty/internal/JettyConfig.java     |  22 ++
 .../felix/http/jetty/internal/JettyService.java    |  86 ++++-
 .../http/jetty/it/AbstractJettyTestSupport.java    | 185 ++++++++++
 .../jetty/it/JakartaEE10SpecificWebsocketIT.java   | 211 ++++++++++++
 .../jetty/it/JettyEE10SpecificWebsocketIT.java     | 209 ++++++++++++
 .../jetty/it/MissingWebsocketDependenciesIT.java   |  91 +++++
 http/pom.xml                                       |   1 +
 http/samples/whiteboard/pom.xml                    |  20 +-
 .../felix/http/samples/whiteboard/Activator.java   |  31 +-
 .../whiteboard/FelixJettyWebSocketServlet.java     | 109 +-----
 .../samples/whiteboard/TestWebSocketServlet.java   |  32 +-
 ...t.java => TestWebSocketServletAlternative.java} |  24 +-
 15 files changed, 1331 insertions(+), 201 deletions(-)

diff --git a/http/README.md b/http/README.md
index 875fcce940..10f8fd4d87 100644
--- a/http/README.md
+++ b/http/README.md
@@ -6,8 +6,8 @@ This is an implementation of the [R8.1 Whiteboard Specification 
for Jakarta Serv
   * Standard OSGi Http Whiteboard implementation
   * Run either with Jetty (version 11 or 12) bundle or inside your own 
application server using the servlet bridge
     * [Felix HTTP Jetty 
12](https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.http.jetty12)
 is the preferred bundle of choice as it supports JavaEE 8 and JakartaEE 8 with 
the `javax` namespace, JakartaEE 9/10/11/future versions with the `jakarta` 
namespace.
-      * [Jetty WebSocket 
support](https://github.com/apache/felix-dev/pull/298), see example code 
[here](https://github.com/apache/felix-dev/blob/master/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java).
-    * [Felix HTTP Jetty 
11](https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.http.jetty)
 is the predecessor of the Jetty 12 bundle, which shipped with [Jetty 
9.4.x](https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.http.jetty/4.2.26)
 in the 4.x range, [Jetty 
11.x](https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.http.jetty/5.1.10)
 in the 5.x range.
+      * [Jetty WebSocket 
support](https://github.com/apache/felix-dev/pull/310), see example code 
[here](https://github.com/apache/felix-dev/blob/master/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java).
+    * [Felix HTTP Jetty 
11](https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.http.jetty)
 is the predecessor of the Jetty 12 bundle, which shipped with [Jetty 
9.4.x](https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.http.jetty/4.2.26)
 in the 4.x range, [Jetty 
11.x](https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.http.jetty/5.1.10)
 in the 5.x range. 
   * Correctly versioned Servlet API.
 
 ## Installing
@@ -27,12 +27,17 @@ Note that as of version **3.x**, the Servlet APIs are **no 
longer** packaged wit
 `org.apache.felix.http.servlet-api` (or any other compatible Serlvet API 
bundle) to your
 classpath and deployment!
 
-### Light bundle
-If you would like to use your own Jetty jar instead of the one packaged with 
the Felix Jetty bundles, you can use the `light` variant.
-When building the Felix Jetty bundle with Maven (`mvn clean install`), the 
`light` bundle will be created in the `target` directory, postfixed with 
`-light.jar`.
-This jar can be deployed to your Felix OSGi environment, along with a 
compatible Jetty jar.
+### Using classifiers: `light`, `with-jetty-ee10-websockets` and 
`with-jakarta-ee10-websockets` bundle
+If you would like to use your own Jetty jars instead of the one packaged with 
the Felix Jetty bundles, you can use the variants with the following 
classifiers:
+* `light` - A light version of the bundle that does not include the Jetty 
classes. This is useful when you want to use your own Jetty jars. Available for 
both Jetty bundles.
+* `with-jetty-ee10-websockets` - A bundle that includes the classes required 
for Jetty WebSocket support for Jakarta EE10. Jetty12 bundle only.
+* `with-jakarta-ee10-websockets` - A bundle that includes the classes required 
for Jakarta WebSocket support for Jakarta EE10. Jetty12 bundle only.
 
-Or just use maven to include the dependency with the `light` classifier. 
+When building the Felix Jetty bundle with Maven (`mvn clean install`), the 
additional bundles will be created in the `target` directory, postfixed with 
classifier.
+This jar can be deployed to your Felix OSGi environment, along with a 
compatible Jetty jars.
+See the unit tests for the required bundles and versions that need to be 
deployed.
+
+Or just use maven to include the dependency with the proper classifier. 
 ```
 <dependency>
    <groupId>org.apache.felix</groupId>
@@ -381,57 +386,59 @@ The service can both be configured using OSGi environment 
properties and using C
 this service is `"org.apache.felix.http"`. If you use both methods, 
Configuration Admin takes precedence. The following
 properties can be used (some legacy property names still exist but are not 
documented here on purpose). As properties might change over time, the actual 
list of properties can be found [here for the Jetty 12 
bundle](https://github.com/apache/felix-dev/blob/master/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java)
 and [here for the Jetty 11 
bundle](https://github.com/apache/felix-dev/blob/master/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/J
 [...]
 
-| Property | Description |
-|--|--|
-| `org.apache.felix.http.host` | Host name or IP Address of the interface to 
listen on. The default is `null` causing Jetty to listen on all interfaces. |
-| `org.osgi.service.http.port` | The port used for servlets and resources 
available via HTTP. The default is `8080`. See [port settings 
below](#http-port-settings) for additional information. A negative port number 
has the same effect as setting `org.apache.felix.http.enable` to `false`. |
-| `org.osgi.service.http.port.secure` | The port used for servlets and 
resources available via HTTPS. The default is `8443`. See [port settings 
below](#http-port-settings) for additional information. A negative port number 
has the same effect as setting `org.apache.felix.https.enable` to `false`. |
-| `org.apache.felix.http.context_path` | The servlet Context Path to use for 
the Http Service. If this property is not configured it  defaults to "/". This 
must be a valid path starting with a slash and not  ending with a slash (unless 
it is the root context). |
-| `org.apache.felix.http.timeout` | Connection timeout in milliseconds. The 
default is `60000` (60 seconds). |
-| `org.apache.felix.http.session.timeout` | Allows for the specification of 
the Session life time as a number of minutes. This property serves the same 
purpose as the `session-timeout` element in a Web Application descriptor. The 
default is "0" (zero) for no timeout at all. |
-| `org.apache.felix.http.enable` | Flag to enable the use of HTTP. The default 
is `true`. |
-| `org.apache.felix.https.enable` | Flag to enable the user of HTTPS. The 
default is `false`. |
-| `org.apache.felix.https.keystore` | The name of the file containing the 
keystore. |
-| `org.apache.felix.https.keystore.password` | The password for the keystore. |
-| `org.apache.felix.https.keystore.key.password` | The password for the key in 
the keystore. |
-| `org.apache.felix.https.truststore` | The name of the file containing the 
truststore. |
-| `org.apache.felix.https.truststore.type` | The type of truststore to use. 
The default is `JKS`. |
-| `org.apache.felix.https.truststore.password` | The password for the 
truststore. |
-| `org.apache.felix.https.jetty.ciphersuites.excluded` | Configures 
comma-separated list of SSL cipher suites to *exclude*. Default is `null`, 
meaning that no cipher suite is excluded. |
-| `org.apache.felix.https.jetty.ciphersuites.included` | Configures 
comma-separated list of SSL cipher suites to *include*. Default is `null`, 
meaning that the default cipher suites are used. |
-| `org.apache.felix.https.jetty.protocols.excluded` | Configures 
comma-separated list of SSL protocols (e.g. SSLv3, TLSv1.0, TLSv1.1, TLSv1.2) 
to *exclude*. Default is `null`, meaning that no protocol is excluded. |
-| `org.apache.felix.https.jetty.protocols.included` | Configures 
comma-separated list of SSL protocols to *include*. Default is `null`, meaning 
that the default protocols are used. |
-| `org.apache.felix.https.clientcertificate` | Flag to determine if the HTTPS 
protocol requires, wants or does not use client certificates. Legal values are 
`needs`, `wants` and `none`. The default is `none`. |
-| `org.apache.felix.http.jetty.headerBufferSize` | Size of the buffer for 
request and response headers, in bytes. Default is 16 KB. |
-| `org.apache.felix.http.jetty.requestBufferSize` | Size of the buffer for 
requests not fitting the header buffer, in bytes. Default is 8 KB. |
-| `org.apache.felix.http.jetty.responseBufferSize` | Size of the buffer for 
responses, in bytes. Default is 24 KB. |
-| `org.apache.felix.http.jetty.maxFormSize` | The maximum size accepted for a 
form post, in bytes. Defaults to 200 KB. |
-| `org.apache.felix.http.mbeans` | If `true`, enables the MBean server 
functionality. The default is `false`. |
-| `org.apache.felix.http.jetty.sendServerHeader` | If `false`, the `Server` 
HTTP header is no longer included in responses. The default is `false`. |
-| `org.eclipse.jetty.servlet.SessionCookie` | Name of the cookie used to 
transport the Session ID. The default is `JSESSIONID`. |
-| `org.eclipse.jetty.servlet.SessionURL` | Name of the request parameter to 
transport the Session ID. The default is `jsessionid`. |
-| `org.eclipse.jetty.servlet.SessionDomain` | Domain to set on the session 
cookie. The default is `null`. |
-| `org.eclipse.jetty.servlet.SessionPath` | The path to set on the session 
cookie. The default is the configured session context path ("/"). |
-| `org.eclipse.jetty.servlet.MaxAge` | The maximum age value to set on the 
cookie. The default is "-1". |
-| `org.eclipse.jetty.UriComplianceMode` | The URI compliance mode to set. The 
default is 
[DEFAULT](https://eclipse.dev/jetty/javadoc/jetty-12/org/eclipse/jetty/http/UriCompliance.html#DEFAULT).
 See 
[documentation](https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-server-compliance-uri.)
 and [possible 
modes](https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java#L186C107-L186C113).
 |
-| `org.apache.felix.proxy.load.balancer.connection.enable` | Set this to 
`true` when running Felix HTTP behind a (offloading) proxy or load balancer 
which rewrites the requests. The default is `false`. |
-| `org.apache.felix.http.runtime.init.` | Properties starting with this prefix 
are added as service registration properties to the HttpServiceRuntime service. 
The prefix is removed for the property name. |
-| `org.apache.felix.jetty.gziphandler.enable` | Whether the server should use 
a server-wide gzip handler. Default is false. |
-| `org.apache.felix.jetty.gzip.minGzipSize` | The minimum response size to 
trigger dynamic compression. Default is GzipHandler.DEFAULT_MIN_GZIP_SIZE. |
-| `org.apache.felix.jetty.gzip.inflateBufferSize` | The size in bytes of the 
buffer to inflate compressed request, or <= 0 for no inflation. Default is -1. |
-| `org.apache.felix.jetty.gzip.syncFlush` | True if Deflater#SYNC_FLUSH should 
be used, else Deflater#NO_FLUSH will be used. Default is false. |
-| `org.apache.felix.jetty.gzip.includedMethods` | The additional http methods 
to include in compression. Default is none. |
-| `org.apache.felix.jetty.gzip.excludedMethods` | The additional http methods 
to exclude in compression. Default is none. |
-| `org.apache.felix.jetty.gzip.includedPaths` | The additional path specs to 
include. Inclusion takes precedence over exclusion. Default is none. |
-| `org.apache.felix.jetty.gzip.excludedPaths` | The additional path specs to 
exclude. Inclusion takes precedence over exclusion. Default is none. |
-| `org.apache.felix.jetty.gzip.includedMimeTypes` | The included mime types. 
Inclusion takes precedence over exclusion. Default is none. |
-| `org.apache.felix.jetty.gzip.excludedMimeTypes` | The excluded mime types. 
Inclusion takes precedence over exclusion. Default is none. |
-| `org.apache.felix.http2.enable` | Whether to enable HTTP/2. Default is 
false.  |
-| `org.apache.felix.jetty.http2.maxConcurrentStreams` | The max number of 
concurrent streams per connection. Default is 128. |
-| `org.apache.felix.jetty.http2.initialStreamRecvWindow` | The initial stream 
receive window (client to server). Default is 524288. |
-| `org.apache.felix.jetty.http2.initialSessionRecvWindow` | The initial 
session receive window (client to server). Default is 1048576. |
-| `org.apache.felix.jetty.alpn.protocols` | The ALPN protocols to consider. 
Default is h2, http/1.1. |
-| `org.apache.felix.jetty.alpn.defaultProtocol` | The default protocol when 
negotiation fails. Default is http/1.1. |
+| Property                                                 | Description       
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+|--|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `org.apache.felix.http.host`                             | Host name or IP 
Address of the interface to listen on. The default is `null` causing Jetty to 
listen on all interfaces.                                                       
                                                                                
                                                                                
                                                                                
                  [...]
+| `org.osgi.service.http.port`                             | The port used for 
servlets and resources available via HTTP. The default is `8080`. See [port 
settings below](#http-port-settings) for additional information. A negative 
port number has the same effect as setting `org.apache.felix.http.enable` to 
`false`.                                                                        
                                                                                
                         [...]
+| `org.osgi.service.http.port.secure`                      | The port used for 
servlets and resources available via HTTPS. The default is `8443`. See [port 
settings below](#http-port-settings) for additional information. A negative 
port number has the same effect as setting `org.apache.felix.https.enable` to 
`false`.                                                                        
                                                                                
                       [...]
+| `org.apache.felix.http.context_path`                     | The servlet 
Context Path to use for the Http Service. If this property is not configured it 
 defaults to "/". This must be a valid path starting with a slash and not  
ending with a slash (unless it is the root context).                            
                                                                                
                                                                                
                         [...]
+| `org.apache.felix.http.timeout`                          | Connection 
timeout in milliseconds. The default is `60000` (60 seconds).                   
                                                                                
                                                                                
                                                                                
                                                                                
                     [...]
+| `org.apache.felix.http.session.timeout`                  | Allows for the 
specification of the Session life time as a number of minutes. This property 
serves the same purpose as the `session-timeout` element in a Web Application 
descriptor. The default is "0" (zero) for no timeout at all.                    
                                                                                
                                                                                
                      [...]
+| `org.apache.felix.http.enable`                           | Flag to enable 
the use of HTTP. The default is `true`.                                         
                                                                                
                                                                                
                                                                                
                                                                                
                 [...]
+| `org.apache.felix.https.enable`                          | Flag to enable 
the user of HTTPS. The default is `false`.                                      
                                                                                
                                                                                
                                                                                
                                                                                
                 [...]
+| `org.apache.felix.https.keystore`                        | The name of the 
file containing the keystore.                                                   
                                                                                
                                                                                
                                                                                
                                                                                
                [...]
+| `org.apache.felix.https.keystore.password`               | The password for 
the keystore.                                                                   
                                                                                
                                                                                
                                                                                
                                                                                
               [...]
+| `org.apache.felix.https.keystore.key.password`           | The password for 
the key in the keystore.                                                        
                                                                                
                                                                                
                                                                                
                                                                                
               [...]
+| `org.apache.felix.https.truststore`                      | The name of the 
file containing the truststore.                                                 
                                                                                
                                                                                
                                                                                
                                                                                
                [...]
+| `org.apache.felix.https.truststore.type`                 | The type of 
truststore to use. The default is `JKS`.                                        
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.https.truststore.password`             | The password for 
the truststore.                                                                 
                                                                                
                                                                                
                                                                                
                                                                                
               [...]
+| `org.apache.felix.https.jetty.ciphersuites.excluded`     | Configures 
comma-separated list of SSL cipher suites to *exclude*. Default is `null`, 
meaning that no cipher suite is excluded.                                       
                                                                                
                                                                                
                                                                                
                          [...]
+| `org.apache.felix.https.jetty.ciphersuites.included`     | Configures 
comma-separated list of SSL cipher suites to *include*. Default is `null`, 
meaning that the default cipher suites are used.                                
                                                                                
                                                                                
                                                                                
                          [...]
+| `org.apache.felix.https.jetty.protocols.excluded`        | Configures 
comma-separated list of SSL protocols (e.g. SSLv3, TLSv1.0, TLSv1.1, TLSv1.2) 
to *exclude*. Default is `null`, meaning that no protocol is excluded.          
                                                                                
                                                                                
                                                                                
                       [...]
+| `org.apache.felix.https.jetty.protocols.included`        | Configures 
comma-separated list of SSL protocols to *include*. Default is `null`, meaning 
that the default protocols are used.                                            
                                                                                
                                                                                
                                                                                
                      [...]
+| `org.apache.felix.https.clientcertificate`               | Flag to determine 
if the HTTPS protocol requires, wants or does not use client certificates. 
Legal values are `needs`, `wants` and `none`. The default is `none`.            
                                                                                
                                                                                
                                                                                
                   [...]
+| `org.apache.felix.http.jetty.headerBufferSize`           | Size of the 
buffer for request and response headers, in bytes. Default is 16 KB.            
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.http.jetty.requestBufferSize`          | Size of the 
buffer for requests not fitting the header buffer, in bytes. Default is 8 KB.   
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.http.jetty.responseBufferSize`         | Size of the 
buffer for responses, in bytes. Default is 24 KB.                               
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.http.jetty.maxFormSize`                | The maximum size 
accepted for a form post, in bytes. Defaults to 200 KB.                         
                                                                                
                                                                                
                                                                                
                                                                                
               [...]
+| `org.apache.felix.http.mbeans`                           | If `true`, 
enables the MBean server functionality. The default is `false`.                 
                                                                                
                                                                                
                                                                                
                                                                                
                     [...]
+| `org.apache.felix.http.jetty.sendServerHeader`           | If `false`, the 
`Server` HTTP header is no longer included in responses. The default is 
`false`.                                                                        
                                                                                
                                                                                
                                                                                
                        [...]
+| `org.eclipse.jetty.servlet.SessionCookie`                | Name of the 
cookie used to transport the Session ID. The default is `JSESSIONID`.           
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.eclipse.jetty.servlet.SessionURL`                   | Name of the 
request parameter to transport the Session ID. The default is `jsessionid`.     
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.eclipse.jetty.servlet.SessionDomain`                | Domain to set on 
the session cookie. The default is `null`.                                      
                                                                                
                                                                                
                                                                                
                                                                                
               [...]
+| `org.eclipse.jetty.servlet.SessionPath`                  | The path to set 
on the session cookie. The default is the configured session context path 
("/").                                                                          
                                                                                
                                                                                
                                                                                
                      [...]
+| `org.eclipse.jetty.servlet.MaxAge`                       | The maximum age 
value to set on the cookie. The default is "-1".                                
                                                                                
                                                                                
                                                                                
                                                                                
                [...]
+| `org.eclipse.jetty.UriComplianceMode`                    | The URI 
compliance mode to set. The default is 
[DEFAULT](https://eclipse.dev/jetty/javadoc/jetty-12/org/eclipse/jetty/http/UriCompliance.html#DEFAULT).
 See 
[documentation](https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-server-compliance-uri.)
 and [possible 
modes](https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.jav
 [...]
+| `org.apache.felix.proxy.load.balancer.connection.enable` | Set this to 
`true` when running Felix HTTP behind a (offloading) proxy or load balancer 
which rewrites the requests. The default is `false`.                            
                                                                                
                                                                                
                                                                                
                        [...]
+| `org.apache.felix.http.runtime.init.`                    | Properties 
starting with this prefix are added as service registration properties to the 
HttpServiceRuntime service. The prefix is removed for the property name.        
                                                                                
                                                                                
                                                                                
                       [...]
+| `org.apache.felix.jetty.gziphandler.enable`              | Whether the 
server should use a server-wide gzip handler. Default is false.                 
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.jetty.gzip.minGzipSize`                | The minimum 
response size to trigger dynamic compression. Default is 
GzipHandler.DEFAULT_MIN_GZIP_SIZE.                                              
                                                                                
                                                                                
                                                                                
                                           [...]
+| `org.apache.felix.jetty.gzip.inflateBufferSize`          | The size in bytes 
of the buffer to inflate compressed request, or <= 0 for no inflation. Default 
is -1.                                                                          
                                                                                
                                                                                
                                                                                
               [...]
+| `org.apache.felix.jetty.gzip.syncFlush`                  | True if 
Deflater#SYNC_FLUSH should be used, else Deflater#NO_FLUSH will be used. 
Default is false.                                                               
                                                                                
                                                                                
                                                                                
                               [...]
+| `org.apache.felix.jetty.gzip.includedMethods`            | The additional 
http methods to include in compression. Default is none.                        
                                                                                
                                                                                
                                                                                
                                                                                
                 [...]
+| `org.apache.felix.jetty.gzip.excludedMethods`            | The additional 
http methods to exclude in compression. Default is none.                        
                                                                                
                                                                                
                                                                                
                                                                                
                 [...]
+| `org.apache.felix.jetty.gzip.includedPaths`              | The additional 
path specs to include. Inclusion takes precedence over exclusion. Default is 
none.                                                                           
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.jetty.gzip.excludedPaths`              | The additional 
path specs to exclude. Inclusion takes precedence over exclusion. Default is 
none.                                                                           
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.jetty.gzip.includedMimeTypes`          | The included mime 
types. Inclusion takes precedence over exclusion. Default is none.              
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+| `org.apache.felix.jetty.gzip.excludedMimeTypes`          | The excluded mime 
types. Inclusion takes precedence over exclusion. Default is none.              
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+| `org.apache.felix.http2.enable`                          | Whether to enable 
HTTP/2. Default is false.                                                       
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+| `org.apache.felix.jetty.http2.maxConcurrentStreams`      | The max number of 
concurrent streams per connection. Default is 128.                              
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+| `org.apache.felix.jetty.http2.initialStreamRecvWindow`   | The initial 
stream receive window (client to server). Default is 524288.                    
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.jetty.http2.initialSessionRecvWindow`  | The initial 
session receive window (client to server). Default is 1048576.                  
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.jetty.alpn.protocols`                  | The ALPN 
protocols to consider. Default is h2, http/1.1.                                 
                                                                                
                                                                                
                                                                                
                                                                                
                       [...]
+| `org.apache.felix.jetty.alpn.defaultProtocol`            | The default 
protocol when negotiation fails. Default is http/1.1.                           
                                                                                
                                                                                
                                                                                
                                                                                
                    [...]
+| `org.apache.felix.jakarta.ee10.websocket.enable`         | Enables Jakarta 
EE10 websocket support. Default is false. Jetty12 only.                         
                                                                                
                                                                                
                                                                                
                                                                                
                [...]
+| `org.apache.felix.jetty.ee10.websocket.enable`           | Enables Jetty 
EE10 websocket support. Default is false. Jetty12 only.                         
                                                                                
                                                                                
                                                                                
                                                                                
                  [...]
 
 ### Multiple Servers
 
diff --git a/http/jetty12/pom.xml b/http/jetty12/pom.xml
index 0267fdb641..450d6ed274 100644
--- a/http/jetty12/pom.xml
+++ b/http/jetty12/pom.xml
@@ -43,6 +43,9 @@
         <felix.java.version>17</felix.java.version>
         <jetty.version>12.0.8</jetty.version>
         <baseline.skip>true</baseline.skip>
+        <org.ops4j.pax.exam.version>4.13.3</org.ops4j.pax.exam.version>
+        <!-- To debug the pax process, override this with -D -->
+        <pax.vm.options>-Xmx512M</pax.vm.options>
     </properties>
 
     <build>
@@ -69,7 +72,12 @@
                                 // scan each of the artifacts to preserve the 
information found in any META-INF/services/* files
                                 project.artifacts.each() { artifact ->
 
-                                    if 
(artifact.getArtifactHandler().isAddedToClasspath() && 
!org.apache.maven.artifact.Artifact.SCOPE_TEST.equals( artifact.getScope() )) {
+                                    if 
(artifact.getArtifactHandler().isAddedToClasspath() && 
!org.apache.maven.artifact.Artifact.SCOPE_TEST.equals( artifact.getScope() )
+                                            && 
!"org.eclipse.jetty.websocket".equals(artifact.getGroupId()) // skip the 
optional websocket artifacts
+                                            && 
!"org.eclipse.jetty.ee10.websocket".equals(artifact.getGroupId()) // skip the 
optional websocket artifacts
+                                            && 
!"jetty-annotations".equals(artifact.getArtifactId()) // skip the transitive 
artifacts from the optional websocket artifacts
+                                            && 
!"jetty-plus".equals(artifact.getArtifactId())
+                                            && 
!"jetty-webapp".equals(artifact.getArtifactId())) {
                                         def jar;
                                         try {
                                             jar = new 
java.util.jar.JarFile(artifact.file)
@@ -164,9 +172,17 @@
                             org.osgi.service.servlet.runtime,
                             org.osgi.service.servlet.runtime.dto,
                             org.osgi.service.servlet.whiteboard,
-                            !org.eclipse.jetty,
-                            !org.eclipse.jetty.version,
-                            org.eclipse.jetty.*,
+                            org.eclipse.jetty.alpn.server,
+                            org.eclipse.jetty.http.*,
+                            org.eclipse.jetty.http2.*,
+                            org.eclipse.jetty.io.*,
+                            org.eclipse.jetty.jmx.*,
+                            org.eclipse.jetty.security.*,
+                            org.eclipse.jetty.session.*,
+                            org.eclipse.jetty.server.*,
+                            org.eclipse.jetty.util.*,
+                            !org.eclipse.jetty.ee10.websocket.*,
+                            org.eclipse.jetty.ee10.servlet.*,
                             org.apache.felix.http.jetty,
                             org.apache.felix.http.jakartawrappers,
                             org.apache.felix.http.javaxwrappers
@@ -180,9 +196,6 @@
                             org.apache.commons.*
                         </Conditional-Package>
                         <Import-Package>
-                            jakarta.annotation.*;resolution:=optional,
-                            jakarta.transaction.*;resolution:=optional,
-                            org.objectweb.asm.*;resolution:=optional,
                             sun.misc;resolution:=optional,
                             sun.nio.ch;resolution:=optional,
                             javax.imageio;resolution:=optional,
@@ -319,8 +332,257 @@
                             </instructions>
                         </configuration>
                     </execution>
+                    <execution>
+                        <id>with-jetty-ee10-websockets</id>
+                        <goals>
+                            <goal>bundle</goal>
+                        </goals>
+                        <configuration>
+                            <classifier>with-jetty-ee10-websockets</classifier>
+                            <instructions>
+                                
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                                
<Bundle-Version>${project.version}</Bundle-Version>
+                                <X-Jetty-Version>
+                                    ${jetty.version}
+                                </X-Jetty-Version>
+                                <Bundle-Activator>
+                                    
org.apache.felix.http.jetty.internal.JettyActivator
+                                </Bundle-Activator>
+                                <Export-Package>
+                                    org.osgi.service.http,
+                                    org.osgi.service.http.context,
+                                    org.osgi.service.http.runtime,
+                                    org.osgi.service.http.runtime.dto,
+                                    org.osgi.service.http.whiteboard,
+                                    org.osgi.service.servlet.context,
+                                    org.osgi.service.servlet.runtime,
+                                    org.osgi.service.servlet.runtime.dto,
+                                    org.osgi.service.servlet.whiteboard,
+                                    org.eclipse.jetty.alpn.server,
+                                    org.eclipse.jetty.http.*,
+                                    org.eclipse.jetty.http2.*,
+                                    org.eclipse.jetty.io.*,
+                                    org.eclipse.jetty.jmx.*,
+                                    org.eclipse.jetty.security.*,
+                                    org.eclipse.jetty.session.*,
+                                    org.eclipse.jetty.server.*,
+                                    org.eclipse.jetty.util.*,
+                                    org.eclipse.jetty.ee10.servlet.*,
+                                    
!org.eclipse.jetty.ee10.websocket.jakarta.*,
+                                    org.eclipse.jetty.ee10.websocket.*,
+                                    org.eclipse.jetty.websocket.*,
+                                    org.apache.felix.http.jetty,
+                                    org.apache.felix.http.jakartawrappers,
+                                    org.apache.felix.http.javaxwrappers
+                                </Export-Package>
+                                <Private-Package>
+                                    org.apache.felix.http.base.*,
+                                    org.apache.felix.http.jetty.*,
+                                    org.eclipse.jetty.version
+                                </Private-Package>
+                                <Conditional-Package>
+                                    org.apache.commons.*
+                                </Conditional-Package>
+                                <Import-Package>
+                                    
org.eclipse.jetty.client;resolution:=optional,
+                                    sun.misc;resolution:=optional,
+                                    sun.nio.ch;resolution:=optional,
+                                    javax.imageio;resolution:=optional,
+                                    javax.sql;resolution:=optional,
+                                    org.ietf.jgss;resolution:=optional,
+                                    
org.osgi.service.cm;resolution:=optional;version="[1.3,2)",
+                                    
org.osgi.service.event;resolution:=optional;version="[1.2,2)",
+                                    
org.osgi.service.log;resolution:=optional;version="[1.3,2)",
+                                    
org.osgi.service.metatype;resolution:=optional;version="[1.1,2)",
+                                    
org.osgi.service.useradmin;resolution:=optional;version="[1.1,2)",
+                                    
org.osgi.service.http;version="[1.2.1,1.3)",
+                                    
org.osgi.service.http.context;version="[1.1,1.2)",
+                                    
org.osgi.service.http.runtime;version="[1.1,1.2)",
+                                    
org.osgi.service.http.runtime.dto;version="[1.1,1.2)",
+                                    org.slf4j;version="[1.0,3.0)",
+                                    *
+                                </Import-Package>
+                                <DynamicImport-Package>
+                                    org.osgi.service.cm;version="[1.3,2)",
+                                    org.osgi.service.event;version="[1.2,2)",
+                                    org.osgi.service.log;version="[1.3,2)",
+                                    org.osgi.service.metatype;version="[1.4,2)"
+                                </DynamicImport-Package>
+                                <Provide-Capability>
+                                    
osgi.implementation;osgi.implementation="osgi.http";version:Version="1.1";
+                                    
uses:="javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard",
+                                    
osgi.implementation;osgi.implementation="osgi.http";version:Version="2.0";
+                                    
uses:="jakarta.servlet,jakarta.servlet.http,org.osgi.service.servlet.context,org.osgi.service.servlet.whiteboard",
+                                    
osgi.service;objectClass:List&lt;String&gt;="org.osgi.service.servlet.runtime.HttpServiceRuntime";
+                                    
uses:="org.osgi.service.servlet.runtime,org.osgi.service.servlet.runtime.dto",
+                                    
osgi.service;objectClass:List&lt;String&gt;="org.osgi.service.http.runtime.HttpServiceRuntime";
+                                    
uses:="org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto",
+                                    
osgi.service;objectClass:List&lt;String&gt;="org.osgi.service.http.HttpService";
+                                    uses:="org.osgi.service.http",
+                                    
osgi.serviceloader;osgi.serviceloader="org.eclipse.jetty.http.HttpFieldPreEncoder"
+                                </Provide-Capability>
+                                <Require-Capability>
+                                    
osgi.contract;filter:="(&amp;(osgi.contract=JavaServlet)(version=4.0))",
+                                    
osgi.contract;filter:="(&amp;(osgi.contract=JakartaServlet)(version=6.0))",
+                                    
osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional,
+                                    
osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional,
+                                    
osgi.serviceloader;filter:="(osgi.serviceloader=org.eclipse.jetty.http.HttpFieldPreEncoder)";resolution:=optional;cardinality:=multiple,
+                                    
osgi.serviceloader;filter:="(osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Server)";resolution:=optional;cardinality:=multiple
+                                </Require-Capability>
+                                <Include-Resource>
+                                    
{maven-resources},${project.build.directory}/serviceloader-resources
+                                </Include-Resource>
+                                <_removeheaders>
+                                    
Private-Package,Conditional-Package,Include-Resource
+                                </_removeheaders>
+                            </instructions>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>with-jakarta-ee10-websockets</id>
+                        <goals>
+                            <goal>bundle</goal>
+                        </goals>
+                        <configuration>
+                            
<classifier>with-jakarta-ee10-websockets</classifier>
+                            <instructions>
+                                
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                                
<Bundle-Version>${project.version}</Bundle-Version>
+                                <X-Jetty-Version>
+                                    ${jetty.version}
+                                </X-Jetty-Version>
+                                <Bundle-Activator>
+                                    
org.apache.felix.http.jetty.internal.JettyActivator
+                                </Bundle-Activator>
+                                <Export-Package>
+                                    org.osgi.service.http,
+                                    org.osgi.service.http.context,
+                                    org.osgi.service.http.runtime,
+                                    org.osgi.service.http.runtime.dto,
+                                    org.osgi.service.http.whiteboard,
+                                    org.osgi.service.servlet.context,
+                                    org.osgi.service.servlet.runtime,
+                                    org.osgi.service.servlet.runtime.dto,
+                                    org.osgi.service.servlet.whiteboard,
+                                    org.eclipse.jetty.alpn.server,
+                                    org.eclipse.jetty.http.*,
+                                    org.eclipse.jetty.http2.*,
+                                    org.eclipse.jetty.io.*,
+                                    org.eclipse.jetty.jmx.*,
+                                    org.eclipse.jetty.security.*,
+                                    org.eclipse.jetty.session.*,
+                                    org.eclipse.jetty.server.*,
+                                    org.eclipse.jetty.util.*,
+                                    !org.eclipse.jetty.ee10.websocket.server.*,
+                                    
!org.eclipse.jetty.ee10.websocket.servlet.*,
+                                    org.eclipse.jetty.ee10.websocket.jakarta.*,
+                                    org.eclipse.jetty.ee10.servlet.*,
+                                    org.eclipse.jetty.websocket.*,
+                                    org.apache.felix.http.jetty,
+                                    org.apache.felix.http.jakartawrappers,
+                                    org.apache.felix.http.javaxwrappers
+                                </Export-Package>
+                                <Private-Package>
+                                    org.apache.felix.http.base.*,
+                                    org.apache.felix.http.jetty.*,
+                                    org.eclipse.jetty.version
+                                </Private-Package>
+                                <Conditional-Package>
+                                    org.apache.commons.*
+                                </Conditional-Package>
+                                <Import-Package>
+                                    
org.eclipse.jetty.client;resolution:=optional,
+                                    sun.misc;resolution:=optional,
+                                    sun.nio.ch;resolution:=optional,
+                                    javax.imageio;resolution:=optional,
+                                    javax.sql;resolution:=optional,
+                                    org.ietf.jgss;resolution:=optional,
+                                    
org.osgi.service.cm;resolution:=optional;version="[1.3,2)",
+                                    
org.osgi.service.event;resolution:=optional;version="[1.2,2)",
+                                    
org.osgi.service.log;resolution:=optional;version="[1.3,2)",
+                                    
org.osgi.service.metatype;resolution:=optional;version="[1.1,2)",
+                                    
org.osgi.service.useradmin;resolution:=optional;version="[1.1,2)",
+                                    
org.osgi.service.http;version="[1.2.1,1.3)",
+                                    
org.osgi.service.http.context;version="[1.1,1.2)",
+                                    
org.osgi.service.http.runtime;version="[1.1,1.2)",
+                                    
org.osgi.service.http.runtime.dto;version="[1.1,1.2)",
+                                    org.slf4j;version="[1.0,3.0)",
+                                    *
+                                </Import-Package>
+                                <DynamicImport-Package>
+                                    org.osgi.service.cm;version="[1.3,2)",
+                                    org.osgi.service.event;version="[1.2,2)",
+                                    org.osgi.service.log;version="[1.3,2)",
+                                    org.osgi.service.metatype;version="[1.4,2)"
+                                </DynamicImport-Package>
+                                <Provide-Capability>
+                                    
osgi.implementation;osgi.implementation="osgi.http";version:Version="1.1";
+                                    
uses:="javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard",
+                                    
osgi.implementation;osgi.implementation="osgi.http";version:Version="2.0";
+                                    
uses:="jakarta.servlet,jakarta.servlet.http,org.osgi.service.servlet.context,org.osgi.service.servlet.whiteboard",
+                                    
osgi.service;objectClass:List&lt;String&gt;="org.osgi.service.servlet.runtime.HttpServiceRuntime";
+                                    
uses:="org.osgi.service.servlet.runtime,org.osgi.service.servlet.runtime.dto",
+                                    
osgi.service;objectClass:List&lt;String&gt;="org.osgi.service.http.runtime.HttpServiceRuntime";
+                                    
uses:="org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto",
+                                    
osgi.service;objectClass:List&lt;String&gt;="org.osgi.service.http.HttpService";
+                                    uses:="org.osgi.service.http",
+                                    
osgi.serviceloader;osgi.serviceloader="org.eclipse.jetty.http.HttpFieldPreEncoder"
+                                </Provide-Capability>
+                                <Require-Capability>
+                                    
osgi.contract;filter:="(&amp;(osgi.contract=JavaServlet)(version=4.0))",
+                                    
osgi.contract;filter:="(&amp;(osgi.contract=JakartaServlet)(version=6.0))",
+                                    
osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional,
+                                    
osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional,
+                                    
osgi.serviceloader;filter:="(osgi.serviceloader=org.eclipse.jetty.http.HttpFieldPreEncoder)";resolution:=optional;cardinality:=multiple,
+                                    
osgi.serviceloader;filter:="(osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Server)";resolution:=optional;cardinality:=multiple
+                                </Require-Capability>
+                                <Include-Resource>
+                                    
{maven-resources},${project.build.directory}/serviceloader-resources
+                                </Include-Resource>
+                                <_removeheaders>
+                                    
Private-Package,Conditional-Package,Include-Resource
+                                </_removeheaders>
+                            </instructions>
+                        </configuration>
+                    </execution>
                 </executions>
             </plugin>
+
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                </configuration>
+            </plugin>
+            <!-- plugins for paxexam integration tests -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>integration-test</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>integration-test</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>verify</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                    <systemPropertyVariables>
+                        <jetty.version>${jetty.version}</jetty.version>
+                        
<bundle.filename>${basedir}/target/${project.build.finalName}.jar</bundle.filename>
+                        <pax.vm.options>${pax.vm.options}</pax.vm.options>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 
@@ -366,11 +628,6 @@
             <artifactId>jetty-ee10-servlet</artifactId>
             <version>${jetty.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty.ee10.websocket</groupId>
-            <artifactId>jetty-ee10-websocket-jetty-server</artifactId>
-            <version>${jetty.version}</version>
-        </dependency>
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-server</artifactId>
@@ -417,19 +674,27 @@
             <version>${jetty.version}</version>
         </dependency>
         <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-session</artifactId>
+            <groupId>org.eclipse.jetty.ee10.websocket</groupId>
+            <artifactId>jetty-ee10-websocket-jakarta-server</artifactId>
+            <version>${jetty.version}</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.ee10.websocket</groupId>
+            <artifactId>jetty-ee10-websocket-jetty-server</artifactId>
             <version>${jetty.version}</version>
+            <optional>true</optional>
         </dependency>
         <dependency>
-           <groupId>org.eclipse.jetty.websocket</groupId>
-           <artifactId>jetty-websocket-jetty-api</artifactId>
-           <version>${jetty.version}</version>
+            <groupId>org.eclipse.jetty.websocket</groupId>
+            <artifactId>jetty-websocket-jetty-server</artifactId>
+            <version>${jetty.version}</version>
+            <optional>true</optional>
         </dependency>
         <dependency>
-           <groupId>org.eclipse.jetty.websocket</groupId>
-           <artifactId>jetty-websocket-jetty-server</artifactId>
-           <version>${jetty.version}</version>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-session</artifactId>
+            <version>${jetty.version}</version>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
@@ -463,6 +728,7 @@
             <groupId>commons-fileupload</groupId>
             <artifactId>commons-fileupload</artifactId>
             <version>1.5</version>
+            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>commons-io</groupId>
@@ -488,5 +754,76 @@
             <version>1.3.0</version>
             <scope>test</scope>
         </dependency>
+
+        <!-- an OSGi framework -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <version>7.0.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+            <version>1</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Pax Exam -->
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam</artifactId>
+            <version>${org.ops4j.pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-cm</artifactId>
+            <version>${org.ops4j.pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-forked</artifactId>
+            <version>${org.ops4j.pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit4</artifactId>
+            <version>${org.ops4j.pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-link-mvn</artifactId>
+            <version>${org.ops4j.pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-client</artifactId>
+            <version>${jetty.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.websocket</groupId>
+            <artifactId>jetty-websocket-jetty-client</artifactId>
+            <version>${jetty.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <version>4.2.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>2.0.13</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
 
b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
index 917e334132..f12c62641c 100644
--- 
a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
+++ 
b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
@@ -489,6 +489,17 @@ class ConfigMetaTypeProvider implements MetaTypeProvider
                 "The format of the request log entries. Only relevant if 
'Enable SLF4J Request Logging' is checked. Valid placeholders are described in 
https://www.eclipse.org/jetty/documentation/jetty-11/operations-guide/index.html#og-module-requestlog";,
                 CustomRequestLog.NCSA_FORMAT,
                 
bundle.getBundleContext().getProperty(JettyConfig.FELIX_HTTP_REQUEST_LOG_FORMAT)));
+
+        adList.add(new 
AttributeDefinitionImpl(JettyConfig.FELIX_JAKARTA_EE10_WEBSOCKET_ENABLE,
+                "Enable Jakarta EE10 standard WebSocket support",
+                "Whether to enable jakarta EE10 standard WebSocket support. 
Default is false.",
+                false,
+                
bundle.getBundleContext().getProperty(JettyConfig.FELIX_JAKARTA_EE10_WEBSOCKET_ENABLE)));
+        adList.add(new 
AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_EE10_WEBSOCKET_ENABLE,
+                "Enable Jetty EE10 specific WebSocket support",
+                "Whether to enable jetty EE10 specific WebSocket support. 
Default is false.",
+                false,
+                
bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_EE10_WEBSOCKET_ENABLE)));
         return new ObjectClassDefinition()
         {
 
diff --git 
a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
 
b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
index 05e6c7f941..cb61ae0b8b 100644
--- 
a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
+++ 
b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
@@ -271,6 +271,12 @@ public final class JettyConfig
     /** Felix specific property to specify the default protocol when 
negotiation fails  */
     public static final String FELIX_JETTY_ALPN_DEFAULT_PROTOCOL = 
"org.apache.felix.jetty.alpn.defaultProtocol";
 
+    /** Felix specific property to control whether to enable the standard 
jakarta.websocket EE10 APIs provided by Jakarta WebSocket 2.1 */
+    public static final String FELIX_JAKARTA_EE10_WEBSOCKET_ENABLE = 
"org.apache.felix.jakarta.ee10.websocket.enable";
+
+    /** Felix specific property to control whether to enable they 
Jetty-specific EE10 WebSocket APIs */
+    public static final String FELIX_JETTY_EE10_WEBSOCKET_ENABLE = 
"org.apache.felix.jetty.ee10.websocket.enable";
+
     private static String validateContextPath(String ctxPath)
     {
         // undefined, empty, or root context path
@@ -677,6 +683,22 @@ public final class JettyConfig
         return getLongProperty(FELIX_JETTY_STOP_TIMEOUT, -1l);
     }
 
+    /**
+     * Returns <code>true</code> if jakarta EE10 websocket is configured to be 
used (
+     * {@link #FELIX_JAKARTA_EE10_WEBSOCKET_ENABLE})
+     */
+    public boolean isUseJakartaEE10Websocket() {
+        return getBooleanProperty(FELIX_JAKARTA_EE10_WEBSOCKET_ENABLE, false);
+    }
+
+    /**
+     * Returns <code>true</code> if jetty websocket is configured to be used (
+     * {@link #FELIX_JETTY_EE10_WEBSOCKET_ENABLE})
+     */
+    public boolean isUseJettyEE10Websocket() {
+        return getBooleanProperty(FELIX_JETTY_EE10_WEBSOCKET_ENABLE, false);
+    }
+
     public void reset()
     {
         update(null);
diff --git 
a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
 
b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
index f75a6af3d8..ea2eca3d32 100644
--- 
a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
+++ 
b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
@@ -253,7 +253,7 @@ public final class JettyService
             loginService.setUserStore(new UserStore());
             this.server.addBean(loginService);
 
-            ServletContextHandler context = new 
ServletContextHandler(this.config.getContextPath(),
+            ServletContextHandler context = new 
ServletContextHandler(this.config.getContextPath(),                    
                     ServletContextHandler.SESSIONS);
 
             this.parent = new ContextHandlerCollection(context);
@@ -309,8 +309,18 @@ public final class JettyService
                 this.server.setStopTimeout(this.config.getStopTimeout());
             }
 
+            if (this.config.isUseJettyEE10Websocket()) {
+                maybeInitializeJettyEE10Websocket(context);
+            }
+
+            if (this.config.isUseJakartaEE10Websocket()) {
+                maybeInitializeJakartaEE10Websocket(context);
+            }
+
             this.server.start();
 
+            maybeStoreWebSocketContainerAttributes(context);
+
             // session id manager is only available after server is started
             
context.getSessionHandler().getSessionIdManager().getSessionHouseKeeper().setIntervalSec(
                     
this.config.getLongProperty(JettyConfig.FELIX_JETTY_SESSION_SCAVENGING_INTERVAL,
@@ -478,6 +488,80 @@ public final class JettyService
         return startConnector(connector);
     }
 
+    /**
+     * Initialize the jakarta EE10 websocket support for the servlet context 
handler.
+     * If the optional initializer class is not present then a warning will be 
logged.
+     *
+     * @param handler the sevlet context handler to initialize
+     */
+    private void maybeInitializeJakartaEE10Websocket(ServletContextHandler 
handler) {
+        if 
(isClassNameVisible("org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer"))
 {
+            // Ensure that JakartaWebSocketServletContainerInitializer is 
initialized,
+            // to setup the ServerContainer for this web application context.
+            
org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer.configure(handler,
 null);
+            SystemLogger.LOGGER.info("Jakarta WebSocket EE10 servlet container 
initialized");
+        } else {
+            SystemLogger.LOGGER.warn("Failed to initialize jakarta EE10 
standard websocket support since the initializer class was not found. "
+                    + "Check if the jetty-ee10-websocket-jakarta-server bundle 
is deployed.");
+        }
+    }
+
+    /**
+     * Initialize the jetty EE10 websocket support for the servlet context 
handler.
+     * If the optional initializer class is not present then a warning will be 
logged.
+     *
+     * @param handler the sevlet context handler to initialize
+     */
+    private void maybeInitializeJettyEE10Websocket(ServletContextHandler 
handler) {
+        if 
(isClassNameVisible("org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketServletContainerInitializer"))
 {
+            // Ensure that JettyWebSocketServletContainerInitializer is 
initialized,
+            // to setup the JettyWebSocketServerContainer for this web 
application context.
+            
org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketServletContainerInitializer.configure(handler,
 null);
+            SystemLogger.LOGGER.info("Jetty WebSocket EE10 servlet container 
initialized");
+        } else {
+            SystemLogger.LOGGER.warn("Failed to initialize jetty EE10 specific 
websocket support since the initializer class was not found. "
+                    + "Check if the jetty-ee10-websocket-jetty-server bundle 
is deployed.");
+        }
+    }
+
+    /**
+     * Based on the configuration, store the WebSocket container attributes 
for the shared servlet context.
+     *
+     * @param context the context
+     */
+    private void maybeStoreWebSocketContainerAttributes(ServletContextHandler 
context) {
+        // when the server is started, retrieve the container attribute and
+        // set it on the shared servlet context once available
+        if (this.config.isUseJettyEE10Websocket() &&
+                
isClassNameVisible("org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketServletContainerInitializer"))
 {
+            String attribute = 
org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer.JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE;
+            this.controller.setAttributeSharedServletContext(attribute, 
context.getServletContext().getAttribute(attribute));
+        }
+        if (this.config.isUseJakartaEE10Websocket() &&
+                
isClassNameVisible("org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer"))
 {
+            String attribute = 
org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer.ATTR_JAKARTA_SERVER_CONTAINER;
+            this.controller.setAttributeSharedServletContext(attribute, 
context.getServletContext().getAttribute(attribute));
+        }
+    }
+
+    /**
+     * Checks if an optional class name is visible to the bundle classloader
+     *
+     * @param className the class name to check
+     * @return true if the class is visible, false otherwise
+     */
+    private boolean isClassNameVisible(String className) {
+        boolean visible;
+        try {
+            // check if the class is visible to our classloader
+            getClass().getClassLoader().loadClass(className);
+            visible = true;
+        } catch (ClassNotFoundException e) {
+            visible = false;
+        }
+        return visible;
+    }
+
     private void configureSslContextFactory(final SslContextFactory.Server 
connector)
     {
         if (this.config.getKeystoreType() != null)
diff --git 
a/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/AbstractJettyTestSupport.java
 
b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/AbstractJettyTestSupport.java
new file mode 100644
index 0000000000..5da84b6d4f
--- /dev/null
+++ 
b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/AbstractJettyTestSupport.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.it;
+
+import static org.ops4j.pax.exam.CoreOptions.bundle;
+import static org.ops4j.pax.exam.CoreOptions.composite;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.keepCaches;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.CoreOptions.vmOption;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.UUID;
+
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.options.ModifiableCompositeOption;
+import org.ops4j.pax.exam.options.OptionalCompositeOption;
+import org.ops4j.pax.exam.options.SystemPropertyOption;
+import org.ops4j.pax.exam.options.UrlProvisionOption;
+import org.ops4j.pax.exam.options.extra.VMOption;
+import org.ops4j.pax.exam.util.PathUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractJettyTestSupport {
+
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final String workingDirectory = 
String.format("%s/target/paxexam/%s/%s", PathUtils.getBaseDir(), 
getClass().getSimpleName(), UUID.randomUUID());
+
+    /**
+     * Provides a random path for a working directory below Maven's build 
target directory.
+     *
+     * @return the absolute path for working directory
+     */
+    protected String workingDirectory() {
+        return workingDirectory;
+    }
+
+    @Configuration
+    public Option[] configuration() throws IOException {
+        final String vmOpt = System.getProperty("pax.vm.options");
+        VMOption vmOption = null;
+        if (vmOpt != null && !vmOpt.isEmpty()) {
+            vmOption = new VMOption(vmOpt);
+        }
+
+        final int httpPort = findFreePort();
+
+        return options(
+                composite(
+                        when(vmOption != null).useOptions(vmOption),
+                        failOnUnresolvedBundles(),
+                        keepCaches(),
+                        localMavenRepo(),
+                        CoreOptions.workingDirectory(workingDirectory()),
+                        optionalRemoteDebug(),
+                        
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.http.servlet-api").version("3.0.0"),
+                        testBundle("bundle.filename"),
+                        junitBundles(),
+                        awaitility(),
+
+                        config(),
+                        felixHttpConfig(httpPort)
+                ).add(
+                        additionalOptions()
+                )
+        );
+    }
+
+    public static ModifiableCompositeOption awaitility() {
+        return composite(
+                
mavenBundle().groupId("org.awaitility").artifactId("awaitility").version("4.2.1"),
+                
mavenBundle().groupId("org.hamcrest").artifactId("hamcrest").version("2.2")
+        );
+    }
+
+    public static ModifiableCompositeOption config() {
+        return composite(
+                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.configadmin").version("1.9.26")
+        );
+    }
+
+    protected Option felixHttpConfig(final int httpPort) {
+        return newConfiguration("org.apache.felix.http")
+                .put("org.osgi.service.http.port", httpPort)
+                .asOption();
+    }
+
+    protected Option[] additionalOptions() throws IOException { // NOSONAR
+        return new Option[]{};
+    }
+
+    /**
+     * Finds a free local port.
+     *
+     * @return the free local port
+     */
+    public static int findFreePort() {
+        try (ServerSocket serverSocket = new ServerSocket(0)) {
+            return serverSocket.getLocalPort();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Provides an option to set the System property {@code 
pax.exam.osgi.unresolved.fail} to {@code "true"}.
+     *
+     * @return the property option
+     */
+    public static SystemPropertyOption failOnUnresolvedBundles() {
+        return systemProperty("pax.exam.osgi.unresolved.fail").value("true");
+    }
+
+    /**
+     * Reads the System property {@code maven.repo.local} and provides an 
option to set the System property {@code org.ops4j.pax.url.mvn.localRepository} 
when former is not empty.
+     *
+     * @return the property option
+     */
+    public static OptionalCompositeOption localMavenRepo() {
+        final String localRepository = System.getProperty("maven.repo.local", 
""); // PAXEXAM-543
+        return when(!localRepository.isBlank()).useOptions(
+                
systemProperty("org.ops4j.pax.url.mvn.localRepository").value(localRepository)
+        );
+    }
+
+    /**
+     * Reads the pathname of the test bundle from the given System property 
and provides a provisioning option.
+     *
+     * @param systemProperty the System property which contains the pathname 
of the test bundle
+     * @return the provisioning option
+     */
+    public static UrlProvisionOption testBundle(final String systemProperty) {
+        final String pathname = System.getProperty(systemProperty);
+        final File file = new File(pathname);
+        return bundle(file.toURI().toString());
+    }
+
+    /**
+     * Optionally configure remote debugging on the port supplied by the 
"debugPort"
+     * system property.
+     */
+    protected ModifiableCompositeOption optionalRemoteDebug() {
+        VMOption option = null;
+        String property = System.getProperty("debugPort");
+        if (property != null) {
+            option = 
vmOption(String.format("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%s",
 property));
+        }
+        return composite(option);
+    }
+
+    public static ModifiableCompositeOption spifly() {
+        return composite(
+                
mavenBundle().groupId("org.apache.aries.spifly").artifactId("org.apache.aries.spifly.dynamic.bundle").version("1.3.7"),
+                
mavenBundle().groupId("org.ow2.asm").artifactId("asm-analysis").version("9.7"),
+                
mavenBundle().groupId("org.ow2.asm").artifactId("asm-commons").version("9.7"),
+                
mavenBundle().groupId("org.ow2.asm").artifactId("asm-tree").version("9.7"),
+                
mavenBundle().groupId("org.ow2.asm").artifactId("asm-util").version("9.7"),
+                
mavenBundle().groupId("org.ow2.asm").artifactId("asm").version("9.7")
+        );
+    }
+}
\ No newline at end of file
diff --git 
a/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JakartaEE10SpecificWebsocketIT.java
 
b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JakartaEE10SpecificWebsocketIT.java
new file mode 100644
index 0000000000..1f19a4ad39
--- /dev/null
+++ 
b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JakartaEE10SpecificWebsocketIT.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.io.IOException;
+import java.net.URI;
+import java.time.Duration;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.inject.Inject;
+import jakarta.servlet.Servlet;
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.websocket.ClientEndpoint;
+import jakarta.websocket.DeploymentException;
+import jakarta.websocket.OnMessage;
+import jakarta.websocket.OnOpen;
+import jakarta.websocket.Session;
+import jakarta.websocket.WebSocketContainer;
+import jakarta.websocket.server.ServerContainer;
+import jakarta.websocket.server.ServerEndpoint;
+
+import org.awaitility.Awaitility;
+import 
org.eclipse.jetty.ee10.websocket.jakarta.client.JakartaWebSocketClientContainerProvider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants;
+
+/**
+ *
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class JakartaEE10SpecificWebsocketIT extends AbstractJettyTestSupport {
+
+    @Inject
+    protected BundleContext bundleContext;
+
+    @Override
+    protected Option[] additionalOptions() throws IOException {
+        String jettyVersion = System.getProperty("jetty.version", "12.0.8");
+        return new Option[]{
+                spifly(),
+
+                // bundles for the server side
+                
mavenBundle().groupId("jakarta.websocket").artifactId("jakarta.websocket-api").version("2.1.1"),
+                
mavenBundle().groupId("jakarta.websocket").artifactId("jakarta.websocket-client-api").version("2.1.1"),
+                
mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-client").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-client").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-webapp").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-core-client").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-core-common").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-core-server").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.ee10.websocket").artifactId("jetty-ee10-websocket-jakarta-client").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.ee10.websocket").artifactId("jetty-ee10-websocket-jakarta-common").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.ee10.websocket").artifactId("jetty-ee10-websocket-jakarta-server").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.ee10.websocket").artifactId("jetty-ee10-websocket-servlet").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-xml").version(jettyVersion)
+        };
+    }
+
+    @Override
+    protected Option felixHttpConfig(int httpPort) {
+        return newConfiguration("org.apache.felix.http")
+                .put("org.osgi.service.http.port", httpPort)
+                .put("org.apache.felix.jakarta.ee10.websocket.enable", true)
+                .asOption();
+    }
+
+    @Test
+    public void testWebSocketConversation() throws Exception {
+        assertNotNull(bundleContext);
+        bundleContext.registerService(Servlet.class, new 
MyWebSocketInitServlet(), new Hashtable<>(Map.of(
+                HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, 
"/mywebsocket1"
+        )));
+
+        WebSocketContainer container = 
JakartaWebSocketClientContainerProvider.getContainer(null);
+
+        // Create client side endpoint
+        MyClientWebSocket clientEndpoint = new MyClientWebSocket();
+
+        // Attempt Connect
+        Object value = 
bundleContext.getServiceReference(HttpService.class).getProperty("org.osgi.service.http.port");
+        int httpPort = Integer.parseInt((String) value);
+        URI destUri = new URI(String.format("ws://localhost:%d/mywebsocket1", 
httpPort));
+        try (Session session = container.connectToServer(clientEndpoint, 
destUri)) {
+
+            // send a message from the client to the server
+            clientEndpoint.sendMessage("Hello WebSocket");
+
+            // wait for the async response from the server
+            Awaitility.await("waitForResponse")
+                    .atMost(Duration.ofSeconds(30))
+                    .pollDelay(Duration.ofMillis(200))
+                    .until(() -> clientEndpoint.getLastMessage() != null);
+            assertEquals("Hello WebSocket", clientEndpoint.getLastMessage());
+        }
+    }
+
+    /**
+     * A servlet that declares the websocket during init
+     */
+    private static final class MyWebSocketInitServlet extends HttpServlet {
+        private static final long serialVersionUID = -6893620059263229183L;
+
+        @Override
+        public void init(ServletConfig config) throws ServletException {
+            super.init(config);
+
+            //  Lookup the ServletContext for the context path where the 
websocket server is attached.
+            ServletContext servletContext = config.getServletContext();
+
+            // Retrieve the ServerContainer from the ServletContext attributes.
+            ServerContainer container = (ServerContainer) 
servletContext.getAttribute(ServerContainer.class.getName());
+
+            // Configure the ServerContainer.
+            container.setDefaultMaxTextMessageBufferSize(128 * 1024);
+
+            // Simple registration of your WebSocket endpoints.
+            try {
+                container.addEndpoint(MyServerWebSocket.class);
+            } catch (DeploymentException e) {
+                throw new ServletException(e);
+            }
+        }
+    }
+
+    /**
+     * WebSocket handler for the client side
+     */
+    @ClientEndpoint
+    public static class MyClientWebSocket {
+        private Session session;
+        private String lastMessage;
+
+        public String getLastMessage() {
+            return lastMessage;
+        }
+
+        @OnOpen
+        public void onConnect(Session session) {
+            this.session = session;
+        }
+
+        /**
+         * Send a message to the server side
+         *
+         * @param msg the message to send
+         */
+        public void sendMessage(String msg) throws IOException {
+            this.session.getBasicRemote().sendText(msg);
+        }
+
+        /**
+         * Receive a message from the server side
+         *
+         * @param msg the message
+         */
+        @OnMessage
+        public void onMessage(String msg) {
+            lastMessage = msg;
+        }
+    }
+
+    /**
+     * WebSocket handler for the server side
+     */
+    @ServerEndpoint(value = "/mywebsocket1")
+    public static class MyServerWebSocket {
+        /**
+         * Receive message sent from the client
+         *
+         * @param session the session
+         * @param message the message
+         */
+        @OnMessage
+        public void onText(Session session, String message) throws IOException 
{
+            // echo a response back to the client 
+            session.getBasicRemote().sendText(message);
+        }
+    }
+
+}
\ No newline at end of file
diff --git 
a/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JettyEE10SpecificWebsocketIT.java
 
b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JettyEE10SpecificWebsocketIT.java
new file mode 100644
index 0000000000..72fe774fcf
--- /dev/null
+++ 
b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JettyEE10SpecificWebsocketIT.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.io.IOException;
+import java.net.URI;
+import java.time.Duration;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+import jakarta.servlet.Servlet;
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+
+import org.awaitility.Awaitility;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer;
+import org.eclipse.jetty.websocket.api.Callback;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants;
+
+/**
+ *
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class JettyEE10SpecificWebsocketIT extends AbstractJettyTestSupport {
+
+    @Inject
+    protected BundleContext bundleContext;
+
+    @Override
+    protected Option[] additionalOptions() throws IOException {
+        String jettyVersion = System.getProperty("jetty.version", "12.0.8");
+        return new Option[] {
+                spifly(),
+
+                // bundles for the server side
+                
mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-webapp").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-core-common").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-core-server").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-jetty-api").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-jetty-common").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-jetty-server").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.ee10.websocket").artifactId("jetty-ee10-websocket-servlet").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.ee10.websocket").artifactId("jetty-ee10-websocket-jetty-server").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-xml").version(jettyVersion),
+
+                // additional bundles for the client side
+                
mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-client").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-client").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-core-client").version(jettyVersion),
+                
mavenBundle().groupId("org.eclipse.jetty.websocket").artifactId("jetty-websocket-jetty-client").version(jettyVersion)
+        };
+    }
+
+    @Override
+    protected Option felixHttpConfig(int httpPort) {
+        return newConfiguration("org.apache.felix.http")
+                .put("org.osgi.service.http.port", httpPort)
+                .put("org.apache.felix.jetty.ee10.websocket.enable", true)
+                .asOption();
+    }
+
+
+    @Test
+    public void testWebSocketConversation() throws Exception {
+        assertNotNull(bundleContext);
+        bundleContext.registerService(Servlet.class, new 
MyWebSocketInitServlet(), new Hashtable<>(Map.of(
+                HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, 
"/mywebsocket1"
+        )));
+
+        HttpClientTransportOverHTTP transport = new 
HttpClientTransportOverHTTP();
+        HttpClient httpClient = new 
org.eclipse.jetty.client.HttpClient(transport);
+        WebSocketClient webSocketClient = new WebSocketClient(httpClient);
+        webSocketClient.start();
+
+        Object value = 
bundleContext.getServiceReference(HttpService.class).getProperty("org.osgi.service.http.port");
+        int httpPort = Integer.parseInt((String)value);
+        URI destUri = new URI(String.format("ws://localhost:%d/mywebsocket1", 
httpPort));
+
+        MyClientWebSocket clientWebSocket = new MyClientWebSocket();
+        ClientUpgradeRequest request = new ClientUpgradeRequest();
+        CompletableFuture<Session> future = 
webSocketClient.connect(clientWebSocket, destUri, request);
+        Session session = future.get();
+        assertNotNull(session);
+
+        // send a message from the client to the server
+        clientWebSocket.sendMessage("Hello WebSocket");
+
+        // wait for the async response from the server
+        Awaitility.await("waitForResponse")
+                .atMost(Duration.ofSeconds(30))
+                .pollDelay(Duration.ofMillis(200))
+                .until(() -> clientWebSocket.getLastMessage() != null);
+        assertEquals("Hello WebSocket", clientWebSocket.getLastMessage());
+    }
+
+    /**
+     * A servlet that declares the websocket during init
+     */
+    private static final class MyWebSocketInitServlet extends HttpServlet {
+        private static final long serialVersionUID = -6893620059263229183L;
+
+        @Override
+        public void init(ServletConfig config) throws ServletException {
+            super.init(config);
+
+            //  Lookup the ServletContext for the context path where the 
websocket server is attached.
+            ServletContext servletContext = config.getServletContext();
+
+            // Retrieve the JettyWebSocketServerContainer.
+            JettyWebSocketServerContainer container = 
JettyWebSocketServerContainer.getContainer(servletContext);
+            assertNotNull(container);
+            container.addMapping("/mywebsocket1", (upgradeRequest, 
upgradeResponse) -> new MyServerWebSocket());
+        }
+    }
+
+    /**
+     * WebSocket handler for the client side
+     */
+    @WebSocket()
+    public static class MyClientWebSocket {
+        private Session session;
+        private String lastMessage;
+
+        public String getLastMessage() {
+            return lastMessage;
+        }
+
+        @OnWebSocketOpen
+        public void onConnect(Session session) {
+            this.session = session;
+        }
+
+        /**
+         * Send a message to the server side
+         * @param msg the message to send
+         */
+        public void sendMessage(String msg) {
+            this.session.sendText(msg, Callback.NOOP);
+        }
+
+        /**
+         * Receive a message from the server side
+         * @param msg the message
+         */
+        @OnWebSocketMessage
+        public void onMessage(String msg) {
+            lastMessage = msg;
+        }
+    }
+
+    /**
+     * WebSocket handler for the server side
+     */
+    @WebSocket()
+    public static class MyServerWebSocket {
+        /**
+         * Receive message sent from the client
+         *
+         * @param session the session
+         * @param message the message
+         */
+        @OnWebSocketMessage
+        public void onText(Session session, String message) {
+            // echo a response back to the client 
+            session.sendText(message, Callback.NOOP);
+        }
+    }
+
+}
\ No newline at end of file
diff --git 
a/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/MissingWebsocketDependenciesIT.java
 
b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/MissingWebsocketDependenciesIT.java
new file mode 100644
index 0000000000..7bb6bf43fc
--- /dev/null
+++ 
b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/MissingWebsocketDependenciesIT.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.it;
+
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import org.awaitility.Awaitility;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.BundleContext;
+
+/**
+ *
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class MissingWebsocketDependenciesIT extends AbstractJettyTestSupport {
+
+    @Inject
+    protected BundleContext bundleContext;
+
+    @Override
+    protected Option felixHttpConfig(int httpPort) {
+        return newConfiguration("org.apache.felix.http")
+                .put("org.osgi.service.http.port", httpPort)
+                .put("org.apache.felix.jetty.ee10.websocket.enable", true)
+                .put("org.apache.felix.jakarta.ee10.websocket.enable", true)
+                .asOption();
+    }
+
+    @Test
+    public void testMissingDepencencyWarningLogs() throws Exception {
+        // should have warnings in the log file output
+        File logFile = new 
File("target/failsafe-reports/org.apache.felix.http.jetty.it.MissingWebsocketDependenciesIT-output.txt");
+        assertTrue(logFile.exists());
+
+        // wait for the log buffer to be written to the file
+        Awaitility.await("waitForLogs")
+                .atMost(Duration.ofSeconds(50))
+                .pollDelay(Duration.ofMillis(200))
+                .until(() -> containsString(logFile, 
"org.apache.felix.http.jetty12[org.apache.felix.http]"));
+
+        assertTrue(containsString(logFile, 
"org.apache.felix.http.jetty12[org.apache.felix.http] : Failed to "
+                + "initialize jetty EE10 specific websocket support since the 
initializer class was not found. "
+                + "Check if the jetty-ee10-websocket-jetty-server bundle is 
deployed."));
+        assertTrue(containsString(logFile, 
"org.apache.felix.http.jetty12[org.apache.felix.http] : Failed to "
+                + "initialize jakarta EE10 standard websocket support since 
the initializer class was not found. "
+                + "Check if the jetty-ee10-websocket-jakarta-server bundle is 
deployed."));
+    }
+
+    /**
+     * Checks if the text is present in the file
+     *
+     * @param file     the file to check
+     * @param expected the text to look for
+     * @return true if the text was found, false otherwise
+     */
+    private boolean containsString(File file, String expected) throws 
IOException {
+        try (Stream<String> stream = Files.lines(file.toPath())) {
+            return stream.anyMatch(line -> line.contains(expected));
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/http/pom.xml b/http/pom.xml
index 8684e405ff..97f623fe7b 100644
--- a/http/pom.xml
+++ b/http/pom.xml
@@ -44,6 +44,7 @@
         <module>inventoryprinter</module>
         <module>itest</module>
         <module>jetty</module>
+        <module>jetty12</module>
         <module>proxy</module>
         <module>samples/whiteboard</module>
         <module>servlet-api</module>
diff --git a/http/samples/whiteboard/pom.xml b/http/samples/whiteboard/pom.xml
index 4f40bff0ce..4f7b63d825 100644
--- a/http/samples/whiteboard/pom.xml
+++ b/http/samples/whiteboard/pom.xml
@@ -32,16 +32,16 @@
     <version>3.0.0-SNAPSHOT</version>
     <packaging>bundle</packaging>
 
-    <properties>
-        <jetty.version>12.0.8</jetty.version>
-    </properties>
-
     <scm>
         
<connection>scm:git:https://github.com/apache/felix-dev.git</connection>
         
<developerConnection>scm:git:https://github.com/apache/felix-dev.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=felix-dev.git</url>
     </scm>
 
+    <properties>
+        <jetty.version>12.0.8</jetty.version>
+    </properties>
+
     <build>
         <plugins>
             <plugin>
@@ -105,6 +105,18 @@
             <version>3.0.0</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${jetty.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.ee10</groupId>
+            <artifactId>jetty-ee10-servlet</artifactId>
+            <version>${jetty.version}</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.eclipse.jetty.ee10.websocket</groupId>
             <artifactId>jetty-ee10-websocket-jetty-server</artifactId>
diff --git 
a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/Activator.java
 
b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/Activator.java
index 8e4346641c..6963791264 100644
--- 
a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/Activator.java
+++ 
b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/Activator.java
@@ -77,21 +77,40 @@ public final class Activator
         context.registerService(Filter.class, filter2, filter2Props);
 
         /**
-         * Register a WebSocket servlet on /filtersample/websocket/*.
+         * Register WebSocket servlet on /websocketservlet/*.
+         * Do note that the path the servlet is registered to is not reflected 
in the WebSocket URL.
+         * This is due to the way of registering the WebSocket code.
          * In the Chrome Console, this snippet can be used to send a message 
to the WebSocket:
          *
-         * const websocket = new 
WebSocket("ws://localhost:8080/filtersample/websocket/example");
-         * websocket.send("test");
+         * const websocket = new 
WebSocket("ws://localhost:8080/websocket/example");
+         * websocket.send("test from websocket");
          *
-         * This will log "test" to the stdout.
+         * This will log "test from websocket" to the stdout.
          */
-        final TestWebSocketServlet webSocketServlet = new 
TestWebSocketServlet("websocketservlet1");
+        final TestWebSocketServlet webSocketServlet = new 
TestWebSocketServlet("websocket1");
         final Dictionary<String, Object> webSocketServletProps = new 
Hashtable<>();
-        
webSocketServletProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN,
 "/websocket/*");
+        
webSocketServletProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN,
 "/websocketservlet/*");
         
webSocketServletProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
                 "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + 
"=filtersample)");
         context.registerService(Servlet.class, webSocketServlet, 
webSocketServletProps);
 
+        /**
+         * Register another WebSocket servlet on /websocketservlet2/*.
+         * Do note that the path the servlet is registered to _is_ reflected 
in the WebSocket URL.
+         * This is due to the way of registering the WebSocket code.
+         * In the Chrome Console, this snippet can be used to send a message 
to the WebSocket:
+         *
+         * const websocket = new 
WebSocket("ws://localhost:8080/filtersample/websocketservlet2/example");
+         * websocket.send("test from websocket");
+         *
+         * This will log "test from websocket" to the stdout.
+         */
+        final TestWebSocketServletAlternative webSocketServlet2 = new 
TestWebSocketServletAlternative("websocket2");
+        final Dictionary<String, Object> webSocketServletProps2 = new 
Hashtable<>();
+        
webSocketServletProps2.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN,
 "/websocketservlet2/*");
+        
webSocketServletProps2.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
+                "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + 
"=filtersample)");
+        context.registerService(Servlet.class, webSocketServlet2, 
webSocketServletProps2);
     }
 
     @Override
diff --git 
a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/FelixJettyWebSocketServlet.java
 
b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/FelixJettyWebSocketServlet.java
index a8ed768291..55af0f1863 100644
--- 
a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/FelixJettyWebSocketServlet.java
+++ 
b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/FelixJettyWebSocketServlet.java
@@ -17,82 +17,32 @@
 package org.apache.felix.http.samples.whiteboard;
 
 import java.io.IOException;
-import java.lang.reflect.Proxy;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
 
-import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
-import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer;
 import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServlet;
-import 
org.eclipse.jetty.ee10.websocket.server.internal.JettyServerFrameHandlerFactory;
-import org.eclipse.jetty.ee10.websocket.servlet.WebSocketUpgradeFilter;
-import org.eclipse.jetty.websocket.core.server.WebSocketMappings;
-import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents;
 
 /**
  * Abstract class that hides all Jetty Websocket specifics and provides a way 
for the developer to focus on the actual WebSocket implementation.
- * @author paulrutters
  */
 public abstract class FelixJettyWebSocketServlet extends JettyWebSocketServlet 
{
-
     private final AtomicBoolean myFirstInitCall = new AtomicBoolean(true);
     private final CountDownLatch myInitBarrier = new CountDownLatch(1);
-    private ServletContext myProxiedContext;
-    private ServletContextHandler myServletContextHandler;
 
-    @Override
-    public void init() throws ServletException {
-        // Init, delaying init call until service method is called...
+    public final void init() {
+        // nothing, see delayed init below in service method
+        // this is a workaround as stated in 
https://issues.apache.org/jira/browse/FELIX-5310
     }
 
     @Override
-    public void destroy() {
-        // only call destroy when the servlet has been initialized
-        if (!myFirstInitCall.get()) {
-            // This is required because WebSocketServlet needs to have it's 
destroy() method called as well
-            // Causes NPE otherwise when calling an WS endpoint
-            super.destroy();
-        }
-    }
-
-
-    // This is a workaround required for WebSockets to work in Jetty12, see
-    // https://www.eclipse.org/forums/index.php/t/1110140/
-    @Override
-    public synchronized ServletContext getServletContext() {
-        if (myProxiedContext == null) {
-            myProxiedContext = (ServletContext) 
Proxy.newProxyInstance(JettyWebSocketServlet.class.getClassLoader(),
-                    new Class[]{ServletContext.class}, (proxy, method, 
methodArgs) -> {
-                        final ServletContext osgiServletContext = 
super.getServletContext();
-                        if (!"getAttribute".equals(method.getName())) {
-                            return method.invoke(osgiServletContext, 
methodArgs);
-                        }
-
-                        final String name = (String) methodArgs[0];
-                        Object value = osgiServletContext.getAttribute(name);
-                        if (value == null && myProxiedContext != null) {
-                            final ServletContext jettyServletContext = 
myServletContextHandler.getServletContext();
-                            value = jettyServletContext.getAttribute(name);
-                        }
-                        return value;
-                    });
-        }
-
-        return myProxiedContext;
-    }
-
-    @Override
-    protected void service(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {
+    public void service(final ServletRequest req, final ServletResponse res) 
throws ServletException, IOException {
         if (myFirstInitCall.compareAndSet(true, false)) {
             try {
-                delayedInit();
-            } catch (Exception e) {
-                System.err.println("Error delayed init: " + e.getMessage());
+                super.init();
             } finally {
                 myInitBarrier.countDown();
             }
@@ -104,42 +54,19 @@ public abstract class FelixJettyWebSocketServlet extends 
JettyWebSocketServlet {
             }
         }
 
-        // Call JettyWebSocketServlet service method to handle upgrade requests
-        super.service(req, resp);
-    }
-
-    private void delayedInit() throws ServletException {
-        // Make sure WebSockets are enabled in Jetty12
-        ensureWebSocketsInitialized();
-
-        // Overide the TCCL so that the internal factory can be found
-        // Jetty tries to use ServiceLoader, and their fallback is to
-        // use TCCL, it would be better if we could provide a loader...
-        final Thread currentThread = Thread.currentThread();
-        final ClassLoader tccl = currentThread.getContextClassLoader();
-        
currentThread.setContextClassLoader(JettyWebSocketServlet.class.getClassLoader());
-        try {
-            super.init();
-        } finally {
-            currentThread.setContextClassLoader(tccl);
-        }
+        super.service(req, res);
     }
 
-    private void ensureWebSocketsInitialized() {
-        final ServletContext osgiServletContext = getServletContext();
-        myServletContextHandler = 
ServletContextHandler.getServletContextHandler(osgiServletContext, 
"WebSockets");
-
-        final JettyWebSocketServerContainer serverContainer = 
JettyWebSocketServerContainer
-                .getContainer(osgiServletContext);
-        if (serverContainer == null) {
-            // Ensure WebSocket components are initialized in Jetty12
-            final ServletContext jettyServletContext = 
myServletContextHandler.getServletContext();
-            
WebSocketServerComponents.ensureWebSocketComponents(myServletContextHandler.getServer(),
-                    myServletContextHandler);
-            WebSocketUpgradeFilter.ensureFilter(jettyServletContext);
-            WebSocketMappings.ensureMappings(myServletContextHandler);
-            JettyServerFrameHandlerFactory.getFactory(jettyServletContext);
-            JettyWebSocketServerContainer.ensureContainer(jettyServletContext);
+    /**
+     * Cleanup method.
+     */
+    @Override
+    public final void destroy() {
+        // only call destroy when the servlet has been initialized
+        if (!myFirstInitCall.get()) {
+            // This is required because WebSocketServlet needs to have it's 
destroy() method called as well
+            // Causes NPE otherwise when calling an WS endpoint
+            super.destroy();
         }
     }
 }
diff --git 
a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java
 
b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java
index b09b7f6bbb..7408c319db 100644
--- 
a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java
+++ 
b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java
@@ -17,9 +17,11 @@
 package org.apache.felix.http.samples.whiteboard;
 
 import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
 
-import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServletFactory;
+import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer;
 import org.eclipse.jetty.websocket.api.Callback;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
@@ -28,29 +30,37 @@ import 
org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen;
 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
 
-public class TestWebSocketServlet extends FelixJettyWebSocketServlet {
+/**
+ * Example of a WebSocket servlet that uses the Jetty WebSocket API.
+ * It does not respect the path this servlet is registered to, but requires no 
further workarounds.
+ * Setting `org.apache.felix.jetty.ee10.websocket.enable=true` is enough.
+ */
+public class TestWebSocketServlet extends HttpServlet {
     private final String name;
 
     public TestWebSocketServlet(String name) {
         this.name = name;
     }
 
+    private void doLog(String message) {
+        System.out.println("## [" + this.name + "] " + message);
+    }
+
+
     @Override
     public void init(ServletConfig config) throws ServletException {
         doLog("Init with config [" + config + "]");
         super.init(config);
-    }
 
-    private void doLog(String message) {
-        System.out.println("## [" + this.name + "] " + message);
-    }
+        // Lookup the ServletContext for the context path where the websocket 
server is attached.
+        ServletContext servletContext = config.getServletContext();
 
-    @Override
-    protected void configure(JettyWebSocketServletFactory 
jettyWebSocketServletFactory) {
-        doLog("Configuring WebSocket factory");
-        jettyWebSocketServletFactory.register(TestWebSocket.class);
+        // Retrieve the JettyWebSocketServerContainer.
+        JettyWebSocketServerContainer container = 
JettyWebSocketServerContainer.getContainer(servletContext);
+        container.addMapping("/websocket/*", (upgradeRequest, upgradeResponse) 
-> new TestWebSocket());
     }
 
+
     @WebSocket
     public static class TestWebSocket {
         @OnWebSocketMessage
@@ -80,4 +90,4 @@ public class TestWebSocketServlet extends 
FelixJettyWebSocketServlet {
             System.out.println("## [" + this.getClass() + "] " + message);
         }
     }
-}
+}
\ No newline at end of file
diff --git 
a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java
 
b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServletAlternative.java
similarity index 82%
copy from 
http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java
copy to 
http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServletAlternative.java
index b09b7f6bbb..df980c4ea2 100644
--- 
a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServlet.java
+++ 
b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestWebSocketServletAlternative.java
@@ -28,27 +28,31 @@ import 
org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen;
 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
 
-public class TestWebSocketServlet extends FelixJettyWebSocketServlet {
+/**
+ * Example of a WebSocket servlet that uses the Jetty WebSocket API, and is 
registered by extending JettyWebSocketServlet.
+ * It does respect the path this servlet is registered to, but requires a 
further workaround. See FelixJettyWebSocketServlet.
+ * Requires setting `org.apache.felix.jetty.ee10.websocket.enable=true`.
+ */
+public class TestWebSocketServletAlternative extends 
FelixJettyWebSocketServlet {
     private final String name;
 
-    public TestWebSocketServlet(String name) {
+    public TestWebSocketServletAlternative(String name) {
         this.name = name;
     }
 
+    private void doLog(String message) {
+        System.out.println("## [" + this.name + "] " + message);
+    }
+
     @Override
     public void init(ServletConfig config) throws ServletException {
         doLog("Init with config [" + config + "]");
         super.init(config);
     }
 
-    private void doLog(String message) {
-        System.out.println("## [" + this.name + "] " + message);
-    }
-
     @Override
-    protected void configure(JettyWebSocketServletFactory 
jettyWebSocketServletFactory) {
-        doLog("Configuring WebSocket factory");
-        jettyWebSocketServletFactory.register(TestWebSocket.class);
+    protected void configure(JettyWebSocketServletFactory factory) {
+        factory.register(TestWebSocket.class);
     }
 
     @WebSocket
@@ -80,4 +84,4 @@ public class TestWebSocketServlet extends 
FelixJettyWebSocketServlet {
             System.out.println("## [" + this.getClass() + "] " + message);
         }
     }
-}
+}
\ No newline at end of file

Reply via email to