]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fixed locking in RequestHandlers and documented that RequestHandlers are called witho...
authorArtur Signell <artur@vaadin.com>
Fri, 22 Mar 2013 15:55:15 +0000 (17:55 +0200)
committerArtur Signell <artur@vaadin.com>
Tue, 2 Apr 2013 07:47:49 +0000 (10:47 +0300)
* Added SynchronizedRequestHandler which locks the session for the whole request
* Made request handlers which do not do do heavy IO implement SynchronizedRequestHandler
* Fixed locking for GlobalResourceHandler, ConnectorResourceHandler, PublishedFileHandler

Change-Id: I0e7b442a9392828f1930685f194dc4f898d0f525

22 files changed:
server/src/com/vaadin/server/AbstractClientConnector.java
server/src/com/vaadin/server/BootstrapHandler.java
server/src/com/vaadin/server/ClientConnector.java
server/src/com/vaadin/server/ConnectorResource.java
server/src/com/vaadin/server/ConnectorResourceHandler.java
server/src/com/vaadin/server/DownloadStream.java
server/src/com/vaadin/server/FileDownloader.java
server/src/com/vaadin/server/GlobalResourceHandler.java
server/src/com/vaadin/server/LegacyCommunicationManager.java
server/src/com/vaadin/server/RequestHandler.java
server/src/com/vaadin/server/SynchronizedRequestHandler.java [new file with mode: 0644]
server/src/com/vaadin/server/UnsupportedBrowserHandler.java
server/src/com/vaadin/server/VaadinPortlet.java
server/src/com/vaadin/server/VaadinServlet.java
server/src/com/vaadin/server/communication/HeartbeatHandler.java
server/src/com/vaadin/server/communication/PortletListenerNotifier.java
server/src/com/vaadin/server/communication/PublishedFileHandler.java
server/src/com/vaadin/server/communication/SessionRequestHandler.java [new file with mode: 0644]
server/src/com/vaadin/server/communication/UIInitHandler.java
server/src/com/vaadin/server/communication/UidlRequestHandler.java
server/src/com/vaadin/ui/AbstractMedia.java
server/src/com/vaadin/ui/LoginForm.java

