Vaadin 7.1 Push and Servlet filters

Hi all.

I’m trying to integrate Vaadin 7.1 and Push with Guice (collaborating in this project : https://github.com/davidsowerby/v7).

Guice heavily rely on servlet filters but vaadin push will not call them. In fact vaadin register a PushHandler, which is a AtmosphereHandler and from #onRequest(AtmosphereResource resource) will call new ServerRpcHandler().handleRpc(ui, reader, vaadinRequest);

How can i call filters ?

Hi,

The actual push messages are typically sent over a websockets connection and are not using a servlet at all. Servlet filter therefore won’t work there as there isn’t even a servlet request. Sounds like you need to invoke the same code as the filters invoke for instance in PushHandler.onRequest (disclaimer: I have no idea how Guice works internally)

There is the servlet request:
In PushHandler#onRequest(AtmosphereResource resource) the PushHandler call AtmosphereRequest req = resource.getRequest(); which return a AtmosphereRequest and Atmosphere is told by PushRequestHandler to keep the session: atmosphere.addInitParameter(ApplicationConfig.PROPERTY_SESSION_SUPPORT,
“true”);

It is odd. It will not even use the standard filter chain, but an injected one that will call all the filters (in my case: GuiceFilter, GuicePersistenceFilter, ShiroFilter) and then the Servlet.

I had also thought of “simulate” a POST call on the servlet from the PushHandler, this way it’s still push, but will route trough the servlet.
But i don’t know how to call a PUSH request on the servet.

The request in AtmosphereResource is a saved copy of the initial Websocket handshake request that is sent when the connection is opened. It looks like HTTP for compatibility reasons. After connecting, any subsequent client-to-server communication completely bypasses any HTTP request handling.

forgive this delay, but i got it:

That’s true, but even if it’s just a copy, it still hold the session id.

I’ve been quiet for a while, while i was trying to get this to work, and now i get it.
It’s not general or pretty, but with some help i think it would be a great addition to the framework.

Here’s how it works:

A simple VaadinServlet extension:

public class MyServlet extends VaadinServlet {

  // the push handler
	FilterablePushHandler handler;
	
	@Override
	protected VaadinServletService createServletService(
			DeploymentConfiguration deploymentConfiguration)
			throws ServiceException {
		VaadinServletService service = super.createServletService(deploymentConfiguration);
		
		final AtmosphereFramework framework = DefaultBroadcasterFactory.getDefault().lookup("/*").getBroadcasterConfig().getAtmosphereConfig().framework();
		
		//replace the handler registered by vaadin with thi one
		handler = new FilterablePushHandler(service);
		framework.addAtmosphereHandler("/*", handler);
		
		assert service.ensurePushAvailable() == true;
		return service;
	}

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//the handler will sett this attribute prior to call the filterchain (that will conclude with the servlet)
		Object p = request.getAttribute(FilterablePushHandler.WEB_SOCKET_PUSH_REROUTED);

		if(p != null && p.equals(true)){
		  //the handler also pass the AtmosphereResource
			AtmosphereResource resource = (AtmosphereResource) request.getAttribute(FilterablePushHandler.ATMOSPHERE_RESOURCE);
			handler.onRequest(resource);
			
			request.removeAttribute(FilterablePushHandler.WEB_SOCKET_PUSH_REROUTED);
			request.removeAttribute(FilterablePushHandler.ATMOSPHERE_RESOURCE);
		}else{
		  // if this is not a "simulated" request from the push handler i should handle normally
			super.service(request, response);
		}
	}
}

The pushHandler

public class FilterablePushHandler extends PushHandler {
	
	public static final String WEB_SOCKET_PUSH_REROUTED = "com.vaadin.server.communication.FilterablePushHandler.WEB_SOCKET_PUSH_REROUTED";
	public static final String ATMOSPHERE_RESOURCE = "com.vaadin.server.communication.FilterablePushHandler.ATMOSPHERE_RESOURCE";
	
	public FilterablePushHandler(VaadinServletService service) {
		super(service);
	}

	@Override
	public void onRequest(AtmosphereResource resource) {
		AtmosphereRequest req = resource.getRequest();

        if (req.getMethod().equalsIgnoreCase("GET")) {
            //if it's a connection inizialization request i handle it normally
            super.onRequest(resource);
        } else if (req.getMethod().equalsIgnoreCase("POST")) {
          //if it's a push request, execute first the filterChain
          
        	Object p = resource.getRequest().getAttribute(WEB_SOCKET_PUSH_REROUTED);
        	if(p != null && p.equals(true)){
        	  //if this has been flagged as push request (and the filters has been already runned)
        	  //handle the request with the original handler...
        		super.onRequest(resource);
        	}else{        	
	        	try {
	        	  //...else flag this ad handled (filterChain executed)
	        		resource.getRequest().setAttribute(WEB_SOCKET_PUSH_REROUTED, true);
	        		resource.getRequest().setAttribute(ATMOSPHERE_RESOURCE, resource);
	        		
	        		//Retrieve the guiceFilter instance and call it.
	        		//It's the first filter in the chain, and will esecute an internal chain (not the default one)
              MyGuiceFilter.instance.doFilter(resource.getRequest(), resource.getResponse(), new FilterChain() {						
                @Override
                public void doFilter(ServletRequest arg0, ServletResponse arg1)
                    throws IOException, ServletException {
                  //will never be called (guice execute internal filterChain and skip this)
                  assert false;
                }
              });
	        		
				} catch (IOException | ServletException e) {
					e.printStackTrace();
					assert false;
				}
        	}
        }
	}
	
}

I need to find a better way…

//this simple let me get the filter instance
public class MyGuiceFilter extends GuiceFilter {

	public static EScrumGuiceFilter instance = null;
	
	public EScrumGuiceFilter() {
		super();
		
		assert instance == null;
		instance = this;
	}
	
}

Here it is. Ugly and Broken as it is, but a starting poin. Is Anyone interested in helping with to put this in the framework?
I think the way push work right now is broken…this should fix.

Yes, and it’s fine if you only need that. But with regular HTTP traffic, many things change from request to request, and not all filter use cases work as expected when there’s only the single stored HTTP request available.

Anyway, it’s true that there are probably lots of use cases for push listeners, both client-to-server and server-to-client. You can actually configure an AtmosphereResourceEventListener as an init parameter right now, but there should probably be a runtime listener API in Vaadin that abstracts out Atmosphere.

You’re right. Probably i’d better find a way to initialize what the filters now initialize, without going through the filters.

Hi guys

The above solution with push filter was exactly what I needed, and I used it with succes until Vaadin 7.2.0.
Until then the PushHandler implemented interface org.atmosphere.cpr.AtmosphereHandler, but for some reason this no longer holds. From Vaadin 7.2.0 PushHandler no longer implements AtmosphereHandler and thus neither the onRequest() method.

Does anyone in here have an alternative solution on how to implement push filtering?

Thanks in advance

All messages, including websockets messages, now (at least in 7.3, can’t remember if it was in 7.2) go through VaadinService.requestStart/requestEnd. Does this help?

Took me a while to find out, how to override requestStart/requestEnd, I found it here:
https://vaadin.com/forum#!/thread/3829798/3829962
. It’s actually a VaadinServletService class, not VaadinService.

I can confirm, that this way it works.

Hello,
Is it possible to open a vaadin app from an external web portal which will redirect the user to a vaadin app with a specific GET request?