diff options
22 files changed, 333 insertions, 177 deletions
diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index 79ccbd8f0b..e998b8ed55 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -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; } /** diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index 03421ce487..9c603ae7ed 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -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(); diff --git a/server/src/com/vaadin/server/ClientConnector.java b/server/src/com/vaadin/server/ClientConnector.java index 294f94f2be..3b52fbc730 100644 --- a/server/src/com/vaadin/server/ClientConnector.java +++ b/server/src/com/vaadin/server/ClientConnector.java @@ -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 diff --git a/server/src/com/vaadin/server/ConnectorResource.java b/server/src/com/vaadin/server/ConnectorResource.java index 8f8591e6b1..8682f8ce6f 100644 --- a/server/src/com/vaadin/server/ConnectorResource.java +++ b/server/src/com/vaadin/server/ConnectorResource.java @@ -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(); diff --git a/server/src/com/vaadin/server/ConnectorResourceHandler.java b/server/src/com/vaadin/server/ConnectorResourceHandler.java index 03a2fcc115..61631c3d23 100644 --- a/server/src/com/vaadin/server/ConnectorResourceHandler.java +++ b/server/src/com/vaadin/server/ConnectorResourceHandler.java @@ -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; diff --git a/server/src/com/vaadin/server/DownloadStream.java b/server/src/com/vaadin/server/DownloadStream.java index e2f9fc5296..4e66831f1d 100644 --- a/server/src/com/vaadin/server/DownloadStream.java +++ b/server/src/com/vaadin/server/DownloadStream.java @@ -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 diff --git a/server/src/com/vaadin/server/FileDownloader.java b/server/src/com/vaadin/server/FileDownloader.java index 7cc1fd7cc8..9b49ad8edd 100644 --- a/server/src/com/vaadin/server/FileDownloader.java +++ b/server/src/com/vaadin/server/FileDownloader.java @@ -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; } } diff --git a/server/src/com/vaadin/server/GlobalResourceHandler.java b/server/src/com/vaadin/server/GlobalResourceHandler.java index 0fac14e20c..d411b286d0 100644 --- a/server/src/com/vaadin/server/GlobalResourceHandler.java +++ b/server/src/com/vaadin/server/GlobalResourceHandler.java @@ -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); diff --git a/server/src/com/vaadin/server/LegacyCommunicationManager.java b/server/src/com/vaadin/server/LegacyCommunicationManager.java index 2b99da7828..b956b806bd 100644 --- a/server/src/com/vaadin/server/LegacyCommunicationManager.java +++ b/server/src/com/vaadin/server/LegacyCommunicationManager.java @@ -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; @@ -519,50 +517,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. * diff --git a/server/src/com/vaadin/server/RequestHandler.java b/server/src/com/vaadin/server/RequestHandler.java index 24107b744b..9c4f0ae6a4 100644 --- a/server/src/com/vaadin/server/RequestHandler.java +++ b/server/src/com/vaadin/server/RequestHandler.java @@ -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 index 0000000000..ac730dcecb --- /dev/null +++ b/server/src/com/vaadin/server/SynchronizedRequestHandler.java @@ -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; + +} diff --git a/server/src/com/vaadin/server/UnsupportedBrowserHandler.java b/server/src/com/vaadin/server/UnsupportedBrowserHandler.java index 65a99ce79c..5fc00408a9 100644 --- a/server/src/com/vaadin/server/UnsupportedBrowserHandler.java +++ b/server/src/com/vaadin/server/UnsupportedBrowserHandler.java @@ -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 diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java index 924310e9ed..75d72be03b 100644 --- a/server/src/com/vaadin/server/VaadinPortlet.java +++ b/server/src/com/vaadin/server/VaadinPortlet.java @@ -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"); } diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index c47ce6bcf1..46fe3da5c6 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -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; } diff --git a/server/src/com/vaadin/server/communication/HeartbeatHandler.java b/server/src/com/vaadin/server/communication/HeartbeatHandler.java index 75d4f870c1..a696ad3919 100644 --- a/server/src/com/vaadin/server/communication/HeartbeatHandler.java +++ b/server/src/com/vaadin/server/communication/HeartbeatHandler.java @@ -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. diff --git a/server/src/com/vaadin/server/communication/PortletListenerNotifier.java b/server/src/com/vaadin/server/communication/PortletListenerNotifier.java index aff5c8a80e..c64819ca44 100644 --- a/server/src/com/vaadin/server/communication/PortletListenerNotifier.java +++ b/server/src/com/vaadin/server/communication/PortletListenerNotifier.java @@ -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) diff --git a/server/src/com/vaadin/server/communication/PublishedFileHandler.java b/server/src/com/vaadin/server/communication/PublishedFileHandler.java index 71fd80099a..1cd70bb173 100644 --- a/server/src/com/vaadin/server/communication/PublishedFileHandler.java +++ b/server/src/com/vaadin/server/communication/PublishedFileHandler.java @@ -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 index 0000000000..244cb0121d --- /dev/null +++ b/server/src/com/vaadin/server/communication/SessionRequestHandler.java @@ -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; + } +} diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java index aea8bbd5cd..e4790e95e5 100644 --- a/server/src/com/vaadin/server/communication/UIInitHandler.java +++ b/server/src/com/vaadin/server/communication/UIInitHandler.java @@ -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(); } diff --git a/server/src/com/vaadin/server/communication/UidlRequestHandler.java b/server/src/com/vaadin/server/communication/UidlRequestHandler.java index b128a34c7d..e55f39246e 100644 --- a/server/src/com/vaadin/server/communication/UidlRequestHandler.java +++ b/server/src/com/vaadin/server/communication/UidlRequestHandler.java @@ -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; } diff --git a/server/src/com/vaadin/ui/AbstractMedia.java b/server/src/com/vaadin/ui/AbstractMedia.java index 41677467bb..97947b568d 100644 --- a/server/src/com/vaadin/ui/AbstractMedia.java +++ b/server/src/com/vaadin/ui/AbstractMedia.java @@ -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() { diff --git a/server/src/com/vaadin/ui/LoginForm.java b/server/src/com/vaadin/ui/LoginForm.java index 11ae1b379b..20ad392679 100644 --- a/server/src/com/vaadin/ui/LoginForm.java +++ b/server/src/com/vaadin/ui/LoginForm.java @@ -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; |