From a1be41f0fcba31c20234b223e36678a8e9a30bf0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Thu, 26 Jul 2012 15:03:28 +0300 Subject: [PATCH] Refactor Portlet URL handling based on Servlet URLs (#9168) --- .../gwt/client/ApplicationConfiguration.java | 17 +- .../gwt/client/ApplicationConnection.java | 33 +++- .../server/AbstractApplicationPortlet.java | 56 +++--- .../server/AbstractApplicationServlet.java | 85 ++------- .../server/AbstractCommunicationManager.java | 147 ++++++++++++++- .../gwt/server/CommunicationManager.java | 171 ------------------ .../gwt/server/GAEApplicationServlet.java | 24 ++- .../server/PortletApplicationContext2.java | 23 --- .../server/PortletCommunicationManager.java | 146 +-------------- .../gwt/server/ServletPortletHelper.java | 40 ++++ .../gwt/server/WrappedPortletRequest.java | 10 +- 11 files changed, 287 insertions(+), 465 deletions(-) diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java index 877f2a70b8..71707e723a 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java @@ -23,6 +23,8 @@ import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector; public class ApplicationConfiguration implements EntryPoint { + public static final String PORTLET_RESOUCE_URL_BASE = "portletAppURLBase"; + /** * Helper class for reading configuration options from the bootstap * javascript @@ -205,8 +207,6 @@ public class ApplicationConfiguration implements EntryPoint { private ErrorMessage communicationError; private ErrorMessage authorizationError; private boolean useDebugIdInDom = true; - private boolean usePortletURLs = false; - private String portletUidlURLBase; private HashMap unknownComponents; @@ -226,11 +226,12 @@ public class ApplicationConfiguration implements EntryPoint { private Map tagToServerSideClassName = new HashMap(); public boolean usePortletURLs() { - return usePortletURLs; + return getPortletResourceUrl() != null; } - public String getPortletUidlURLBase() { - return portletUidlURLBase; + public String getPortletResourceUrl() { + return getJsoConfiguration(id) + .getConfigString(PORTLET_RESOUCE_URL_BASE); } public String getRootPanelId() { @@ -318,12 +319,6 @@ public class ApplicationConfiguration implements EntryPoint { // null -> true useDebugIdInDom = jsoConfiguration.getConfigBoolean("useDebugIdInDom") != Boolean.FALSE; - // null -> false - usePortletURLs = jsoConfiguration.getConfigBoolean("usePortletURLs") == Boolean.TRUE; - - portletUidlURLBase = jsoConfiguration - .getConfigString("portletUidlURLBase"); - // null -> false standalone = jsoConfiguration.getConfigBoolean("standalone") == Boolean.TRUE; diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index 5db7221bdb..0647f2fe96 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -27,6 +27,7 @@ import com.google.gwt.http.client.RequestBuilder; import com.google.gwt.http.client.RequestCallback; import com.google.gwt.http.client.RequestException; import com.google.gwt.http.client.Response; +import com.google.gwt.http.client.URL; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONString; @@ -76,6 +77,8 @@ import com.vaadin.terminal.gwt.server.AbstractCommunicationManager; * Entry point classes (widgetsets) define onModuleLoad(). */ public class ApplicationConnection { + public static final String V_RESOURCE_PATH = "v-resourcePath"; + private static final String CONNECTOR_PROTOCOL_PREFIX = "connector://"; public static final String CONNECTOR_RESOURCE_PREFIX = "APP/CONNECTOR"; @@ -511,12 +514,7 @@ public class ApplicationConnection { final String payload = uidlSecurityKey + VAR_BURST_SEPARATOR + requestData; VConsole.log("Making UIDL Request with params: " + payload); - String uri; - if (configuration.usePortletURLs()) { - uri = configuration.getPortletUidlURLBase(); - } else { - uri = getAppUri() + "UIDL"; - } + String uri = translateVaadinUri("app://UIDL"); if (extraParams != null && extraParams.length() > 0) { uri = addGetParameters(uri, extraParams); @@ -2298,7 +2296,28 @@ public class ApplicationConnection { uidlUri = themeUri + uidlUri.substring(7); } if (uidlUri.startsWith("app://")) { - uidlUri = getAppUri() + uidlUri.substring(6); + String relativeUrl = uidlUri.substring(6); + if (getConfiguration().usePortletURLs()) { + // Should put path in v-resourcePath parameter and append query + // params to base portlet url + String[] parts = relativeUrl.split("\\?", 2); + String path = parts[0]; + + String url = getConfiguration().getPortletResourceUrl(); + if (parts.length > 1) { + String appUrlParams = parts[1]; + url = addGetParameters(url, appUrlParams); + } + if (!path.startsWith("/")) { + path = '/' + path; + } + String pathParam = V_RESOURCE_PATH + "=" + + URL.encodeQueryString(path); + url = addGetParameters(url, pathParam); + uidlUri = url; + } else { + uidlUri = getAppUri() + relativeUrl; + } } else if (uidlUri.startsWith(CONNECTOR_PROTOCOL_PREFIX)) { // getAppUri *should* always end with / // substring *should* always start with / (connector:///foo.bar diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java index 3fcd61036f..978738a1d8 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java @@ -25,7 +25,6 @@ import javax.portlet.ActionResponse; import javax.portlet.EventRequest; import javax.portlet.EventResponse; import javax.portlet.GenericPortlet; -import javax.portlet.MimeResponse; import javax.portlet.PortletConfig; import javax.portlet.PortletContext; import javax.portlet.PortletException; @@ -64,6 +63,8 @@ import com.vaadin.ui.Root; public abstract class AbstractApplicationPortlet extends GenericPortlet implements Constants { + public static final String RESOURCE_URL_ID = "APP"; + public static class WrappedHttpAndPortletRequest extends WrappedPortletRequest { @@ -179,12 +180,8 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet public void criticalNotification(WrappedRequest request, WrappedResponse response, String cap, String msg, String details, String outOfSyncURL) throws IOException { - PortletRequest portletRequest = WrappedPortletRequest.cast(request) - .getPortletRequest(); - PortletResponse portletResponse = ((WrappedPortletResponse) response) - .getPortletResponse(); - portlet.criticalNotification(portletRequest, - (MimeResponse) portletResponse, cap, msg, details, + portlet.criticalNotification(WrappedPortletRequest.cast(request), + (WrappedPortletResponse) response, cap, msg, details, outOfSyncURL); } } @@ -440,18 +437,20 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN, BROWSER_DETAILS; } - protected RequestType getRequestType(PortletRequest request) { + protected RequestType getRequestType(WrappedPortletRequest wrappedRequest) { + PortletRequest request = wrappedRequest.getPortletRequest(); if (request instanceof RenderRequest) { return RequestType.RENDER; } else if (request instanceof ResourceRequest) { ResourceRequest resourceRequest = (ResourceRequest) request; - if (isUIDLRequest(resourceRequest)) { + if (ServletPortletHelper.isUIDLRequest(wrappedRequest)) { return RequestType.UIDL; } else if (isBrowserDetailsRequeset(resourceRequest)) { return RequestType.BROWSER_DETAILS; - } else if (isFileUploadRequest(resourceRequest)) { + } else if (ServletPortletHelper.isFileUploadRequest(wrappedRequest)) { return RequestType.FILE_UPLOAD; - } else if (isApplicationResourceRequest(resourceRequest)) { + } else if (ServletPortletHelper + .isApplicationResourceRequest(wrappedRequest)) { return RequestType.APPLICATION_RESOURCE; } else if (isDummyRequest(resourceRequest)) { return RequestType.DUMMY; @@ -471,25 +470,11 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet && request.getResourceID().equals("browserDetails"); } - private boolean isApplicationResourceRequest(ResourceRequest request) { - return request.getResourceID() != null - && request.getResourceID().startsWith("APP"); - } - - private boolean isUIDLRequest(ResourceRequest request) { - return request.getResourceID() != null - && request.getResourceID().equals("UIDL"); - } - private boolean isDummyRequest(ResourceRequest request) { return request.getResourceID() != null && request.getResourceID().equals("DUMMY"); } - private boolean isFileUploadRequest(ResourceRequest request) { - return "UPLOAD".equals(request.getResourceID()); - } - /** * Returns true if the servlet is running in production mode. Production * mode disables all debug facilities. @@ -513,7 +498,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet WrappedPortletResponse wrappedResponse = new WrappedPortletResponse( response, getDeploymentConfiguration()); - RequestType requestType = getRequestType(request); + RequestType requestType = getRequestType(wrappedRequest); if (requestType == RequestType.UNKNOWN) { handleUnknownRequest(request, response); @@ -682,7 +667,8 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet getLogger() .fine("General security exception, the security key was probably incorrect."); } catch (final Throwable e) { - handleServiceException(request, response, application, e); + handleServiceException(wrappedRequest, wrappedResponse, + application, e); } finally { // Notifies transaction end try { @@ -1040,16 +1026,16 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet return Application.getSystemMessages(); } - private void handleServiceException(PortletRequest request, - PortletResponse response, Application application, Throwable e) - throws IOException, PortletException { + private void handleServiceException(WrappedPortletRequest request, + WrappedPortletResponse response, Application application, + Throwable e) throws IOException, PortletException { // TODO Check that this error handler is working when running inside a // portlet // if this was an UIDL request, response UIDL back to client if (getRequestType(request) == RequestType.UIDL) { Application.SystemMessages ci = getSystemMessages(); - criticalNotification(request, (ResourceResponse) response, + criticalNotification(request, response, ci.getInternalErrorCaption(), ci.getInternalErrorMessage(), null, ci.getInternalErrorURL()); if (application != null) { @@ -1103,9 +1089,9 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet * @throws IOException * if the writing failed due to input/output error. */ - void criticalNotification(PortletRequest request, MimeResponse response, - String caption, String message, String details, String url) - throws IOException { + void criticalNotification(WrappedPortletRequest request, + WrappedPortletResponse response, String caption, String message, + String details, String url) throws IOException { // clients JS app is still running, but server application either // no longer exists or it might fail to perform reasonably. @@ -1131,7 +1117,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet // Set the response type response.setContentType("application/json; charset=UTF-8"); - final OutputStream out = response.getPortletOutputStream(); + final OutputStream out = response.getOutputStream(); final PrintWriter outWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(out, "UTF-8"))); outWriter.print("for(;;);[{\"changes\":[], \"meta\" : {" diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java index 3c8fc596d1..f0b8c7e3b1 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java @@ -141,8 +141,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } }; - static final String UPLOAD_URL_PREFIX = "APP/UPLOAD/"; - /** * Called by the servlet container to indicate to a servlet that the servlet * is being placed into service. @@ -556,8 +554,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements * @throws IOException */ private boolean ensureCookiesEnabled(RequestType requestType, - HttpServletRequest request, HttpServletResponse response) - throws IOException { + WrappedHttpServletRequest request, + WrappedHttpServletResponse response) throws IOException { if (requestType == RequestType.UIDL && !isRepaintAll(request)) { // In all other but the first UIDL request a cookie should be // returned by the browser. @@ -622,11 +620,11 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements * @throws IOException * if the writing failed due to input/output error. */ - protected void criticalNotification(HttpServletRequest request, + protected void criticalNotification(WrappedHttpServletRequest request, HttpServletResponse response, String caption, String message, String details, String url) throws IOException { - if (isUIDLRequest(request)) { + if (ServletPortletHelper.isUIDLRequest(request)) { if (caption != null) { caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\""; @@ -848,9 +846,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements return newApplication; } - private void handleServiceException(HttpServletRequest request, - HttpServletResponse response, Application application, Throwable e) - throws IOException, ServletException { + private void handleServiceException(WrappedHttpServletRequest request, + WrappedHttpServletResponse response, Application application, + Throwable e) throws IOException, ServletException { // if this was an UIDL request, response UIDL back to client if (getRequestType(request) == RequestType.UIDL) { Application.SystemMessages ci = getSystemMessages(); @@ -903,8 +901,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements return DEFAULT_THEME_NAME; } - void handleServiceSessionExpired(HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + void handleServiceSessionExpired(WrappedHttpServletRequest request, + WrappedHttpServletResponse response) throws IOException, + ServletException { if (isOnUnloadRequest(request)) { /* @@ -944,8 +943,10 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } - private void handleServiceSecurityException(HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + private void handleServiceSecurityException( + WrappedHttpServletRequest request, + WrappedHttpServletResponse response) throws IOException, + ServletException { if (isOnUnloadRequest(request)) { /* * Request was an unload request (e.g. window close event) and the @@ -1273,18 +1274,18 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements FILE_UPLOAD, BROWSER_DETAILS, UIDL, OTHER, STATIC_FILE, APPLICATION_RESOURCE, CONNECTOR_RESOURCE; } - protected RequestType getRequestType(HttpServletRequest request) { - if (isFileUploadRequest(request)) { + protected RequestType getRequestType(WrappedHttpServletRequest request) { + if (ServletPortletHelper.isFileUploadRequest(request)) { return RequestType.FILE_UPLOAD; - } else if (isConnectorResourceRequest(request)) { + } else if (ServletPortletHelper.isConnectorResourceRequest(request)) { return RequestType.CONNECTOR_RESOURCE; } else if (isBrowserDetailsRequest(request)) { return RequestType.BROWSER_DETAILS; - } else if (isUIDLRequest(request)) { + } else if (ServletPortletHelper.isUIDLRequest(request)) { return RequestType.UIDL; } else if (isStaticResourceRequest(request)) { return RequestType.STATIC_FILE; - } else if (isApplicationRequest(request)) { + } else if (ServletPortletHelper.isApplicationResourceRequest(request)) { return RequestType.APPLICATION_RESOURCE; } return RequestType.OTHER; @@ -1296,23 +1297,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements && request.getParameter("browserDetails") != null; } - private boolean isConnectorResourceRequest(HttpServletRequest request) { - String path = getRequestPathInfo(request); - if (path != null - && path.startsWith('/' + ApplicationConnection.CONNECTOR_RESOURCE_PREFIX + '/')) { - return true; - } - return false; - } - - private boolean isApplicationRequest(HttpServletRequest request) { - String path = getRequestPathInfo(request); - if (path != null && path.startsWith("/APP/")) { - return true; - } - return false; - } - private boolean isStaticResourceRequest(HttpServletRequest request) { String pathInfo = request.getPathInfo(); if (pathInfo == null || pathInfo.length() <= 10) { @@ -1330,37 +1314,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements return false; } - private boolean isUIDLRequest(HttpServletRequest request) { - String pathInfo = getRequestPathInfo(request); - - if (pathInfo == null) { - return false; - } - - String compare = AJAX_UIDL_URI; - - if (pathInfo.startsWith(compare + "/") || pathInfo.endsWith(compare)) { - return true; - } - - return false; - } - - private boolean isFileUploadRequest(HttpServletRequest request) { - String pathInfo = getRequestPathInfo(request); - - if (pathInfo == null) { - return false; - } - - if (pathInfo.startsWith("/" + UPLOAD_URL_PREFIX)) { - return true; - } - - return false; - - } - private boolean isOnUnloadRequest(HttpServletRequest request) { return request.getParameter(ApplicationConnection.PARAM_UNLOADBURST) != null; } diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 76dab13502..bf69d3bae0 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -162,6 +162,10 @@ public abstract class AbstractCommunicationManager implements Serializable { private Map> connectorResourceContexts = new HashMap>(); + private Map> pidToNameToStreamVariable; + + private Map streamVariableToSeckey; + /** * TODO New constructor - document me! * @@ -634,6 +638,23 @@ public abstract class AbstractCommunicationManager implements Serializable { // Remove connectors that have been detached from the application during // handling of the request root.getConnectorTracker().cleanConnectorMap(); + + if (pidToNameToStreamVariable != null) { + Iterator iterator = pidToNameToStreamVariable.keySet() + .iterator(); + while (iterator.hasNext()) { + String connectorId = iterator.next(); + if (root.getConnectorTracker().getConnector(connectorId) == null) { + // Owner is no longer attached to the application + Map removed = pidToNameToStreamVariable + .get(connectorId); + for (String key : removed.keySet()) { + streamVariableToSeckey.remove(removed.get(key)); + } + iterator.remove(); + } + } + } } protected void highlightConnector(Connector highlightedConnector) { @@ -2281,11 +2302,57 @@ public abstract class AbstractCommunicationManager implements Serializable { } - abstract String getStreamVariableTargetUrl(ClientConnector owner, - String name, StreamVariable value); + public String getStreamVariableTargetUrl(ClientConnector owner, + String name, StreamVariable value) { + /* + * We will use the same APP/* URI space as ApplicationResources but + * prefix url with UPLOAD + * + * eg. APP/UPLOAD/[ROOTID]/[PID]/[NAME]/[SECKEY] + * + * SECKEY is created on each paint to make URL's unpredictable (to + * prevent CSRF attacks). + * + * NAME and PID from URI forms a key to fetch StreamVariable when + * handling post + */ + String paintableId = owner.getConnectorId(); + int rootId = owner.getRoot().getRootId(); + String key = rootId + "/" + paintableId + "/" + name; - abstract protected void cleanStreamVariable(ClientConnector owner, - String name); + if (pidToNameToStreamVariable == null) { + pidToNameToStreamVariable = new HashMap>(); + } + Map nameToStreamVariable = pidToNameToStreamVariable + .get(paintableId); + if (nameToStreamVariable == null) { + nameToStreamVariable = new HashMap(); + pidToNameToStreamVariable.put(paintableId, nameToStreamVariable); + } + nameToStreamVariable.put(name, value); + + if (streamVariableToSeckey == null) { + streamVariableToSeckey = new HashMap(); + } + String seckey = streamVariableToSeckey.get(value); + if (seckey == null) { + seckey = UUID.randomUUID().toString(); + streamVariableToSeckey.put(value, seckey); + } + + return "app://" + ServletPortletHelper.UPLOAD_URL_PREFIX + key + "/" + + seckey; + + } + + public void cleanStreamVariable(ClientConnector owner, String name) { + Map nameToStreamVar = pidToNameToStreamVariable + .get(owner.getConnectorId()); + nameToStreamVar.remove(name); + if (nameToStreamVar.isEmpty()) { + pidToNameToStreamVariable.remove(owner.getConnectorId()); + } + } /** * Gets the bootstrap handler that should be used for generating the pages @@ -2485,6 +2552,78 @@ public abstract class AbstractCommunicationManager implements Serializable { } } + /** + * Handles file upload request submitted via Upload component. + * + * @param root + * The root for this request + * + * @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable) + * + * @param request + * @param response + * @throws IOException + * @throws InvalidUIDLSecurityKeyException + */ + public void handleFileUpload(Application application, + WrappedRequest request, WrappedResponse response) + throws IOException, InvalidUIDLSecurityKeyException { + + /* + * URI pattern: APP/UPLOAD/[ROOTID]/[PID]/[NAME]/[SECKEY] See + * #createReceiverUrl + */ + + String pathInfo = request.getRequestPathInfo(); + // strip away part until the data we are interested starts + int startOfData = pathInfo + .indexOf(ServletPortletHelper.UPLOAD_URL_PREFIX) + + ServletPortletHelper.UPLOAD_URL_PREFIX.length(); + String uppUri = pathInfo.substring(startOfData); + String[] parts = uppUri.split("/", 4); // 0= rootid, 1 = cid, 2= name, 3 + // = sec key + String rootId = parts[0]; + String connectorId = parts[1]; + String variableName = parts[2]; + Root root = application.getRootById(Integer.parseInt(rootId)); + Root.setCurrent(root); + + StreamVariable streamVariable = getStreamVariable(connectorId, + variableName); + String secKey = streamVariableToSeckey.get(streamVariable); + if (secKey.equals(parts[3])) { + + ClientConnector source = getConnector(root, connectorId); + String contentType = request.getContentType(); + if (contentType.contains("boundary")) { + // Multipart requests contain boundary string + doHandleSimpleMultipartFileUpload(request, response, + streamVariable, variableName, source, + contentType.split("boundary=")[1]); + } else { + // if boundary string does not exist, the posted file is from + // XHR2.post(File) + doHandleXhrFilePost(request, response, streamVariable, + variableName, source, request.getContentLength()); + } + } else { + throw new InvalidUIDLSecurityKeyException( + "Security key in upload post did not match!"); + } + + } + + public StreamVariable getStreamVariable(String connectorId, + String variableName) { + Map map = pidToNameToStreamVariable + .get(connectorId); + if (map == null) { + return null; + } + StreamVariable streamVariable = map.get(variableName); + return streamVariable; + } + /** * Stream that extracts content from another stream until the boundary * string is encountered. diff --git a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java index f083252897..3cc3a8cb64 100644 --- a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java @@ -4,22 +4,15 @@ package com.vaadin.terminal.gwt.server; -import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.UUID; import javax.servlet.ServletContext; import com.vaadin.Application; import com.vaadin.external.json.JSONException; import com.vaadin.terminal.PaintException; -import com.vaadin.terminal.StreamVariable; import com.vaadin.terminal.WrappedRequest; -import com.vaadin.terminal.WrappedResponse; import com.vaadin.ui.Root; /** @@ -58,170 +51,6 @@ public class CommunicationManager extends AbstractCommunicationManager { super(application); } - /** - * Handles file upload request submitted via Upload component. - * - * @param root - * The root for this request - * - * @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable) - * - * @param request - * @param response - * @throws IOException - * @throws InvalidUIDLSecurityKeyException - */ - public void handleFileUpload(Application application, - WrappedRequest request, WrappedResponse response) - throws IOException, InvalidUIDLSecurityKeyException { - - /* - * URI pattern: APP/UPLOAD/[ROOTID]/[PID]/[NAME]/[SECKEY] See - * #createReceiverUrl - */ - - String pathInfo = request.getRequestPathInfo(); - // strip away part until the data we are interested starts - int startOfData = pathInfo - .indexOf(AbstractApplicationServlet.UPLOAD_URL_PREFIX) - + AbstractApplicationServlet.UPLOAD_URL_PREFIX.length(); - String uppUri = pathInfo.substring(startOfData); - String[] parts = uppUri.split("/", 4); // 0= rootid, 1 = cid, 2= name, 3 - // = sec key - String rootId = parts[0]; - String connectorId = parts[1]; - String variableName = parts[2]; - Root root = application.getRootById(Integer.parseInt(rootId)); - Root.setCurrent(root); - - StreamVariable streamVariable = getStreamVariable(connectorId, - variableName); - String secKey = streamVariableToSeckey.get(streamVariable); - if (secKey.equals(parts[3])) { - - ClientConnector source = getConnector(root, connectorId); - String contentType = request.getContentType(); - if (contentType.contains("boundary")) { - // Multipart requests contain boundary string - doHandleSimpleMultipartFileUpload(request, response, - streamVariable, variableName, source, - contentType.split("boundary=")[1]); - } else { - // if boundary string does not exist, the posted file is from - // XHR2.post(File) - doHandleXhrFilePost(request, response, streamVariable, - variableName, source, request.getContentLength()); - } - } else { - throw new InvalidUIDLSecurityKeyException( - "Security key in upload post did not match!"); - } - - } - - /** - * Gets a stream variable based on paintable id and variable name. Returns - * null if no matching variable has been registered. - * - * @param paintableId - * id of paintable to get variable for - * @param variableName - * name of the stream variable - * @return the corresponding stream variable, or null if not - * found - */ - public StreamVariable getStreamVariable(String paintableId, - String variableName) { - Map nameToStreamVariable = pidToNameToStreamVariable - .get(paintableId); - if (nameToStreamVariable == null) { - return null; - } - StreamVariable streamVariable = nameToStreamVariable.get(variableName); - return streamVariable; - } - - @Override - protected void postPaint(Root root) { - super.postPaint(root); - - if (pidToNameToStreamVariable != null) { - Iterator iterator = pidToNameToStreamVariable.keySet() - .iterator(); - while (iterator.hasNext()) { - String connectorId = iterator.next(); - if (root.getConnectorTracker().getConnector(connectorId) == null) { - // Owner is no longer attached to the application - Map removed = pidToNameToStreamVariable - .get(connectorId); - for (String key : removed.keySet()) { - streamVariableToSeckey.remove(removed.get(key)); - } - iterator.remove(); - } - } - } - - } - - private Map> pidToNameToStreamVariable; - - private Map streamVariableToSeckey; - - @Override - public String getStreamVariableTargetUrl(ClientConnector owner, - String name, StreamVariable value) { - /* - * We will use the same APP/* URI space as ApplicationResources but - * prefix url with UPLOAD - * - * eg. APP/UPLOAD/[ROOTID]/[PID]/[NAME]/[SECKEY] - * - * SECKEY is created on each paint to make URL's unpredictable (to - * prevent CSRF attacks). - * - * NAME and PID from URI forms a key to fetch StreamVariable when - * handling post - */ - String paintableId = owner.getConnectorId(); - int rootId = owner.getRoot().getRootId(); - String key = rootId + "/" + paintableId + "/" + name; - - if (pidToNameToStreamVariable == null) { - pidToNameToStreamVariable = new HashMap>(); - } - Map nameToStreamVariable = pidToNameToStreamVariable - .get(paintableId); - if (nameToStreamVariable == null) { - nameToStreamVariable = new HashMap(); - pidToNameToStreamVariable.put(paintableId, nameToStreamVariable); - } - nameToStreamVariable.put(name, value); - - if (streamVariableToSeckey == null) { - streamVariableToSeckey = new HashMap(); - } - String seckey = streamVariableToSeckey.get(value); - if (seckey == null) { - seckey = UUID.randomUUID().toString(); - streamVariableToSeckey.put(value, seckey); - } - - return "app://" + AbstractApplicationServlet.UPLOAD_URL_PREFIX + key - + "/" + seckey; - - } - - @Override - public void cleanStreamVariable(ClientConnector owner, String name) { - Map nameToStreamVar = pidToNameToStreamVariable - .get(owner.getConnectorId()); - nameToStreamVar.remove(name); - if (nameToStreamVar.isEmpty()) { - pidToNameToStreamVariable.remove(owner.getConnectorId()); - } - } - @Override protected BootstrapHandler createBootstrapHandler() { return new BootstrapHandler() { diff --git a/src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java index a6032fa98d..cc12c9cc43 100644 --- a/src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java +++ b/src/com/vaadin/terminal/gwt/server/GAEApplicationServlet.java @@ -121,8 +121,9 @@ public class GAEApplicationServlet extends ApplicationServlet { // appengine session expires-parameter private static final String PROPERTY_APPENGINE_EXPIRES = "_expires"; - protected void sendDeadlineExceededNotification(HttpServletRequest request, - HttpServletResponse response) throws IOException { + protected void sendDeadlineExceededNotification( + WrappedHttpServletRequest request, + WrappedHttpServletResponse response) throws IOException { criticalNotification( request, response, @@ -131,8 +132,9 @@ public class GAEApplicationServlet extends ApplicationServlet { "", null); } - protected void sendNotSerializableNotification(HttpServletRequest request, - HttpServletResponse response) throws IOException { + protected void sendNotSerializableNotification( + WrappedHttpServletRequest request, + WrappedHttpServletResponse response) throws IOException { criticalNotification( request, response, @@ -142,8 +144,9 @@ public class GAEApplicationServlet extends ApplicationServlet { + "?restartApplication"); } - protected void sendCriticalErrorNotification(HttpServletRequest request, - HttpServletResponse response) throws IOException { + protected void sendCriticalErrorNotification( + WrappedHttpServletRequest request, + WrappedHttpServletResponse response) throws IOException { criticalNotification( request, response, @@ -154,8 +157,13 @@ public class GAEApplicationServlet extends ApplicationServlet { } @Override - protected void service(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { + protected void service(HttpServletRequest unwrappedRequest, + HttpServletResponse unwrappedResponse) throws ServletException, + IOException { + WrappedHttpServletRequest request = new WrappedHttpServletRequest( + unwrappedRequest, getDeploymentConfiguration()); + WrappedHttpServletResponse response = new WrappedHttpServletResponse( + unwrappedResponse, getDeploymentConfiguration()); if (isCleanupRequest(request)) { cleanDatastore(); diff --git a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java index b888ad8e2d..0214cb2b35 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java +++ b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java @@ -28,13 +28,11 @@ import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; -import javax.portlet.ResourceURL; import javax.portlet.StateAwareResponse; import javax.servlet.http.HttpSessionBindingListener; import javax.xml.namespace.QName; import com.vaadin.Application; -import com.vaadin.terminal.ApplicationResource; import com.vaadin.ui.Root; /** @@ -254,27 +252,6 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext { this.response = response; } - @Override - public String generateApplicationResourceURL(ApplicationResource resource, - String mapKey) { - if (response instanceof MimeResponse) { - ResourceURL resourceURL = ((MimeResponse) response) - .createResourceURL(); - final String filename = resource.getFilename(); - if (filename == null) { - resourceURL.setResourceID("APP/" + mapKey + "/"); - } else { - resourceURL.setResourceID("APP/" + mapKey + "/" - + urlEncode(filename)); - } - return resourceURL.toString(); - } else { - // in a background thread or otherwise outside a request - // TODO exception ?? - return null; - } - } - /** * Creates a new action URL. * diff --git a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java index d45e652110..edd970a31f 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java @@ -5,9 +5,6 @@ package com.vaadin.terminal.gwt.server; import java.io.IOException; import java.io.InputStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; import javax.portlet.MimeResponse; import javax.portlet.PortletContext; @@ -22,10 +19,9 @@ import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; import com.vaadin.terminal.DeploymentConfiguration; import com.vaadin.terminal.PaintException; -import com.vaadin.terminal.StreamVariable; import com.vaadin.terminal.WrappedRequest; import com.vaadin.terminal.WrappedResponse; -import com.vaadin.terminal.gwt.client.Connector; +import com.vaadin.terminal.gwt.client.ApplicationConfiguration; import com.vaadin.ui.Root; /** @@ -37,140 +33,10 @@ import com.vaadin.ui.Root; @SuppressWarnings("serial") public class PortletCommunicationManager extends AbstractCommunicationManager { - private transient MimeResponse currentMimeResponse; - public PortletCommunicationManager(Application application) { super(application); } - public void handleFileUpload(Application application, - WrappedRequest request, WrappedResponse response) - throws IOException { - String contentType = request.getContentType(); - String name = request.getParameter("name"); - String ownerId = request.getParameter("rec-owner"); - String rootId = request.getParameter("rootId"); - - Root root = application.getRootById(Integer.parseInt(rootId)); - Root.setCurrent(root); - - ClientConnector owner = getConnector(root, ownerId); - - StreamVariable streamVariable = ownerToNameToStreamVariable.get(owner) - .get(name); - - if (contentType.contains("boundary")) { - doHandleSimpleMultipartFileUpload(request, response, - streamVariable, name, owner, - contentType.split("boundary=")[1]); - } else { - doHandleXhrFilePost(request, response, streamVariable, name, owner, - request.getContentLength()); - } - - } - - @Override - protected void postPaint(Root root) { - super.postPaint(root); - - Application application = root.getApplication(); - if (ownerToNameToStreamVariable != null) { - Iterator iterator = ownerToNameToStreamVariable.keySet() - .iterator(); - while (iterator.hasNext()) { - Connector owner = iterator.next(); - if (getConnector(root, owner.getConnectorId()) == null) { - // Owner is no longer attached to the application - iterator.remove(); - } - } - } - } - - @Override - protected boolean handleApplicationRequest(WrappedRequest request, - WrappedResponse response) throws IOException { - setCurrentMimeReponse(response); - try { - return super.handleApplicationRequest(request, response); - } finally { - currentMimeResponse = null; - } - } - - private void setCurrentMimeReponse(WrappedResponse response) { - PortletResponse portletResponse = ((WrappedPortletResponse) response) - .getPortletResponse(); - if (portletResponse instanceof MimeResponse) { - currentMimeResponse = (MimeResponse) portletResponse; - } - - } - - @Override - public void handleUidlRequest(WrappedRequest request, - WrappedResponse response, Callback callback, Root root) - throws IOException, InvalidUIDLSecurityKeyException, JSONException { - setCurrentMimeReponse(response); - super.handleUidlRequest(request, response, callback, root); - currentMimeResponse = null; - } - - @Override - public void handleBrowserDetailsRequest(WrappedRequest request, - WrappedResponse response, Application application) - throws IOException { - setCurrentMimeReponse(response); - super.handleBrowserDetailsRequest(request, response, application); - currentMimeResponse = null; - - } - - private Map> ownerToNameToStreamVariable; - - @Override - String getStreamVariableTargetUrl(ClientConnector owner, String name, - StreamVariable value) { - if (ownerToNameToStreamVariable == null) { - ownerToNameToStreamVariable = new HashMap>(); - } - Map nameToReceiver = ownerToNameToStreamVariable - .get(owner); - if (nameToReceiver == null) { - nameToReceiver = new HashMap(); - ownerToNameToStreamVariable.put(owner, nameToReceiver); - } - nameToReceiver.put(name, value); - ResourceURL resurl = createResourceURL(); - resurl.setResourceID("UPLOAD"); - resurl.setParameter("name", name); - resurl.setParameter("rec-owner", owner.getConnectorId()); - resurl.setParameter("rootId", "" + owner.getRoot().getRootId()); - resurl.setProperty("name", name); - resurl.setProperty("rec-owner", owner.getConnectorId()); - resurl.setProperty("rootId", "" + owner.getRoot().getRootId()); - return resurl.toString(); - } - - private ResourceURL createResourceURL() { - if (currentMimeResponse == null) { - throw new RuntimeException( - "No reponse object available. Cannot create a resource URL"); - } - return currentMimeResponse.createResourceURL(); - } - - @Override - protected void cleanStreamVariable(ClientConnector owner, String name) { - Map map = ownerToNameToStreamVariable - .get(owner); - map.remove(name); - if (map.isEmpty()) { - ownerToNameToStreamVariable.remove(owner); - } - } - @Override protected BootstrapHandler createBootstrapHandler() { return new BootstrapHandler() { @@ -220,12 +86,14 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { * some other things */ JSONObject defaults = super.getDefaultParameters(context); - defaults.put("usePortletURLs", true); - ResourceURL uidlUrlBase = getRenderResponse(context) + ResourceURL portletResourceUrl = getRenderResponse(context) .createResourceURL(); - uidlUrlBase.setResourceID("UIDL"); - defaults.put("portletUidlURLBase", uidlUrlBase.toString()); + portletResourceUrl + .setResourceID(AbstractApplicationPortlet.RESOURCE_URL_ID); + defaults.put(ApplicationConfiguration.PORTLET_RESOUCE_URL_BASE, + portletResourceUrl.toString()); + defaults.put("pathInfo", ""); return defaults; diff --git a/src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java b/src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java index 9b1e60e621..55f2756604 100644 --- a/src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java +++ b/src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java @@ -3,6 +3,8 @@ package com.vaadin.terminal.gwt.server; import java.io.Serializable; import com.vaadin.Application; +import com.vaadin.terminal.WrappedRequest; +import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.ui.Root; /* @@ -10,6 +12,8 @@ import com.vaadin.ui.Root; */ class ServletPortletHelper implements Serializable { + public static final String UPLOAD_URL_PREFIX = "APP/UPLOAD/"; + public static class ApplicationClassException extends Exception { public ApplicationClassException(String message, Throwable cause) { @@ -70,4 +74,40 @@ class ServletPortletHelper implements Serializable { + " doesn't have a public no-args constructor"); } } + + private static boolean hasPathPrefix(WrappedRequest request, String prefix) { + String pathInfo = request.getRequestPathInfo(); + + if (pathInfo == null) { + return false; + } + + if (!prefix.startsWith("/")) { + prefix = '/' + prefix; + } + + if (pathInfo.startsWith(prefix)) { + return true; + } + + return false; + } + + public static boolean isFileUploadRequest(WrappedRequest request) { + return hasPathPrefix(request, UPLOAD_URL_PREFIX); + } + + public static boolean isConnectorResourceRequest(WrappedRequest request) { + return hasPathPrefix(request, + ApplicationConnection.CONNECTOR_RESOURCE_PREFIX + "/"); + } + + public static boolean isUIDLRequest(WrappedRequest request) { + return hasPathPrefix(request, Constants.AJAX_UIDL_URI); + } + + public static boolean isApplicationResourceRequest(WrappedRequest request) { + return hasPathPrefix(request, "APP/"); + } + } diff --git a/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java b/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java index 44858b3e10..a3fa172034 100644 --- a/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java +++ b/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java @@ -17,6 +17,7 @@ import com.vaadin.Application; import com.vaadin.terminal.CombinedRequest; import com.vaadin.terminal.DeploymentConfiguration; import com.vaadin.terminal.WrappedRequest; +import com.vaadin.terminal.gwt.client.ApplicationConnection; /** * Wrapper for {@link PortletRequest} and its subclasses. @@ -89,7 +90,14 @@ public class WrappedPortletRequest implements WrappedRequest { @Override public String getRequestPathInfo() { if (request instanceof ResourceRequest) { - return ((ResourceRequest) request).getResourceID(); + ResourceRequest resourceRequest = (ResourceRequest) request; + String resourceID = resourceRequest.getResourceID(); + if (AbstractApplicationPortlet.RESOURCE_URL_ID.equals(resourceID)) { + String resourcePath = resourceRequest + .getParameter(ApplicationConnection.V_RESOURCE_PATH); + return resourcePath; + } + return resourceID; } else { return null; } -- 2.39.5