vaadin / flow

Vaadin Flow is a Java framework binding Vaadin web components to Java. This is part of Vaadin 10+.
Apache License 2.0
616 stars 169 forks source link

@Push broken in OSGi for Flow V12.0.6 #5081

Closed enver-haase closed 5 years ago

enver-haase commented 5 years ago
denis-anisimov commented 5 years ago

The error message is a result of unavailable push resources (vaadinPush.js) via HTTP. There are static web resource files in flow-push module which should be accessible via HTTP. This issue is fixed by #5112.

But this is far away from the end of the story.

Push doesn't work if only the push resources are registered.

I don't know the exact reason why but here are some notes (basically for myself but may be useful for understanding of issues overall in the future).

I believe those two classes are required to make Push work. So we need to solve those issues.

I'm trying to use HTTP Whiteboard specification to register ServletContextListener:

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);

And this doesn't work. I see a message in the output:

[DEBUG] [ServiceReference 4 from bundle 21 : com.vaadin.flow.server:1.5.0.201902251245 ref=[javax.servlet.ServletContextListener] properties={objectClass=[javax.servlet.ServletContextListener], osgi.http.whiteboard.listener=true, service.bundleid=21, service.id=4, service.scope=singleton}] Ignoring invalid Listener service

I've been trying register a servlet using HTT Whiteboard specification and without it. The result is the same in both cases. The error somehow relates to HTTP Whiteboard impl (in Felix) because if I remove props.put("osgi.http.whiteboard.listener", true); then there is no the error message.

Now, here are several implementation notes:

Anyway there are a lot of issues with Push. It won't be easy to solve all of them.

I wonder whether Push works in Framework 8 because it should have similar issues. @Artur- , do you know anything about Push in FW8 (any known issues, whether it has been ever tested) ?

denis-anisimov commented 5 years ago

Updates:

As a result:

denis-anisimov commented 5 years ago

We still need to figure out what to do with ServletContextListener but for now I would like to get Push working in some (may be very hacky and ugly) way.

So to be able to see the current state one should :

The result is: page is reloading all the time. The same view works as it should in plain Jetty: the counter is updated from 0 to 50 with a short delay between number changes.

Here in this hack I've used JSR356WebsocketInitializer instance being applied to a servlet context directly in servlet's init method and since the initializer doesn't use any ServletContext features that may be used only during context initialization it should work in the same way as being called as * javax.servlet.ServletContextListener. At least that's my assumption. But Push still doesn't work. So either my assumption is wrong and JSR356WebsocketInitializer doesn't do something or there is some functionality which is not called for some reason in OSGi.

@Artur- , could you please take a look at it. At least may be you can say the reason why Push doesn't work with this approach.

denis-anisimov commented 5 years ago

The base-starter-flow-osgi project doesn't have websocket-api dependency. This dependency is required to enable websocket:

<dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
            <scope>provided</scope>
        </dependency>

That still doesn't enable Push for the project (push via WS). An exception is thrown when the Activator is modified to configure Atmosphere with JSR356WebsocketInitializer:

java.lang.IllegalStateException: Unable to configure jsr356 at that stage. ServerContainer is null
    at org.atmosphere.container.JSR356AsyncSupport.<init>(JSR356AsyncSupport.java:53)
    at org.atmosphere.container.JSR356AsyncSupport.<init>(JSR356AsyncSupport.java:42)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.atmosphere.cpr.DefaultAsyncSupportResolver.newCometSupport(DefaultAsyncSupportResolver.java:237)
    at org.atmosphere.cpr.DefaultAsyncSupportResolver.resolveWebSocket(DefaultAsyncSupportResolver.java:308)
    at org.atmosphere.cpr.DefaultAsyncSupportResolver.resolve(DefaultAsyncSupportResolver.java:294)
    at org.atmosphere.cpr.AtmosphereFramework.autoDetectContainer(AtmosphereFramework.java:2092)
    at org.atmosphere.cpr.AtmosphereFramework.init(AtmosphereFramework.java:914)
    at org.atmosphere.cpr.AtmosphereFramework.init(AtmosphereFramework.java:838)
    at com.vaadin.flow.server.communication.PushRequestHandler.initAtmosphere(PushRequestHandler.java:218)

The exception is caused by the fact that ServletContext instance has no "javax.websocket.server.ServerContainer" attribute.

Apparently this attribute should be set by some initializer. It's not clear which initializer should set it. There are two dependencies :

<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>

which should theoretically add jetty websocket implementation bundle. And javax-websocket-server-impl contains WebSocketServerContainerInitializer which sets "javax.websocket.server.ServerContainer" attribute. But this class is ServletContainerInitializer (and once again there is an issue to execute in in OSGi) and it uses ContextHandler class whose instance is not available inside Felix Jetty.

So it's still not clear which class inside Felix Jetty should initialize javax.websocket.server.ServerContainer.

denis-anisimov commented 5 years ago

This ticket in its original description should be fixed by https://github.com/vaadin/flow/pull/5112.

In fact Push still doesn't work out of the box because default transport is Websocket. Websocket requires a very non trivial additional configuration in the project which I have no even idea about.

But if transport is set to Long polling: @Push(transport = Transport.LONG_POLLING) then the fix is provided.

The issue for websockets is a completely different thing and I will create a separate ticket for this.