[SOLVED] How to get rid of EventBus NPE for any situation

Push Framework with Atmosphere
Post Reply
Miguel Cubells
Posts: 90
Joined: 25 Feb 2015, 11:02

05 Jan 2017, 05:31

Hi all.

I also suffered the EventBus NPE problems starting from PF 5.2+, and finally found a solution that seems to work in all cases, with any Application Server.
Therefore I would like to share my findings, should they be useful to others still suffering the same problems.

The key points are:

- Use the original Atmosphere @ManagedService annotation, instead of the @PushEndpoint one provided by primefaces.
- Use a Singleton wrapper (either Spring bean or CDI/EJB) for initializing the PrimeFaces EventBus with an Atmosphere MetaBroadcaster.
- Initialize the Atmosphere servlet in the web.xml, instead of the PrimeFaces PushServlet, for serving the <p:socket> connections.

So let's go...

1. Singleton wrapper for initializing the EventBus. In my case I use a Spring Bean, but it could be perfectly a CDI or EJB.
You may need to synchronize on the "setDefault" method.

Code: Select all

package test.websockets;

import org.atmosphere.cpr.MetaBroadcaster;
import org.primefaces.push.EventBus;
import org.primefaces.push.impl.EventBusImpl;

/**
 * A Factory for retrieving the current {@link EventBus} for PUSHing events.
 */
public class CustomEventBusFactory {

    private static CustomEventBusFactory p = null;
    private EventBus eventBus;

    protected CustomEventBusFactory() {

    }
    
    protected CustomEventBusFactory(MetaBroadcaster mb) {
    	eventBus = new EventBusImpl(mb);
    	p = this;
    }

    /**
     * Return the default factory
     * @return the default factory
     */
    public final static CustomEventBusFactory getDefault() {
    	return p;
    }
    
    public final static void setDefault(MetaBroadcaster mb) {
    	p = new CustomEventBusFactory(mb);    	
    }

    /**
     * Return a {@link EventBus}
     * @return a {@link EventBus}
     */
    public EventBus eventBus() {
        return eventBus;
    }
}
2. Setup the Atmosphere Servlet in web.xml, instead of the PushServlet of PrimeFaces.
In my case, I am using Spring Boot, so the configuration is Java based, but it should be straightforward to translate it into web.xml tags.

Code: Select all

        import org.atmosphere.cpr.AtmosphereServlet;

        private void configureAtmosphere(ServletContext servletContext, AtmosphereServlet servlet) {
            final ServletRegistration.Dynamic atmosphereServlet = servletContext.addServlet("atmosphereServlet", servlet);
            atmosphereServlet.setInitParameter(ApplicationConfig.ANNOTATION_PACKAGE, PushEndpoint.class.getPackage().getName());            
            atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_CACHE, UUIDBroadcasterCache.class.getName());
            atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_SHARABLE_THREAD_POOLS, "true");
            atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_MESSAGE_PROCESSING_THREADPOOL_MAXSIZE, "10");
            atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_ASYNC_WRITE_THREADPOOL_MAXSIZE, "10");           
            //atmosphereServlet.setInitParameter(ApplicationConfig.WEBSOCKET_SUPPORT, "true");
            //atmosphereServlet.setInitParameter(ApplicationConfig.WEBSOCKET_SUPPORT_SERVLET3, "false");
            //atmosphereServlet.setInitParameter(ApplicationConfig.PROPERTY_COMET_SUPPORT, "org.atmosphere.container.JSR356AsyncSupport");
            atmosphereServlet.setInitParameter(ApplicationConfig.READ_GET_BODY, "true");
            servletContext.addListener(new org.atmosphere.cpr.SessionSupport());
            
            atmosphereServlet.addMapping("/primepush/*");
            atmosphereServlet.setLoadOnStartup(2);
            atmosphereServlet.setAsyncSupported(true);
	}
3. Develop an Endpoint (counter example) using the @ManagedService as follows:
Key point is to initialize the CustomEventBusFactory with the MetaBroadcaster from Atmosphere.

Code: Select all

package test.websockets;

import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.Message;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.primefaces.push.impl.JSONDecoder;
import org.primefaces.push.impl.JSONEncoder;

/**
 * We have to use Atmosphere's @ManagedService
 */
@ManagedService(path = "/counter")
public class CounterEndpoint {
 
	@Ready
	public void init(final AtmosphereResource resource) {
		
		// a wrapper around the original PrimeFaces EventBusFactory is needed.
		// we need to ensure we set the event bus with the correct broadcaster,
		// so the EventBus can be retrieved for later use.  The "setDefault" method would probably need to be synchronized, 
                // to avoid several endpoints trying to initialize the metabroadcaster on the singleton.
		if (CustomEventBusFactory.getDefault() == null) {
			CustomEventBusFactory.setDefault(resource.getAtmosphereConfig().metaBroadcaster());
		}
	}
	
	@Message(encoders = {JSONEncoder.class}, decoders = {JSONDecoder.class})
         public String onMessage(String count) {
                return count;
         }
}
4. Setup the websocket from the client with the <p:socket>, as usual, using the correct endpoint path:

Code: Select all

        <p:socket onMessage="handleMessage" channel="/counter" />
5. And finally... how to PUSH an event from a managed bean...
In the below example I will increment a counter and sent it as a push message.

Code: Select all

   public void increment() {
        count++;
        EventBus eventBus = CustomEventBusFactory.getDefault().eventBus();
        eventBus.publish("/counter", String.valueOf(count));
    }
And that's all...
This solution is working fine, both using Spring / Spring Boot, or standard JEE 6/7 with CDI (doing the necessary adjustments for each environment, of course).

I hope this can be useful to anyone there still crushing their melons trying to figure out how to get rid of the EventBus NPE ...
There may be easier solutions, but this one worked for me in every scenario...

Cheers !
PrimeFaces 5.3 / PF Extensions 4.0.0 / Atmosphere 2.4.3
Apache MyFaces 2.2.7
WildFly 8.2.0.Final / IBM WebSphere 9.x

Post Reply
  • Information
  • Who is online

    Users browsing this forum: No registered users and 2 guests