Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSGi: Make sure that Push with Websocket transport works in OSGi #64

Open
denis-anisimov opened this issue Feb 28, 2019 · 24 comments
Open

Comments

@denis-anisimov
Copy link

This ticket follows up vaadin/flow#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 :

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

But this is not enough. Push with websockets won't work.
The reason why it doesn't work is: ServerContainer is null in the org.atmosphere.container.JSR356AsyncSupport here:

public JSR356AsyncSupport(AtmosphereConfig config, ServletContext ctx) {
        super(config);
        ServerContainer container = (ServerContainer) ctx.getAttribute(ServerContainer.class.getName());

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 or ServletContextListener.
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:

  • how to enable webscoket support in the Felix Jetty as an OSGi web container?
    • which bundles should be deployed to the Felix to enable websocket support ?
    • what's required from Flow OSGi support in addition to that?

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 make JSR356AsyncSupport 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 by ServletContextListeners (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 : vaadin/flow#5081 (comment).

@denis-anisimov
Copy link
Author

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.

@denis-anisimov
Copy link
Author

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

@denis-anisimov
Copy link
Author

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.

@pleku
Copy link
Contributor

pleku commented Mar 5, 2019

Apprently for Vaadin Framework 8.7, Push with web sockets works on Karaf 4.2.3 and the included Jetty 9.1.14

@denis-anisimov
Copy link
Author

How it can be seen ?
Do we have tests for this somewhere ?
Karaf is based on Felix though it uses another web server.

@pleku
Copy link
Contributor

pleku commented Mar 5, 2019

Apparently it is working fine in a customer project using 8.7.1 and the recently released Karaf version linked ^.

@denis-anisimov
Copy link
Author

denis-anisimov commented Mar 5, 2019

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.

@Sandared
Copy link

Sandared commented Mar 7, 2019

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

@Sandared
Copy link

Sandared commented Mar 7, 2019

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

@denis-anisimov
Copy link
Author

denis-anisimov commented Mar 7, 2019

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: vaadin/flow#5081 (comment)

@Sandared
Copy link

Sandared commented Mar 7, 2019

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.

@denis-anisimov
Copy link
Author

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.

@denis-anisimov
Copy link
Author

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.

@cosbadescu
Copy link

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

@Maurice-Betzel
Copy link

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.slf4j/vaadin-slf4j-jdk14/${vaadin.slf4j.jdk14.version}</bundle> <bundle start-level="80">mvn:com.vaadin.external.atmosphere/atmosphere-runtime/${vaadin.atmosphere.version}</bundle> <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>

@Maurice-Betzel
Copy link

Maurice-Betzel commented Mar 22, 2019

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

@Maurice-Betzel
Copy link

Maurice-Betzel commented Mar 22, 2019

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) ~[?:?]

@Maurice-Betzel
Copy link

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) ~[?:?]

@Maurice-Betzel
Copy link

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

@Maurice-Betzel
Copy link

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.

@Maurice-Betzel
Copy link

Maurice-Betzel commented Mar 22, 2019

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

@mpetzold
Copy link

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

@denis-anisimov
Copy link
Author

Here are also comments about WS in Karaf: #63 (comment)

@denis-anisimov
Copy link
Author

Moving this ticket into OSGi repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

6 participants