index 79ccbd8f0bee5329e89ec722b2deb007340c96ef..e998b8ed55ba9de8bd5ce7f446e2e7257dac2aab 100644 (file)
@@ -645,17 +645,22 @@ public abstract class AbstractClientConnector implements ClientConnector,
     @Override
     public boolean handleConnectorRequest(VaadinRequest request,
             VaadinResponse response, String path) throws IOException {
+        DownloadStream stream = null;
         String[] parts = path.split("/", 2);
         String key = parts[0];
 
-        ConnectorResource resource = (ConnectorResource) getResource(key);
-        if (resource != null) {
-            DownloadStream stream = resource.getStream();
-            stream.writeResponse(request, response);
-            return true;
-        } else {
-            return false;
+        getSession().lock();
+        try {
+            ConnectorResource resource = (ConnectorResource) getResource(key);
+            if (resource == null) {
+                return false;
+            }
+            stream = resource.getStream();
+        } finally {
+            getSession().unlock();
         }
+        stream.writeResponse(request, response);
+        return true;
     }
 
     /**
index 03421ce4874bb681b71dada024f08c9a7bf31c36..9c603ae7ed696ee0b823a2b196b305227ee26be6 100644 (file)
@@ -51,7 +51,7 @@ import com.vaadin.ui.UI;
  * @deprecated As of 7.0. Will likely change or be removed in a future version
  */
 @Deprecated
-public abstract class BootstrapHandler implements RequestHandler {
+public abstract class BootstrapHandler extends SynchronizedRequestHandler {
 
     protected class BootstrapContext implements Serializable {
 
@@ -113,8 +113,8 @@ public abstract class BootstrapHandler implements RequestHandler {
     }
 
     @Override
-    public boolean handleRequest(VaadinSession session, VaadinRequest request,
-            VaadinResponse response) throws IOException {
+    public boolean synchronizedHandleRequest(VaadinSession session,
+            VaadinRequest request, VaadinResponse response) throws IOException {
 
         try {
             List<UIProvider> uiProviders = session.getUIProviders();
index 294f94f2be1ed4e90eaa0ba0c820e14a17afc2a2..3b52fbc730e3cf0566b30c6fc5c80637c6cb81c9 100644 (file)
@@ -318,8 +318,12 @@ public interface ClientConnector extends Connector {
      * routed to this method with the remaining part of the requested path
      * available in the path parameter.
      * <p>
-     * {@link DynamicConnectorResource} can be used to easily make an
-     * appropriate URL available to the client-side code.
+     * NOTE that the session is not locked when this method is called. It is the
+     * responsibility of the connector to ensure that the session is locked
+     * while handling state or other session related data. For best performance
+     * the session should be unlocked before writing a large response to the
+     * client.
+     * </p>
      * 
      * @param request
      *            the request that should be handled
index 8f8591e6b1fe0b9eef8cca38095b792b07c4b97c..8682f8ce6fe0b346e6b8a2e2fbe856527b4c8b20 100644 (file)
@@ -30,6 +30,15 @@ public interface ConnectorResource extends Resource {
 
     /**
      * Gets resource as stream.
+     * <p>
+     * Note that this method is called while the session is locked to prevent
+     * race conditions but the methods in the returned {@link DownloadStream}
+     * are assumed to be unrelated to the VaadinSession and are called without
+     * holding session locks (to prevent locking the session during long file
+     * downloads).
+     * </p>
+     * 
+     * @return A download stream which produces the resource content
      */
     public DownloadStream getStream();
 
index 03a2fcc115fdd0341b5f3dd01e893810344d0647..61631c3d23cab74c41a7198c993c1c10ba4a3c9c 100644 (file)
@@ -16,6 +16,8 @@
 package com.vaadin.server;
 
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Matcher;
@@ -25,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import com.vaadin.shared.ApplicationConstants;
 import com.vaadin.ui.UI;
+import com.vaadin.util.CurrentInstance;
 
 public class ConnectorResourceHandler implements RequestHandler {
     // APP/connector/[uiid]/[cid]/[filename.xyz]
@@ -50,30 +53,41 @@ public class ConnectorResourceHandler implements RequestHandler {
             String uiId = matcher.group(1);
             String cid = matcher.group(2);
             String key = matcher.group(3);
-            UI ui = session.getUIById(Integer.parseInt(uiId));
-            if (ui == null) {
-                return error(request, response,
-                        "Ignoring connector request for no-existent root "
-                                + uiId);
-            }
 
-            UI.setCurrent(ui);
-            VaadinSession.setCurrent(ui.getSession());
+            session.lock();
+            UI ui;
+            ClientConnector connector;
+            try {
+                ui = session.getUIById(Integer.parseInt(uiId));
+                if (ui == null) {
+                    return error(request, response,
+                            "Ignoring connector request for no-existent root "
+                                    + uiId);
+                }
+
+                connector = ui.getConnectorTracker().getConnector(cid);
+                if (connector == null) {
+                    return error(request, response,
+                            "Ignoring connector request for no-existent connector "
+                                    + cid + " in root " + uiId);
+                }
 
-            ClientConnector connector = ui.getConnectorTracker().getConnector(
-                    cid);
-            if (connector == null) {
-                return error(request, response,
-                        "Ignoring connector request for no-existent connector "
-                                + cid + " in root " + uiId);
+            } finally {
+                session.unlock();
             }
 
-            if (!connector.handleConnectorRequest(request, response, key)) {
-                return error(request, response, connector.getClass()
-                        .getSimpleName()
-                        + " ("
-                        + connector.getConnectorId()
-                        + ") did not handle connector request for " + key);
+            Map<Class<?>, Object> oldThreadLocals = new HashMap<Class<?>, Object>();
+            CurrentInstance.setThreadLocals(ui, oldThreadLocals);
+            try {
+                if (!connector.handleConnectorRequest(request, response, key)) {
+                    return error(request, response,
+                            connector.getClass().getSimpleName() + " ("
+                                    + connector.getConnectorId()
+                                    + ") did not handle connector request for "
+                                    + key);
+                }
+            } finally {
+                CurrentInstance.setThreadLocals(oldThreadLocals);
             }
 
             return true;
index e2f9fc5296e36d6c119142525d2f5fa1a84198b4..4e66831f1df6062ce8dafd6ab5d7cfec42c404ab 100644 (file)
@@ -28,6 +28,11 @@ import javax.servlet.http.HttpServletResponse;
 
 /**
  * Downloadable stream.
+ * <p>
+ * Note that the methods in a DownloadStream are called without locking the
+ * session to prevent locking the session during long file downloads. If your
+ * DownloadStream uses anything from the session, you must handle the locking.
+ * </p>
  * 
  * @author Vaadin Ltd.
  * @since 3.0
index 7cc1fd7cc8a5af3bd4e3701b9c752b20b63fdb68..9b49ad8edddd0186bb9c8cb9fc21ac95a2a08e15 100644 (file)
@@ -129,10 +129,15 @@ public class FileDownloader extends AbstractExtension {
             // Ignore if it isn't for us
             return false;
         }
+        getSession().lock();
+        DownloadStream stream;
 
-        Resource resource = getFileDownloadResource();
-        if (resource instanceof ConnectorResource) {
-            DownloadStream stream = ((ConnectorResource) resource).getStream();
+        try {
+            Resource resource = getFileDownloadResource();
+            if (!(resource instanceof ConnectorResource)) {
+                return false;
+            }
+            stream = ((ConnectorResource) resource).getStream();
 
             if (stream.getParameter("Content-Disposition") == null) {
                 // Content-Disposition: attachment generally forces download
@@ -140,15 +145,15 @@ public class FileDownloader extends AbstractExtension {
                         "attachment; filename=\"" + stream.getFileName() + "\"");
             }
 
-            // Content-Type to block eager browser plug-ins from hijacking the
-            // file
+            // Content-Type to block eager browser plug-ins from hijacking
+            // the file
             if (isOverrideContentType()) {
                 stream.setContentType("application/octet-stream;charset=UTF-8");
             }
-            stream.writeResponse(request, response);
-            return true;
-        } else {
-            return false;
+        } finally {
+            getSession().unlock();
         }
+        stream.writeResponse(request, response);
+        return true;
     }
 }
index 0fac14e20c6c438a0e46d8e9df9fb1dd0cbec7d7..d411b286d0f2ce3e327b9319c7c722f4f526a406 100644 (file)
@@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletResponse;
 import com.vaadin.shared.ApplicationConstants;
 import com.vaadin.ui.LegacyComponent;
 import com.vaadin.ui.UI;
+import com.vaadin.util.CurrentInstance;
 
 /**
  * A {@link RequestHandler} that takes care of {@link ConnectorResource}s that
@@ -85,30 +86,38 @@ public class GlobalResourceHandler implements RequestHandler {
             return error(request, response, pathInfo
                     + " is not a valid global resource path");
         }
+        session.lock();
+        Map<Class<?>, CurrentInstance> oldThreadLocals = null;
+        DownloadStream stream = null;
+        try {
+            UI ui = session.getUIById(Integer.parseInt(uiid));
+            if (ui == null) {
+                return error(request, response, "No UI found for id  " + uiid);
+            }
+            oldThreadLocals = CurrentInstance.setThreadLocals(ui);
+            ConnectorResource resource;
+            if (LEGACY_TYPE.equals(type)) {
+                resource = legacyResources.get(key);
+            } else {
+                return error(request, response, "Unknown global resource type "
+                        + type + " in requested path " + pathInfo);
+            }
 
-        UI ui = session.getUIById(Integer.parseInt(uiid));
-        if (ui == null) {
-            return error(request, response, "No UI found for id  " + uiid);
-        }
-        UI.setCurrent(ui);
-
-        ConnectorResource resource;
-        if (LEGACY_TYPE.equals(type)) {
-            resource = legacyResources.get(key);
-        } else {
-            return error(request, response, "Unknown global resource type "
-                    + type + " in requested path " + pathInfo);
-        }
-
-        if (resource == null) {
-            return error(request, response, "Global resource " + key
-                    + " not found");
-        }
+            if (resource == null) {
+                return error(request, response, "Global resource " + key
+                        + " not found");
+            }
 
-        DownloadStream stream = resource.getStream();
-        if (stream == null) {
-            return error(request, response, "Resource " + resource
-                    + " didn't produce any stream.");
+            stream = resource.getStream();
+            if (stream == null) {
+                return error(request, response, "Resource " + resource
+                        + " didn't produce any stream.");
+            }
+        } finally {
+            session.unlock();
+            if (oldThreadLocals != null) {
+                CurrentInstance.restoreThreadLocals(oldThreadLocals);
+            }
         }
 
         stream.writeResponse(request, response);
index 2b99da78289d8811e32d585dfd15f6d90b155237..b956b806bd7e3d9f2849d1c6423cff1bea0cf896 100644 (file)
@@ -237,19 +237,17 @@ public class LegacyCommunicationManager implements Serializable {
     }
 
     private String registerPublishedFile(String name, Class<?> context) {
-        synchronized (publishedFileContexts) {
-            // Add to map of names accepted by servePublishedFile
-            if (publishedFileContexts.containsKey(name)) {
-                Class<?> oldContext = publishedFileContexts.get(name);
-                if (oldContext != context) {
-                    getLogger()
-                            .log(Level.WARNING,
-                                    "{0} published by both {1} and {2}. File from {2} will be used.",
-                                    new Object[] { name, context, oldContext });
-                }
-            } else {
-                publishedFileContexts.put(name, context);
+        // Add to map of names accepted by servePublishedFile
+        if (publishedFileContexts.containsKey(name)) {
+            Class<?> oldContext = publishedFileContexts.get(name);
+            if (oldContext != context) {
+                getLogger()
+                        .log(Level.WARNING,
+                                "{0} published by both {1} and {2}. File from {2} will be used.",
+                                new Object[] { name, context, oldContext });
             }
+        } else {
+            publishedFileContexts.put(name, context);
         }
 
         return ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX + "/" + name;
@@ -518,50 +516,6 @@ public class LegacyCommunicationManager implements Serializable {
 
     }
 
-    /**
-     * Handles a request by passing it to each registered {@link RequestHandler}
-     * in turn until one produces a response. This method is used for requests
-     * that have not been handled by any specific functionality in the terminal
-     * implementation (e.g. {@link VaadinServlet}).
-     * <p>
-     * The request handlers are invoked in the revere order in which they were
-     * added to the session until a response has been produced. This means that
-     * the most recently added handler is used first and the first request
-     * handler that was added to the session is invoked towards the end unless
-     * any previous handler has already produced a response.
-     * </p>
-     * 
-     * @param request
-     *            the Vaadin request to get information from
-     * @param response
-     *            the response to which data can be written
-     * @return returns <code>true</code> if a {@link RequestHandler} has
-     *         produced a response and <code>false</code> if no response has
-     *         been written.
-     * @throws IOException
-     *             if a handler throws an exception
-     * 
-     * @see VaadinSession#addRequestHandler(RequestHandler)
-     * @see RequestHandler
-     * 
-     * @since 7.0
-     * 
-     * @deprecated As of 7.1. Should be moved to VaadinService.
-     */
-    @Deprecated
-    protected boolean handleOtherRequest(VaadinRequest request,
-            VaadinResponse response) throws IOException {
-        // Use a copy to avoid ConcurrentModificationException
-        for (RequestHandler handler : new ArrayList<RequestHandler>(
-                session.getRequestHandlers())) {
-            if (handler.handleRequest(session, request, response)) {
-                return true;
-            }
-        }
-        // If not handled
-        return false;
-    }
-
     /**
      * Handles an exception that occurred when processing RPC calls or a file
      * upload.
index 24107b744b3c3cc0b75c2aed2fcbc5e78a96e235..9c4f0ae6a4866476e6897e3d45cc73ea37a011ec 100644 (file)
@@ -19,6 +19,8 @@ package com.vaadin.server;
 import java.io.IOException;
 import java.io.Serializable;
 
+import com.vaadin.ui.UI;
+
 /**
  * Handler for producing a response to non-UIDL requests. Handlers can be added
  * to service sessions using
@@ -30,6 +32,14 @@ public interface RequestHandler extends Serializable {
      * Handles a non-UIDL request. If a response is written, this method should
      * return <code>true</code> to indicate that no more request handlers should
      * be invoked for the request.
+     * <p>
+     * Note that request handlers by default do not lock the session. If you are
+     * using VaadinSession or anything inside the VaadinSession you must ensure
+     * the session is locked. This can be done by extending
+     * {@link SynchronizedRequestHandler} or by using
+     * {@link VaadinSession#runSafelyInContext(Runnable)} or
+     * {@link UI#runSafelyInContext(Runnable)}.
+     * </p>
      * 
      * @param session
      *            The session for the request
@@ -40,6 +50,7 @@ public interface RequestHandler extends Serializable {
      * @return true if a response has been written and no further request
      *         handlers should be called, otherwise false
      * @throws IOException
+     *             If an IO error occurred
      */
     boolean handleRequest(VaadinSession session, VaadinRequest request,
             VaadinResponse response) throws IOException;
diff --git a/server/src/com/vaadin/server/SynchronizedRequestHandler.java b/server/src/com/vaadin/server/SynchronizedRequestHandler.java
new file mode 100644 (file)
index 0000000..ac730dc
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.server;
+
+import java.io.IOException;
+
+/**
+ * RequestHandler which takes care of locking and unlocking of the VaadinSession
+ * automatically. The session is locked before
+ * {@link #synchronizedHandleRequest(VaadinSession, VaadinRequest, VaadinResponse)}
+ * is called and unlocked after it has completed.
+ * 
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.1
+ */
+public abstract class SynchronizedRequestHandler implements RequestHandler {
+
+    @Override
+    public boolean handleRequest(VaadinSession session, VaadinRequest request,
+            VaadinResponse response) throws IOException {
+        session.lock();
+        try {
+            return synchronizedHandleRequest(session, request, response);
+        } finally {
+            session.unlock();
+        }
+    }
+
+    /**
+     * Identical to
+     * {@link #handleRequest(VaadinSession, VaadinRequest, VaadinResponse)}
+     * except the {@link VaadinSession} is locked before this is called and
+     * unlocked after this has completed.
+     * 
+     * @see #handleRequest(VaadinSession, VaadinRequest, VaadinResponse)
+     * @param session
+     *            The session for the request
+     * @param request
+     *            The request to handle
+     * @param response
+     *            The response object to which a response can be written.
+     * @return true if a response has been written and no further request
+     *         handlers should be called, otherwise false
+     * 
+     * @throws IOException
+     *             If an IO error occurred
+     */
+    public abstract boolean synchronizedHandleRequest(VaadinSession session,
+            VaadinRequest request, VaadinResponse response) throws IOException;
+
+}
index 65a99ce79ce4f88ef5f90404d71d9cf28a3d25bf..5fc00408a9f280cccb38a4b2488a86abf41cac60 100644 (file)
@@ -28,14 +28,14 @@ import java.io.Writer;
  * </p>
  */
 @SuppressWarnings("serial")
-public class UnsupportedBrowserHandler implements RequestHandler {
+public class UnsupportedBrowserHandler extends SynchronizedRequestHandler {
 
     /** Cookie used to ignore browser checks */
     public static final String FORCE_LOAD_COOKIE = "vaadinforceload=1";
 
     @Override
-    public boolean handleRequest(VaadinSession session, VaadinRequest request,
-            VaadinResponse response) throws IOException {
+    public boolean synchronizedHandleRequest(VaadinSession session,
+            VaadinRequest request, VaadinResponse response) throws IOException {
 
         // Check if the browser is supported
         // If Chrome Frame is available we'll assume it's ok
index 924310e9edabf01ddc18047c714c2864f5316f8b..75d72be03bfd880bd46c0afed26b0ce267c374dc 100644 (file)
@@ -55,6 +55,7 @@ import com.vaadin.server.communication.FileUploadHandler;
 import com.vaadin.server.communication.HeartbeatHandler;
 import com.vaadin.server.communication.PortletListenerNotifier;
 import com.vaadin.server.communication.PublishedFileHandler;
+import com.vaadin.server.communication.SessionRequestHandler;
 import com.vaadin.server.communication.UIInitHandler;
 import com.vaadin.server.communication.UidlRequestHandler;
 import com.vaadin.util.CurrentInstance;
@@ -525,7 +526,8 @@ public class VaadinPortlet extends GenericPortlet implements Constants,
             LegacyCommunicationManager communicationManager)
             throws PortletException, IOException, MalformedURLException {
         if (requestType == RequestType.APP || requestType == RequestType.RENDER) {
-            if (!communicationManager.handleOtherRequest(request, response)) {
+            if (!new SessionRequestHandler().handleRequest(vaadinSession,
+                    request, response)) {
                 response.sendError(HttpServletResponse.SC_NOT_FOUND,
                         "Not found");
             }
index c47ce6bcf10f6cfdb20c653bdcec3b68f0d5621d..46fe3da5c6aa74a70820654fe748c9b10227e916 100644 (file)
@@ -43,6 +43,7 @@ import com.vaadin.server.LegacyCommunicationManager.Callback;
 import com.vaadin.server.communication.FileUploadHandler;
 import com.vaadin.server.communication.HeartbeatHandler;
 import com.vaadin.server.communication.PublishedFileHandler;
+import com.vaadin.server.communication.SessionRequestHandler;
 import com.vaadin.server.communication.UIInitHandler;
 import com.vaadin.server.communication.UidlRequestHandler;
 import com.vaadin.util.CurrentInstance;
@@ -286,8 +287,8 @@ public class VaadinServlet extends HttpServlet implements Constants {
                     new UIInitHandler().handleRequest(vaadinSession, request,
                             response);
                     return;
-                } else if (vaadinSession.getCommunicationManager()
-                        .handleOtherRequest(request, response)) {
+                } else if (new SessionRequestHandler().handleRequest(
+                        vaadinSession, request, response)) {
                     return;
                 }
 
index 75d4f870c1437cb6690b466d509e97b42c12b7ea..a696ad3919972915ad1bbc27efcbe87205e12732 100644 (file)
@@ -20,11 +20,10 @@ import java.io.IOException;
 
 import javax.servlet.http.HttpServletResponse;
 
-import com.vaadin.server.RequestHandler;
+import com.vaadin.server.SynchronizedRequestHandler;
 import com.vaadin.server.VaadinRequest;
 import com.vaadin.server.VaadinResponse;
 import com.vaadin.server.VaadinSession;
-import com.vaadin.shared.ui.ui.UIConstants;
 import com.vaadin.ui.UI;
 
 /**
@@ -38,7 +37,7 @@ import com.vaadin.ui.UI;
  * @author Vaadin Ltd
  * @since 7.1
  */
-public class HeartbeatHandler implements RequestHandler {
+public class HeartbeatHandler extends SynchronizedRequestHandler {
 
     /**
      * Handles a heartbeat request for the given session. Reads the GET
@@ -48,17 +47,9 @@ public class HeartbeatHandler implements RequestHandler {
      * time. Otherwise, writes a HTTP Not Found error to the response.
      */
     @Override
-    public boolean handleRequest(VaadinSession session, VaadinRequest request,
-            VaadinResponse response) throws IOException {
-
-        UI ui = null;
-        try {
-            int uiId = Integer.parseInt(request
-                    .getParameter(UIConstants.UI_ID_PARAMETER));
-            ui = session.getUIById(uiId);
-        } catch (NumberFormatException nfe) {
-            // null-check below handles this as well
-        }
+    public boolean synchronizedHandleRequest(VaadinSession session,
+            VaadinRequest request, VaadinResponse response) throws IOException {
+        UI ui = session.getService().findUI(request);
         if (ui != null) {
             ui.setLastHeartbeatTimestamp(System.currentTimeMillis());
             // Ensure that the browser does not cache heartbeat responses.
index aff5c8a80e87f7ffe246112804057fdaa6ec6753..c64819ca44603428e0b290f6e1c8d3c954e9a101 100644 (file)
@@ -29,7 +29,7 @@ import javax.portlet.RenderResponse;
 import javax.portlet.ResourceRequest;
 import javax.portlet.ResourceResponse;
 
-import com.vaadin.server.RequestHandler;
+import com.vaadin.server.SynchronizedRequestHandler;
 import com.vaadin.server.ServletPortletHelper;
 import com.vaadin.server.VaadinPortletRequest;
 import com.vaadin.server.VaadinPortletResponse;
@@ -46,7 +46,7 @@ import com.vaadin.ui.UI;
  * @author Vaadin Ltd
  * @since 7.1
  */
-public class PortletListenerNotifier implements RequestHandler {
+public class PortletListenerNotifier extends SynchronizedRequestHandler {
 
     /**
      * Fires portlet request events to any {@link PortletListener}s registered
@@ -55,8 +55,8 @@ public class PortletListenerNotifier implements RequestHandler {
      * PortletListener method corresponding to the request type is invoked.
      */
     @Override
-    public boolean handleRequest(VaadinSession session, VaadinRequest request,
-            VaadinResponse response) throws IOException {
+    public boolean synchronizedHandleRequest(VaadinSession session,
+            VaadinRequest request, VaadinResponse response) throws IOException {
 
         VaadinPortletSession sess = (VaadinPortletSession) session;
         PortletRequest req = ((VaadinPortletRequest) request)
index 71fd80099a2acd13437e0addf396030b641b7fda..1cd70bb1731da29ffbb4fb0d6169631d52dde00f 100644 (file)
@@ -74,11 +74,13 @@ public class PublishedFileHandler implements RequestHandler {
         }
 
         // Check that the resource name has been registered
-        // TODO PUSH refactor - is the synchronization correct at all?
+        session.lock();
         Class<?> context;
-        synchronized (session.getCommunicationManager().getDependencies()) {
+        try {
             context = session.getCommunicationManager().getDependencies()
                     .get(fileName);
+        } finally {
+            session.unlock();
         }
 
         // Security check: don't serve resource if the name hasn't been
diff --git a/server/src/com/vaadin/server/communication/SessionRequestHandler.java b/server/src/com/vaadin/server/communication/SessionRequestHandler.java
new file mode 100644 (file)
index 0000000..244cb01
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.server.communication;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.vaadin.server.RequestHandler;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinResponse;
+import com.vaadin.server.VaadinSession;
+
+/**
+ * Handles a request by passing it to each registered {@link RequestHandler} in
+ * the session in turn until one produces a response. This method is used for
+ * requests that have not been handled by any specific functionality in the
+ * servlet/portlet.
+ * <p>
+ * The request handlers are invoked in the reverse order in which they were
+ * added to the session until a response has been produced. This means that the
+ * most recently added handler is used first and the first request handler that
+ * was added to the session is invoked towards the end unless any previous
+ * handler has already produced a response.
+ * </p>
+ * <p>
+ * The session is not locked during execution of the request handlers. The
+ * request handler can itself decide if it needs to lock the session or not.
+ * </p>
+ * 
+ * @see VaadinSession#addRequestHandler(RequestHandler)
+ * @see RequestHandler
+ * 
+ * @since 7.1
+ */
+public class SessionRequestHandler implements RequestHandler {
+
+    @Override
+    public boolean handleRequest(VaadinSession session, VaadinRequest request,
+            VaadinResponse response) throws IOException {
+        // Use a copy to avoid ConcurrentModificationException
+        session.lock();
+        ArrayList<RequestHandler> requestHandlers;
+        try {
+            requestHandlers = new ArrayList<RequestHandler>(
+                    session.getRequestHandlers());
+        } finally {
+            session.unlock();
+        }
+        for (RequestHandler handler : requestHandlers) {
+            if (handler.handleRequest(session, request, response)) {
+                return true;
+            }
+        }
+        // If not handled
+        return false;
+    }
+}
index aea8bbd5cd2c8bdf462559d722f6846f453476c7..e4790e95e5dee71a62313d3e3fb7e446606a463c 100644 (file)
@@ -32,7 +32,7 @@ import org.json.JSONObject;
 
 import com.vaadin.annotations.PreserveOnRefresh;
 import com.vaadin.server.LegacyApplicationUIProvider;
-import com.vaadin.server.RequestHandler;
+import com.vaadin.server.SynchronizedRequestHandler;
 import com.vaadin.server.UIClassSelectionEvent;
 import com.vaadin.server.UICreateEvent;
 import com.vaadin.server.UIProvider;
@@ -49,11 +49,11 @@ import com.vaadin.ui.UI;
  * @author Vaadin Ltd
  * @since 7.1
  */
-public class UIInitHandler implements RequestHandler {
+public class UIInitHandler extends SynchronizedRequestHandler {
 
     @Override
-    public boolean handleRequest(VaadinSession session, VaadinRequest request,
-            VaadinResponse response) throws IOException {
+    public boolean synchronizedHandleRequest(VaadinSession session,
+            VaadinRequest request, VaadinResponse response) throws IOException {
 
         // NOTE! GateIn requires, for some weird reason, getOutputStream
         // to be used instead of getWriter() (it seems to interpret
@@ -62,7 +62,6 @@ public class UIInitHandler implements RequestHandler {
         final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
                 new OutputStreamWriter(out, "UTF-8")));
 
-        session.lock();
         try {
             assert UI.getCurrent() == null;
 
@@ -88,7 +87,6 @@ public class UIInitHandler implements RequestHandler {
             // TODO PUSH handle
             e.printStackTrace();
         } finally {
-            session.unlock();
             outWriter.close();
         }
 
index b128a34c7df7d3ed6b11a9860dd4f9f46e59cd6b..e55f39246e8912270f76aedefc5beb0cf798bddd 100644 (file)
@@ -32,7 +32,7 @@ import com.vaadin.server.Constants;
 import com.vaadin.server.LegacyCommunicationManager;
 import com.vaadin.server.LegacyCommunicationManager.Callback;
 import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException;
-import com.vaadin.server.RequestHandler;
+import com.vaadin.server.SynchronizedRequestHandler;
 import com.vaadin.server.VaadinRequest;
 import com.vaadin.server.VaadinResponse;
 import com.vaadin.server.VaadinSession;
@@ -51,7 +51,7 @@ import com.vaadin.ui.UI;
  * @author Vaadin Ltd
  * @since 7.1
  */
-public class UidlRequestHandler implements RequestHandler {
+public class UidlRequestHandler extends SynchronizedRequestHandler {
 
     private Callback criticalNotifier;
 
@@ -62,8 +62,8 @@ public class UidlRequestHandler implements RequestHandler {
     }
 
     @Override
-    public boolean handleRequest(VaadinSession session, VaadinRequest request,
-            VaadinResponse response) throws IOException {
+    public boolean synchronizedHandleRequest(VaadinSession session,
+            VaadinRequest request, VaadinResponse response) throws IOException {
 
         UI uI = session.getService().findUI(request);
 
@@ -99,10 +99,6 @@ public class UidlRequestHandler implements RequestHandler {
         final Writer outWriter = new BufferedWriter(new OutputStreamWriter(out,
                 "UTF-8"));
 
-        // The rest of the process is synchronized with the session
-        // in order to guarantee that no parallel variable handling is
-        // made
-        session.lock();
         try {
             rpcHandler.handleRpc(uI, request.getReader(), request);
 
@@ -126,7 +122,6 @@ public class UidlRequestHandler implements RequestHandler {
             criticalNotifier.criticalNotification(request, response, null,
                     null, null, null);
         } finally {
-            session.unlock();
             outWriter.close();
             requestThemeName = null;
         }
index 41677467bb96df22d00a38c10cfa66a0075aa48f..97947b568d72ef29f718f038206bc02912ed8ae3 100644 (file)
@@ -25,6 +25,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import com.vaadin.server.ConnectorResource;
+import com.vaadin.server.DownloadStream;
 import com.vaadin.server.Resource;
 import com.vaadin.server.ResourceReference;
 import com.vaadin.server.VaadinRequest;
@@ -83,7 +84,14 @@ public abstract class AbstractMedia extends AbstractComponent {
     public boolean handleConnectorRequest(VaadinRequest request,
             VaadinResponse response, String path) throws IOException {
         Matcher matcher = Pattern.compile("(\\d+)(/.*)?").matcher(path);
-        if (matcher.matches()) {
+        if (!matcher.matches()) {
+            return super.handleConnectorRequest(request, response, path);
+        }
+
+        DownloadStream stream;
+
+        getSession().lock();
+        try {
             List<URLReference> sources = getState().sources;
 
             int sourceIndex = Integer.parseInt(matcher.group(1));
@@ -98,11 +106,13 @@ public abstract class AbstractMedia extends AbstractComponent {
             URLReference reference = sources.get(sourceIndex);
             ConnectorResource resource = (ConnectorResource) ResourceReference
                     .getResource(reference);
-            resource.getStream().writeResponse(request, response);
-            return true;
-        } else {
-            return super.handleConnectorRequest(request, response, path);
+            stream = resource.getStream();
+        } finally {
+            getSession().unlock();
         }
+
+        stream.writeResponse(request, response);
+        return true;
     }
 
     private Logger getLogger() {
index 11ae1b379b23861228387d440c592b8389390af7..20ad392679e5a8bc046e982aedf2c2299e769c04 100644 (file)
@@ -61,24 +61,30 @@ public class LoginForm extends CustomComponent {
     private Embedded iframe = new Embedded();
 
     @Override
-    public boolean handleConnectorRequest(VaadinRequest request,
-            VaadinResponse response, String path) throws IOException {
-        String method = VaadinServletService.getCurrentServletRequest()
-                .getMethod();
+    public boolean handleConnectorRequest(final VaadinRequest request,
+            final VaadinResponse response, String path) throws IOException {
         if (!path.equals("login")) {
             return super.handleConnectorRequest(request, response, path);
         }
-        String responseString = null;
-        if (method.equalsIgnoreCase("post")) {
-            responseString = handleLogin(request);
-        } else {
-            responseString = getLoginHTML();
-        }
+        final StringBuilder responseBuilder = new StringBuilder();
+
+        getUI().runSafely(new Runnable() {
+            @Override
+            public void run() {
+                String method = VaadinServletService.getCurrentServletRequest()
+                        .getMethod();
+                if (method.equalsIgnoreCase("post")) {
+                    responseBuilder.append(handleLogin(request));
+                } else {
+                    responseBuilder.append(getLoginHTML());
+                }
+            }
+        });
 
-        if (responseString != null) {
+        if (responseBuilder.length() > 0) {
             response.setContentType("text/html; charset=utf-8");
             response.setCacheTime(-1);
-            response.getWriter().write(responseString);
+            response.getWriter().write(responseBuilder.toString());
             return true;
         } else {
             return false;