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

Make Push work with Portlets #200

Open
Tracked by #199
mshabarov opened this issue Feb 16, 2022 · 3 comments
Open
Tracked by #199

Make Push work with Portlets #200

mshabarov opened this issue Feb 16, 2022 · 3 comments

Comments

@mshabarov
Copy link
Contributor

mshabarov commented Feb 16, 2022

Vaadin Push functionality doesn't work with Vaadin 14 Portlet integration.

The following code used to enable Push in a portlet application:

public class MyPortlet extends VaadinPortlet<MyView> {

    @Override
    public WebComponentExporter<MyView> create() {
        return new MyExporter();
    }
    
    @Push
    private class MyExporter extends PortletWebComponentExporter {
        public MyExporter() {
            super(MyPortlet.this.getPortletTag());
        }
    }
}

doesn't work in both Apache Pluto and Liferay 7 (tested with 7.3) containers.

  • In Apache Pluto the portlet shows a black view and ends up with being not able to get the vaadinPush-min.js resource as shown below:

Screenshot 2022-02-16 at 14 59 14

  • In Liferay 7 it fails with (see attached file for a full exception stacktrace):
[Refresh Thread: Equinox Container: 80949cc8-3195-4193-bd29-0c65469ba3b7] ERROR org.atmosphere.cpr.DefaultAsyncSupportResolver - Real error: Unable to configure jsr356 at that stage. ServerContainer is null
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 java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        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:222)
        at com.vaadin.flow.server.communication.JSR356WebsocketInitializer.initAtmosphereForVaadinServlet(JSR356WebsocketInitializer.java:186)
        at com.vaadin.flow.server.communication.JSR356WebsocketInitializer.init(JSR356WebsocketInitializer.java:151)

Jsr356_exception.txt

and also with the failed request to vaadinPush-min.js:

image

In the Liferay case, the ServerContainer implementation cannot be found in the given ServletContext, here is a call sequence:

  1. Servlet container calls Vaadin's JSR356WebsocketInitializer::contextInitialized
  2. Then PushRequestHandler::initAtmosphere is called and the atmosphere framework is being customized
  3. Atmosphere framework is being initialized then via AtmosphereFramework::init
  4. Atmosphere framework tries to instantiate a JSR356AsyncSupport but throws because the servlerContainer is not found:
ServerContainer container = (ServerContainer) ctx.getAttribute(ServerContainer.class.getName());

        if (container == null) {
            if (ctx.getServerInfo().contains("WebLogic")) {
                logger.error("{} must use JDK 1.8+ with WebSocket", ctx.getServerInfo());
            }
            throw new IllegalStateException("Unable to configure jsr356 at that stage. ServerContainer is null");
        }

Liferay 7.3 provides a WebSocket implementation already, according to https://help.liferay.com/hc/en-us/articles/360018161191-Liferay-WebSocket-Whiteboard , but it seems the servlet context the atmosphere framework is looking into, has no server container, but it is available in the OSGi context, for instance if the Liferay puts the ServerContainer into one ServletContext object, but the portlet uses another object (this is my raw guess).

Vaadin OSGi integration has a similar Push issue, so the problem might be in OSGi specifics.

This ticket is an investigation ticket and doesn't contain any certain suggestions about the feature support implementation so far.

While it is an issue for a Vaadin Portlet and if it an option for you to replace server-to-client async updates in your project, you can use client-to-server async updates like shown below:

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        super.onAttach(attachEvent);
        attachEvent.getUI().setPollInterval(POLL_INTERVAL);
        pollListenerRegistration = attachEvent.getUI().addPollListener(event -> {
                 // do UI components updates when the client sends an async request to server
        });
    }

    @Override
    protected void onDetach(DetachEvent detachEvent) {
        if (pollListenerRegistration != null) {
            pollListenerRegistration.remove();
            pollListenerRegistration = null;
            detachEvent.getUI().setPollInterval(-1);
        }
        super.onDetach(detachEvent);
    }

    // do UI updates in the background thread:
    ui.access(() -> {
          // update UI components
          textField.setValue("foo");
    }); 
@mcollovati
Copy link
Contributor

The issue was triaged and currently added to the backlog priority queue for further investigation

@tltv tltv self-assigned this Apr 24, 2023
@mshabarov mshabarov moved this from Product backlog to In progress in OLD Vaadin Flow ongoing work (Vaadin 10+) Apr 26, 2023
@mshabarov mshabarov removed the BFP label Apr 26, 2023
@tltv
Copy link
Member

tltv commented Apr 27, 2023

Running Liferay with -Dvaadin.disable.automatic.servlet.registration=true helps to get rid of some extra noise like reported in #222 when Push is disabled. Verified with simple test cases in main branch with Vaadin 23.3. Vaadin 14 has the same property so it should help with Vaadin portlet version 1.0.0 too.

@tltv
Copy link
Member

tltv commented Apr 28, 2023

Findings regarding enabling Push for portlets with main branch (Vaadin 23.3) and Liferay 7.4:

  1. As written in description, WebSocket init fails to IllegalStateException from JSR356AsyncSupport. Its a result from ServletContext#getAttribute(ServerContainer.class.getName()) call which ends up (through proxies and wrappers) to org.eclipse.equinox.http.servlet.internal.ServletContextAdaptor#getAttribute(String) which can't find the ServerContainer even though it is available in the 'real' ServletContext that this object wraps.

Could this be a bug in Equinox or maybe a missing OSGi configuration in portlet app? Needs more investigation to answer these. Forcing ServerContainer to be available for JSR356AsyncSupport lead to other error so this is not the only obstacle.

  1. WebSockets can be suppressed with org.atmosphere.websocket.suppressJSR356=true init parameter for the Atmosphere which will end up using Long Polling instead and initialization works without errors. Looks like Push with Long polling could work easier. It would make sense to start to make Long polling work first.

  2. Portlet's client side is not setup with correct paths regarding Push client (vaadinPush-min.js), so this may need changes in Flow's Push Java API to make it possible to change paths easier for portlets. This issue makes it currently harder to verify if Push is setup correctly and works when suppressJSR356 is enabled.

  3. There's also open question if we really need to create Vaadin servlet instance at all for portlet just to get Flow's Push feature to work or should we write completely new initialization code for the portlet's Push instead and disable automatic Vaadin servlet creation permanently.

@mshabarov mshabarov moved this from In progress to Product backlog in OLD Vaadin Flow ongoing work (Vaadin 10+) May 3, 2023
@mshabarov mshabarov moved this from Product backlog to Parking lot - under consideration in OLD Vaadin Flow ongoing work (Vaadin 10+) Jun 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request investigation
Projects
OLD Vaadin Flow ongoing work (Vaadin ...
  
Parking lot - under consideration
Development

No branches or pull requests

3 participants