Open denis-anisimov opened 5 years ago
Some investigation notes:
Seems those dependencies have websocket impl for jetty:
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
<version>9.4.12.v20180830</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-client-impl</artifactId>
<version>9.4.12.v20180830</version>
<scope>provided</scope>
</dependency>
BUT I have no idea whether they are those deps which we need to enable proper websocket support in Felix Jetty.
The functionality in those bundles heavily relies on ServletContainerInitializer
(which doesn't work in Felix Jetty).
E.g. there is class WebSocketServerContainerInitializer
which sets javax.websocket.server.ServerContainer
attribute value.
I've made a very very ugly hack in the test-root-context
module: I've added the javax-websocket-server-impl
dep to it and changed the Activator
code to:
@VaadinServletConfiguration(productionMode = false)
private static class FixedViewServlet extends ViewTestServlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
ServletContext servletContext = servletConfig.getServletContext();
ServletContext originalContext = servletContext.getContext("/");
try {
WebSocketServerContainerInitializer initializer = new WebSocketServerContainerInitializer();
initializer.onStartup(Collections.emptySet(), originalContext);
} catch (Exception e) {
}
String attr = javax.websocket.server.ServerContainer.class
.getName();
servletContext.setAttribute(attr,
originalContext.getAttribute(attr));
System.out.println(
"wwwwwwwwwww " + servletContext.getAttribute(attr));
JSR356WebsocketInitializer jsr356Initializer = new JSR356WebsocketInitializer();
jsr356Initializer.contextInitialized(
new ServletContextEvent(servletConfig.getServletContext()));
super.init(servletConfig);
getService().setClassLoader(getClass().getClassLoader());
}
}
This code shows how things are complicated : the servletContext
is not the real instance which Jetty classes expects. Inside OSGi this is a proxy for a real servlet context.
The real servlet context is originalContext
. I pass it to WebSocketServerContainerInitializer
and
then it sets the requires attribute value.
Then I need to transfer it to the servletContext
and I have to call servletContext.getAttribute
otherwise nothing will work (proxy implementation detail).
Then I initialize servlet context via JSR356WebsocketInitializer
directly.
Also I had to register a servlet in this way:
Dictionary<String, String> params = new Hashtable<>();
params.put(
"org.atmosphere.container.JSR356AsyncSupport.mappingPath",
"/view/*");
httpService.registerServlet("/view/*",
new FixedViewServlet(), params, null);
Otherwise there will be an exception again related to the fact that in OSGi the servlet registered in the servlet context is a proxy for Jetty servlet (and there is some code which tries to find a servlet by its name which differs).
With this config I'm able at least get JSR356AsyncSupport
somehow working.
At least there is ServerContainer
attribute value.
But Push with websockets still doesn't work.
So there are so many questions and the very basic questions are about proper project setup so that websocket works with web server in OSGi.
And final questions: what's needed from Flow to be able to support web sockets Push in OSGi.
It might be that we will need to make changes in the current impl just like we added route registration logic in OSGi.
And it's not clear whether we need to do anything in Flow at the moment: there are too many ways to go (and many wrong ways) in different areas (Felix Jetty issues? Websocket support in Jetty issues? Atmoshpere issues? Project config issues ? Our issues ?)
It's not clear whether there is a bug or not. The main question is how to make WenSockets work in OSGi overall and in Felix Jetty in particular (if it's possible at all). Or may be another OSGi container/web server should be used to make a test.
But also there is at least one issue: JSR356WebsocketInitializer
should work and it doesn't since @WebListener
doesn't work.
Apprently for Vaadin Framework 8.7, Push with web sockets works on Karaf 4.2.3 and the included Jetty 9.1.14
How it can be seen ? Do we have tests for this somewhere ? Karaf is based on Felix though it uses another web server.
Apparently it is working fine in a customer project using 8.7.1 and the recently released Karaf version linked ^.
Ah, OK.
Would be good to see the config to understand which bundles are deployed.
But I think it's because Karaf uses Pax as a web server. It might be that Pax supports ServletContainerInitializer
, ServletContextListener
and @WebListener
.
Another question is : how do they deploy their web application into Karaf.
If it's deployed as a WAR file then it works just because it's WAR and everything works in WAR in OSGi just because it's a war: it has all dependencies inside WEB-INF/lib
folder deployed in one archive and not as standalone bundles.
We are trying to deploy a web application as a standalone bundle without any dep inside it. So there are still questions even about Karaf.
Hi,
as far as I understood your problem all you want to achieve is to register your JSR356WebsocketInitializer
as a ServletContextListener
, do you?
If so this can be achieved in OSGi by either annotating it with the right annotations as described here
@Component
@HttpWhiteboardListener
public class JSR356WebsocketInitializer implements ServletContextListener {...}
Unfortunately with the shift from plain properties to ComponentPropertyTypes
, i.e., the @HttpWhiteboardListener
annotation, there was a bug introduced in Felix Jetty, which prevented the registration of ServletContextListeners
declared in this way. I've filed a bug report here. This should be fixed when you use http.jetty-4.0.8, http.base-4.0.6 and http.bridge-4.0.6 instead of 4.0.6/4.0.4/4.0.4 respectively.
If you are unable to make this version shift then you just can declare the property by hand instead of using ComponentPropertyTypes
like this:
@Component(property= {HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER + "=true"})
public class JSR356WebsocketInitializer implements ServletContextListener {...}
This solution also works with the old versions of felix.
As I understand it from reading your code in ServletContextListeners
you need to enforce a specific starting order for your ServletContextListeners
. This can be achieved in OSgi by setting a @ServiceRanking
. The higher the service ranking the earlier the repsective ServletContextListener
is called if using the same service and ServletContext
as another ServletContextListener
.
I did this in my old sketch for a Vaadin OSGi integration: https://github.com/Sandared/flow-osgi/blob/master/flow.osgi.integration/src/main/java/io/jatoms/flow/osgi/integration/FlowOsgiRouteRegistryInitializer.java
Kind regards, Thomas
An additional note regarding Karaf:
Karaf is not a pure OSGi runtime. It is a polyglot apllication container that is based on an OSGi runtime. Polyglot means you can not only deploy OSGi applications into it, but also standard wars or Spring applications and Karaf knows how to handle them. This might also be the reason why your code works in Karaf but not with Felix Jetty. Karaf might be able to understand annotations like @WebListener
, wheras Felix does not understand them.
Karaf is a very powerful application container, but might not be the best choice to verify an OSGi integration of Flow, because of the aforementioned points.
Kind regards, Thomas
as far as I understood your problem all you want to achieve is to register your JSR356WebsocketInitializer as a ServletContextListener, do you?
That's only one part of the problem.
The main problem : it's not clear how to make Push works with felix-jetty
.
I didn't know about @HttpWhiteboardListener
annotation but it's just a shorthand for add a service with property HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER + "=true"
.
In your case you are adding a service via DS , I've been trying to add it programatically.
It doesn't work because of this: http://felix.apache.org/documentation/subprojects/apache-felix-http-service.html#servlet-api-events. It says that at the moment javax.servlet.ServletContextListener is just not supported at all. See my comment here about this: https://github.com/vaadin/flow/issues/5081#issuecomment-467007295
The reason your programmatically registered service is not registered is the same as why my service with @HttpWhiteboardListener
is not registerd: the mentioned bug.
Try to change the value of the property to be a String instead of a boolean, i.e.,
Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put("osgi.http.whiteboard.listener", "true");
context.registerService(ServletContextListener.class,
new ServletContextListener() {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Init");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}, props);
This should work with the older versions of Felix too.
We don't use Karaf in Flow in our tests. So there is no any uncertainty about OSGi support in Flow. But any statement about working something in OSGi (like here about WebSockets) should be clarified, that's true. Because it's not clear how the application is deployed : as a WAR or as a separate standalone real OSGi bundle.
The reason your programmatically registered service is not registered is the same as why my service with @HttpWhiteboardListener is not registerd: the mentioned bug.
Ah, alright. Thanks for the info. I will try.
We face the same issue with Equinox 4.7, VAADIN 8.6.4, Jetty 9.4.14/Tomcat 8.5.37, Atmosphere 2.4.30 (VAADIN1), when we try to use Vaadin UI w/ Push and WebSocket transport distributed as OSGi component. The PUSH always fallback to LONG_POOLING no matter if we registered the VaadinServlet to a Felix Jetty HttpService provider, Equinox Jetty HttpService provider or Equinox ServletBridge HttpService provider.
Our application is a VAADIN gateway in front of a micro-service ecosystem where PUSH plays quite a important role. It is required for gateway to run in any web-container as long as an HTTPService is available in OSGi runtime via native support or bridges, therefore we can not limit ourselves just to support Jetty as HttpService provider.
Even we knew it is not enough, we want to see if PUSH runs with Jetty and WebSocket ServerContainer enabled. For that we registered a ServletContextListener via DS, where on contextInitialized we did: ctx.setAttribute(ServerContainer.class.getName(), WebSocketServerContainerInitializer.configureContext(handler))
This was not enough, by first attempt, JSR365 async support does not start because it can not find a servlet-path to map to. Afterwards we force path mapping by manually providing the mapping:
args.put("org.atmosphere.container.JSR356AsyncSupport.mappingPath", root);
Using Equinox HttpService provider, we were not able to start JSR365 async support:
2019.03.07 20:23:54 INFO [org.atmosphere.cpr.AtmosphereFramework::addAtmosphereHandler] Installed AtmosphereHandler com.vaadin.server.communication.PushAtmosphereHandler mapped to context-path: /* 2019.03.07 20:23:54 INFO [org.atmosphere.cpr.AtmosphereFramework::addAtmosphereHandler] Installed the following AtmosphereInterceptor mapped to AtmosphereHandler com.vaadin.server.communication.PushAtmosphereHandler 2019.03.07 20:23:54 ERROR [org.atmosphere.cpr.DefaultAsyncSupportResolver::newCometSupport] Failed to create AsyncSupport class: org.atmosphere.container.JSR356AsyncSupport, error: java.lang.reflect.InvocationTargetException 2019.03.07 20:23:54 ERROR [org.atmosphere.cpr.DefaultAsyncSupportResolver::newCometSupport] Real error: Unable to configure jsr356 at that stage. ServerContainer is null
Using Felix HttpService provider, we were able to start JSR365 async support:
2019.03.07 20:08:05 INFO [org.atmosphere.cpr.AtmosphereFramework::info] Atmosphere is using org.atmosphere.inject.InjectableObjectFactory for dependency injection and object creation 2019.03.07 20:08:05 INFO [org.atmosphere.cpr.AtmosphereFramework::info] Atmosphere is using async support: org.atmosphere.container.JSR356AsyncSupport running under container: jetty/9.4.15.v20190215 using javax.servlet/3.0 and jsr356/WebSocket API 2019.03.07 20:08:05 INFO [org.atmosphere.cpr.AtmosphereFramework::info] Atmosphere Framework 2.4.30.vaadin1 started. 2019.03.07 20:08:05 INFO [org.atmosphere.cpr.AtmosphereFramework::addInterceptorToAllWrappers] Installed AtmosphereInterceptor Track Message Size Interceptor using | with priority BEFORE_DEFAULT
Nevertheless we were not able to use VAADIN PUSH due to client side error:
WebSocket connection to 'ws://localhost:8082/main/PUSH?v-uiId=0&v-pushId=bd8aeef6-0956-4f3a-9b21-afd1fa87dba9&X-Atmosphere-tracking-id=0&X-Atmosphere-Framework=2.3.2.vaadin1-javascript&X-Atmosphere-Transport=websocket&X-Atmosphere-TrackMessageSize=true&Content-Type=application/json;%20charset=UTF-8&X-atmo-protocol=true' failed: Error during WebSocket handshake: Unexpected response code: 200
Looking into Vaadin 13 this would be a showstopper for my projects. On Vaadin 8 i got push working on Karaf with SCR and pax-web HTTP-Whiteboard and these settings on the Vaadin-Servlet:
@WebServlet(urlPatterns = "/*", name = "RiaUI", asyncSupported = true, initParams = { @WebInitParam(name = "org.atmosphere.websocket.suppressJSR356", value = "true"), @WebInitParam(name = "org.atmosphere.websocket.maxIdleTime", value = "600000") } ) @VaadinServletConfiguration(ui = RiaUI.class, productionMode = false, heartbeatInterval = 120) @Component(immediate = true, service = VaadinServlet.class) public class RiaServlet extends OsgiVaadinServlet {...
and the UI class:
@PushStateNavigation @Push(PushMode.MANUAL) @Component(service = UI.class, scope = ServiceScope.PROTOTYPE) public class RiaUI extends UI {...
and using following Vaadin bundles:
`
<bundle start-level="80">mvn:com.vaadin.external/gentyref/${vaadin.gentyref.version}</bundle>
<bundle start-level="80">mvn:com.vaadin/vaadin-shared/${vaadin.version}</bundle>
<bundle start-level="90">mvn:com.vaadin/vaadin-server/${vaadin.version}</bundle>
<bundle start-level="90">mvn:com.vaadin/vaadin-client-compiled/${vaadin.version}</bundle>
<bundle start-level="90">mvn:com.vaadin/vaadin-themes/${vaadin.version}</bundle>
<bundle start-level="90">mvn:com.vaadin/vaadin-push/${vaadin.version}</bundle>`
Deployed on Karaf 4.2.4, using Apache ServiceMix :: Bundles :: javax.websocket-api:
45 │ Active │ 80 │ 9.3.1 │ ph-commons 46 │ Active │ 80 │ 6.1.2 │ ph-css 47 │ Active │ 80 │ 2.4.30.vaadin1 │ atmosphere-runtime 48 │ Active │ 80 │ 1.2.0.vaadin1 │ GenTyRef 49 │ Active │ 80 │ 2.8.2.vaadin2 │ Vaadin GWT Elemental 50 │ Active │ 80 │ 1.6.1 │ vaadin-slf4j-jdk14 51 │ Active │ 80 │ 1.4.2 │ Flow Client Engine 52 │ Active │ 80 │ 1.4.2 │ Vaadin Flow Html Components 53 │ Active │ 80 │ 1.4.2 │ Vaadin Flow OSGi Support 54 │ Active │ 80 │ 1.4.2 │ Vaadin Flow Push 55 │ Active │ 80 │ 1.4.2 │ Vaadin Flow Server 56 │ Active │ 80 │ 1.4.2 │ Vaadin Flow Lumo Theme 57 │ Active │ 30 │ 1.3 │ javax.annotation API 58 │ Active │ 30 │ 3.0.0 │ Expression Language 3.0 API 59 │ Active │ 30 │ 1.4.7 │ JavaMail API (compat) 60 │ Active │ 30 │ 3.1.0 │ Java Servlet API 61 │ Active │ 80 │ 2.0.1.Final │ Bean Validation API 62 │ Active │ 30 │ 1.1 │ WebSocket server API 63 │ Active │ 80 │ 1.9.10 │ Byte Buddy (without dependencies) 64 │ Active │ 30 │ 1.2.0 │ Apache Aries SPI Fly Dynamic Weaving Bundle 65 │ Active │ 80 │ 1.4.0 │ Apache Commons FileUpload 66 │ Active │ 80 │ 2.6.0 │ Apache Commons IO 67 │ Active │ 30 │ 2.1.16 │ Apache Felix Declarative Services 68 │ Active │ 30 │ 1.1 │ Java Authentication SPI for Containers 69 │ Active │ 30 │ 1.1.1 │ geronimo-jta_1.1_spec 70 │ Active │ 30 │ 4.2.4 │ Apache Karaf :: HTTP :: Core 71 │ Active │ 30 │ 4.2.4 │ Apache Karaf :: SCR :: Management MBeans 72 │ Active │ 30 │ 4.2.4 │ Apache Karaf :: SCR :: Bundle State 73 │ Active │ 80 │ 4.12.0 │ Apache XBean OSGI Bundle Utilities 74 │ Active │ 80 │ 4.12.0 │ Apache XBean :: Classpath Resource Finder 75 │ Active │ 30 │ 3.11.1.v20150902-1521 │ Eclipse Compiler for Java(TM) 76 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Asynchronous HTTP Client 77 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Continuation 78 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Deployers 79 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Http Utility 80 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: IO Utility 81 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: JAAS 82 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: JMX Management 83 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: JNDI Naming 84 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Plus 85 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Rewrite Handler 86 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Security 87 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: JASPI Security 88 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Server Core 89 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Servlet Handling 90 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Utility Servlets and Filters 91 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Utilities 92 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Utilities :: Ajax(JSON) 93 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Webapp Application Support 94 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Websocket :: API 95 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Websocket :: Client 96 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Websocket :: Common 97 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Websocket :: javax.websocket :: Client Implementation 98 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Websocket :: javax.websocket.server :: Server Implementation 99 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Websocket :: Server 100 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: Websocket :: Servlet Interface 101 │ Active │ 30 │ 9.4.12.v20180830 │ Jetty :: XML utilities 102 │ Active │ 80 │ 1.11.3 │ jsoup Java HTML Parser 103 │ Active │ 80 │ 7.0.0 │ org.objectweb.asm 104 │ Active │ 80 │ 7.0.0 │ org.objectweb.asm.commons 105 │ Active │ 80 │ 7.0.0 │ org.objectweb.asm.tree 106 │ Active │ 30 │ 7.2.8 │ OPS4J Pax Web - API 107 │ Active │ 30 │ 7.2.8 │ OPS4J Pax Web - Extender - Whiteboard 108 │ Active │ 30 │ 7.2.8 │ OPS4J Pax Web - Jetty 109 │ Active │ 30 │ 7.2.8 │ OPS4J Pax Web - Jsp Support 110 │ Active │ 30 │ 7.2.8 │ OPS4J Pax Web - Runtime 111 │ Active │ 30 │ 7.2.8 │ OPS4J Pax Web - Service SPI 112 │ Active │ 30 │ 1.0.0.201505202023 │ org.osgi:org.osgi.util.function 113 │ Active │ 30 │ 1.0.0.201505202023 │ org.osgi:org.osgi.util.promise 114 │ Active │ 80 │ 1.1.0.1 │ Apache ServiceMix :: Bundles :: javax.websocket-api 115 │ Active │ 80 │ 1.4.2 │ Vaadin Flow Data 116 │ Active │ 80 │ 1.3.0 │ vaadin-ordered-layout-flow 117 │ Active │ 80 │ 1.3.0 │ vaadin-notification-flow 118 │ Active │ 80 │ 1.3.1 │ vaadin-button-flow 119 │ Active │ 80 │ 1.0.0 │ Base Starter for Vaadin Flow and OSGi
The log is displaying 2 warnings and push is not working:
2019-03-22T10:56:06,406 | WARN | paxweb-config-1-thread-1 | ServletContainerInitializerScanner | 106 - org.ops4j.pax.web.pax-web-api - 7.2.8 | failed to parse and instantiate of javax.servlet.ServletContainerInitializer in classpath
2019-03-22T10:56:06,516 | WARN | paxweb-config-1-thread-1 | ServletContainerInitializerScanner | 106 - org.ops4j.pax.web.pax-web-api - 7.2.8 | failed to parse and instantiate of javax.servlet.ServletContainerInitializer in classpath
After opening the webpage it is reloading if the frequency of my server-side updates and displaying errors in the webpage:
Cookies disabled This application requires cookies to function. Please enable cookies in your browser and click here or press ESC to try again.
...and in the Karaf log:
2019-03-22T11:45:39,866 | WARN | qtp1194084166-125 | HttpChannel | 91 - org.eclipse.jetty.util - 9.4.12.v20180830 | / java.lang.RuntimeException: Cannot load platform configurator at javax.websocket.server.ServerEndpointConfig$Configurator.fetchContainerDefaultConfigurator(ServerEndpointConfig.java:123) ~[?:?] at javax.websocket.server.ServerEndpointConfig$Configurator.getContainerDefaultConfigurator(ServerEndpointConfig.java:128) ~[?:?] at javax.websocket.server.ServerEndpointConfig$Configurator.checkOrigin(ServerEndpointConfig.java:192) ~[?:?] at org.eclipse.jetty.websocket.jsr356.server.JsrCreator.createWebSocket(JsrCreator.java:88) ~[?:?] at org.eclipse.jetty.websocket.server.WebSocketServerFactory.acceptWebSocket(WebSocketServerFactory.java:217) ~[?:?] at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:247) ~[?:?] at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[?:?] at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533) ~[?:?] at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:71) ~[?:?] at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548) ~[?:?] at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1340) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:293) ~[?:?] at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473) ~[?:?]
OK, got manual push working now with the following settings on the project-base-osgi project:
Main view:
@Route("") @Push(value = PushMode.MANUAL, transport = Transport.WEBSOCKET) @Theme(Lumo.class) @PWA(name = "Project Base for Vaadin Flow", shortName = "Project Base")
FixedVaadinServlet:
@WebServlet(asyncSupported = true)
Only the following exception keeps popping up in the logs:
2019-03-22T12:22:20,721 | INFO | activator-1-thread-2 | CommandExtension | 35 - org.apache.karaf.shell.core - 4.2.4 | Registering commands for bundle org.apache.karaf.http.core/4.2.4 2019-03-22T12:22:23,529 | WARN | qtp1012809459-149 | HttpChannel | 91 - org.eclipse.jetty.util - 9.4.12.v20180830 | / java.lang.RuntimeException: Cannot load platform configurator at javax.websocket.server.ServerEndpointConfig$Configurator.fetchContainerDefaultConfigurator(ServerEndpointConfig.java:123) ~[?:?] at javax.websocket.server.ServerEndpointConfig$Configurator.getContainerDefaultConfigurator(ServerEndpointConfig.java:128) ~[?:?] at javax.websocket.server.ServerEndpointConfig$Configurator.checkOrigin(ServerEndpointConfig.java:192) ~[?:?] at org.eclipse.jetty.websocket.jsr356.server.JsrCreator.createWebSocket(JsrCreator.java:88) ~[?:?] at org.eclipse.jetty.websocket.server.WebSocketServerFactory.acceptWebSocket(WebSocketServerFactory.java:217) ~[?:?] at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:247) ~[?:?] at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642) ~[?:?] at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533) ~[?:?] at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:71) ~[?:?] at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548) ~[?:?] at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1340) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:293) ~[?:?] at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203) ~[88:org.eclipse.jetty.server:9.4.12.v20180830] at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473) ~[?:?]
Client side:
Request URL:http://localhost:8181/?v-r=push&v-uiId=5&v-pushId=b13a6930-8591-4721-a42e-1b5a31df9dde&X-Atmosphere-tracking-id=0&X-Atmosphere-Framework=2.3.2.vaadin1-javascript&X-Atmosphere-Transport=websocket&Content-Type=application/json; charset=UTF-8&X-atmo-protocol=true Request method:GET Remote address:127.0.0.1:8181 Status code: 500 Version:HTTP/1.1 Referrer Policy:no-referrer-when-downgrade
Great, after a redeploy of the Servicemix web-socket bundle this exception disapears, it seems that Karaf did not do a complete reload of these classes and kept using the default javax.websocket classes. On Karaf Vaadin push is working.
Oh, i forgot to mention i changed the VaadinServletRegistration class:
@WebServlet(asyncSupported = true, urlPatterns = "/*") @Component(immediate = true, service = Servlet.class) public class VaadinServletRegistration extends VaadinServlet {
// @WebServlet(asyncSupported = true, urlPatterns = "/*") // private static class FixedVaadinServlet extends VaadinServlet { // @Override // public void init(ServletConfig servletConfig) throws ServletException { // super.init(servletConfig); // // getService().setClassLoader(getClass().getClassLoader()); // } // }
// @Activate // void activate(BundleContext ctx) { // Hashtable<String, Object> properties = new Hashtable<>(); // properties.put( // HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED, // true); // properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, // "/*"); // ctx.registerService(Servlet.class, new FixedVaadinServlet(), // properties); // } }
I am using OSGi, Jetty, and Vaadin with Push. However, I am not using Whiteboard Pattern. I prefer to have Jetty jars embedded in a bundle because I require further deep integrations.
I also needed to handle some trouble with Push. In my case the solution was a patch in the Manifest of the "jakarta.websocket-api-1.1.2.jar" (and maybe - I am not sure anymore - using spifly [1]). Add the following header to jakarta.websocket-api, also explained in [2]:
Require-Capability: osgi.serviceloader;filter:="(osgi.serviceloader=javax.websocket.server.ServerEndpointConfig$Configurator)";cardinality:=multiple,osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)",osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.6))"
I also had to add the following header to my bundle embedding the Jetty jars:
Provide-Capability:osgi.serviceloader;osgi.serviceloader="javax.servlet.ServletContainerInitializer",osgi.serviceloader;osgi.serviceloader="javax.websocket.server.ServerEndpointConfig$Configurator" Require-Capability: osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional,osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
[1] https://aries.apache.org/modules/spi-fly.html [2] https://stackoverflow.com/questions/39740531/jetty-websocket-java-lang-runtimeexception-cannot-load-platform-configurator
Here are also comments about WS in Karaf: https://github.com/vaadin/osgi/issues/63#issuecomment-841981103
Moving this ticket into OSGi repo.
This ticket follows up https://github.com/vaadin/flow/issues/5081.
It's quite complicated even just describe the ticket.
The simplest description is:
Use base-starter-flow-osgi and add @Push annotation to MainView.java. Push won't work.
By default the transport is Websockets. And that's the reason why it doesn't work. It's possible to use Long polling as a transport.
Note that it's not clear whether this issue is a project configuration issue or it's an issue in our websocket support with OSGi.
First of all you should add websocket api dependency to the project :
But this is not enough. Push with websockets won't work. The reason why it doesn't work is:
ServerContainer is null
in theorg.atmosphere.container.JSR356AsyncSupport
here:And that's the most complicated thing: it's totally unknown which bundle should set the attribute in the
ServletContext
. Apparently there should be some websocket implementation bundle for jetty which should set the attribute at the servlet initialization phase.This is normally done either by a
ServletContainerInitializer
orServletContextListener
. The problem is : neither one nor another doesn't work in OSGi out of the box (at least they doesn't work in Felix jetty).So the questions are:
It's not even clear whether it's possible to configure Felix Jetty at all to work properly with Atmosphere so that
JSR356AsyncSupport
doesn't fail. May be it's just not possible at all and we should use another OSGi container/ web container ?If it's possible then what bundles should be deployed in addition to
javax.websocket-api
to enable websocket support (not a generic web sockets support but makeJSR356AsyncSupport
works as it should)?If we find those bundles and deploy then then what's required from Flow to make websocket Push works ?
We have
JSR356WebsocketInitializer
which is aggregated byServletContextListeners
(which is@WebListener
) and it's not activated in OSGi out of the box. So this is one thing in addition to all other which we should take care.Read also comments in another ticket starting from this one : https://github.com/vaadin/flow/issues/5081#issuecomment-467372123.