diff options
Diffstat (limited to 'server/src')
-rw-r--r-- | server/src/com/vaadin/server/communication/PushHandler.java | 236 |
1 files changed, 143 insertions, 93 deletions
diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java index bfa1067aa8..a9e6c17751 100644 --- a/server/src/com/vaadin/server/communication/PushHandler.java +++ b/server/src/com/vaadin/server/communication/PushHandler.java @@ -32,6 +32,7 @@ import org.json.JSONException; import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException; import com.vaadin.server.ServiceException; import com.vaadin.server.SessionExpiredException; +import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinService; import com.vaadin.server.VaadinServletRequest; import com.vaadin.server.VaadinServletService; @@ -47,7 +48,98 @@ import com.vaadin.ui.UI; */ public class PushHandler implements AtmosphereHandler { + /** + * Callback interface used internally to process an event with the + * corresponding UI properly locked. + */ + private interface PushEventCallback { + public void run(AtmosphereResource resource, UI ui) throws IOException; + } + + /** + * Callback used when we receive a request to establish a push channel for a + * UI. Associate the AtmosphereResource with the UI and leave the connection + * open by calling resource.suspend(). If there is a pending push, send it + * now. + */ + private static PushEventCallback establishCallback = new PushEventCallback() { + @Override + public void run(AtmosphereResource resource, UI ui) throws IOException { + getLogger().log(Level.FINER, + "New push connection with transport {0}", + resource.transport()); + resource.getResponse().setContentType("text/plain; charset=UTF-8"); + if (resource.transport() == TRANSPORT.STREAMING) { + // IE8 requires a longer padding to work properly if the + // initial message is small (#11573). Chrome does not work + // without the original padding... + WebBrowser browser = ui.getSession().getBrowser(); + if (browser.isIE() && browser.getBrowserMajorVersion() == 8) { + resource.padding(LONG_PADDING); + } + } + resource.suspend(); + + AtmospherePushConnection connection = new AtmospherePushConnection( + ui); + connection.connect(resource); + + ui.setPushConnection(connection); + } + }; + + /** + * Callback used when we receive a UIDL request through Atmosphere. If the + * push channel is bidirectional (websockets), the request was sent via the + * same channel. Otherwise, the client used a separate AJAX request. Handle + * the request and send changed UI state via the push channel (we do not + * respond to the request directly.) + */ + private static PushEventCallback receiveCallback = new PushEventCallback() { + @Override + public void run(AtmosphereResource resource, UI ui) throws IOException { + AtmosphereRequest req = resource.getRequest(); + + AtmospherePushConnection connection = getConnectionForUI(ui); + + assert connection != null : "Got push from the client " + + "even though the connection does not seem to be " + + "valid. This might happen if a HttpSession is " + + "serialized and deserialized while the push " + + "connection is kept open or if the UI has a " + + "connection of unexpected type."; + + // Should be set up by caller + VaadinRequest vaadinRequest = VaadinService.getCurrentRequest(); + assert vaadinRequest != null; + + try { + new ServerRpcHandler().handleRpc(ui, req.getReader(), + vaadinRequest); + connection.push(false); + } catch (JSONException e) { + getLogger().log(Level.SEVERE, "Error writing JSON to response", + e); + // Refresh on client side + connection + .sendMessage(VaadinService + .createCriticalNotificationJSON(null, null, + null, null)); + } catch (InvalidUIDLSecurityKeyException e) { + getLogger().log(Level.WARNING, + "Invalid security key received from {0}", + resource.getRequest().getRemoteHost()); + // Refresh on client side + connection + .sendMessage(VaadinService + .createCriticalNotificationJSON(null, null, + null, null)); + } + } + }; + private static final String LONG_PADDING; + static { char[] array = new char[4096]; Arrays.fill(array, '-'); @@ -60,109 +152,67 @@ public class PushHandler implements AtmosphereHandler { this.service = service; } - @Override - public void onRequest(AtmosphereResource resource) { - + /** + * Find the UI for the atmosphere resource, lock it and invoke the callback. + * + * @param resource + * the atmosphere resource for the current request + * @param callback + * the push callback to call when a UI is found and locked + */ + private void callWithUi(final AtmosphereResource resource, + final PushEventCallback callback) { AtmosphereRequest req = resource.getRequest(); VaadinServletRequest vaadinRequest = new VaadinServletRequest(req, service); + VaadinSession session = null; - VaadinSession session; - try { - session = service.findVaadinSession(vaadinRequest); - } catch (ServiceException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return; - } catch (SessionExpiredException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - return; - } - - session.lock(); + service.requestStart(vaadinRequest, null); try { - UI ui = service.findUI(vaadinRequest); - if (ui == null) { - // This should not happen - getLogger().warning( - "Could not find the requested UI in session"); + try { + session = service.findVaadinSession(vaadinRequest); + } catch (ServiceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return; + } catch (SessionExpiredException e) { + // TODO Auto-generated catch block + e.printStackTrace(); return; } - assert ui.getPushMode().isEnabled(); - - if (req.getMethod().equalsIgnoreCase("GET")) { - /* - * We received a request to establish a push channel for a UI. - * Associate the AtmosphereResource with the UI and leave the - * connection open by calling resource.suspend(). If there is a - * pending push, send it now. - */ - getLogger().log(Level.FINER, - "New push connection with transport {0}", - resource.transport()); - resource.getResponse().setContentType( - "text/plain; charset=UTF-8"); - if (resource.transport() == TRANSPORT.STREAMING) { - // IE8 requires a longer padding to work properly if the - // initial message is small (#11573). Chrome does not work - // without the original padding... - WebBrowser browser = session.getBrowser(); - if (browser.isIE() && browser.getBrowserMajorVersion() == 8) { - resource.padding(LONG_PADDING); - } - } - resource.suspend(); - - AtmospherePushConnection connection = new AtmospherePushConnection( - ui); - connection.connect(resource); - - ui.setPushConnection(connection); - } else if (req.getMethod().equalsIgnoreCase("POST")) { - AtmospherePushConnection connection = getConnectionForUI(ui); - - assert connection != null : "Got push from the client " - + "even though the connection does not seem to be " - + "valid. This might happen if a HttpSession is " - + "serialized and deserialized while the push " - + "connection is kept open or if the UI has a " - + "connection of unexpected type."; - - /* - * We received a UIDL request through Atmosphere. If the push - * channel is bidirectional (websockets), the request was sent - * via the same channel. Otherwise, the client used a separate - * AJAX request. Handle the request and send changed UI state - * via the push channel (we do not respond to the request - * directly.) - */ - try { - new ServerRpcHandler().handleRpc(ui, req.getReader(), - vaadinRequest); - connection.push(false); - } catch (JSONException e) { - getLogger().log(Level.SEVERE, - "Error writing JSON to response", e); - // Refresh on client side - connection.sendMessage(VaadinService - .createCriticalNotificationJSON(null, null, null, - null)); - } catch (InvalidUIDLSecurityKeyException e) { - getLogger().log(Level.WARNING, - "Invalid security key received from {0}", - resource.getRequest().getRemoteHost()); - // Refresh on client side - connection.sendMessage(VaadinService - .createCriticalNotificationJSON(null, null, null, - null)); + + session.lock(); + try { + VaadinSession.setCurrent(session); + // Sets UI.currentInstance + final UI ui = service.findUI(vaadinRequest); + if (ui == null) { + // This should not happen + getLogger().warning( + "Could not find the requested UI in session"); + return; } + + callback.run(resource, ui); + } catch (IOException e) { + getLogger().log(Level.INFO, + "An error occured while writing a push response", e); + } finally { + session.unlock(); } - } catch (IOException e) { - getLogger().log(Level.INFO, - "An error occured while writing a push response", e); } finally { - session.unlock(); + service.requestEnd(vaadinRequest, null, session); + } + } + + @Override + public void onRequest(AtmosphereResource resource) { + AtmosphereRequest req = resource.getRequest(); + + if (req.getMethod().equalsIgnoreCase("GET")) { + callWithUi(resource, establishCallback); + } else if (req.getMethod().equalsIgnoreCase("POST")) { + callWithUi(resource, receiveCallback); } } |