diff options
author | Leif Åstrand <leif@vaadin.com> | 2014-07-22 12:59:14 +0300 |
---|---|---|
committer | Leif Åstrand <leif@vaadin.com> | 2014-07-22 12:59:32 +0300 |
commit | 86e1c718578f8c81323b47e3efed03178d123c03 (patch) | |
tree | 9eaf3779ce79b67963c9fcb3a43c7fd6c39f25fd | |
parent | aca2902c9cafcb02db19a0cdd42b1488786c0f56 (diff) | |
parent | 6ae7843e46ee906adfc99bde71ded5f296caea74 (diff) | |
download | vaadin-framework-86e1c718578f8c81323b47e3efed03178d123c03.tar.gz vaadin-framework-86e1c718578f8c81323b47e3efed03178d123c03.zip |
Merge branch 'master' into grid
Change-Id: I6878b3c64eb5c98174537f4e5864e2ff2cb1863f
70 files changed, 2517 insertions, 732 deletions
diff --git a/WebContent/VAADIN/themes/base/common/common.scss b/WebContent/VAADIN/themes/base/common/common.scss index 0a493e0356..77248c0c96 100644 --- a/WebContent/VAADIN/themes/base/common/common.scss +++ b/WebContent/VAADIN/themes/base/common/common.scss @@ -146,6 +146,12 @@ body &.v-app .v-app-loading { border: 0; margin: 0; } + +.v-contextmenu .gwt-MenuBar { + overflow-y: auto; + overflow-x: hidden; +} + .v-contextmenu .gwt-MenuItem div { cursor: pointer; vertical-align: middle; diff --git a/WebContent/VAADIN/themes/chameleon/components/notification/notification.scss b/WebContent/VAADIN/themes/chameleon/components/notification/notification.scss index fbf78d40c4..783e4bcc1f 100644 --- a/WebContent/VAADIN/themes/chameleon/components/notification/notification.scss +++ b/WebContent/VAADIN/themes/chameleon/components/notification/notification.scss @@ -8,6 +8,7 @@ div.#{$primaryStyleName} { -webkit-box-shadow: 0 2px 5px rgba(0,0,0,.7); -moz-box-shadow: 0 2px 5px rgba(0,0,0,.7); box-shadow: 0 2px 5px rgba(0,0,0,.7); + background:rgba(255,255,255,.90) url(../img/grad-light-top.png) repeat-x; } .#{$primaryStyleName} p { diff --git a/WebContent/VAADIN/themes/chameleon/components/tabsheet/tabsheet.scss b/WebContent/VAADIN/themes/chameleon/components/tabsheet/tabsheet.scss index dfa1c51c3a..d7f968fe43 100644 --- a/WebContent/VAADIN/themes/chameleon/components/tabsheet/tabsheet.scss +++ b/WebContent/VAADIN/themes/chameleon/components/tabsheet/tabsheet.scss @@ -89,6 +89,7 @@ .#{$primaryStyleName}-scrollerNext-disabled, .#{$primaryStyleName}-scrollerPrev-disabled:active, .#{$primaryStyleName}-scrollerNext-disabled:active { + padding-top: 12px; border: 1px solid #b3b3b3; border-width: 0; background: transparent url(../../img/tab-arrows.png) no-repeat 6px 50%; diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml index fb2ddbd998..9ca5be2bdf 100644 --- a/WebContent/WEB-INF/web.xml +++ b/WebContent/WEB-INF/web.xml @@ -118,11 +118,11 @@ <param-name>pushmode</param-name> <param-value>automatic</param-value> </init-param> - <async-supported>true</async-supported> <init-param> <param-name>org.atmosphere.cpr.scanClassPath</param-name> <param-value>false</param-value> </init-param> + <async-supported>true</async-supported> </servlet> <!-- For testing GAE - the deployment script changes this to use GAEVaadinServlet --> @@ -133,11 +133,11 @@ <param-name>UI</param-name> <param-value>com.vaadin.tests.integration.ServletIntegrationUI</param-value> </init-param> - <async-supported>true</async-supported> <init-param> <param-name>org.atmosphere.cpr.scanClassPath</param-name> <param-value>false</param-value> </init-param> + <async-supported>true</async-supported> </servlet> <servlet-mapping> diff --git a/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java b/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java index 028880b2e1..dc2a676ab8 100644 --- a/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java +++ b/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -58,6 +58,10 @@ public class FetchReleaseNotesTickets { List<String> tickets = IOUtils.readLines(urlStream); for (String ticket : tickets) { + // Omit BOM + if (!ticket.isEmpty() && ticket.charAt(0) == 65279) { + ticket = ticket.substring(1); + } String[] fields = ticket.split("\t"); if ("id".equals(fields[0])) { // This is the header diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java index 6c242dfd74..a31dafe05c 100644 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java +++ b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java @@ -45,25 +45,17 @@ public class FieldProperty extends Property { @Override public void writeSetterBody(TreeLogger logger, SourceWriter w, String beanVariable, String valueVariable) { - // Don't try to unbox Longs in javascript, as it's not supported. - // (#13692) - boolean shouldUnbox = !"long".equals(field.getType() - .getSimpleSourceName()); w.println("%s.@%s::%s = %s;", beanVariable, getBeanType() - .getQualifiedSourceName(), getName(), - shouldUnbox ? unboxValue(valueVariable) : valueVariable); + .getQualifiedSourceName(), getName(), unboxValue(valueVariable)); } @Override public void writeGetterBody(TreeLogger logger, SourceWriter w, String beanVariable) { - // Longs are not unboxed, as it's not supported. (#13692) - boolean shouldBox = !"long".equals(field.getType() - .getSimpleSourceName()); String value = String.format("%s.@%s::%s", beanVariable, getBeanType() .getQualifiedSourceName(), getName()); w.print("return "); - w.print(shouldBox ? boxValue(value) : value); + w.print(boxValue(value)); w.println(";"); } diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 6bbca98042..6abcdac487 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -111,14 +111,14 @@ import com.vaadin.shared.ui.ui.UIState.PushConfigurationState; * This is the client side communication "engine", managing client-server * communication with its server side counterpart * com.vaadin.server.VaadinService. - * + * * Client-side connectors receive updates from the corresponding server-side * connector (typically component) as state updates or RPC calls. The connector * has the possibility to communicate back with its server side counter part * through RPC calls. - * + * * TODO document better - * + * * Entry point classes (widgetsets) define <code>onModuleLoad()</code>. */ public class ApplicationConnection implements HasHandlers { @@ -150,12 +150,12 @@ public class ApplicationConnection implements HasHandlers { * A string that, if found in a non-JSON response to a UIDL request, will * cause the browser to refresh the page. If followed by a colon, optional * whitespace, and a URI, causes the browser to synchronously load the URI. - * + * * <p> * This allows, for instance, a servlet filter to redirect the application * to a custom login page when the session expires. For example: * </p> - * + * * <pre> * if (sessionExpired) { * response.setHeader("Content-Type", "text/html"); @@ -168,7 +168,7 @@ public class ApplicationConnection implements HasHandlers { public static final String UIDL_REFRESH_TOKEN = "Vaadin-Refresh"; // will hold the CSRF token once received - private String csrfToken = "init"; + private String csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE; private final HashMap<String, String> resourcesMap = new HashMap<String, String>(); @@ -346,7 +346,7 @@ public class ApplicationConnection implements HasHandlers { /** * Event triggered when a XHR request has finished with the status code of * the response. - * + * * Useful for handlers observing network failures like online/off-line * monitors. */ @@ -402,12 +402,12 @@ public class ApplicationConnection implements HasHandlers { /** * Event triggered when a application is stopped by calling * {@link ApplicationConnection#setApplicationRunning(false)}. - * + * * To listen for the event add a {@link ApplicationStoppedHandler} by * invoking * {@link ApplicationConnection#addHandler(ApplicationConnection.ApplicationStoppedEvent.Type, ApplicationStoppedHandler)} * to the {@link ApplicationConnection} - * + * * @since 7.1.8 * @author Vaadin Ltd */ @@ -434,7 +434,7 @@ public class ApplicationConnection implements HasHandlers { /** * Called when a communication error has occurred. Returning * <code>true</code> from this method suppresses error handling. - * + * * @param details * A string describing the error. * @param statusCode @@ -449,7 +449,7 @@ public class ApplicationConnection implements HasHandlers { * A listener for listening to application stopped events. The listener can * be added to a {@link ApplicationConnection} by invoking * {@link ApplicationConnection#addHandler(ApplicationStoppedEvent.Type, ApplicationStoppedHandler)} - * + * * @since 7.1.8 * @author Vaadin Ltd */ @@ -459,7 +459,7 @@ public class ApplicationConnection implements HasHandlers { * Triggered when the {@link ApplicationConnection} marks a previously * running application as stopped by invoking * {@link ApplicationConnection#setApplicationRunning(false)} - * + * * @param event * the event triggered by the {@link ApplicationConnection} */ @@ -570,7 +570,7 @@ public class ApplicationConnection implements HasHandlers { * called once this application has started (first response received) or * failed to start. This ensures that the applications are started in order, * to avoid session-id problems. - * + * */ public void start() { String jsonText = configuration.getUIDL(); @@ -671,7 +671,7 @@ public class ApplicationConnection implements HasHandlers { * <li><code>vaadin.postRequestHooks</code> is a map of functions which gets * called after each XHR made by vaadin application. Note, that it is * attaching js functions responsibility to create the variable like this: - * + * * <code><pre> * if(!vaadin.postRequestHooks) {vaadin.postRequestHooks = new Object();} * postRequestHooks.myHook = function(appId) { @@ -682,7 +682,7 @@ public class ApplicationConnection implements HasHandlers { * </pre></code> First parameter passed to these functions is the identifier * of Vaadin application that made the request. * </ul> - * + * * TODO make this multi-app aware */ private native void initializeClientHooks() @@ -713,7 +713,7 @@ public class ApplicationConnection implements HasHandlers { /** * Runs possibly registered client side post request hooks. This is expected * to be run after each uidl request made by Vaadin application. - * + * * @param appId */ private static native void runPostRequestHooks(String appId) @@ -733,7 +733,7 @@ public class ApplicationConnection implements HasHandlers { /** * If on Liferay and logged in, ask the client side session management * JavaScript to extend the session duration. - * + * * Otherwise, Liferay client side JavaScript will explicitly expire the * session even though the server side considers the session to be active. * See ticket #8305 for more information. @@ -752,7 +752,7 @@ public class ApplicationConnection implements HasHandlers { /** * Indicates whether or not there are currently active UIDL requests. Used * internally to sequence requests properly, seldom needed in Widgets. - * + * * @return true if there are active requests */ public boolean hasActiveRequest() { @@ -772,7 +772,7 @@ public class ApplicationConnection implements HasHandlers { /** * Requests an analyze of layouts, to find inconsistencies. Exclusively used * for debugging during development. - * + * * @deprecated as of 7.1. Replaced by {@link UIConnector#analyzeLayouts()} */ @Deprecated @@ -784,7 +784,7 @@ public class ApplicationConnection implements HasHandlers { * Sends a request to the server to print details to console that will help * the developer to locate the corresponding server-side connector in the * source code. - * + * * @param serverConnector * @deprecated as of 7.1. Replaced by * {@link UIConnector#showServerDebugInfo(ServerConnector)} @@ -796,7 +796,7 @@ public class ApplicationConnection implements HasHandlers { /** * Makes an UIDL request to the server. - * + * * @param reqInvocations * Data containing RPC invocations and all related information. * @param extraParams @@ -810,8 +810,10 @@ public class ApplicationConnection implements HasHandlers { startRequest(); JSONObject payload = new JSONObject(); - payload.put(ApplicationConstants.CSRF_TOKEN, new JSONString( - getCsrfToken())); + if (!getCsrfToken().equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) { + payload.put(ApplicationConstants.CSRF_TOKEN, new JSONString( + getCsrfToken())); + } payload.put(ApplicationConstants.RPC_INVOCATIONS, reqInvocations); payload.put(ApplicationConstants.SERVER_SYNC_ID, new JSONNumber( lastSeenServerSyncId)); @@ -833,7 +835,7 @@ public class ApplicationConnection implements HasHandlers { /** * Sends an asynchronous or synchronous UIDL request to the server using the * given URI. - * + * * @param uri * The URI to use for the request. May includes GET parameters * @param payload @@ -971,7 +973,7 @@ public class ApplicationConnection implements HasHandlers { /** * Handles received UIDL JSON text, parsing it, and passing it on to the * appropriate handlers, while logging timing information. - * + * * @param jsonText * @param statusCode */ @@ -999,7 +1001,7 @@ public class ApplicationConnection implements HasHandlers { /** * Sends an asynchronous UIDL request to the server using the given URI. - * + * * @param uri * The URI to use for the request. May includes GET parameters * @param payload @@ -1134,7 +1136,7 @@ public class ApplicationConnection implements HasHandlers { /** * Checks whether or not the CSS is loaded. By default checks the size of * the loading indicator element. - * + * * @return */ protected boolean isCSSLoaded() { @@ -1144,12 +1146,12 @@ public class ApplicationConnection implements HasHandlers { /** * Shows the communication error notification. - * + * * @param details * Optional details for debugging. * @param statusCode * The status code returned for the request - * + * */ protected void showCommunicationError(String details, int statusCode) { VConsole.error("Communication error: " + details); @@ -1158,7 +1160,7 @@ public class ApplicationConnection implements HasHandlers { /** * Shows the authentication error notification. - * + * * @param details * Optional details for debugging. */ @@ -1169,7 +1171,7 @@ public class ApplicationConnection implements HasHandlers { /** * Shows the session expiration notification. - * + * * @param details * Optional details for debugging. */ @@ -1180,7 +1182,7 @@ public class ApplicationConnection implements HasHandlers { /** * Shows an error notification. - * + * * @param details * Optional details for debugging. * @param message @@ -1193,7 +1195,7 @@ public class ApplicationConnection implements HasHandlers { /** * Shows the error notification. - * + * * @param details * Optional details for debugging. */ @@ -1281,7 +1283,7 @@ public class ApplicationConnection implements HasHandlers { /** * This method is called after applying uidl change set to application. - * + * * It will clean current and queued variable change sets. And send next * change set if it exists. */ @@ -1300,7 +1302,7 @@ public class ApplicationConnection implements HasHandlers { /** * Cleans given queue of variable changes of such changes that came from * components that do not exist anymore. - * + * * @param variableBurst */ private void cleanVariableBurst( @@ -1325,7 +1327,7 @@ public class ApplicationConnection implements HasHandlers { * <p> * Used by the native "client.isActive" function. * </p> - * + * * @return true if deferred commands are (potentially) being executed, false * otherwise */ @@ -1340,7 +1342,7 @@ public class ApplicationConnection implements HasHandlers { /** * Returns the loading indicator used by this ApplicationConnection - * + * * @return The loading indicator for this ApplicationConnection */ public VLoadingIndicator getLoadingIndicator() { @@ -1349,7 +1351,7 @@ public class ApplicationConnection implements HasHandlers { /** * Determines whether or not the loading indicator is showing. - * + * * @return true if the loading indicator is visible * @deprecated As of 7.1. Use {@link #getLoadingIndicator()} and * {@link VLoadingIndicator#isVisible()}.isVisible() instead. @@ -1383,7 +1385,7 @@ public class ApplicationConnection implements HasHandlers { * server is received. * <p> * The initial id when no request has yet been processed is -1. - * + * * @return and id identifying the response */ public int getLastResponseId() { @@ -1806,13 +1808,13 @@ public class ApplicationConnection implements HasHandlers { /** * Sends the state change events created while updating the state * information. - * + * * This must be called after hierarchy change listeners have been * called. At least caption updates for the parent are strange if * fired from state change listeners and thus calls the parent * BEFORE the parent is aware of the child (through a * ConnectorHierarchyChangedEvent) - * + * * @param pendingStateChangeEvents * The events to send */ @@ -2127,7 +2129,7 @@ public class ApplicationConnection implements HasHandlers { * Updates the connector hierarchy and returns a list of events that * should be fired after update of the hierarchy and the state is * done. - * + * * @param json * The JSON containing the hierarchy information * @return A collection of events that should be fired when update @@ -2524,9 +2526,9 @@ public class ApplicationConnection implements HasHandlers { /** * Adds an explicit RPC method invocation to the send queue. - * + * * @since 7.0 - * + * * @param invocation * RPC method invocation * @param delayed @@ -2566,7 +2568,7 @@ public class ApplicationConnection implements HasHandlers { /** * Removes any pending invocation of the given method from the queue - * + * * @param invocation * The invocation to remove */ @@ -2584,12 +2586,12 @@ public class ApplicationConnection implements HasHandlers { /** * This method sends currently queued variable changes to server. It is * called when immediate variable update must happen. - * + * * To ensure correct order for variable changes (due servers multithreading * or network), we always wait for active request to be handler before * sending a new one. If there is an active request, we will put varible * "burst" to queue that will be purged after current request is handled. - * + * */ public void sendPendingVariableChanges() { if (!deferedSendPending) { @@ -2630,11 +2632,11 @@ public class ApplicationConnection implements HasHandlers { /** * Build the variable burst and send it to server. - * + * * When sync is forced, we also force sending of all pending variable-bursts * at the same time. This is ok as we can assume that DOM will never be * updated after this. - * + * * @param pendingInvocations * List of RPC method invocations to send */ @@ -2721,7 +2723,7 @@ public class ApplicationConnection implements HasHandlers { * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. * </p> - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2743,7 +2745,7 @@ public class ApplicationConnection implements HasHandlers { * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. * </p> - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2766,7 +2768,7 @@ public class ApplicationConnection implements HasHandlers { * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. * </p> - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2789,7 +2791,7 @@ public class ApplicationConnection implements HasHandlers { * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. * </p> - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2812,7 +2814,7 @@ public class ApplicationConnection implements HasHandlers { * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. * </p> - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2835,7 +2837,7 @@ public class ApplicationConnection implements HasHandlers { * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. * </p> - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2858,7 +2860,7 @@ public class ApplicationConnection implements HasHandlers { * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. * </p> - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2881,7 +2883,7 @@ public class ApplicationConnection implements HasHandlers { * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. * </p> - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2898,13 +2900,13 @@ public class ApplicationConnection implements HasHandlers { /** * Sends a new value for the given paintables given variable to the server. - * + * * The update is actually queued to be sent at a suitable time. If immediate * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. - * + * * A null array is sent as an empty array. - * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2921,14 +2923,14 @@ public class ApplicationConnection implements HasHandlers { /** * Sends a new value for the given paintables given variable to the server. - * + * * The update is actually queued to be sent at a suitable time. If immediate * is true, the update is sent as soon as possible. If immediate is false, * the update will be sent along with the next immediate update. </p> - * + * * A null array is sent as an empty array. - * - * + * + * * @param paintableId * the id of the paintable that owns the variable * @param variableName @@ -2945,7 +2947,7 @@ public class ApplicationConnection implements HasHandlers { /** * Does absolutely nothing. Replaced by {@link LayoutManager}. - * + * * @param container * @deprecated As of 7.0, serves no purpose */ @@ -2967,7 +2969,7 @@ public class ApplicationConnection implements HasHandlers { /** * Returns false - * + * * @param paintable * @return false, always * @deprecated As of 7.0, serves no purpose @@ -2979,7 +2981,7 @@ public class ApplicationConnection implements HasHandlers { /** * Returns false - * + * * @param paintable * @return false, always * @deprecated As of 7.0, serves no purpose @@ -3000,16 +3002,16 @@ public class ApplicationConnection implements HasHandlers { /** * Get either an existing ComponentConnector or create a new * ComponentConnector with the given type and id. - * + * * If a ComponentConnector with the given id already exists, returns it. * Otherwise creates and registers a new ComponentConnector of the given * type. - * + * * @param connectorId * Id of the paintable * @param connectorType * Type of the connector, as passed from the server side - * + * * @return Either an existing ComponentConnector or a new ComponentConnector * of the given type */ @@ -3022,15 +3024,15 @@ public class ApplicationConnection implements HasHandlers { /** * Creates a new ServerConnector with the given type and id. - * + * * Creates and registers a new ServerConnector of the given type. Should * never be called with the connector id of an existing connector. - * + * * @param connectorId * Id of the new connector * @param connectorType * Type of the connector, as passed from the server side - * + * * @return A new ServerConnector of the given type */ private ServerConnector createAndRegisterConnector(String connectorId, @@ -3050,7 +3052,7 @@ public class ApplicationConnection implements HasHandlers { /** * Gets a recource that has been pre-loaded via UIDL, such as custom * layouts. - * + * * @param name * identifier of the resource to get * @return the resource @@ -3061,7 +3063,7 @@ public class ApplicationConnection implements HasHandlers { /** * Singleton method to get instance of app's context menu. - * + * * @return VContextMenu object */ public VContextMenu getContextMenu() { @@ -3076,7 +3078,7 @@ public class ApplicationConnection implements HasHandlers { /** * Gets an {@link Icon} instance corresponding to a URI. - * + * * @since 7.2 * @param uri * @return Icon object @@ -3098,7 +3100,7 @@ public class ApplicationConnection implements HasHandlers { * Translates custom protocols in UIDL URI's to be recognizable by browser. * All uri's from UIDL should be routed via this method before giving them * to browser due URI's in UIDL may contain custom protocols like theme://. - * + * * @param uidlUri * Vaadin URI from uidl * @return translated URI ready for browser @@ -3170,7 +3172,7 @@ public class ApplicationConnection implements HasHandlers { /** * Gets the URI for the current theme. Can be used to reference theme * resources. - * + * * @return URI to the current theme */ public String getThemeUri() { @@ -3180,7 +3182,7 @@ public class ApplicationConnection implements HasHandlers { /** * Listens for Notification hide event, and redirects. Used for system * messages, such as session expired. - * + * */ private class NotificationRedirect implements VNotification.EventListener { String url; @@ -3209,7 +3211,7 @@ public class ApplicationConnection implements HasHandlers { /** * Gets the token (aka double submit cookie) that the server uses to protect * against Cross Site Request Forgery attacks. - * + * * @return the CSRF token string */ public String getCsrfToken() { @@ -3219,7 +3221,7 @@ public class ApplicationConnection implements HasHandlers { /** * Use to notify that the given component's caption has changed; layouts may * have to be recalculated. - * + * * @param component * the Paintable whose caption has changed * @deprecated As of 7.0.2, has not had any effect for a long time @@ -3231,7 +3233,7 @@ public class ApplicationConnection implements HasHandlers { /** * Gets the main view - * + * * @return the main view */ public UIConnector getUIConnector() { @@ -3240,7 +3242,7 @@ public class ApplicationConnection implements HasHandlers { /** * Gets the {@link ApplicationConfiguration} for the current application. - * + * * @see ApplicationConfiguration * @return the configuration for this application */ @@ -3253,7 +3255,7 @@ public class ApplicationConnection implements HasHandlers { * list of events which has server side listeners is updated automatically * before the component is updated so the value is correct if called from * updatedFromUIDL. - * + * * @param paintable * The connector to register event listeners for * @param eventIdentifier @@ -3273,7 +3275,7 @@ public class ApplicationConnection implements HasHandlers { /** * Adds the get parameters to the uri and returns the new uri that contains * the parameters. - * + * * @param uri * The uri to which the parameters should be added. * @param extraParams @@ -3326,7 +3328,7 @@ public class ApplicationConnection implements HasHandlers { /** * Get VTooltip instance related to application connection - * + * * @return VTooltip instance */ public VTooltip getVTooltip() { @@ -3338,7 +3340,7 @@ public class ApplicationConnection implements HasHandlers { * this method is now handled by the state change event handler in * AbstractComponentConnector. The only function this method has is to * return true if the UIDL is a "cached" update. - * + * * @param component * @param uidl * @param manageCaption @@ -3389,7 +3391,7 @@ public class ApplicationConnection implements HasHandlers { * Schedules a heartbeat request to occur after the configured heartbeat * interval elapses if the interval is a positive number. Otherwise, does * nothing. - * + * * @deprecated as of 7.2, use {@link Heartbeat#schedule()} instead */ @Deprecated @@ -3403,7 +3405,7 @@ public class ApplicationConnection implements HasHandlers { * Heartbeat requests are used to inform the server that the client-side is * still alive. If the client page is closed or the connection lost, the * server will eventually close the inactive UI. - * + * * @deprecated as of 7.2, use {@link Heartbeat#send()} instead */ @Deprecated @@ -3427,7 +3429,7 @@ public class ApplicationConnection implements HasHandlers { /** * This method can be used to postpone rendering of a response for a short * period of time (e.g. to avoid the rendering process during animation). - * + * * @param lock */ public void suspendReponseHandling(Object lock) { @@ -3436,7 +3438,7 @@ public class ApplicationConnection implements HasHandlers { /** * Resumes the rendering process once all locks have been removed. - * + * * @param lock */ public void resumeResponseHandling(Object lock) { @@ -3481,7 +3483,7 @@ public class ApplicationConnection implements HasHandlers { /** * Sets the delegate that is called whenever a communication error occurrs. - * + * * @param delegate * the delegate. */ @@ -3524,7 +3526,7 @@ public class ApplicationConnection implements HasHandlers { /** * Gets the active connector for focused element in browser. - * + * * @return Connector for focused element or null. */ private ComponentConnector getActiveConnector() { @@ -3538,7 +3540,7 @@ public class ApplicationConnection implements HasHandlers { /** * Sets the status for the push connection. - * + * * @param enabled * <code>true</code> to enable the push connection; * <code>false</code> to disable the push connection. @@ -3588,7 +3590,7 @@ public class ApplicationConnection implements HasHandlers { /** * Returns a human readable string representation of the method used to * communicate with the server. - * + * * @since 7.1 * @return A string representation of the current transport type */ diff --git a/client/src/com/vaadin/client/Util.java b/client/src/com/vaadin/client/Util.java index 507a5c2c2d..f175bbe714 100644 --- a/client/src/com/vaadin/client/Util.java +++ b/client/src/com/vaadin/client/Util.java @@ -925,12 +925,27 @@ public class Util { * Performs a hack to trigger a re-layout in the IE8. This is usually * necessary in cases where IE8 "forgets" to update child elements when they * resize. - * + * * @param e * The element to perform the hack on */ public static final void forceIE8Redraw(Element e) { if (BrowserInfo.get().isIE8()) { + forceIERedraw(e); + } + } + + /** + * Performs a hack to trigger a re-layout in the IE browser. This is usually + * necessary in cases where IE "forgets" to update child elements when they + * resize. + * + * @since + * @param e + * The element to perform the hack on + */ + public static void forceIERedraw(Element e) { + if (BrowserInfo.get().isIE()) { setStyleTemporarily(e, "zoom", "1"); } } diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java index 48e17cde05..5073e0ce5d 100644 --- a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java +++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -37,7 +37,7 @@ import com.vaadin.shared.ui.ui.UIState.PushConfigurationState; /** * The default {@link PushConnection} implementation that uses Atmosphere for * handling the communication channel. - * + * * @author Vaadin Ltd * @since 7.1 */ @@ -171,8 +171,12 @@ public class AtmospherePushConnection implements PushConnection { + ApplicationConstants.PUSH_PATH + '/'); String extraParams = UIConstants.UI_ID_PARAMETER + "=" + connection.getConfiguration().getUIId(); - extraParams += "&" + ApplicationConstants.CSRF_TOKEN_PARAMETER + "=" - + connection.getCsrfToken(); + + if (!connection.getCsrfToken().equals( + ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) { + extraParams += "&" + ApplicationConstants.CSRF_TOKEN_PARAMETER + + "=" + connection.getCsrfToken(); + } // uri is needed to identify the right connection when closing uri = ApplicationConnection.addGetParameters(baseUrl, extraParams); @@ -239,9 +243,9 @@ public class AtmospherePushConnection implements PushConnection { /** * Called whenever a server push connection is established (or * re-established). - * + * * @param response - * + * * @since 7.2 */ protected void onConnect(AtmosphereResponse response) { @@ -330,7 +334,7 @@ public class AtmospherePushConnection implements PushConnection { /** * Called if the push connection fails. Atmosphere will automatically retry * the connection until successful. - * + * */ protected void onError(AtmosphereResponse response) { state = State.DISCONNECTED; @@ -448,7 +452,7 @@ public class AtmospherePushConnection implements PushConnection { contentType: 'application/json; charset=UTF-8', reconnectInterval: 5000, timeout: -1, - maxReconnectOnClose: 10000000, + maxReconnectOnClose: 10000000, trackMessageLength: true, enableProtocol: true, messageDelimiter: String.fromCharCode(@com.vaadin.shared.communication.PushConstants::MESSAGE_DELIMITER) @@ -501,7 +505,7 @@ public class AtmospherePushConnection implements PushConnection { private static native boolean isAtmosphereLoaded() /*-{ - return $wnd.jQueryVaadin != undefined; + return $wnd.jQueryVaadin != undefined; }-*/; private void runWhenAtmosphereLoaded(final Command command) { diff --git a/client/src/com/vaadin/client/ui/VContextMenu.java b/client/src/com/vaadin/client/ui/VContextMenu.java index 038ee27dad..1b0181fb7d 100644 --- a/client/src/com/vaadin/client/ui/VContextMenu.java +++ b/client/src/com/vaadin/client/ui/VContextMenu.java @@ -161,6 +161,8 @@ public class VContextMenu extends VOverlay implements SubPartAware { top = top - offsetHeight; if (top < 0) { top = 0; + + setHeight(Window.getClientHeight() + "px"); } } setPopupPosition(left, top); diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java index 004072d04a..7f67c39500 100644 --- a/client/src/com/vaadin/client/ui/VFilterSelect.java +++ b/client/src/com/vaadin/client/ui/VFilterSelect.java @@ -332,6 +332,14 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, .clearWidth(); setPopupPositionAndShow(popup); + // Fix for #14173 + // IE9 and IE10 have a bug, when resize an a element with + // box-shadow. + // IE9 and IE10 need explicit update to remove extra + // box-shadows + if (BrowserInfo.get().isIE9() || BrowserInfo.get().isIE10()) { + forceReflow(); + } } }); } diff --git a/client/src/com/vaadin/client/ui/VOverlay.java b/client/src/com/vaadin/client/ui/VOverlay.java index f9aa2ef2f8..d2c3ee2dae 100644 --- a/client/src/com/vaadin/client/ui/VOverlay.java +++ b/client/src/com/vaadin/client/ui/VOverlay.java @@ -656,6 +656,12 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> { } } } + // Fix for #14173 + // IE9 and IE10 have a bug, when resize an a element with box-shadow. + // IE9 and IE10 need explicit update to remove extra box-shadows + if (BrowserInfo.get().isIE9() || BrowserInfo.get().isIE10()) { + Util.forceIERedraw(getElement()); + } } /** diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index ef8be5aadc..d88f7426ef 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -588,6 +588,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets, private boolean hadScrollBars = false; + private HandlerRegistration addCloseHandler; + public VScrollTable() { setMultiSelectMode(MULTISELECT_MODE_DEFAULT); @@ -669,13 +671,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets, this.client = client; // Add a handler to clear saved context menu details when the menu // closes. See #8526. - client.getContextMenu().addCloseHandler(new CloseHandler<PopupPanel>() { + addCloseHandler = client.getContextMenu().addCloseHandler( + new CloseHandler<PopupPanel>() { - @Override - public void onClose(CloseEvent<PopupPanel> event) { - contextMenu = null; - } - }); + @Override + public void onClose(CloseEvent<PopupPanel> event) { + contextMenu = null; + } + }); } private void handleBodyContextMenu(ContextMenuEvent event) { @@ -7929,4 +7932,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // Nothing found. return null; } + + /** + * @since + */ + public void onUnregister() { + if (addCloseHandler != null) { + addCloseHandler.removeHandler(); + } + } } diff --git a/client/src/com/vaadin/client/ui/VTextField.java b/client/src/com/vaadin/client/ui/VTextField.java index c517f8fec0..1968c7e0a6 100644 --- a/client/src/com/vaadin/client/ui/VTextField.java +++ b/client/src/com/vaadin/client/ui/VTextField.java @@ -16,6 +16,8 @@ package com.vaadin.client.ui; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; @@ -254,6 +256,48 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler, el.oncut = null; }-*/; + private void onDrop() { + if (focusedTextField == this) { + return; + } + updateText(false); + } + + private void updateText(boolean blurred) { + String text = getText(); + setPrompting(inputPrompt != null && (text == null || text.isEmpty())); + if (prompting) { + setText(isReadOnly() ? "" : inputPrompt); + if (blurred) { + addStyleDependentName(CLASSNAME_PROMPT); + } + } + + valueChange(blurred); + } + + private void scheduleOnDropEvent() { + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + onDrop(); + } + }); + } + + private native void attachDropEventListener(Element el) + /*-{ + var me = this; + el.ondrop = $entry(function() { + me.@com.vaadin.client.ui.VTextField::scheduleOnDropEvent()(); + }); + }-*/; + + private native void detachDropEventListener(Element el) + /*-{ + el.ondrop = null; + }-*/; + @Override protected void onDetach() { super.onDetach(); @@ -263,6 +307,7 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler, } if (BrowserInfo.get().isFirefox()) { removeOnInputListener(getElement()); + detachDropEventListener(getElement()); } } @@ -276,6 +321,9 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler, // Workaround for FF setting input prompt as the value if esc is // pressed while the field is focused and empty (#8051). addOnInputListener(getElement()); + // Workaround for FF updating component's internal value after + // having drag-and-dropped text from another element (#14056) + attachDropEventListener(getElement()); } } @@ -418,14 +466,7 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler, } removeStyleDependentName(CLASSNAME_FOCUS); focusedTextField = null; - String text = getText(); - setPrompting(inputPrompt != null && (text == null || "".equals(text))); - if (prompting) { - setText(isReadOnly() ? "" : inputPrompt); - addStyleDependentName(CLASSNAME_PROMPT); - } - - valueChange(true); + updateText(true); } private void setPrompting(boolean prompting) { diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index bea49e5f27..017d1d1024 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -55,6 +55,17 @@ public class TableConnector extends AbstractHasComponentsConnector implements /* * (non-Javadoc) * + * @see com.vaadin.client.ui.AbstractComponentConnector#onUnregister() + */ + @Override + public void onUnregister() { + super.onUnregister(); + getWidget().onUnregister(); + } + + /* + * (non-Javadoc) + * * @see com.vaadin.client.Paintable#updateFromUIDL(com.vaadin.client.UIDL, * com.vaadin.client.ApplicationConnection) */ diff --git a/push/build.xml b/push/build.xml index dfc8d03156..dee5820efb 100644 --- a/push/build.xml +++ b/push/build.xml @@ -16,7 +16,7 @@ <property name="vaadinPush.debug.js" location="${result.dir}/js/VAADIN/vaadinPush.debug.js" /> <!-- Keep the version number in sync with ivy.xml, server/src/com/vaadin/server/Constants.java --> - <property name="atmosphere.runtime.version" value="2.1.2.vaadin2" /> + <property name="atmosphere.runtime.version" value="2.1.2.vaadin3" /> <property name="jquery.js" location="lib/jquery/jquery-1.11.0.js" /> <path id="classpath.compile.custom" /> diff --git a/push/ivy.xml b/push/ivy.xml index 1b98969fca..d3b4944353 100644 --- a/push/ivy.xml +++ b/push/ivy.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ivy-module [ <!-- Keep the version number in sync with build.xml --> - <!ENTITY atmosphere.runtime.version "2.1.2.vaadin2"> + <!ENTITY atmosphere.runtime.version "2.1.2.vaadin3"> <!ENTITY atmosphere.js.version "2.1.5.vaadin4"> ]> diff --git a/server/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java b/server/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java index eafd3573bc..0bfec33957 100644 --- a/server/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java +++ b/server/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java @@ -701,7 +701,9 @@ public class ContainerHierarchicalWrapper implements Container.Hierarchical, */ @Override public int size() { - return container.size(); + int size = container.size(); + assert size >= 0; + return size; } /* diff --git a/server/src/com/vaadin/data/util/ContainerOrderedWrapper.java b/server/src/com/vaadin/data/util/ContainerOrderedWrapper.java index 483753da88..4bb4e4c1b2 100644 --- a/server/src/com/vaadin/data/util/ContainerOrderedWrapper.java +++ b/server/src/com/vaadin/data/util/ContainerOrderedWrapper.java @@ -494,6 +494,7 @@ public class ContainerOrderedWrapper implements Container.Ordered, @Override public int size() { int newSize = container.size(); + assert newSize >= 0; if (lastKnownSize != -1 && newSize != lastKnownSize && !(container instanceof Container.ItemSetChangeNotifier)) { // Update the internal cache when the size of the container changes diff --git a/server/src/com/vaadin/server/AbstractJavaScriptExtension.java b/server/src/com/vaadin/server/AbstractJavaScriptExtension.java index acf6a870c6..410eea3c01 100644 --- a/server/src/com/vaadin/server/AbstractJavaScriptExtension.java +++ b/server/src/com/vaadin/server/AbstractJavaScriptExtension.java @@ -103,6 +103,8 @@ import com.vaadin.ui.JavaScriptFunction; * <li>The primitive Java boolean and the boxed Boolean are represented by * JavaScript booleans.</li> * <li>Java Strings are represented by JavaScript strings.</li> + * <li>Java Dates are represented by JavaScript numbers containing the timestamp + * </li> * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li> * <li>Map<String, ?> in Java is represented by JavaScript object with fields * corresponding to the map keys.</li> diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java index 34c9b5b767..08b5b70f50 100644 --- a/server/src/com/vaadin/server/Constants.java +++ b/server/src/com/vaadin/server/Constants.java @@ -67,7 +67,7 @@ public interface Constants { // Keep the version number in sync with push/build.xml and other locations // listed in that file - static final String REQUIRED_ATMOSPHERE_RUNTIME_VERSION = "2.1.2.vaadin2"; + static final String REQUIRED_ATMOSPHERE_RUNTIME_VERSION = "2.1.2.vaadin3"; static final String INVALID_ATMOSPHERE_VERSION_WARNING = "\n" + "=================================================================\n" diff --git a/server/src/com/vaadin/server/JsonCodec.java b/server/src/com/vaadin/server/JsonCodec.java index 93074abcdb..34b05f73bf 100644 --- a/server/src/com/vaadin/server/JsonCodec.java +++ b/server/src/com/vaadin/server/JsonCodec.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -56,7 +56,7 @@ import com.vaadin.ui.ConnectorTracker; /** * Decoder for converting RPC parameters and other values from JSON in transfer * between the client and the server and vice versa. - * + * * @since 7.0 */ public class JsonCodec implements Serializable { @@ -119,9 +119,9 @@ public class JsonCodec implements Serializable { public static Collection<FieldProperty> find(Class<?> type) throws IntrospectionException { - Collection<FieldProperty> properties = new ArrayList<FieldProperty>(); - Field[] fields = type.getFields(); + Collection<FieldProperty> properties = new ArrayList<FieldProperty>( + fields.length); for (Field field : fields) { if (!Modifier.isStatic(field.getModifiers())) { properties.add(new FieldProperty(field)); @@ -341,7 +341,7 @@ public class JsonCodec implements Serializable { * using the declared type. Otherwise only internal types are allowed in * collections. * </p> - * + * * @param targetType * The type that should be returned by this method * @param valueAndType @@ -471,13 +471,13 @@ public class JsonCodec implements Serializable { private static Map<Object, Object> decodeObjectMap(Type keyType, Type valueType, JSONArray jsonMap, ConnectorTracker connectorTracker) throws JSONException { - Map<Object, Object> map = new HashMap<Object, Object>(); JSONArray keys = jsonMap.getJSONArray(0); JSONArray values = jsonMap.getJSONArray(1); assert (keys.length() == values.length()); + Map<Object, Object> map = new HashMap<Object, Object>(keys.length() * 2); for (int i = 0; i < keys.length(); i++) { Object key = decodeInternalOrCustomType(keyType, keys.get(i), connectorTracker); @@ -580,8 +580,9 @@ public class JsonCodec implements Serializable { private static List<Object> decodeList(Type targetType, boolean restrictToInternalTypes, JSONArray jsonArray, ConnectorTracker connectorTracker) throws JSONException { - List<Object> list = new ArrayList<Object>(); - for (int i = 0; i < jsonArray.length(); ++i) { + int arrayLength = jsonArray.length(); + List<Object> list = new ArrayList<Object>(arrayLength); + for (int i = 0; i < arrayLength; ++i) { // each entry always has two elements: type and value Object encodedValue = jsonArray.get(i); Object decodedChild = decodeParametrizedType(targetType, @@ -754,7 +755,7 @@ public class JsonCodec implements Serializable { /** * Compares the value with the reference. If they match, returns true. - * + * * @param fieldValue * @param referenceValue * @return diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java index e8cdcd7055..8d44ff74ed 100644 --- a/server/src/com/vaadin/server/VaadinService.java +++ b/server/src/com/vaadin/server/VaadinService.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -34,6 +34,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; @@ -69,9 +70,9 @@ import com.vaadin.util.ReflectTools; /** * Provide deployment specific settings that are required outside terminal * specific code. - * + * * @author Vaadin Ltd. - * + * * @since 7.0 */ public abstract class VaadinService implements Serializable { @@ -144,7 +145,7 @@ public abstract class VaadinService implements Serializable { /** * Creates a new vaadin service based on a deployment configuration - * + * * @param deploymentConfiguration * the deployment configuration for the service */ @@ -172,7 +173,7 @@ public abstract class VaadinService implements Serializable { /** * Initializes this service. The service should be initialized before it is * used. - * + * * @since 7.1 * @throws ServiceException * if a problem occurs when creating the service @@ -191,7 +192,7 @@ public abstract class VaadinService implements Serializable { * called first. This enables overriding this method and using add on the * returned list to add a custom request handler which overrides any * predefined handler. - * + * * @return The list of request handlers used by this service. * @throws ServiceException * if a problem occurs when creating the request handlers @@ -214,13 +215,13 @@ public abstract class VaadinService implements Serializable { * Return the URL from where static files, e.g. the widgetset and the theme, * are served. In a standard configuration the VAADIN folder inside the * returned folder is what is used for widgetsets and themes. - * + * * The returned folder is usually the same as the context path and * independent of e.g. the servlet mapping. - * + * * @param request * the request for which the location should be determined - * + * * @return The location of static resources (should contain the VAADIN * directory). Never ends with a slash (/). */ @@ -229,7 +230,7 @@ public abstract class VaadinService implements Serializable { /** * Gets the widgetset that is configured for this deployment, e.g. from a * parameter in web.xml. - * + * * @param request * the request for which a widgetset is required * @return the name of the widgetset @@ -239,7 +240,7 @@ public abstract class VaadinService implements Serializable { /** * Gets the theme that is configured for this deployment, e.g. from a portal * parameter or just some sensible default value. - * + * * @param request * the request for which a theme is required * @return the name of the theme @@ -251,7 +252,7 @@ public abstract class VaadinService implements Serializable { * whether it will be included into some other context. A standalone UI may * do things that might interfere with other parts of a page, e.g. changing * the page title and requesting focus upon loading. - * + * * @param request * the request for which the UI is loaded * @return a boolean indicating whether the UI should be standalone @@ -262,9 +263,9 @@ public abstract class VaadinService implements Serializable { * Gets the class loader to use for loading classes loaded by name, e.g. * custom UI classes. This is by default the class loader that was used to * load the Servlet or Portlet class to which this service belongs. - * + * * @return the class loader to use, or <code>null</code> - * + * * @see #setClassLoader(ClassLoader) */ public ClassLoader getClassLoader() { @@ -277,10 +278,10 @@ public abstract class VaadinService implements Serializable { * any existing class loader hierarchy, e.g. by ensuring that a class loader * set for this service delegates to the previously set class loader if the * class is not found. - * + * * @param classLoader * the new class loader to set, not <code>null</code>. - * + * * @see #getClassLoader() */ public void setClassLoader(ClassLoader classLoader) { @@ -296,11 +297,11 @@ public abstract class VaadinService implements Serializable { * not known. The MIME type is determined by the configuration of the * container, and may be specified in a deployment descriptor. Common MIME * types are "text/html" and "image/gif". - * + * * @param resourceName * a String specifying the name of a file * @return a String specifying the file's MIME type - * + * * @see ServletContext#getMimeType(String) * @see PortletContext#getMimeType(String) */ @@ -308,7 +309,7 @@ public abstract class VaadinService implements Serializable { /** * Gets the deployment configuration. - * + * * @return the deployment configuration */ public DeploymentConfiguration getDeploymentConfiguration() { @@ -318,9 +319,9 @@ public abstract class VaadinService implements Serializable { /** * Sets the system messages provider to use for getting system messages to * display to users of this service. - * + * * @see #getSystemMessagesProvider() - * + * * @param systemMessagesProvider * the system messages provider; <code>null</code> is not * allowed. @@ -340,11 +341,11 @@ public abstract class VaadinService implements Serializable { * By default, the {@link DefaultSystemMessagesProvider} which always * provides the built-in default {@link SystemMessages} is used. * </p> - * + * * @see #setSystemMessagesProvider(SystemMessagesProvider) * @see SystemMessagesProvider * @see SystemMessages - * + * * @return the system messages provider; not <code>null</code> */ public SystemMessagesProvider getSystemMessagesProvider() { @@ -356,7 +357,7 @@ public abstract class VaadinService implements Serializable { * also be implemented to use information from current instances of various * objects, which means that this method might return different values for * the same locale under different circumstances. - * + * * @param locale * the desired locale for the system messages * @param request @@ -373,13 +374,13 @@ public abstract class VaadinService implements Serializable { /** * Returns the context base directory. - * + * * Typically an application is deployed in a such way that is has an * application directory. For web applications this directory is the root * directory of the web applications. In some cases applications might not * have an application directory (for example web applications running * inside a war). - * + * * @return The application base directory or null if the application has no * base directory. */ @@ -393,10 +394,10 @@ public abstract class VaadinService implements Serializable { * the listener is not necessarily notified immediately when the session is * created but only when the first request for that session is handled by * this service. - * + * * @see #removeSessionInitListener(SessionInitListener) * @see SessionInitListener - * + * * @param listener * the Vaadin service session initialization listener */ @@ -408,9 +409,9 @@ public abstract class VaadinService implements Serializable { /** * Removes a Vaadin service session initialization listener from this * service. - * + * * @see #addSessionInitListener(SessionInitListener) - * + * * @param listener * the Vaadin service session initialization listener to remove. */ @@ -425,9 +426,9 @@ public abstract class VaadinService implements Serializable { * <p> * The session being destroyed is locked and its UIs have been removed when * the listeners are called. - * + * * @see #addSessionInitListener(SessionInitListener) - * + * * @param listener * the vaadin service session destroy listener */ @@ -439,7 +440,7 @@ public abstract class VaadinService implements Serializable { /** * Handles destruction of the given session. Internally ensures proper * locking is done. - * + * * @param vaadinSession * The session to destroy */ @@ -485,9 +486,9 @@ public abstract class VaadinService implements Serializable { /** * Removes a Vaadin service session destroy listener from this service. - * + * * @see #addSessionDestroyListener(SessionDestroyListener) - * + * * @param listener * the vaadin service session destroy listener */ @@ -502,12 +503,12 @@ public abstract class VaadinService implements Serializable { * Handles locking of the session internally to avoid creation of duplicate * sessions by two threads simultaneously. * </p> - * + * * @param request * the request to get a vaadin service session for. - * + * * @see VaadinSession - * + * * @return the vaadin service session for the request, or <code>null</code> * if no session is found and this is a request for which a new * session shouldn't be created. @@ -529,7 +530,7 @@ public abstract class VaadinService implements Serializable { * Associates the given lock with this service and the given wrapped * session. This method should not be called more than once when the lock is * initialized for the session. - * + * * @see #getSessionLock(WrappedSession) * @param wrappedSession * The wrapped session the lock is associated with @@ -550,7 +551,7 @@ public abstract class VaadinService implements Serializable { /** * Returns the name used to store the lock in the HTTP session. - * + * * @return The attribute name for the lock */ private String getLockAttributeName() { @@ -564,7 +565,7 @@ public abstract class VaadinService implements Serializable { * This method uses the wrapped session instead of VaadinSession to be able * to lock even before the VaadinSession has been initialized. * </p> - * + * * @param wrappedSession * The wrapped session * @return A lock instance used for locking access to the wrapped session @@ -588,10 +589,10 @@ public abstract class VaadinService implements Serializable { /** * Locks the given session for this service instance. Typically you want to * call {@link VaadinSession#lock()} instead of this method. - * + * * @param wrappedSession * The session to lock - * + * * @throws IllegalStateException * if the session is invalidated before it can be locked */ @@ -631,7 +632,7 @@ public abstract class VaadinService implements Serializable { * Releases the lock for the given session for this service instance. * Typically you want to call {@link VaadinSession#unlock()} instead of this * method. - * + * * @param wrappedSession * The session to unlock */ @@ -666,7 +667,7 @@ public abstract class VaadinService implements Serializable { * Finds or creates a Vaadin session. Assumes necessary synchronization has * been done by the caller to ensure this is not called simultaneously by * several threads. - * + * * @param request * @param requestCanCreateSession * @return @@ -733,8 +734,8 @@ public abstract class VaadinService implements Serializable { /** * Creates and registers a new VaadinSession for this service. Assumes * proper locking has been taken care of by the caller. - * - * + * + * * @param request * The request which triggered session creation. * @return A new VaadinSession instance @@ -771,11 +772,11 @@ public abstract class VaadinService implements Serializable { * service. * <p> * This is only used to support legacy cases. - * + * * @param request * @return * @throws MalformedURLException - * + * * @deprecated As of 7.0. Only used to support {@link LegacyApplication}. */ @Deprecated @@ -786,12 +787,12 @@ public abstract class VaadinService implements Serializable { /** * Creates a new Vaadin session for this service and request - * + * * @param request * The request for which to create a VaadinSession * @return A new VaadinSession * @throws ServiceException - * + * */ protected VaadinSession createVaadinSession(VaadinRequest request) throws ServiceException { @@ -839,7 +840,7 @@ public abstract class VaadinService implements Serializable { /** * Retrieves the wrapped session for the request. - * + * * @param request * The request for which to retrieve a session * @param requestCanCreateSession @@ -862,7 +863,7 @@ public abstract class VaadinService implements Serializable { /** * Checks whether it's valid to create a new service session as a result of * the given request. - * + * * @param request * the request * @return <code>true</code> if it's valid to create a new service session @@ -877,10 +878,10 @@ public abstract class VaadinService implements Serializable { * {@link InheritableThreadLocal}). In other cases, (e.g. from background * threads started in some other way), the current service is not * automatically defined. - * + * * @return the current Vaadin service instance if available, otherwise * <code>null</code> - * + * * @see #setCurrentInstances(VaadinRequest, VaadinResponse) */ public static VaadinService getCurrent() { @@ -898,14 +899,14 @@ public abstract class VaadinService implements Serializable { * instances outside the normal request handling, e.g. when initiating * custom background threads. * </p> - * + * * @param request * the Vaadin request to set as the current request, or * <code>null</code> if no request should be set. * @param response * the Vaadin response to set as the current response, or * <code>null</code> if no response should be set. - * + * * @see #getCurrent() * @see #getCurrentRequest() * @see #getCurrentResponse() @@ -919,7 +920,7 @@ public abstract class VaadinService implements Serializable { /** * Sets the given Vaadin service as the current service. - * + * * @param service */ public static void setCurrent(VaadinService service) { @@ -931,10 +932,10 @@ public abstract class VaadinService implements Serializable { * automatically defined when the request is started. The current request * can not be used in e.g. background threads because of the way server * implementations reuse request instances. - * + * * @return the current Vaadin request instance if available, otherwise * <code>null</code> - * + * * @see #setCurrentInstances(VaadinRequest, VaadinResponse) */ public static VaadinRequest getCurrentRequest() { @@ -946,10 +947,10 @@ public abstract class VaadinService implements Serializable { * automatically defined when the request is started. The current response * can not be used in e.g. background threads because of the way server * implementations reuse response instances. - * + * * @return the current Vaadin response instance if available, otherwise * <code>null</code> - * + * * @see #setCurrentInstances(VaadinRequest, VaadinResponse) */ public static VaadinResponse getCurrentResponse() { @@ -961,7 +962,7 @@ public abstract class VaadinService implements Serializable { * different services of the same type but the same for corresponding * instances running in different JVMs in a cluster. This is typically based * on e.g. the configured servlet's or portlet's name. - * + * * @return the unique name of this service instance. */ public abstract String getServiceName(); @@ -972,11 +973,11 @@ public abstract class VaadinService implements Serializable { * related to any particular UI or have the UI information encoded in a * non-standard way. The returned UI is also set as the current UI ( * {@link UI#setCurrent(UI)}). - * + * * @param request * the request for which a UI is desired * @return the UI belonging to the request or null if no UI is found - * + * */ public UI findUI(VaadinRequest request) { // getForSession asserts that the lock is held @@ -1002,12 +1003,12 @@ public abstract class VaadinService implements Serializable { * typically checks the @{@link PreserveOnRefresh} annotation but UI * providers and ultimately VaadinService implementations may choose to * override the defaults. - * + * * @param provider * the UI provider responsible for the UI * @param event * the UI create event with details about the UI - * + * * @return <code>true</code> if the UI should be preserved on refresh; * <code>false</code> if a new UI instance should be initialized on * refreshed. @@ -1024,7 +1025,7 @@ public abstract class VaadinService implements Serializable { * Please note that this method makes certain assumptions about how data is * stored in the underlying session and may thus not be compatible with some * environments. - * + * * @param request * The Vaadin request for which the session should be * reinitialized @@ -1034,8 +1035,10 @@ public abstract class VaadinService implements Serializable { // Stores all attributes (security key, reference to this context // instance) so they can be added to the new session - HashMap<String, Object> attrs = new HashMap<String, Object>(); - for (String name : oldSession.getAttributeNames()) { + Set<String> attributeNames = oldSession.getAttributeNames(); + HashMap<String, Object> attrs = new HashMap<String, Object>( + attributeNames.size() * 2); + for (String name : attributeNames) { Object value = oldSession.getAttribute(name); if (value instanceof VaadinSession) { // set flag to avoid cleanup @@ -1076,9 +1079,9 @@ public abstract class VaadinService implements Serializable { /** * TODO PUSH Document - * + * * TODO Pass UI or VaadinSession? - * + * * @param uI * @param themeName * @param resource @@ -1090,14 +1093,14 @@ public abstract class VaadinService implements Serializable { /** * Creates and returns a unique ID for the DIV where the UI is to be * rendered. - * + * * @param session * The service session to which the bootstrapped UI will belong. * @param request * The request for which a div id is needed * @param uiClass * The class of the UI that will be bootstrapped - * + * * @return the id to use in the DOM */ public abstract String getMainDivId(VaadinSession session, @@ -1115,9 +1118,9 @@ public abstract class VaadinService implements Serializable { * To avoid causing out of sync errors, you should typically redirect to * some other page using {@link Page#setLocation(String)} to make the * browser unload the invalidated UI. - * + * * @see SystemMessages#getSessionExpiredCaption() - * + * * @param session * the session to close */ @@ -1129,7 +1132,7 @@ public abstract class VaadinService implements Serializable { * Called at the end of a request, after sending the response. Closes * inactive UIs in the given session, removes closed UIs from the session, * and closes the session if it is itself inactive. - * + * * @param session */ void cleanupSession(VaadinSession session) { @@ -1164,29 +1167,29 @@ public abstract class VaadinService implements Serializable { /** * Removes those UIs from the given session for which {@link UI#isClosing() * isClosing} yields true. - * + * * @param session */ private void removeClosedUIs(final VaadinSession session) { ArrayList<UI> uis = new ArrayList<UI>(session.getUIs()); for (final UI ui : uis) { - ui.accessSynchronously(new Runnable() { - @Override - public void run() { - if (ui.isClosing()) { + if (ui.isClosing()) { + ui.accessSynchronously(new Runnable() { + @Override + public void run() { getLogger().log(Level.FINER, "Removing closed UI {0}", ui.getUIId()); session.removeUI(ui); } - } - }); + }); + } } } /** * Closes those UIs in the given session for which {@link #isUIActive} * yields false. - * + * * @since 7.0.0 */ private void closeInactiveUIs(VaadinSession session) { @@ -1212,11 +1215,11 @@ public abstract class VaadinService implements Serializable { * session. This is a lower bound; it might take longer to close an inactive * UI. Returns a negative number if heartbeat is disabled and timeout never * occurs. - * + * * @see DeploymentConfiguration#getHeartbeatInterval() - * + * * @since 7.0.0 - * + * * @return The heartbeat timeout in seconds or a negative number if timeout * never occurs. */ @@ -1235,12 +1238,12 @@ public abstract class VaadinService implements Serializable { * requests suffice to keep the session alive, but it will still eventually * expire in the regular manner if there are no requests at all (see * {@link WrappedSession#getMaxInactiveInterval()}). - * + * * @see DeploymentConfiguration#isCloseIdleSessions() * @see #getHeartbeatTimeout() - * + * * @since 7.0.0 - * + * * @return The UIDL request timeout in seconds, or a negative number if * timeout never occurs. */ @@ -1257,12 +1260,12 @@ public abstract class VaadinService implements Serializable { * A UI is active if and only if its {@link UI#isClosing() isClosing} * returns false and {@link #getHeartbeatTimeout() getHeartbeatTimeout} is * negative or has not yet expired. - * + * * @since 7.0.0 - * + * * @param ui * The UI whose status to check - * + * * @return true if the UI is active, false if it could be removed. */ private boolean isUIActive(UI ui) { @@ -1282,10 +1285,10 @@ public abstract class VaadinService implements Serializable { * A session is active if and only if its {@link #isClosing} returns false * and {@link #getUidlRequestTimeout(VaadinSession) getUidlRequestTimeout} * is negative or has not yet expired. - * + * * @param session * The session whose status to check - * + * * @return true if the session is active, false if it could be closed. */ private boolean isSessionActive(VaadinSession session) { @@ -1305,7 +1308,7 @@ public abstract class VaadinService implements Serializable { /** * Called before the framework starts handling a request - * + * * @param request * The request * @param response @@ -1323,7 +1326,7 @@ public abstract class VaadinService implements Serializable { /** * Called after the framework has handled a request and the response has * been written. - * + * * @param request * The request object * @param response @@ -1335,23 +1338,16 @@ public abstract class VaadinService implements Serializable { public void requestEnd(VaadinRequest request, VaadinResponse response, VaadinSession session) { if (session != null) { - final VaadinSession finalSession = session; - - session.accessSynchronously(new Runnable() { - @Override - public void run() { - cleanupSession(finalSession); - } - }); - - final long duration = (System.nanoTime() - (Long) request - .getAttribute(REQUEST_START_TIME_ATTRIBUTE)) / 1000000; - session.accessSynchronously(new Runnable() { - @Override - public void run() { - finalSession.setLastRequestDuration(duration); - } - }); + assert VaadinSession.getCurrent() == session; + session.lock(); + try { + cleanupSession(session); + final long duration = (System.nanoTime() - (Long) request + .getAttribute(REQUEST_START_TIME_ATTRIBUTE)) / 1000000; + session.setLastRequestDuration(duration); + } finally { + session.unlock(); + } } CurrentInstance.clearAll(); } @@ -1360,11 +1356,11 @@ public abstract class VaadinService implements Serializable { * Returns the request handlers that are registered with this service. The * iteration order of the returned collection is the same as the order in * which the request handlers will be invoked when a request is handled. - * + * * @return a collection of request handlers in the order they are invoked - * + * * @see #createRequestHandlers() - * + * * @since 7.1 */ public Iterable<RequestHandler> getRequestHandlers() { @@ -1381,7 +1377,7 @@ public abstract class VaadinService implements Serializable { * request handler handles session expiration a default expiration message * will be written. * </p> - * + * * @param request * The incoming request * @param response @@ -1473,7 +1469,7 @@ public abstract class VaadinService implements Serializable { /** * Writes the given string as a response using the given content type. - * + * * @param response * The response reference * @param contentType @@ -1498,7 +1494,7 @@ public abstract class VaadinService implements Serializable { /** * Called when the session has expired and the request handling is therefore * aborted. - * + * * @param request * The request * @param response @@ -1553,7 +1549,7 @@ public abstract class VaadinService implements Serializable { /** * Creates a JSON message which, when sent to client as-is, will cause a * critical error to be shown with the given details. - * + * * @param caption * The caption of the error or null to omit * @param message @@ -1616,10 +1612,10 @@ public abstract class VaadinService implements Serializable { /** * Enables push if push support is available and push has not yet been * enabled. - * + * * If push support is not available, a warning explaining the situation will * be logged at least the first time this method is invoked. - * + * * @return <code>true</code> if push can be used; <code>false</code> if push * is not available. */ @@ -1638,7 +1634,7 @@ public abstract class VaadinService implements Serializable { * internally used by {@link VaadinSession#accessSynchronously(Runnable)} * and {@link UI#accessSynchronously(Runnable)} to help avoid causing * deadlocks. - * + * * @since 7.1 * @param session * the session that is being locked @@ -1657,7 +1653,7 @@ public abstract class VaadinService implements Serializable { * provided one for which the current thread holds a lock. This method might * not detect all cases where some other session is locked, but it should * cover the most typical situations. - * + * * @since 7.2 * @param session * the session that is expected to be locked @@ -1681,11 +1677,11 @@ public abstract class VaadinService implements Serializable { * to allow a certain type of testing. For these cases, the check can be * disabled by setting the init parameter * <code>disable-xsrf-protection</code> to <code>true</code>. - * + * * @see DeploymentConfiguration#isXsrfProtectionEnabled() - * + * * @since 7.1 - * + * * @param session * the vaadin session for which the check should be done * @param requestToken @@ -1712,15 +1708,15 @@ public abstract class VaadinService implements Serializable { * Implementation for {@link VaadinSession#access(Runnable)}. This method is * implemented here instead of in {@link VaadinSession} to enable overriding * the implementation without using a custom subclass of VaadinSession. - * + * * @since 7.1 * @see VaadinSession#access(Runnable) - * + * * @param session * the vaadin session to access * @param runnable * the runnable to run with the session locked - * + * * @return a future that can be used to check for task completion and to * cancel the task */ @@ -1739,7 +1735,7 @@ public abstract class VaadinService implements Serializable { * thread, the queue will be purged when the session is unlocked. If the * lock is not held by any thread, it is acquired and the queue is purged * right away. - * + * * @since 7.1.2 * @param session * the session for which the access queue should be purged @@ -1775,7 +1771,7 @@ public abstract class VaadinService implements Serializable { * <p> * This method is automatically run by the framework at appropriate * situations and is not intended to be used by application developers. - * + * * @param session * the vaadin session to purge the queue for * @since 7.1 @@ -1817,11 +1813,11 @@ public abstract class VaadinService implements Serializable { /** * Adds a service destroy listener that gets notified when this service is * destroyed. - * + * * @since 7.2 * @param listener * the service destroy listener to add - * + * * @see #destroy() * @see #removeServiceDestroyListener(ServiceDestroyListener) * @see ServiceDestroyListener @@ -1834,7 +1830,7 @@ public abstract class VaadinService implements Serializable { /** * Removes a service destroy listener that was previously added with * {@link #addServiceDestroyListener(ServiceDestroyListener)}. - * + * * @since 7.2 * @param listener * the service destroy listener to remove @@ -1848,11 +1844,11 @@ public abstract class VaadinService implements Serializable { * Called when the servlet, portlet or similar for this service is being * destroyed. After this method has been called, no more requests will be * handled by this service. - * + * * @see #addServiceDestroyListener(ServiceDestroyListener) * @see Servlet#destroy() * @see Portlet#destroy() - * + * * @since 7.2 */ public void destroy() { diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index 12e7c28cd8..09b8a22a46 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -16,12 +16,14 @@ package com.vaadin.server; import java.io.BufferedWriter; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.io.Serializable; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; @@ -29,7 +31,10 @@ import java.net.URLConnection; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; @@ -51,6 +56,59 @@ import com.vaadin.util.CurrentInstance; @SuppressWarnings("serial") public class VaadinServlet extends HttpServlet implements Constants { + private static class ScssCacheEntry implements Serializable { + + private final String css; + private final List<String> sourceUris; + private final long timestamp; + + public ScssCacheEntry(String css, List<String> sourceUris) { + this.css = css; + this.sourceUris = sourceUris; + + timestamp = getLastModified(); + } + + public String getCss() { + return css; + } + + private long getLastModified() { + long newest = 0; + for (String uri : sourceUris) { + File file = new File(uri); + if (!file.exists()) { + return -1; + } else { + newest = Math.max(newest, file.lastModified()); + } + } + + return newest; + } + + public boolean isStillValid() { + if (timestamp == -1) { + /* + * Don't ever bother checking anything if files used during the + * compilation were gone before the cache entry was created. + */ + return false; + } else if (timestamp != getLastModified()) { + /* + * Would in theory still be valid if the last modification is + * before the recorded timestamp, but that would still mean that + * something has changed since we last checked, so let's + * invalidate in that case as well to be on the safe side. + */ + return false; + } else { + return true; + } + } + + } + private VaadinServletService servletService; /** @@ -535,10 +593,18 @@ public class VaadinServlet extends HttpServlet implements Constants { * Mutex for preventing to scss compilations to take place simultaneously. * This is a workaround needed as the scss compiler currently is not thread * safe (#10292). + * <p> + * In addition, this is also used to protect the cached compilation results. */ private static final Object SCSS_MUTEX = new Object(); /** + * Global cache of scss compilation results. This map is protected from + * concurrent access by {@link #SCSS_MUTEX}. + */ + private static final Map<String, ScssCacheEntry> scssCache = new HashMap<String, ScssCacheEntry>(); + + /** * Returns the default theme. Must never return null. * * @return @@ -824,46 +890,62 @@ public class VaadinServlet extends HttpServlet implements Constants { } synchronized (SCSS_MUTEX) { - String realFilename = sc.getRealPath(scssFilename); - ScssStylesheet scss = ScssStylesheet.get(realFilename); - if (scss == null) { - // Not a file in the file system (WebContent directory). Use the - // identifier directly (VAADIN/themes/.../styles.css) so - // ScssStylesheet will try using the class loader. - if (scssFilename.startsWith("/")) { - scssFilename = scssFilename.substring(1); - } + ScssCacheEntry cacheEntry = scssCache.get(scssFilename); - scss = ScssStylesheet.get(scssFilename); + if (cacheEntry == null || !cacheEntry.isStillValid()) { + cacheEntry = compileScssOnTheFly(filename, scssFilename, sc); + scssCache.put(scssFilename, cacheEntry); } - if (scss == null) { - getLogger() - .log(Level.WARNING, - "Scss file {0} exists but ScssStylesheet was not able to find it", - scssFilename); - return false; - } - try { - getLogger().log(Level.FINE, "Compiling {0} for request to {1}", - new Object[] { realFilename, filename }); - scss.compile(); - } catch (Exception e) { - getLogger().log(Level.WARNING, "Scss compilation failed", e); + if (cacheEntry == null) { + // compilation did not produce any result, but logged a message return false; } // This is for development mode only so instruct the browser to - // never - // cache it + // never cache it response.setHeader("Cache-Control", "no-cache"); final String mimetype = getService().getMimeType(filename); - writeResponse(response, mimetype, scss.printState()); + writeResponse(response, mimetype, cacheEntry.getCss()); return true; } } + private ScssCacheEntry compileScssOnTheFly(String filename, + String scssFilename, ServletContext sc) throws IOException { + String realFilename = sc.getRealPath(scssFilename); + ScssStylesheet scss = ScssStylesheet.get(realFilename); + if (scss == null) { + // Not a file in the file system (WebContent directory). Use the + // identifier directly (VAADIN/themes/.../styles.css) so + // ScssStylesheet will try using the class loader. + if (scssFilename.startsWith("/")) { + scssFilename = scssFilename.substring(1); + } + + scss = ScssStylesheet.get(scssFilename); + } + + if (scss == null) { + getLogger() + .log(Level.WARNING, + "Scss file {0} exists but ScssStylesheet was not able to find it", + scssFilename); + return null; + } + try { + getLogger().log(Level.FINE, "Compiling {0} for request to {1}", + new Object[] { realFilename, filename }); + scss.compile(); + } catch (Exception e) { + getLogger().log(Level.WARNING, "Scss compilation failed", e); + return null; + } + + return new ScssCacheEntry(scss.printState(), scss.getSourceUris()); + } + /** * Check whether a URL obtained from a classloader refers to a valid static * resource in the directory VAADIN. diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java index bbabd881f8..2ab8c52dad 100644 --- a/server/src/com/vaadin/server/VaadinSession.java +++ b/server/src/com/vaadin/server/VaadinSession.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -65,7 +65,7 @@ import com.vaadin.util.ReflectTools; * Everything inside a {@link VaadinSession} should be serializable to ensure * compatibility with schemes using serialization for persisting the session * data. - * + * * @author Vaadin Ltd * @since 7.0.0 */ @@ -77,7 +77,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * {@link VaadinSession#access(Runnable)}. This class is used internally by * the framework and is not intended to be directly used by application * developers. - * + * * @since 7.1 * @author Vaadin Ltd */ @@ -93,10 +93,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Creates an instance for the given runnable - * + * * @param session * the session to which the task belongs - * + * * @param runnable * the runnable to run when this task is purged from the * queue @@ -114,7 +114,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * deadlocks unless implemented very carefully. get(long, TimeUnit) * does not have the same detection since a sensible timeout should * avoid completely locking up the application. - * + * * Even though no deadlock could occur after the runnable has been * run, the check is always done as the deterministic behavior makes * it easier to detect potential problems. @@ -126,9 +126,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Gets the current instance values that should be used when running * this task. - * + * * @see CurrentInstance#restoreInstances(Map) - * + * * @return a map of current instances. */ public Map<Class<?>, CurrentInstance> getCurrentInstances() { @@ -137,7 +137,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Handles exceptions thrown during the execution of this task. - * + * * @since 7.1.8 * @param exception * the thrown exception. @@ -168,7 +168,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * The lifecycle state of a VaadinSession. - * + * * @since 7.2 */ public enum State { @@ -273,7 +273,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Creates a new VaadinSession tied to a VaadinService. - * + * * @param service * the Vaadin service for the new session */ @@ -327,9 +327,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Get the web browser associated with this session. - * + * * @return the web browser object - * + * * @deprecated As of 7.0, use {@link Page#getWebBrowser()} instead. */ @Deprecated @@ -350,7 +350,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Sets the time spent servicing the last request in the session and updates * the total time spent servicing requests in this session. - * + * * @param time * The time spent in the last request, in milliseconds. */ @@ -371,11 +371,11 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Sets the time when the last UIDL request was serviced in this session. - * + * * @param timestamp * The time when the last request was handled, in milliseconds * since the epoch. - * + * */ public void setLastRequestTimestamp(long timestamp) { assert hasLock(); @@ -384,7 +384,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Returns the time when the last request was serviced in this session. - * + * * @return The time when the last request was handled, in milliseconds since * the epoch. */ @@ -396,7 +396,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Gets the underlying session to which this service session is currently * associated. - * + * * @return the wrapped session for this context */ public WrappedSession getSession() { @@ -410,7 +410,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * @return - * + * * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @@ -430,7 +430,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Loads the VaadinSession for the given service and WrappedSession from the * HTTP session. - * + * * @param service * The service the VaadinSession is associated with * @param underlyingSession @@ -460,7 +460,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Retrieves all {@link VaadinSession}s which are stored in the given HTTP * session - * + * * @since 7.2 * @param httpSession * the HTTP session @@ -485,7 +485,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Removes this VaadinSession from the HTTP session. - * + * * @param service * The service this session is associated with * @deprecated As of 7.0. Should be moved to a separate session storage @@ -500,7 +500,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Retrieves the name of the attribute used for storing a VaadinSession for * the given service. - * + * * @param service * The service associated with the sessio * @return The attribute name used for storing the session @@ -511,7 +511,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Stores this VaadinSession in the HTTP session. - * + * * @param service * The service this session is associated with * @param session @@ -564,7 +564,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Gets the configuration for this session - * + * * @return the deployment configuration */ public DeploymentConfiguration getConfiguration() { @@ -574,10 +574,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Gets the default locale for this session. - * + * * By default this is the preferred locale of the user using the session. In * most cases it is read from the browser defaults. - * + * * @return the locale of this session. */ public Locale getLocale() { @@ -590,13 +590,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Sets the default locale for this session. - * + * * By default this is the preferred locale of the user using the * application. In most cases it is read from the browser defaults. - * + * * @param locale * the Locale object. - * + * */ public void setLocale(Locale locale) { assert hasLock(); @@ -605,7 +605,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Gets the session's error handler. - * + * * @return the current error handler */ public ErrorHandler getErrorHandler() { @@ -615,7 +615,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Sets the session error handler. - * + * * @param errorHandler */ public void setErrorHandler(ErrorHandler errorHandler) { @@ -626,9 +626,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Gets the {@link ConverterFactory} used to locate a suitable * {@link Converter} for fields in the session. - * + * * See {@link #setConverterFactory(ConverterFactory)} for more details - * + * * @return The converter factory used in the session */ public ConverterFactory getConverterFactory() { @@ -653,7 +653,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * </p> * <p> * The converter factory must never be set to null. - * + * * @param converterFactory * The converter factory used in the session */ @@ -670,12 +670,12 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * Handlers are called in reverse order of addition, so the most recently * added handler will be called first. * </p> - * + * * @param handler * the request handler to add - * + * * @see #removeRequestHandler(RequestHandler) - * + * * @since 7.0 */ public void addRequestHandler(RequestHandler handler) { @@ -685,10 +685,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Removes a request handler from the session. - * + * * @param handler * the request handler to remove - * + * * @since 7.0 */ public void removeRequestHandler(RequestHandler handler) { @@ -700,13 +700,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * Gets the request handlers that are registered to the session. The * iteration order of the returned collection is the same as the order in * which the request handlers will be invoked when a request is handled. - * + * * @return a collection of request handlers, with the iteration order * according to the order they would be invoked - * + * * @see #addRequestHandler(RequestHandler) * @see #removeRequestHandler(RequestHandler) - * + * * @since 7.0 */ public Collection<RequestHandler> getRequestHandlers() { @@ -721,12 +721,12 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * {@link InheritableThreadLocal}). In other cases, (e.g. from background * threads started in some other way), the current session is not * automatically defined. - * + * * @return the current session instance if available, otherwise * <code>null</code> - * + * * @see #setCurrent(VaadinSession) - * + * * @since 7.0 */ public static VaadinSession getCurrent() { @@ -742,12 +742,12 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * session outside the normal request handling and treads started from * request handling threads, e.g. when initiating custom background threads. * </p> - * + * * @param session - * + * * @see #getCurrent() * @see ThreadLocal - * + * * @since 7.0 */ public static void setCurrent(VaadinSession session) { @@ -758,9 +758,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * Gets all the UIs of this session. This includes UIs that have been * requested but not yet initialized. UIs that receive no heartbeat requests * from the client are eventually removed from the session. - * + * * @return a collection of UIs belonging to this application - * + * * @since 7.0 */ public Collection<UI> getUIs() { @@ -775,11 +775,11 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Generate an id for the given Connector. Connectors must not call this * method more than once, the first time they need an id. - * + * * @param connector * A connector that has not yet been assigned an id. * @return A new id for the connector - * + * * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @@ -794,7 +794,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * <p> * This is meant for framework internal use. * </p> - * + * * @param uiId * The UI id * @return The UI with the given id or null if not found @@ -806,7 +806,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Checks if the current thread has exclusive access to this VaadinSession - * + * * @return true if the thread has exclusive access, false otherwise */ public boolean hasLock() { @@ -817,7 +817,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Checks if the current thread has exclusive access to the given * WrappedSession. - * + * * @return true if this thread has exclusive access, false otherwise */ private static boolean hasLock(VaadinService service, WrappedSession session) { @@ -830,10 +830,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * be generated. This can be used to modify the contents of the HTML that * loads the Vaadin application in the browser and the HTTP headers that are * included in the response serving the HTML. - * + * * @see BootstrapListener#modifyBootstrapFragment(BootstrapFragmentResponse) * @see BootstrapListener#modifyBootstrapPage(BootstrapPageResponse) - * + * * @param listener * the bootstrap listener to add */ @@ -847,9 +847,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Remove a bootstrap listener that was previously added. - * + * * @see #addBootstrapListener(BootstrapListener) - * + * * @param listener * the bootstrap listener to remove */ @@ -865,11 +865,11 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * Fires a bootstrap event to all registered listeners. There are currently * two supported events, both inheriting from {@link BootstrapResponse}: * {@link BootstrapFragmentResponse} and {@link BootstrapPageResponse}. - * + * * @param response * the bootstrap response event for which listeners should be * fired - * + * * @deprecated As of 7.0. Will likely change or be removed in a future * version */ @@ -882,12 +882,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Called by the framework to remove an UI instance from the session because * it has been closed. - * + * * @param ui * the UI to remove */ public void removeUI(UI ui) { assert hasLock(); + assert UI.getCurrent() == ui; Integer id = Integer.valueOf(ui.getUIId()); ui.setSession(null); uIs.remove(id); @@ -902,7 +903,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * connector resources that are not served by any single connector because * e.g. because they are served with strong caching or because of legacy * reasons. - * + * * @param createOnDemand * <code>true</code> if a resource handler should be initialized * if there is no handler associated with this application. @@ -911,7 +912,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return this session's global resource handler, or <code>null</code> if * there is no handler and the createOnDemand parameter is * <code>false</code>. - * + * * @since 7.0.0 */ public GlobalResourceHandler getGlobalResourceHandler(boolean createOnDemand) { @@ -933,10 +934,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * instance is not guaranteed to support any other features of the * <code>Lock</code> interface than {@link Lock#lock()} and * {@link Lock#unlock()}. - * + * * @return the <code>Lock</code> that is used for synchronization, never * <code>null</code> - * + * * @see #lock() * @see Lock */ @@ -951,7 +952,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * is done correctly is to wrap your code using {@link UI#access(Runnable)} * (or {@link VaadinSession#access(Runnable)} if you are only touching the * session and not any UI), e.g.: - * + * * <pre> * myUI.access(new Runnable() { * @Override @@ -962,10 +963,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * } * }); * </pre> - * + * * If you for whatever reason want to do locking manually, you should do it * like: - * + * * <pre> * session.lock(); * try { @@ -974,12 +975,12 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * session.unlock(); * } * </pre> - * + * * This method will block until the lock can be retrieved. * <p> * {@link #getLockInstance()} can be used if more control over the locking * is required. - * + * * @see #unlock() * @see #getLockInstance() * @see #hasLock() @@ -995,7 +996,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * For UIs in this session that have its push mode set to * {@link PushMode#AUTOMATIC automatic}, pending changes will be pushed to * their respective clients. - * + * * @see #lock() * @see UI#push() */ @@ -1045,9 +1046,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * data with the current user so that it can be retrieved at a later point * from some other part of the application. Setting the value to * <code>null</code> clears the stored value. - * + * * @see #getAttribute(String) - * + * * @param name * the name to associate the value with, can not be * <code>null</code> @@ -1077,10 +1078,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * value. The outcome of calling this method is thus the same as if calling<br /> * <br /> * <code>setAttribute(type.getName(), value);</code> - * + * * @see #getAttribute(Class) * @see #setAttribute(String, Object) - * + * * @param type * the type that the stored value represents, can not be null * @param value @@ -1104,9 +1105,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * Gets a stored attribute value. If a value has been stored for the * session, that value is returned. If no value is stored for the name, * <code>null</code> is returned. - * + * * @see #setAttribute(String, Object) - * + * * @param name * the name of the value to get, can not be <code>null</code>. * @return the value, or <code>null</code> if no value has been stored or if @@ -1129,10 +1130,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * value. The outcome of calling this method is thus the same as if calling<br /> * <br /> * <code>getAttribute(type.getName());</code> - * + * * @see #setAttribute(Class, Object) * @see #getAttribute(String) - * + * * @param type * the type of the value to get, can not be <code>null</code>. * @return the value, or <code>null</code> if no value has been stored or if @@ -1153,7 +1154,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Creates a new unique id for a UI. - * + * * @return a unique UI id */ public int getNextUIid() { @@ -1163,7 +1164,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Adds an initialized UI to this session. - * + * * @param ui * the initialized UI to add. */ @@ -1197,7 +1198,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Adds a UI provider to this session. - * + * * @param uiProvider * the UI provider that should be added */ @@ -1208,7 +1209,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Removes a UI provider association from this session. - * + * * @param uiProvider * the UI provider that should be removed */ @@ -1219,7 +1220,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Gets the UI providers configured for this session. - * + * * @return an unmodifiable list of UI providers */ public List<UIProvider> getUIProviders() { @@ -1243,9 +1244,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * To avoid causing out of sync errors, you should typically redirect to * some other page using {@link Page#setLocation(String)} to make the * browser unload the invalidated UI. - * + * * @see SystemMessages#getSessionExpiredCaption() - * + * */ public void close() { assert hasLock(); @@ -1255,13 +1256,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Returns whether this session is marked to be closed. Note that this * method also returns true if the session is actually already closed. - * + * * @see #close() - * + * * @deprecated As of 7.2, use * <code>{@link #getState() getState() != State.OPEN}</code> * instead. - * + * * @return true if this session is marked to be closed, false otherwise */ @Deprecated @@ -1272,7 +1273,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Returns the lifecycle state of this session. - * + * * @since 7.2 * @return the current state */ @@ -1284,7 +1285,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Sets the lifecycle state of this session. The allowed transitions are * OPEN to CLOSING and CLOSING to CLOSED. - * + * * @since 7.2 * @param state * the new state @@ -1323,15 +1324,15 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * later point in time.</li> * </ul> * </p> - * + * * @param runnable * the runnable which accesses the session - * + * * @throws IllegalStateException * if the current thread holds the lock for another session - * + * * @since 7.1 - * + * * @see #lock() * @see #getCurrent() * @see #access(Runnable) @@ -1385,14 +1386,14 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * an exception if it is detected that the current thread holds the lock for * some other session. * </p> - * + * * @see #lock() * @see #getCurrent() * @see #accessSynchronously(Runnable) * @see UI#access(Runnable) - * + * * @since 7.1 - * + * * @param runnable * the runnable which accesses the session * @return a future that can be used to check for task completion and to @@ -1406,9 +1407,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * Gets the queue of tasks submitted using {@link #access(Runnable)}. It is * safe to call this method and access the returned queue without holding * the {@link #lock() session lock}. - * + * * @since 7.1 - * + * * @return the queue of pending access tasks */ public Queue<FutureAccess> getPendingAccessQueue() { @@ -1418,7 +1419,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Gets the CSRF token (aka double submit cookie) that is used to protect * against Cross Site Request Forgery attacks. - * + * * @since 7.1 * @return the csrf token string */ @@ -1439,13 +1440,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Finds the UI with the corresponding embed id. - * + * * @since 7.2 * @param embedId * the embed id * @return the UI with the corresponding embed id, or <code>null</code> if * no UI is found - * + * * @see UI#getEmbedId() */ public UI getUIByEmbedId(String embedId) { diff --git a/server/src/com/vaadin/server/communication/LegacyUidlWriter.java b/server/src/com/vaadin/server/communication/LegacyUidlWriter.java index 43ea1aca67..3f70a25c5b 100644 --- a/server/src/com/vaadin/server/communication/LegacyUidlWriter.java +++ b/server/src/com/vaadin/server/communication/LegacyUidlWriter.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -35,7 +35,7 @@ import com.vaadin.ui.UI; /** * Serializes legacy UIDL changes to JSON. - * + * * @author Vaadin Ltd * @since 7.1 */ @@ -44,7 +44,7 @@ public class LegacyUidlWriter implements Serializable { /** * Writes a JSON array containing the changes of all dirty * {@link LegacyComponent}s in the given UI. - * + * * @param ui * The {@link UI} whose legacy changes to write * @param writer @@ -60,7 +60,8 @@ public class LegacyUidlWriter implements Serializable { Collection<ClientConnector> dirtyVisibleConnectors = ui .getConnectorTracker().getDirtyVisibleConnectors(); - List<Component> legacyComponents = new ArrayList<Component>(); + List<Component> legacyComponents = new ArrayList<Component>( + dirtyVisibleConnectors.size()); for (ClientConnector connector : dirtyVisibleConnectors) { // All Components that want to use paintContent must implement // LegacyComponent diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java index 983ada3279..5a7ae5b3e8 100644 --- a/server/src/com/vaadin/server/communication/PushHandler.java +++ b/server/src/com/vaadin/server/communication/PushHandler.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -48,7 +48,7 @@ import com.vaadin.ui.UI; /** * Establishes bidirectional ("push") communication channels - * + * * @author Vaadin Ltd * @since 7.1 */ @@ -194,7 +194,7 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter { /** * Find the UI for the atmosphere resource, lock it and invoke the callback. - * + * * @param resource * the atmosphere resource for the current request * @param callback @@ -211,6 +211,8 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter { try { try { session = service.findVaadinSession(vaadinRequest); + assert VaadinSession.getCurrent() == session; + } catch (ServiceException e) { getLogger().log(Level.SEVERE, "Could not get session. This should never happen", e); @@ -231,9 +233,9 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter { UI ui = null; session.lock(); try { - VaadinSession.setCurrent(session); - // Sets UI.currentInstance ui = service.findUI(vaadinRequest); + assert UI.getCurrent() == ui; + if (ui == null) { sendNotificationAndDisconnect(resource, UidlRequestHandler.getUINotFoundErrorJSON(service, @@ -417,10 +419,10 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter { * two push connections which try to use the same UI. Using the * AtmosphereResource directly guarantees the message goes to the correct * recipient. - * + * * @param resource * The atmosphere resource to send refresh to - * + * */ private static void sendRefreshAndDisconnect(AtmosphereResource resource) throws IOException { diff --git a/server/src/com/vaadin/server/communication/PushRequestHandler.java b/server/src/com/vaadin/server/communication/PushRequestHandler.java index db14e73c1a..308f94686f 100644 --- a/server/src/com/vaadin/server/communication/PushRequestHandler.java +++ b/server/src/com/vaadin/server/communication/PushRequestHandler.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -47,7 +47,7 @@ import com.vaadin.shared.communication.PushConstants; * Handles requests to open a push (bidirectional) communication channel between * the client and the server. After the initial request, communication through * the push channel is managed by {@link PushHandler}. - * + * * @author Vaadin Ltd * @since 7.1 */ @@ -66,7 +66,8 @@ public class PushRequestHandler implements RequestHandler, public PushRequestHandler(VaadinServletService service) throws ServiceException { - final ServletConfig config = service.getServlet().getServletConfig(); + final ServletConfig vaadinServletConfig = service.getServlet() + .getServletConfig(); atmosphere = new AtmosphereFramework() { @Override @@ -77,7 +78,7 @@ public class PushRequestHandler implements RequestHandler, @Override public AtmosphereFramework addInitParameter(String name, String value) { - if (config.getInitParameter(name) == null) { + if (vaadinServletConfig.getInitParameter(name) == null) { super.addInitParameter(name, value); } return this; @@ -117,7 +118,7 @@ public class PushRequestHandler implements RequestHandler, "org.atmosphere.cpr.showSupportMessage", "false"); try { - atmosphere.init(config); + atmosphere.init(vaadinServletConfig); // Ensure the client-side knows how to split the message stream // into individual messages when using certain transports diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java index 36bfc8bcc6..9107a4e049 100644 --- a/server/src/com/vaadin/server/communication/ServerRpcHandler.java +++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -55,7 +55,7 @@ import com.vaadin.ui.UI; /** * Handles a client-to-server message containing serialized {@link ServerRpc * server RPC} invocations. - * + * * @author Vaadin Ltd * @since 7.1 */ @@ -64,7 +64,7 @@ public class ServerRpcHandler implements Serializable { /** * A data transfer object representing an RPC request sent by the client * side. - * + * * @since 7.2 * @author Vaadin Ltd */ @@ -78,7 +78,13 @@ public class ServerRpcHandler implements Serializable { public RpcRequest(String jsonString, VaadinRequest request) throws JSONException { json = new JSONObject(jsonString); - csrfToken = json.getString(ApplicationConstants.CSRF_TOKEN); + + String csrfToken = json.optString(ApplicationConstants.CSRF_TOKEN); + if (csrfToken.equals("")) { + csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE; + } + this.csrfToken = csrfToken; + if (request.getService().getDeploymentConfiguration() .isSyncIdCheckEnabled()) { syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID); @@ -91,7 +97,7 @@ public class ServerRpcHandler implements Serializable { /** * Gets the CSRF security token (double submit cookie) for this request. - * + * * @return the CSRF security token for this current change request */ public String getCsrfToken() { @@ -100,7 +106,7 @@ public class ServerRpcHandler implements Serializable { /** * Gets the data to recreate the RPC as requested by the client side. - * + * * @return the data describing which RPC should be made, and all their * data */ @@ -110,7 +116,7 @@ public class ServerRpcHandler implements Serializable { /** * Gets the sync id last seen by the client. - * + * * @return the last sync id given by the server, according to the * client's request */ @@ -124,9 +130,9 @@ public class ServerRpcHandler implements Serializable { * <p> * <em>Note:</em> This is a shared reference - any modifications made * will be shared. - * + * * @return the raw JSON object that was received from the client - * + * */ public JSONObject getRawJson() { return json; @@ -138,7 +144,7 @@ public class ServerRpcHandler implements Serializable { /** * Reads JSON containing zero or more serialized RPC calls (including legacy * variable changes) and executes the calls. - * + * * @param ui * The {@link UI} receiving the calls. Cannot be null. * @param reader @@ -188,7 +194,7 @@ public class ServerRpcHandler implements Serializable { * changeVariables() is only called once for them. This preserves the Vaadin * 6 semantics for components and add-ons that do not use Vaadin 7 RPC * directly. - * + * * @param uI * the UI receiving the invocations data * @param lastSyncIdSeenByClient @@ -318,7 +324,7 @@ public class ServerRpcHandler implements Serializable { /** * Parse JSON from the client into a list of MethodInvocation instances. - * + * * @param connectorTracker * The ConnectorTracker used to lookup connectors * @param invocationsJson @@ -333,11 +339,13 @@ public class ServerRpcHandler implements Serializable { private List<MethodInvocation> parseInvocations( ConnectorTracker connectorTracker, JSONArray invocationsJson, int lastSyncIdSeenByClient) throws JSONException { - ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>(); + int invocationCount = invocationsJson.length(); + ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>( + invocationCount); MethodInvocation previousInvocation = null; // parse JSON to MethodInvocations - for (int i = 0; i < invocationsJson.length(); ++i) { + for (int i = 0; i < invocationCount; ++i) { JSONArray invocationJson = invocationsJson.getJSONArray(i); @@ -500,7 +508,7 @@ public class ServerRpcHandler implements Serializable { /** * Generates an error message when the client is trying to to something * ('what') with a connector which is disabled or invisible. - * + * * @since 7.1.8 * @param connector * the connector which is disabled (or invisible) diff --git a/server/src/com/vaadin/ui/AbstractJavaScriptComponent.java b/server/src/com/vaadin/ui/AbstractJavaScriptComponent.java index d6e232035b..f3cbf47b62 100644 --- a/server/src/com/vaadin/ui/AbstractJavaScriptComponent.java +++ b/server/src/com/vaadin/ui/AbstractJavaScriptComponent.java @@ -116,6 +116,8 @@ import com.vaadin.shared.ui.JavaScriptComponentState; * <li>The primitive Java boolean and the boxed Boolean are represented by * JavaScript booleans.</li> * <li>Java Strings are represented by JavaScript strings.</li> + * <li>Java Dates are represented by JavaScript numbers containing the timestamp + * </li> * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li> * <li>Map<String, ?> in Java is represented by JavaScript object with fields * corresponding to the map keys.</li> diff --git a/server/src/com/vaadin/ui/AbstractSelect.java b/server/src/com/vaadin/ui/AbstractSelect.java index b8db329906..b083db3183 100644 --- a/server/src/com/vaadin/ui/AbstractSelect.java +++ b/server/src/com/vaadin/ui/AbstractSelect.java @@ -759,7 +759,9 @@ public abstract class AbstractSelect extends AbstractField<Object> implements */ @Override public int size() { - return items.size(); + int size = items.size(); + assert size >= 0; + return size; } /** diff --git a/server/src/com/vaadin/ui/ComboBox.java b/server/src/com/vaadin/ui/ComboBox.java index 048726dc84..5367505c56 100644 --- a/server/src/com/vaadin/ui/ComboBox.java +++ b/server/src/com/vaadin/ui/ComboBox.java @@ -354,6 +354,7 @@ public class ComboBox extends AbstractSelect implements if (pageLength == 0) { // no paging: return all items filteredSize = container.size(); + assert filteredSize >= 0; return new ArrayList<Object>(container.getItemIds()); } @@ -391,6 +392,7 @@ public class ComboBox extends AbstractSelect implements } filteredSize = container.size(); + assert filteredSize >= 0; currentPage = adjustCurrentPage(currentPage, needNullSelectOption, indexToEnsureInView, filteredSize); int first = getFirstItemIndexOnCurrentPage(needNullSelectOption, diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java index b5a0227d99..9b8729f779 100644 --- a/server/src/com/vaadin/ui/ConnectorTracker.java +++ b/server/src/com/vaadin/ui/ConnectorTracker.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -55,10 +55,10 @@ import com.vaadin.server.StreamVariable; * operation new information needs to be sent to its * {@link com.vaadin.client.ServerConnector}. * </p> - * + * * @author Vaadin Ltd * @since 7.0.0 - * + * */ public class ConnectorTracker implements Serializable { @@ -87,7 +87,7 @@ public class ConnectorTracker implements Serializable { /** * Map to track on which syncId each connector was removed. - * + * * @see #getCurrentSyncId() * @see #cleanConcurrentlyRemovedConnectorIds(long) */ @@ -95,9 +95,9 @@ public class ConnectorTracker implements Serializable { /** * Gets a logger for this class - * + * * @return A logger instance for logging within this class - * + * */ public static Logger getLogger() { return Logger.getLogger(ConnectorTracker.class.getName()); @@ -107,7 +107,7 @@ public class ConnectorTracker implements Serializable { * Creates a new ConnectorTracker for the given uI. A tracker is always * attached to a uI and the uI cannot be changed during the lifetime of a * {@link ConnectorTracker}. - * + * * @param uI * The uI to attach to. Cannot be null. */ @@ -121,7 +121,7 @@ public class ConnectorTracker implements Serializable { * The lookup method {@link #getConnector(String)} only returns registered * connectors. * </p> - * + * * @param connector * The connector to register. */ @@ -157,12 +157,12 @@ public class ConnectorTracker implements Serializable { /** * Unregister the given connector. - * + * * <p> * The lookup method {@link #getConnector(String)} only returns registered * connectors. * </p> - * + * * @param connector * The connector to unregister */ @@ -213,7 +213,7 @@ public class ConnectorTracker implements Serializable { * Checks whether the given connector has already been initialized in the * browser. The given connector should be registered with this connector * tracker. - * + * * @param connector * the client connector to check * @return <code>true</code> if the initial state has previously been sent @@ -228,9 +228,9 @@ public class ConnectorTracker implements Serializable { /** * Marks the given connector as initialized, meaning that the client-side * state has been initialized for the connector. - * + * * @see #isClientSideInitialized(ClientConnector) - * + * * @param connector * the connector that should be marked as initialized */ @@ -242,7 +242,7 @@ public class ConnectorTracker implements Serializable { * Marks all currently registered connectors as uninitialized. This should * be done when the client-side has been reset but the server-side state is * retained. - * + * * @see #isClientSideInitialized(ClientConnector) */ public void markAllClientSidesUninitialized() { @@ -252,7 +252,7 @@ public class ConnectorTracker implements Serializable { /** * Gets a connector by its id. - * + * * @param connectorId * The connector id to look for * @return The connector with the given id or null if no connector has the @@ -400,10 +400,10 @@ public class ConnectorTracker implements Serializable { /** * Mark the connector as dirty. This should not be done while the response * is being written. - * + * * @see #getDirtyConnectors() * @see #isWritingResponse() - * + * * @param connector * The connector that should be marked clean. */ @@ -425,7 +425,7 @@ public class ConnectorTracker implements Serializable { /** * Mark the connector as clean. - * + * * @param connector * The connector that should be marked clean. */ @@ -443,7 +443,7 @@ public class ConnectorTracker implements Serializable { /** * Returns {@link #getConnectorString(ClientConnector)} for the connector * and its parent (if it has a parent). - * + * * @param connector * The connector * @return A string describing the connector and its parent @@ -460,7 +460,7 @@ public class ConnectorTracker implements Serializable { /** * Returns a string with the connector name and id. Useful mostly for * debugging and logging. - * + * * @param connector * The connector * @return A string that describes the connector @@ -500,7 +500,7 @@ public class ConnectorTracker implements Serializable { /** * Marks all visible connectors dirty, starting from the given connector and * going downwards in the hierarchy. - * + * * @param c * The component to start iterating downwards from */ @@ -521,7 +521,7 @@ public class ConnectorTracker implements Serializable { * The state and pending RPC calls for dirty connectors are sent to the * client in the following request. * </p> - * + * * @return A collection of all dirty connectors for this uI. This list may * contain invisible connectors. */ @@ -531,7 +531,7 @@ public class ConnectorTracker implements Serializable { /** * Checks if there a dirty connectors. - * + * * @return true if there are dirty connectors, false otherwise */ public boolean hasDirtyConnectors() { @@ -541,17 +541,19 @@ public class ConnectorTracker implements Serializable { /** * Returns a collection of those {@link #getDirtyConnectors() dirty * connectors} that are actually visible to the client. - * + * * @return A list of dirty and visible connectors. */ public ArrayList<ClientConnector> getDirtyVisibleConnectors() { - ArrayList<ClientConnector> dirtyConnectors = new ArrayList<ClientConnector>(); - for (ClientConnector c : getDirtyConnectors()) { + Collection<ClientConnector> dirtyConnectors = getDirtyConnectors(); + ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>( + dirtyConnectors.size()); + for (ClientConnector c : dirtyConnectors) { if (LegacyCommunicationManager.isConnectorVisibleToClient(c)) { - dirtyConnectors.add(c); + dirtyVisibleConnectors.add(c); } } - return dirtyConnectors; + return dirtyVisibleConnectors; } public JSONObject getDiffState(ClientConnector connector) { @@ -571,10 +573,10 @@ public class ConnectorTracker implements Serializable { /** * Checks whether the response is currently being written. Connectors can * not be marked as dirty when a response is being written. - * + * * @see #setWritingResponse(boolean) * @see #markDirty(ClientConnector) - * + * * @return <code>true</code> if the response is currently being written, * <code>false</code> if outside the response writing phase. */ @@ -590,14 +592,14 @@ public class ConnectorTracker implements Serializable { * {@link #getCurrentSyncId()}), if {@link #isWritingResponse()} returns * <code>false</code> and <code>writingResponse</code> is set to * <code>true</code>. - * + * * @param writingResponse * the new response status. - * + * * @see #markDirty(ClientConnector) * @see #isWritingResponse() * @see #getCurrentSyncId() - * + * * @throws IllegalArgumentException * if the new response status is the same as the previous value. * This is done to help detecting problems caused by missed @@ -625,7 +627,7 @@ public class ConnectorTracker implements Serializable { // Convert JSONObjects in diff state to String representation as // JSONObject is not serializable HashMap<ClientConnector, String> stringDiffStates = new HashMap<ClientConnector, String>( - diffStates.size()); + diffStates.size() * 2); for (ClientConnector key : diffStates.keySet()) { stringDiffStates.put(key, diffStates.get(key).toString()); } @@ -643,7 +645,8 @@ public class ConnectorTracker implements Serializable { @SuppressWarnings("unchecked") HashMap<ClientConnector, String> stringDiffStates = (HashMap<ClientConnector, String>) in .readObject(); - diffStates = new HashMap<ClientConnector, JSONObject>(); + diffStates = new HashMap<ClientConnector, JSONObject>( + stringDiffStates.size() * 2); for (ClientConnector key : stringDiffStates.keySet()) { try { diffStates.put(key, new JSONObject(stringDiffStates.get(key))); @@ -657,7 +660,7 @@ public class ConnectorTracker implements Serializable { /** * Checks if the indicated connector has a StreamVariable of the given name * and returns the variable if one is found. - * + * * @param connectorId * @param variableName * @return variable if a matching one exists, otherwise null @@ -678,7 +681,7 @@ public class ConnectorTracker implements Serializable { /** * Adds a StreamVariable of the given name to the indicated connector. - * + * * @param connectorId * @param variableName * @param variable @@ -734,7 +737,7 @@ public class ConnectorTracker implements Serializable { /** * Removes any StreamVariable of the given name from the indicated * connector. - * + * * @param connectorId * @param variableName */ @@ -752,7 +755,7 @@ public class ConnectorTracker implements Serializable { /** * Returns the security key associated with the given StreamVariable. - * + * * @param variable * @return matching security key if one exists, null otherwise */ @@ -767,7 +770,7 @@ public class ConnectorTracker implements Serializable { * Check whether a connector was present on the client when the it was * creating this request, but was removed server-side before the request * arrived. - * + * * @since 7.2 * @param connectorId * The connector id to check for whether it was removed @@ -827,7 +830,7 @@ public class ConnectorTracker implements Serializable { * <p> * The sync id value <code>-1</code> is ignored to facilitate testing with * pre-recorded requests. - * + * * @see #setWritingResponse(boolean) * @see #connectorWasPresentAsRequestWasSent(String, long) * @since 7.2 @@ -853,7 +856,7 @@ public class ConnectorTracker implements Serializable { * <p> * The sync id value <code>-1</code> is ignored to facilitate testing with * pre-recorded requests. - * + * * @see #connectorWasPresentAsRequestWasSent(String, long) * @since 7.2 * @param lastSyncIdSeenByClient diff --git a/server/src/com/vaadin/ui/Upload.java b/server/src/com/vaadin/ui/Upload.java index 869c32751a..4c248d68ae 100644 --- a/server/src/com/vaadin/ui/Upload.java +++ b/server/src/com/vaadin/ui/Upload.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -35,16 +35,16 @@ import com.vaadin.util.ReflectTools; /** * Component for uploading files from client to server. - * + * * <p> * The visible component consists of a file name input box and a browse button * and an upload submit button to start uploading. - * + * * <p> * The Upload component needs a java.io.OutputStream to write the uploaded data. * You need to implement the Upload.Receiver interface and return the output * stream in the receiveUpload() method. - * + * * <p> * You can get an event regarding starting (StartedEvent), progress * (ProgressEvent), and finishing (FinishedEvent) of upload by implementing @@ -52,22 +52,22 @@ import com.vaadin.util.ReflectTools; * FinishedListener is called for both failed and succeeded uploads. If you wish * to separate between these two cases, you can use SucceededListener * (SucceededEvenet) and FailedListener (FailedEvent). - * + * * <p> * The upload component does not itself show upload progress, but you can use * the ProgressIndicator for providing progress feedback by implementing * ProgressListener and updating the indicator in updateProgress(). - * + * * <p> * Setting upload component immediate initiates the upload as soon as a file is * selected, instead of the common pattern of file selection field and upload * button. - * + * * <p> * Note! Because of browser dependent implementations of <input type="file"> * element, setting size for Upload component is not supported. For some * browsers setting size may work to some extend. - * + * * @author Vaadin Ltd. * @since 3.0 */ @@ -112,7 +112,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Creates a new instance of Upload. - * + * * The receiver must be set before performing an upload. */ public Upload() { @@ -132,7 +132,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Invoked when the value of a variable has changed. - * + * * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object, * java.util.Map) */ @@ -150,7 +150,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Paints the content of this component. - * + * * @param target * Target to paint the content on. * @throws PaintException @@ -189,7 +189,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Interface that must be implemented by the upload receivers to provide the * Upload component an output stream to write the uploaded data. - * + * * @author Vaadin Ltd. * @since 3.0 */ @@ -197,7 +197,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Invoked when a new upload arrives. - * + * * @param filename * the desired filename of the upload, usually as specified * by the client. @@ -242,7 +242,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, * of whether the reception was successful or failed. If you wish to * distinguish between the two cases, use either SucceededEvent or * FailedEvent, which are both subclasses of the FinishedEvent. - * + * * @author Vaadin Ltd. * @since 3.0 */ @@ -264,7 +264,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, private final String filename; /** - * + * * @param source * the source of the file. * @param filename @@ -284,7 +284,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Uploads where the event occurred. - * + * * @return the Source of the event. */ public Upload getUpload() { @@ -293,7 +293,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets the file name. - * + * * @return the filename. */ public String getFilename() { @@ -302,7 +302,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets the MIME Type of the file. - * + * * @return the MIME type. */ public String getMIMEType() { @@ -311,7 +311,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets the length of the file. - * + * * @return the length. */ public long getLength() { @@ -323,7 +323,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Upload.FailedEvent event is sent when the upload is received, but the * reception is interrupted for some reason. - * + * * @author Vaadin Ltd. * @since 3.0 */ @@ -332,7 +332,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, private Exception reason = null; /** - * + * * @param source * @param filename * @param MIMEType @@ -346,7 +346,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, } /** - * + * * @param source * @param filename * @param MIMEType @@ -360,7 +360,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets the exception that caused the failure. - * + * * @return the exception that caused the failure, null if n/a */ public Exception getReason() { @@ -375,7 +375,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, public static class NoOutputStreamEvent extends FailedEvent { /** - * + * * @param source * @param filename * @param MIMEType @@ -393,7 +393,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, public static class NoInputStreamEvent extends FailedEvent { /** - * + * * @param source * @param filename * @param MIMEType @@ -409,14 +409,14 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Upload.SucceededEvent event is sent when the upload is received * successfully. - * + * * @author Vaadin Ltd. * @since 3.0 */ public static class SucceededEvent extends FinishedEvent { /** - * + * * @param source * @param filename * @param MIMEType @@ -431,7 +431,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Upload.StartedEvent event is sent when the upload is started to received. - * + * * @author Vaadin Ltd. * @since 5.0 */ @@ -445,7 +445,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, private final long length; /** - * + * * @param source * @param filename * @param MIMEType @@ -461,7 +461,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Uploads where the event occurred. - * + * * @return the Source of the event. */ public Upload getUpload() { @@ -470,7 +470,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets the file name. - * + * * @return the filename. */ public String getFilename() { @@ -479,7 +479,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets the MIME Type of the file. - * + * * @return the MIME type. */ public String getMIMEType() { @@ -498,7 +498,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Upload.ChangeEvent event is sent when the value (filename) of the upload * changes. - * + * * @since 7.2 */ public static class ChangeEvent extends Component.Event { @@ -512,7 +512,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Uploads where the event occurred. - * + * * @return the Source of the event. */ @Override @@ -522,7 +522,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets the file name. - * + * * @return the filename. */ public String getFilename() { @@ -533,7 +533,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Receives the events when the upload starts. - * + * * @author Vaadin Ltd. * @since 5.0 */ @@ -541,7 +541,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Upload has started. - * + * * @param event * the Upload started event. */ @@ -550,7 +550,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Receives the events when the uploads are ready. - * + * * @author Vaadin Ltd. * @since 3.0 */ @@ -558,7 +558,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Upload has finished. - * + * * @param event * the Upload finished event. */ @@ -567,7 +567,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Receives events when the uploads are finished, but unsuccessful. - * + * * @author Vaadin Ltd. * @since 3.0 */ @@ -575,7 +575,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Upload has finished unsuccessfully. - * + * * @param event * the Upload failed event. */ @@ -584,7 +584,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Receives events when the uploads are successfully finished. - * + * * @author Vaadin Ltd. * @since 3.0 */ @@ -592,7 +592,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Upload successfull.. - * + * * @param event * the Upload successfull event. */ @@ -601,7 +601,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Listener for {@link ChangeEvent} - * + * * @since 7.2 */ public interface ChangeListener extends Serializable { @@ -611,7 +611,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * A file has been selected but upload has not yet started. - * + * * @param event * the change event */ @@ -620,7 +620,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Adds the upload started event listener. - * + * * @param listener * the Listener to be added. */ @@ -639,7 +639,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Removes the upload started event listener. - * + * * @param listener * the Listener to be removed. */ @@ -658,7 +658,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Adds the upload received event listener. - * + * * @param listener * the Listener to be added. */ @@ -677,7 +677,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Removes the upload received event listener. - * + * * @param listener * the Listener to be removed. */ @@ -696,7 +696,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Adds the upload interrupted event listener. - * + * * @param listener * the Listener to be added. */ @@ -715,7 +715,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Removes the upload interrupted event listener. - * + * * @param listener * the Listener to be removed. */ @@ -734,7 +734,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Adds the upload success event listener. - * + * * @param listener * the Listener to be added. */ @@ -753,7 +753,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Removes the upload success event listener. - * + * * @param listener * the Listener to be removed. */ @@ -771,10 +771,10 @@ public class Upload extends AbstractComponent implements Component.Focusable, } /** - * Adds the upload success event listener. - * + * Adds the upload progress event listener. + * * @param listener - * the Listener to be added. + * the progress listener to be added */ public void addProgressListener(ProgressListener listener) { if (progressListeners == null) { @@ -793,10 +793,10 @@ public class Upload extends AbstractComponent implements Component.Focusable, } /** - * Removes the upload success event listener. - * + * Removes the upload progress event listener. + * * @param listener - * the Listener to be removed. + * the progress listener to be removed */ public void removeProgressListener(ProgressListener listener) { if (progressListeners != null) { @@ -806,7 +806,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Adds a filename change event listener - * + * * @param listener * the Listener to add */ @@ -817,7 +817,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Removes a filename change event listener - * + * * @param listener * the listener to be removed */ @@ -836,7 +836,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Emit upload received event. - * + * * @param filename * @param MIMEType * @param length @@ -848,7 +848,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Emits the upload failed event. - * + * * @param filename * @param MIMEType * @param length @@ -877,11 +877,11 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Emits the upload success event. - * + * * @param filename * @param MIMEType * @param length - * + * */ protected void fireUploadSuccess(String filename, String MIMEType, long length) { @@ -890,12 +890,12 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Emits the progress event. - * + * * @param totalBytes * bytes received so far * @param contentLength * actual size of the file being uploaded, if known - * + * */ protected void fireUpdateProgress(long totalBytes, long contentLength) { // this is implemented differently than other listeners to maintain @@ -911,7 +911,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Returns the current receiver. - * + * * @return the StreamVariable. */ public Receiver getReceiver() { @@ -920,7 +920,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Sets the receiver. - * + * * @param receiver * the receiver to set. */ @@ -938,7 +938,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets the Tabulator index of this Focusable component. - * + * * @see com.vaadin.ui.Component.Focusable#getTabIndex() */ @Override @@ -948,7 +948,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Sets the Tabulator index of this Focusable component. - * + * * @see com.vaadin.ui.Component.Focusable#setTabIndex(int) */ @Override @@ -959,7 +959,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Go into upload state. This is to prevent double uploading on same * component. - * + * * Warning: this is an internal method used by the framework and should not * be used by user of the Upload component. Using it results in the Upload * component going in wrong state and not working. It is currently public @@ -986,7 +986,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Go into state where new uploading can begin. - * + * * Warning: this is an internal method used by the framework and should not * be used by user of the Upload component. */ @@ -1003,7 +1003,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Gets read bytes of the file currently being uploaded. - * + * * @return bytes */ public long getBytesRead() { @@ -1013,7 +1013,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, /** * Returns size of file currently being uploaded. Value sane only during * upload. - * + * * @return size in bytes */ public long getUploadSize() { @@ -1026,7 +1026,7 @@ public class Upload extends AbstractComponent implements Component.Focusable, public interface ProgressListener extends Serializable { /** * Updates progress to listener - * + * * @param readBytes * bytes transferred * @param contentLength @@ -1055,12 +1055,12 @@ public class Upload extends AbstractComponent implements Component.Focusable, * {@link #setImmediate(boolean)}, the file choose (html input with type * "file") is hidden and only the button with this text is shown. * <p> - * + * * <p> * <strong>Note</strong> the string given is set as is to the button. HTML * formatting is not stripped. Be sure to properly validate your value * according to your needs. - * + * * @param buttonCaption * text for upload components button. */ diff --git a/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java b/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java index 7c19395df2..a8804caedb 100644 --- a/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java +++ b/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java @@ -262,6 +262,7 @@ public class ContainerEventProvider implements CalendarEditableEventProvider, private int[] getFirstAndLastEventIndex(Date start, Date end) { int startIndex = 0; int size = container.size(); + assert size >= 0; int endIndex = size - 1; if (start != null) { diff --git a/server/src/com/vaadin/util/CurrentInstance.java b/server/src/com/vaadin/util/CurrentInstance.java index 00d2e7346d..6f2c0a2eca 100644 --- a/server/src/com/vaadin/util/CurrentInstance.java +++ b/server/src/com/vaadin/util/CurrentInstance.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -52,12 +52,14 @@ import com.vaadin.ui.UI; * <p> * Non-inheritable: {@link VaadinRequest}, {@link VaadinResponse}. * </p> - * + * * @author Vaadin Ltd * @since 7.0.0 */ public class CurrentInstance implements Serializable { private static final Object NULL_OBJECT = new Object(); + private static final CurrentInstance CURRENT_INSTANCE_NULL = new CurrentInstance( + NULL_OBJECT, true); private final WeakReference<Object> instance; private final boolean inheritable; @@ -90,7 +92,7 @@ public class CurrentInstance implements Serializable { /** * Gets the current instance of a specific type if available. - * + * * @param type * the class to get an instance of * @return the current instance or the provided type, or <code>null</code> @@ -110,7 +112,7 @@ public class CurrentInstance implements Serializable { * ThreadLocal should only outlive the referenced object on * threads that are not doing anything related to Vaadin, which * should thus never invoke CurrentInstance.get(). - * + * * At this point, there might also be other values that have * been collected, so we'll scan the entire map and remove stale * CurrentInstance objects. Using a ReferenceQueue could make @@ -148,10 +150,10 @@ public class CurrentInstance implements Serializable { /** * Sets the current instance of the given type. - * + * * @see #setInheritable(Class, Object) * @see ThreadLocal - * + * * @param type * the class that should be used when getting the current * instance back @@ -167,10 +169,10 @@ public class CurrentInstance implements Serializable { * instance that is inheritable will be available for child threads and in * code run by {@link VaadinSession#access(Runnable)} and * {@link UI#access(Runnable)}. - * + * * @see #set(Class, Object) * @see InheritableThreadLocal - * + * * @param type * the class that should be used when getting the current * instance back @@ -181,17 +183,18 @@ public class CurrentInstance implements Serializable { set(type, instance, true); } - private static <T> void set(Class<T> type, T instance, boolean inheritable) { + private static <T> CurrentInstance set(Class<T> type, T instance, + boolean inheritable) { Map<Class<?>, CurrentInstance> map = instances.get(); + CurrentInstance previousInstance = null; if (instance == null) { // remove the instance - if (map == null) { - return; - } - map.remove(type); - if (map.isEmpty()) { - instances.remove(); - map = null; + if (map != null) { + previousInstance = map.remove(type); + if (map.isEmpty()) { + instances.remove(); + map = null; + } } } else { assert type.isInstance(instance) : "Invald instance type"; @@ -200,8 +203,8 @@ public class CurrentInstance implements Serializable { instances.set(map); } - CurrentInstance previousInstance = map.put(type, - new CurrentInstance(instance, inheritable)); + previousInstance = map.put(type, new CurrentInstance(instance, + inheritable)); if (previousInstance != null) { assert previousInstance.inheritable == inheritable : "Inheritable status mismatch for " + type @@ -211,6 +214,10 @@ public class CurrentInstance implements Serializable { + inheritable + ")"; } } + if (previousInstance == null) { + previousInstance = CURRENT_INSTANCE_NULL; + } + return previousInstance; } /** @@ -223,9 +230,9 @@ public class CurrentInstance implements Serializable { /** * Restores the given instances to the given values. Note that this should * only be used internally to restore Vaadin classes. - * + * * @since 7.1 - * + * * @param old * A Class -> CurrentInstance map to set as current instances */ @@ -243,7 +250,7 @@ public class CurrentInstance implements Serializable { * CurrentInstance. Without this a reference to an already * collected instance may be left in the CurrentInstance when it * really should be restored to null. - * + * * One example case that this fixes: * VaadinService.runPendingAccessTasks() clears all current * instances and then sets everything but the UI. This makes @@ -267,9 +274,9 @@ public class CurrentInstance implements Serializable { /** * Gets the currently set instances so that they can later be restored using * {@link #restoreInstances(Map)}. - * + * * @since 7.1 - * + * * @param onlyInheritable * <code>true</code> if only the inheritable instances should be * included; <code>false</code> to get all instances. @@ -305,20 +312,17 @@ public class CurrentInstance implements Serializable { * Sets current instances for the UI and all related classes. The previously * defined values can be restored by passing the returned map to * {@link #restoreInstances(Map)}. - * + * * @since 7.1 - * + * * @param ui * The UI * @return A map containing the old values of the instances that this method * updated. */ public static Map<Class<?>, CurrentInstance> setCurrent(UI ui) { - Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>(); - old.put(UI.class, - new CurrentInstance(getSameOrNullObject(UI.getCurrent()), true)); - UI.setCurrent(ui); - old.putAll(setCurrent(ui.getSession())); + Map<Class<?>, CurrentInstance> old = setCurrent(ui.getSession()); + old.put(UI.class, set(UI.class, ui, true)); return old; } @@ -326,9 +330,9 @@ public class CurrentInstance implements Serializable { * Sets current instances for the {@link VaadinSession} and all related * classes. The previously defined values can be restored by passing the * returned map to {@link #restoreInstances(Map)}. - * + * * @since 7.1 - * + * * @param session * The VaadinSession * @return A map containing the old values of the instances this method @@ -337,33 +341,15 @@ public class CurrentInstance implements Serializable { public static Map<Class<?>, CurrentInstance> setCurrent( VaadinSession session) { Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>(); - old.put(VaadinSession.class, new CurrentInstance( - getSameOrNullObject(VaadinSession.getCurrent()), true)); - old.put(VaadinService.class, new CurrentInstance( - getSameOrNullObject(VaadinService.getCurrent()), true)); + old.put(VaadinSession.class, set(VaadinSession.class, session, true)); VaadinService service = null; if (session != null) { service = session.getService(); } - - VaadinSession.setCurrent(session); - VaadinService.setCurrent(service); - + old.put(VaadinService.class, set(VaadinService.class, service, true)); return old; } - /** - * Returns {@code object} unless it is null, in which case #NULL_OBJECT is - * returned. - * - * @param object - * The instance to return if non-null. - * @return {@code object} or #NULL_OBJECT if {@code object} is null. - */ - private static Object getSameOrNullObject(Object object) { - return object == null ? NULL_OBJECT : object; - } - private static Logger getLogger() { return Logger.getLogger(CurrentInstance.class.getName()); } diff --git a/server/tests/src/com/vaadin/data/util/ContainerSizeAssertTest.java b/server/tests/src/com/vaadin/data/util/ContainerSizeAssertTest.java new file mode 100644 index 0000000000..04fd8d3cd1 --- /dev/null +++ b/server/tests/src/com/vaadin/data/util/ContainerSizeAssertTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2014 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.data.util; + +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.ui.Table; + +public class ContainerSizeAssertTest { + + @Test(expected = AssertionError.class) + public void testNegativeSizeAssert() { + Table table = createAttachedTable(); + + table.setContainerDataSource(createNegativeSizeContainer()); + } + + @Test + public void testZeroSizeNoAssert() { + Table table = createAttachedTable(); + + table.setContainerDataSource(new IndexedContainer()); + } + + private Container createNegativeSizeContainer() { + return new IndexedContainer() { + @Override + public int size() { + return -1; + } + }; + } + + private Table createAttachedTable() { + return new Table() { + private boolean initialized = true; + + @Override + public boolean isAttached() { + // This returns false until the super constructor has finished + return initialized; + } + }; + } +} diff --git a/server/tests/src/com/vaadin/tests/server/CsrfTokenMissingTestServer.java b/server/tests/src/com/vaadin/tests/server/CsrfTokenMissingTestServer.java new file mode 100644 index 0000000000..30f1c85fef --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/CsrfTokenMissingTestServer.java @@ -0,0 +1,248 @@ +/* + * Copyright 2000-2014 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.tests.server; + +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.http.HttpServletRequest; + +import org.easymock.EasyMock; +import org.json.JSONException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.server.ServiceException; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinServletRequest; +import com.vaadin.server.VaadinServletService; +import com.vaadin.server.VaadinSession; +import com.vaadin.server.communication.ServerRpcHandler.RpcRequest; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; +import com.vaadin.tests.util.MockDeploymentConfiguration; + +/** + * Test the actual csrf token validation by the server. + * + * @since + * @author Vaadin Ltd + */ +public class CsrfTokenMissingTestServer { + + // Dummy fields just to run the test. + private VaadinServlet mockServlet; + + // The mock deployment configuration. + private MockDeploymentConfiguration mockDeploymentConfiguration; + + private VaadinServletService mockService; + + // The mock UI session. + private VaadinSession mockSession; + + // The mock vaadin request. + private VaadinServletRequest vaadinRequest; + + /** + * Initialize the mock servlet and other stuff for our tests. + */ + @Before + public void initMockStuff() throws ServiceException { + mockServlet = new VaadinServlet(); + mockDeploymentConfiguration = new MockDeploymentConfiguration(); + + mockService = new VaadinServletService(mockServlet, + mockDeploymentConfiguration); + + mockSession = new AlwaysLockedVaadinSession(mockService); + + vaadinRequest = new VaadinServletRequest( + EasyMock.createMock(HttpServletRequest.class), mockService); + + } + + private enum TokenType { + MISSING, INVALID, VALID + } + + private TokenType tokenType; + + private String invalidToken; + + public String getInvalidToken() { + if (invalidToken == null) { + // Just making sure this will never be in the same format as a valid + // token. + invalidToken = UUID.randomUUID().toString().substring(1); + } + return invalidToken; + } + + private String getValidToken() { + return mockSession.getCsrfToken(); + } + + /* + * Gets the payload with the default token. + */ + private String getPayload() { + switch (tokenType) { + case MISSING: + return getPayload(null); + + case INVALID: + return getPayload(getInvalidToken()); + + case VALID: + return getPayload(getValidToken()); + } + + return null; + } + + /* + * Gets the payload with the specified token. + */ + private String getPayload(String token) { + return "{" + + (token != null ? "\"csrfToken\":" + "\"" + token + "\", " + : "") + + "\"rpc\":[[\"0\",\"com.vaadin.shared.ui.ui.UIServerRpc\",\"resize\",[\"449\",\"1155\",\"1155\",\"449\"]],[\"4\",\"com.vaadin.shared.ui.button.ButtonServerRpc\",\"click\",[{\"clientY\":\"53\", \"clientX\":\"79\", \"shiftKey\":false, \"button\":\"LEFT\", \"ctrlKey\":false, \"type\":\"1\", \"metaKey\":false, \"altKey\":false, \"relativeY\":\"17\", \"relativeX\":\"61\"}]]], \"syncId\":1}"; + } + + /* + * Init the test parameters. + */ + private void initTest(boolean enableSecurity, TokenType tokenType) { + mockDeploymentConfiguration.setXsrfProtectionEnabled(enableSecurity); + this.tokenType = tokenType; + } + + /* + * Create the requets. + */ + private RpcRequest createRequest() { + try { + return new RpcRequest(getPayload(), vaadinRequest); + } catch (JSONException e) { + LOGGER.log(Level.SEVERE, "", e); + + Assert.assertTrue(false); + return null; + } + } + + /* + * Gets whether the token from the request is the default one. + */ + private boolean isDefaultToken(RpcRequest rpcRequest) { + return ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE.equals(rpcRequest + .getCsrfToken()); + } + + /* + * Gets whether the token from the request is the invalid one. + */ + private boolean isInvalidToken(RpcRequest rpcRequest) { + return getInvalidToken().equals(rpcRequest.getCsrfToken()); + } + + /* + * Gets whether the token from the request is the valid one. + */ + private boolean isValidToken(RpcRequest rpcRequest) { + return getValidToken().equals(rpcRequest.getCsrfToken()); + } + + /* + * Gets whether the token from the request is valid. + */ + private boolean isRequestValid(RpcRequest rpcRequest) { + return VaadinService.isCsrfTokenValid(mockSession, + rpcRequest.getCsrfToken()); + } + + private static Logger LOGGER = Logger + .getLogger(CsrfTokenMissingTestServer.class.getName()); + static { + LOGGER.setLevel(Level.ALL); + } + + @Test + public void securityOnAndNoToken() { + initTest(true, TokenType.MISSING); + + RpcRequest rpcRequest = createRequest(); + + Assert.assertTrue(isDefaultToken(rpcRequest)); + Assert.assertFalse(isRequestValid(rpcRequest)); + } + + @Test + public void securityOffAndNoToken() { + initTest(false, TokenType.MISSING); + + RpcRequest rpcRequest = createRequest(); + + Assert.assertTrue(isDefaultToken(rpcRequest)); + Assert.assertTrue(isRequestValid(rpcRequest)); + } + + @Test + public void securityOnAndInvalidToken() { + initTest(true, TokenType.INVALID); + + RpcRequest rpcRequest = createRequest(); + + Assert.assertTrue(isInvalidToken(rpcRequest)); + Assert.assertFalse(isRequestValid(rpcRequest)); + } + + @Test + public void securityOffAndInvalidToken() { + initTest(false, TokenType.INVALID); + + RpcRequest rpcRequest = createRequest(); + + Assert.assertTrue(isInvalidToken(rpcRequest)); + Assert.assertTrue(isRequestValid(rpcRequest)); + } + + @Test + public void securityOnAndValidToken() { + initTest(true, TokenType.VALID); + + RpcRequest rpcRequest = createRequest(); + + Assert.assertTrue(isValidToken(rpcRequest)); + Assert.assertTrue(isRequestValid(rpcRequest)); + } + + @Test + public void securityOffAndValidToken() { + initTest(false, TokenType.VALID); + + RpcRequest rpcRequest = createRequest(); + + Assert.assertTrue(isValidToken(rpcRequest)); + Assert.assertTrue(isRequestValid(rpcRequest)); + } + +} diff --git a/shared/src/com/vaadin/shared/ApplicationConstants.java b/shared/src/com/vaadin/shared/ApplicationConstants.java index da4ac7450d..15eefe3b21 100644 --- a/shared/src/com/vaadin/shared/ApplicationConstants.java +++ b/shared/src/com/vaadin/shared/ApplicationConstants.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -86,7 +86,7 @@ public class ApplicationConstants implements Serializable { /** * The name of the debug version of the javascript containing push support. * The file is located in the VAADIN directory. - * + * * @since 7.1.6 */ public static final String VAADIN_PUSH_DEBUG_JS = "vaadinPush.debug.js"; @@ -98,14 +98,14 @@ public class ApplicationConstants implements Serializable { /** * The name of the parameter used to transmit RPC invocations - * + * * @since 7.2 */ public static final String RPC_INVOCATIONS = "rpc"; /** * The name of the parameter used to transmit the CSRF token - * + * * @since 7.2 */ public static final String CSRF_TOKEN = "csrfToken"; @@ -114,9 +114,15 @@ public class ApplicationConstants implements Serializable { * The name of the parameter used to transmit the sync id. The value can be * set to -1 e.g. when testing with pre-recorded requests to make the * framework ignore the sync id. - * + * * @see com.vaadin.ui.ConnectorTracker#getCurrentSyncId() * @since 7.2 */ public static final String SERVER_SYNC_ID = "syncId"; + + /** + * Default value to use in case the security protection is disabled. + */ + public static final String CSRF_TOKEN_DEFAULT_VALUE = "init"; + } diff --git a/uitest/eclipse-run-selected-test.properties b/uitest/eclipse-run-selected-test.properties index cbd1ab1cef..70010fd1da 100644 --- a/uitest/eclipse-run-selected-test.properties +++ b/uitest/eclipse-run-selected-test.properties @@ -1,4 +1,11 @@ ; +; This is an example property file showing how to control how TestBench is used +; in the Vaadin Framework project. You should not modify this file since it's +; under version control. Instead, create a copy of it inside the /work/ folder +; in the project and make your customizations to that file. +; + +; ; For both TestBench 2 and 3 ; @@ -8,6 +15,14 @@ com.vaadin.testbench.screenshot.directory=<enter the full path to the screenshot ; +; For only TestBench 3 +; + +; Simulates @RunLocally with the given value on all test classes without a @RunLocally annotation. +; com.vaadin.testbench.runLocally=firefox + + +; ; For only TestBench 2 ; diff --git a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java index a89ec4e587..1f72495596 100644 --- a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java +++ b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java @@ -17,12 +17,17 @@ package com.vaadin.launcher; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; @@ -30,7 +35,11 @@ import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import com.vaadin.launcher.CustomDeploymentConfiguration.Conf; +import com.vaadin.server.DefaultDeploymentConfiguration; +import com.vaadin.server.DeploymentConfiguration; import com.vaadin.server.LegacyApplication; import com.vaadin.server.LegacyVaadinServlet; import com.vaadin.server.ServiceException; @@ -43,6 +52,7 @@ import com.vaadin.server.VaadinServletRequest; import com.vaadin.server.VaadinSession; import com.vaadin.tests.components.TestBase; import com.vaadin.ui.UI; +import com.vaadin.util.CurrentInstance; @SuppressWarnings("serial") public class ApplicationRunnerServlet extends LegacyVaadinServlet { @@ -287,4 +297,129 @@ public class ApplicationRunnerServlet extends LegacyVaadinServlet { return Logger.getLogger(ApplicationRunnerServlet.class.getName()); } + @Override + protected DeploymentConfiguration createDeploymentConfiguration( + Properties initParameters) { + // Get the original configuration from the super class + final DeploymentConfiguration originalConfiguration = super + .createDeploymentConfiguration(initParameters); + + // And then create a proxy instance that delegates to the original + // configuration or a customized version + return (DeploymentConfiguration) Proxy.newProxyInstance( + DeploymentConfiguration.class.getClassLoader(), + new Class[] { DeploymentConfiguration.class }, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, + Object[] args) throws Throwable { + if (method.getDeclaringClass() == DeploymentConfiguration.class) { + // Find the configuration instance to delegate to + DeploymentConfiguration configuration = findDeploymentConfiguration(originalConfiguration); + + return method.invoke(configuration, args); + } else { + return method.invoke(proxy, args); + } + } + }); + } + + private DeploymentConfiguration findDeploymentConfiguration( + DeploymentConfiguration originalConfiguration) throws Exception { + // First level of cache + DeploymentConfiguration configuration = CurrentInstance + .get(DeploymentConfiguration.class); + + if (configuration == null) { + // Not in cache, try to find a VaadinSession to get it from + VaadinSession session = VaadinSession.getCurrent(); + + if (session == null) { + /* + * There's no current session, request or response when serving + * static resources, but there's still the current request + * maintained by AppliationRunnerServlet, and there's most + * likely also a HttpSession containing a VaadinSession for that + * request. + */ + + HttpServletRequest currentRequest = request.get(); + if (currentRequest != null) { + HttpSession httpSession = currentRequest.getSession(false); + if (httpSession != null) { + Map<Class<?>, CurrentInstance> oldCurrent = CurrentInstance + .setCurrent((VaadinSession) null); + try { + session = getService().findVaadinSession( + new VaadinServletRequest(currentRequest, + getService())); + } finally { + /* + * Clear some state set by findVaadinSession to + * avoid accidentally depending on it when coding on + * e.g. static request handling. + */ + CurrentInstance.restoreInstances(oldCurrent); + currentRequest.removeAttribute(VaadinSession.class + .getName()); + } + } + } + } + + if (session != null) { + String name = ApplicationRunnerServlet.class.getName() + + ".deploymentConfiguration"; + try { + session.lock(); + configuration = (DeploymentConfiguration) session + .getAttribute(name); + + if (configuration == null) { + Class<?> classToRun; + try { + classToRun = getClassToRun(); + } catch (ClassNotFoundException e) { + /* + * This happens e.g. if the UI class defined in the + * URL is not found or if this servlet just serves + * static resources while there's some other servlet + * that serves the UI (e.g. when using /run-push/). + */ + return originalConfiguration; + } + + CustomDeploymentConfiguration customDeploymentConfiguration = classToRun + .getAnnotation(CustomDeploymentConfiguration.class); + if (customDeploymentConfiguration != null) { + Properties initParameters = new Properties( + originalConfiguration.getInitParameters()); + + for (Conf entry : customDeploymentConfiguration + .value()) { + initParameters.put(entry.name(), entry.value()); + } + + configuration = new DefaultDeploymentConfiguration( + getClass(), initParameters); + } else { + configuration = originalConfiguration; + } + + session.setAttribute(name, configuration); + } + } finally { + session.unlock(); + } + + CurrentInstance.set(DeploymentConfiguration.class, + configuration); + + } else { + configuration = originalConfiguration; + } + } + return configuration; + } } diff --git a/uitest/src/com/vaadin/launcher/CustomDeploymentConfiguration.java b/uitest/src/com/vaadin/launcher/CustomDeploymentConfiguration.java new file mode 100644 index 0000000000..da903c13e0 --- /dev/null +++ b/uitest/src/com/vaadin/launcher/CustomDeploymentConfiguration.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2014 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.launcher; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(value = ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface CustomDeploymentConfiguration { + + @Target(value = ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + public @interface Conf { + public String name(); + + public String value(); + } + + public CustomDeploymentConfiguration.Conf[] value(); +} diff --git a/uitest/src/com/vaadin/tests/applicationservlet/CustomDeploymentConf.java b/uitest/src/com/vaadin/tests/applicationservlet/CustomDeploymentConf.java new file mode 100644 index 0000000000..8ae7715a77 --- /dev/null +++ b/uitest/src/com/vaadin/tests/applicationservlet/CustomDeploymentConf.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2014 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.tests.applicationservlet; + +import com.vaadin.launcher.ApplicationRunnerServlet; +import com.vaadin.launcher.CustomDeploymentConfiguration; +import com.vaadin.launcher.CustomDeploymentConfiguration.Conf; +import com.vaadin.server.DeploymentConfiguration; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Label; + +@CustomDeploymentConfiguration({ + @Conf(name = "customParam", value = "customValue"), + @Conf(name = "resourceCacheTime", value = "3599") }) +public class CustomDeploymentConf extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + DeploymentConfiguration deploymentConfiguration = getSession() + .getService().getDeploymentConfiguration(); + addComponent(new Label("Resource cache time: " + + deploymentConfiguration.getResourceCacheTime())); + addComponent(new Label("Custom config param: " + + deploymentConfiguration.getApplicationOrSystemProperty( + "customParam", null))); + } + + @Override + protected String getTestDescription() { + return "Demonstrates the @" + + CustomDeploymentConfiguration.class.getSimpleName() + + " feature that allows customizing the effective deployment configuration for test UIs run through " + + ApplicationRunnerServlet.class.getSimpleName() + "."; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(14215); + } + +} diff --git a/uitest/src/com/vaadin/tests/applicationservlet/CustomDeploymentConfTest.java b/uitest/src/com/vaadin/tests/applicationservlet/CustomDeploymentConfTest.java new file mode 100644 index 0000000000..e74eea6c55 --- /dev/null +++ b/uitest/src/com/vaadin/tests/applicationservlet/CustomDeploymentConfTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2014 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.tests.applicationservlet; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.testbench.elements.VerticalLayoutElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class CustomDeploymentConfTest extends MultiBrowserTest { + @Test + public void testCustomDeploymentConf() { + openTestURL(); + + LabelElement cacheTimeLabel = $$(VerticalLayoutElement.class) + .$$(VerticalLayoutElement.class).$$(LabelElement.class).first(); + + LabelElement customParamLabel = $$(VerticalLayoutElement.class) + .$$(VerticalLayoutElement.class).$$(LabelElement.class).get(1); + + Assert.assertEquals("Resource cache time: 3599", + cacheTimeLabel.getText()); + Assert.assertEquals("Custom config param: customValue", + customParamLabel.getText()); + } +} diff --git a/uitest/src/com/vaadin/tests/components/button/ButtonTooltips.html b/uitest/src/com/vaadin/tests/components/button/ButtonTooltips.html deleted file mode 100644 index 13fdf52c76..0000000000 --- a/uitest/src/com/vaadin/tests/components/button/ButtonTooltips.html +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head profile="http://selenium-ide.openqa.org/profiles/test-case"> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -<link rel="selenium.base" href="http://192.168.2.168:8888/" /> -<title>New Test</title> -</head> -<body> -<table cellpadding="1" cellspacing="1" border="1"> -<thead> -<tr><td rowspan="1" colspan="3">New Test</td></tr> -</thead><tbody> -<tr> - <td>open</td> - <td>/run/com.vaadin.tests.components.button.ButtonTooltips?restartApplication</td> - <td></td> -</tr> -<tr> - <td>showTooltip</td> - <td>vaadin=runcomvaadintestscomponentsbuttonButtonTooltips::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> - <td>0,0</td> -</tr> -<tr> - <td>showTooltip</td> - <td>vaadin=runcomvaadintestscomponentsbuttonButtonTooltips::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> - <td>0,0</td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td></td> -</tr> - -</tbody></table> -</body> -</html> diff --git a/uitest/src/com/vaadin/tests/components/table/MemoryLeakTable.java b/uitest/src/com/vaadin/tests/components/table/MemoryLeakTable.java new file mode 100644 index 0000000000..1b76be3b72 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/MemoryLeakTable.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2014 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.tests.components.table; + +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Table; + +/** + * Test UI Class for testing memory leak in table (#14159). + * + * @since + * @author Vaadin Ltd + */ +public class MemoryLeakTable extends AbstractTestUI { + Button btnAdd = new Button("Add rows"); + Button btnRemove = new Button("Remove rows"); + Button btnTenTimes = new Button("Do ten times"); + Table tbl = new Table(); + static final int COLS = 15; + static final int ROWS = 2000; + + private void addRows() { + IndexedContainer idx = new IndexedContainer(); + for (int i = 0; i < COLS; i++) { + idx.addContainerProperty("name " + i, String.class, "value"); + } + for (int i = 0; i < ROWS; i++) { + idx.addItem("item" + i); + } + tbl.setContainerDataSource(idx); + addComponent(tbl); + } + + private void removeRows() { + tbl.removeAllItems(); + removeComponent(tbl); + } + + @Override + protected void setup(VaadinRequest request) { + btnAdd.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + addRows(); + } + }); + btnRemove.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + removeRows(); + } + }); + addComponent(btnAdd); + addComponent(btnRemove); + } + + @Override + protected String getTestDescription() { + return "Generates table for memory leaking test"; + } + + @Override + protected Integer getTicketNumber() { + return 14159; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/table/MemoryLeakTableTest.java b/uitest/src/com/vaadin/tests/components/table/MemoryLeakTableTest.java new file mode 100644 index 0000000000..b4b8d93fbe --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/MemoryLeakTableTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2000-2014 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.tests.components.table; + +import java.io.IOException; +import java.util.Random; + +import org.junit.Ignore; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.TableElement; +import com.vaadin.tests.tb3.AbstractTB3Test.RunLocally; +import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.tests.tb3.MultiBrowserTest.Browser; + +/** + * Test case creating and deleting table component in a loop, testing memory + * lead in Table component. This test should not be used in auto testing. + * + * To test memory consuption. Run test in debug mode. Take memory snapshot in + * Profiler in browser before and after the loop. Compare memory consuption. + * + * @since + * @author Vaadin Ltd + */ +@RunLocally(Browser.CHROME) +public class MemoryLeakTableTest extends MultiBrowserTest { + + /** + * + */ + private static final int ITERATIONS = 200; + + // To run locally in chrome download ChromeDriver for TB3 + // Set path to the chrome driver. In + // ./work/eclipse-run-selected-test.properties add line + // chrome.driver.path=path_to_driver + + // Test is marked as ignore to exclude it from auto testing + @Test + @Ignore + public void memoryTest() throws IOException { + // Set breakoint and look memory consuption in Profiler + // Mozilla Firefox doesn't provide memory usage profiler, use chrome. + + openTestURL(); + + ButtonElement btnAdd = $(ButtonElement.class).get(0); + + for (int i = 0; i < ITERATIONS; i++) { + btnAdd.click(); + ButtonElement btnDel = $(ButtonElement.class).get(1); + TableElement tbl = $(TableElement.class).get(0); + Random rand = new Random(); + int scrollValue = rand.nextInt(1500); + scrollTable(tbl, scrollValue); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + btnDel.click(); + } + // Set breakoint and look memory consuption in Profiler + btnAdd = $(ButtonElement.class).get(0); + } + + // Scrolls table element + // Method scroll in TalbeElement class has a bug + // + private void scrollTable(TableElement tbl, int value) { + WebElement actualElement = tbl.findElement(By + .className("v-table-body-wrapper")); + JavascriptExecutor js = tbl.getCommandExecutor(); + js.executeScript("arguments[0].scrollTop = " + value, actualElement); + } +} diff --git a/uitest/src/com/vaadin/tests/components/table/TableTooManyColumns.java b/uitest/src/com/vaadin/tests/components/table/TableTooManyColumns.java new file mode 100644 index 0000000000..2d27636420 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableTooManyColumns.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2014 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.tests.components.table; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Table; +import com.vaadin.ui.Table.ColumnGenerator; + +public class TableTooManyColumns extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Table table = new Table(); + + table.setColumnCollapsingAllowed(true); + + for (int i = 0; i < 91; i++) { + table.addGeneratedColumn("COLUMN " + i, new ColumnGenerator() { + + @Override + public Object generateCell(Table source, Object itemId, + Object columnId) { + return columnId; + } + }); + } + + addComponent(table); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription() + */ + @Override + protected String getTestDescription() { + return "Table column drop down becomes too large to fit the screen."; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber() + */ + @Override + protected Integer getTicketNumber() { + return 14156; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/table/TableTooManyColumnsTest.java b/uitest/src/com/vaadin/tests/components/table/TableTooManyColumnsTest.java new file mode 100644 index 0000000000..2244365e00 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableTooManyColumnsTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2014 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.tests.components.table; + +import java.io.IOException; + +import org.junit.Test; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.commands.TestBenchElementCommands; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class TableTooManyColumnsTest extends MultiBrowserTest { + + @Test + public void testDropdownTable() throws IOException { + openTestURL(); + + WebElement element = findElement(By + .className("v-table-column-selector")); + + element.click(); + + WebElement menu = findElement(By.className("gwt-MenuBar-vertical")); + + TestBenchElementCommands scrollable = testBenchElement(menu); + scrollable.scroll(3000); + + compareScreen(getScreenshotBaseName()); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetNotEnoughHorizontalSpace.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetNotEnoughHorizontalSpace.java new file mode 100644 index 0000000000..0105498f27 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetNotEnoughHorizontalSpace.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2014 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.tests.components.tabsheet; + +import com.vaadin.annotations.Theme; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Panel; +import com.vaadin.ui.TabSheet; + +/** + * Test to see if tabsheet navigation buttons render correctly in Chameleon + * + * @author Vaadin Ltd + */ +@Theme("chameleon") +public class TabsheetNotEnoughHorizontalSpace extends AbstractTestUI { + + private TabSheet tabsheet = new TabSheet(); + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#setup(com.vaadin.server. + * VaadinRequest) + */ + @Override + protected void setup(VaadinRequest request) { + generateTabs(); + tabsheet.setSizeFull(); + addComponent(tabsheet); + + } + + private void generateTabs() { + tabsheet.removeAllComponents(); + for (int i = 0; i < 100; ++i) { + tabsheet.addTab(new Panel(), "Tab" + i); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription() + */ + @Override + protected String getTestDescription() { + return "Scroll-buttons should render correctly on all browsers"; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber() + */ + @Override + protected Integer getTicketNumber() { + return 12154; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetNotEnoughHorizontalSpaceTest.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetNotEnoughHorizontalSpaceTest.java new file mode 100644 index 0000000000..990f545697 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetNotEnoughHorizontalSpaceTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2014 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.tests.components.tabsheet; + +import java.io.IOException; + +import org.junit.Test; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Tests that tabsheet's scroll button are rendered correctly in Chameleon + * theme. + * + * Ticket #12154 + * + * @since + * @author Vaadin Ltd + */ +public class TabsheetNotEnoughHorizontalSpaceTest extends MultiBrowserTest { + + @Test + public void testThatTabScrollButtonsAreRenderedCorrectly() + throws IOException { + openTestURL(); + + driver.findElement(By.className("v-tabsheet-scrollerPrev-disabled")); + driver.findElement(By.className("v-tabsheet-scrollerNext")); + + compareScreen(getScreenshotBaseName()); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/upload/TestFileUploadTest.java b/uitest/src/com/vaadin/tests/components/upload/TestFileUploadTest.java index 1887427a72..ae966a5b07 100644 --- a/uitest/src/com/vaadin/tests/components/upload/TestFileUploadTest.java +++ b/uitest/src/com/vaadin/tests/components/upload/TestFileUploadTest.java @@ -109,7 +109,7 @@ public class TestFileUploadTest extends MultiBrowserTest { } private void setLocalFileDetector(WebElement element) throws Exception { - if (getClass().isAnnotationPresent(RunLocally.class)) { + if (getRunLocallyBrowser() != null) { return; } diff --git a/uitest/src/com/vaadin/tests/serialization/SerializerTest.java b/uitest/src/com/vaadin/tests/serialization/SerializerTest.java index 1c18fb1912..333964e9bf 100644 --- a/uitest/src/com/vaadin/tests/serialization/SerializerTest.java +++ b/uitest/src/com/vaadin/tests/serialization/SerializerTest.java @@ -93,13 +93,13 @@ public class SerializerTest extends AbstractTestUI { rpc.sendInt(Integer.MAX_VALUE, Integer.valueOf(0), new int[] { 5, 7 }); state.intValue = Integer.MAX_VALUE; - state.intObjectValue = Integer.valueOf(0); + state.intObjectValue = Integer.valueOf(42); state.intArray = new int[] { 5, 7 }; rpc.sendLong(577431841358l, Long.valueOf(0), new long[] { -57841235865l, 57 }); - state.longValue = 577431841358l; - state.longObjectValue = Long.valueOf(0); + state.longValue = 577431841359l; + state.longObjectValue = Long.valueOf(577431841360l); state.longArray = new long[] { -57841235865l, 57 }; rpc.sendFloat(3.14159f, Float.valueOf(Math.nextUp(1)), new float[] { @@ -111,7 +111,7 @@ public class SerializerTest extends AbstractTestUI { rpc.sendDouble(Math.PI, Double.valueOf(-Math.E), new double[] { Double.MAX_VALUE, Double.MIN_VALUE }); state.doubleValue = Math.PI; - state.doubleValue = Double.valueOf(-Math.E); + state.doubleObjectValue = Double.valueOf(-Math.E); state.doubleArray = new double[] { Double.MAX_VALUE, Double.MIN_VALUE }; rpc.sendString("This is a tesing string ‡"); diff --git a/uitest/src/com/vaadin/tests/serialization/SerializerTestTest.java b/uitest/src/com/vaadin/tests/serialization/SerializerTestTest.java index 5ca1e9ce6a..47bb212347 100644 --- a/uitest/src/com/vaadin/tests/serialization/SerializerTestTest.java +++ b/uitest/src/com/vaadin/tests/serialization/SerializerTestTest.java @@ -77,5 +77,36 @@ public class SerializerTestTest extends MultiBrowserTest { "sendBoolean: false, false, [false, false, true, false, true, true]", getLogRow(logRow++)); Assert.assertEquals("sendBeanSubclass: 43", getLogRow(logRow++)); + Assert.assertEquals( + "state.doubleArray: [1.7976931348623157e+308, 5e-324]", + getLogRow(logRow++)); + Assert.assertEquals("state.doubleObjectValue: -2.718281828459045", + getLogRow(logRow++)); + Assert.assertEquals("state.doubleValue: 3.141592653589793", + getLogRow(logRow++)); + Assert.assertEquals("state.floatArray: [57, 0, -12]", + getLogRow(logRow++)); + Assert.assertEquals("state.floatObjectValue: 1.0000001", + getLogRow(logRow++)); + Assert.assertEquals("state.floatValue: 3.14159", getLogRow(logRow++)); + Assert.assertEquals("state.longArray: [-57841235865, 57]", + getLogRow(logRow++)); + Assert.assertEquals("state.longObjectValue: 577431841360", + getLogRow(logRow++)); + Assert.assertEquals("state.longValue: 577431841359", + getLogRow(logRow++)); + Assert.assertEquals("state.intArray: [5, 7]", getLogRow(logRow++)); + Assert.assertEquals("state.intObjectValue: 42", getLogRow(logRow++)); + Assert.assertEquals("state.intValue: 2147483647", getLogRow(logRow++)); + Assert.assertEquals("state.charArray: aBcD", getLogRow(logRow++)); + Assert.assertEquals("state.charObjectValue: å", getLogRow(logRow++)); + Assert.assertEquals("state.charValue: ∫", getLogRow(logRow++)); + Assert.assertEquals("state.byteArray: [3, 1, 2]", getLogRow(logRow++)); + Assert.assertEquals("state.byteObjectValue: -12", getLogRow(logRow++)); + Assert.assertEquals("state.byteValue: 5", getLogRow(logRow++)); + Assert.assertEquals( + "state.booleanArray: [true, true, false, true, false, false]", + getLogRow(logRow++)); + } } diff --git a/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java b/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java index fa704d7b0b..8dd10216d2 100644 --- a/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java +++ b/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java @@ -120,9 +120,13 @@ public abstract class AbstractTB3Test extends TestBenchTestCase { protected void setupDriver() throws Exception { DesiredCapabilities capabilities; - RunLocally runLocally = getClass().getAnnotation(RunLocally.class); - if (runLocally != null) { - capabilities = runLocally.value().getDesiredCapabilities(); + Browser runLocallyBrowser = getRunLocallyBrowser(); + if (runLocallyBrowser != null) { + if (System.getenv().containsKey("TEAMCITY_VERSION")) { + throw new RuntimeException( + "@RunLocally is not supported for tests run on the build server"); + } + capabilities = runLocallyBrowser.getDesiredCapabilities(); setupLocalDriver(capabilities); } else { capabilities = getDesiredCapabilities(); @@ -152,6 +156,15 @@ public abstract class AbstractTB3Test extends TestBenchTestCase { } + protected Browser getRunLocallyBrowser() { + RunLocally runLocally = getClass().getAnnotation(RunLocally.class); + if (runLocally != null) { + return runLocally.value(); + } else { + return null; + } + } + protected WebElement getTooltipElement() { return getDriver().findElement(com.vaadin.testbench.By.className("v-tooltip-text")); } diff --git a/uitest/src/com/vaadin/tests/tb3/PrivateTB3Configuration.java b/uitest/src/com/vaadin/tests/tb3/PrivateTB3Configuration.java index 305caf1cb5..15ca97f701 100644 --- a/uitest/src/com/vaadin/tests/tb3/PrivateTB3Configuration.java +++ b/uitest/src/com/vaadin/tests/tb3/PrivateTB3Configuration.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; +import java.util.Arrays; import java.util.Enumeration; import java.util.Properties; @@ -34,6 +35,7 @@ import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.safari.SafariDriver; import com.vaadin.testbench.TestBench; +import com.vaadin.tests.tb3.MultiBrowserTest.Browser; /** * Provides values for parameters which depend on where the test is run. @@ -43,14 +45,16 @@ import com.vaadin.testbench.TestBench; * @author Vaadin Ltd */ public abstract class PrivateTB3Configuration extends ScreenshotTB3Test { + private static final String RUN_LOCALLY_PROPERTY = "com.vaadin.testbench.runLocally"; private static final String HOSTNAME_PROPERTY = "com.vaadin.testbench.deployment.hostname"; private static final String PORT_PROPERTY = "com.vaadin.testbench.deployment.port"; private static final Properties properties = new Properties(); + private static final File propertiesFile = new File("work", + "eclipse-run-selected-test.properties"); static { - File file = new File("work", "eclipse-run-selected-test.properties"); - if (file.exists()) { + if (propertiesFile.exists()) { try { - properties.load(new FileInputStream(file)); + properties.load(new FileInputStream(propertiesFile)); } catch (IOException e) { throw new RuntimeException(e); } @@ -66,6 +70,16 @@ public abstract class PrivateTB3Configuration extends ScreenshotTB3Test { return property; } + private static String getSource(String propertyName) { + if (properties.containsKey(propertyName)) { + return propertiesFile.getAbsolutePath(); + } else if (System.getProperty(propertyName) != null) { + return "System.getProperty()"; + } else { + return null; + } + } + @Override protected String getScreenshotDirectory() { String screenshotDirectory = getProperty("com.vaadin.testbench.screenshot.directory"); @@ -83,7 +97,7 @@ public abstract class PrivateTB3Configuration extends ScreenshotTB3Test { @Override protected String getDeploymentHostname() { - if (getClass().getAnnotation(RunLocally.class) != null) { + if (getRunLocallyBrowser() != null) { return "localhost"; } return getConfiguredDeploymentHostname(); @@ -180,8 +194,21 @@ public abstract class PrivateTB3Configuration extends ScreenshotTB3Test { driver = new FirefoxDriver(); } } else if (BrowserUtil.isChrome(desiredCapabilities)) { - System.setProperty("webdriver.chrome.driver", - getProperty("chrome.driver.path")); + String propertyName = "chrome.driver.path"; + String chromeDriverPath = getProperty(propertyName); + if (chromeDriverPath == null) { + throw new RuntimeException( + "You need to install ChromeDriver to use @" + + RunLocally.class.getSimpleName() + + " with Chrome." + + "\nFirst install it from https://code.google.com/p/selenium/wiki/ChromeDriver." + + "\nThen update " + + propertiesFile.getAbsolutePath() + + " to define a property named " + + propertyName + + " containing the path of your local ChromeDriver installation."); + } + System.setProperty("webdriver.chrome.driver", chromeDriverPath); driver = new ChromeDriver(); } else if (BrowserUtil.isSafari(desiredCapabilities)) { driver = new SafariDriver(); @@ -196,4 +223,28 @@ public abstract class PrivateTB3Configuration extends ScreenshotTB3Test { setDriver(TestBench.createDriver(driver)); setDesiredCapabilities(desiredCapabilities); } + + @Override + protected Browser getRunLocallyBrowser() { + Browser runLocallyBrowser = super.getRunLocallyBrowser(); + if (runLocallyBrowser != null) { + // Always use annotation value if present + return runLocallyBrowser; + } + + String runLocallyValue = getProperty(RUN_LOCALLY_PROPERTY); + if (runLocallyValue == null || runLocallyValue.trim().isEmpty()) { + return null; + } + + String browserName = runLocallyValue.trim().toUpperCase(); + try { + return Browser.valueOf(browserName); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Invalid " + RUN_LOCALLY_PROPERTY + + " property from " + getSource(RUN_LOCALLY_PROPERTY) + + ": " + runLocallyValue + ". Expected one of " + + Arrays.toString(Browser.values())); + } + } } diff --git a/uitest/src/com/vaadin/tests/tb3/TB3Runner.java b/uitest/src/com/vaadin/tests/tb3/TB3Runner.java index 4d29e479e2..5b5a6dcf39 100644 --- a/uitest/src/com/vaadin/tests/tb3/TB3Runner.java +++ b/uitest/src/com/vaadin/tests/tb3/TB3Runner.java @@ -37,7 +37,7 @@ import org.openqa.selenium.remote.DesiredCapabilities; import com.vaadin.tests.annotations.TestCategory; import com.vaadin.tests.tb3.AbstractTB3Test.BrowserUtil; -import com.vaadin.tests.tb3.AbstractTB3Test.RunLocally; +import com.vaadin.tests.tb3.MultiBrowserTest.Browser; /** * This runner is loosely based on FactoryTestRunner by Ted Young @@ -185,17 +185,17 @@ public class TB3Runner extends BlockJUnit4ClassRunner { /* * Returns a list of desired browser capabilities according to browsers * defined in the test class, filtered by possible filter parameters. Use - * {@code @RunLocally} annotation to override all capabilities. + * {@code @RunLocally} annotation or com.vaadin.testbench.runLocally + * property to override all capabilities. */ private Collection<DesiredCapabilities> getDesiredCapabilities( AbstractTB3Test testClassInstance) { Collection<DesiredCapabilities> desiredCapabilites = getFilteredCapabilities(testClassInstance); - if (isRunLocally(testClassInstance)) { + Browser runLocallyBrowser = testClassInstance.getRunLocallyBrowser(); + if (runLocallyBrowser != null) { desiredCapabilites = new ArrayList<DesiredCapabilities>(); - desiredCapabilites.add(testClassInstance.getClass() - .getAnnotation(RunLocally.class).value() - .getDesiredCapabilities()); + desiredCapabilites.add(runLocallyBrowser.getDesiredCapabilities()); } return desiredCapabilites; @@ -237,10 +237,6 @@ public class TB3Runner extends BlockJUnit4ClassRunner { return filteredCapabilities; } - private boolean isRunLocally(AbstractTB3Test testClassInstance) { - return testClassInstance.getClass().getAnnotation(RunLocally.class) != null; - } - private AbstractTB3Test getTestClassInstance() throws InstantiationException, IllegalAccessException, InvocationTargetException { diff --git a/uitest/src/com/vaadin/tests/tooltip/ButtonTooltips.java b/uitest/src/com/vaadin/tests/tooltip/ButtonTooltips.java index d212a13058..c341aa689b 100644 --- a/uitest/src/com/vaadin/tests/tooltip/ButtonTooltips.java +++ b/uitest/src/com/vaadin/tests/tooltip/ButtonTooltips.java @@ -1,14 +1,18 @@ package com.vaadin.tests.tooltip; -import com.vaadin.tests.components.TestBase; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.VerticalLayout; -public class ButtonTooltips extends TestBase { +public class ButtonTooltips extends AbstractTestUI { + + public static final String shortDescription = "Another"; + public static final String longDescription = "long descidescidescpription"; @Override - protected String getDescription() { + protected String getTestDescription() { return "Button tooltip's size gets messed up if moving from one tooltip to another before a timer expires."; } @@ -18,12 +22,12 @@ public class ButtonTooltips extends TestBase { } @Override - protected void setup() { + protected void setup(VaadinRequest request) { VerticalLayout vl = new VerticalLayout(); Button button = new Button("One"); - button.setDescription("long descidescidescpription"); + button.setDescription(longDescription); Button button2 = new Button("Two"); - button2.setDescription("Another"); + button2.setDescription(shortDescription); vl.addComponent(button); vl.addComponent(button2); vl.setComponentAlignment(button, Alignment.TOP_RIGHT); diff --git a/uitest/src/com/vaadin/tests/tooltip/ButtonTooltipsTest.java b/uitest/src/com/vaadin/tests/tooltip/ButtonTooltipsTest.java index d64dd900a7..08436b1332 100644 --- a/uitest/src/com/vaadin/tests/tooltip/ButtonTooltipsTest.java +++ b/uitest/src/com/vaadin/tests/tooltip/ButtonTooltipsTest.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 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 @@ -26,7 +26,7 @@ import com.vaadin.tests.tb3.TooltipTest; /** * Tests that tooltip sizes do not change when moving between adjacent elements - * + * * @author Vaadin Ltd */ public class ButtonTooltipsTest extends TooltipTest { @@ -38,12 +38,12 @@ public class ButtonTooltipsTest extends TooltipTest { WebElement buttonOne = $(ButtonElement.class).caption("One").first(); WebElement buttonTwo = $(ButtonElement.class).caption("Two").first(); - checkTooltip(buttonOne, "long descidescidescpription"); + checkTooltip(buttonOne, ButtonTooltips.longDescription); int originalWidth = getTooltipElement().getSize().getWidth(); int originalHeight = getTooltipElement().getSize().getHeight(); clearTooltip(); - checkTooltip(buttonTwo, "Another"); + checkTooltip(buttonTwo, ButtonTooltips.shortDescription); moveMouseTo(buttonOne, 5, 5); sleep(100); assertThat(getTooltipElement().getSize().getWidth(), is(originalWidth)); diff --git a/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml b/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml index ac93efd7d4..d23903f9db 100644 --- a/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml +++ b/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml @@ -15,4 +15,9 @@ <when-type-is class="com.vaadin.client.communication.PushConnection" /> </replace-with> + <replace-with + class="com.vaadin.tests.widgetset.client.MockApplicationConnection"> + <when-type-is class="com.vaadin.client.ApplicationConnection" /> + </replace-with> + </module> diff --git a/uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java b/uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java new file mode 100644 index 0000000000..4ee5b71387 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.client; + +import java.util.Date; +import java.util.logging.Logger; + +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONValue; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ValueMap; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.tests.widgetset.server.csrf.ui.CsrfTokenDisabled; + +/** + * Mock ApplicationConnection for several issues where we need to hack it. + * + * @since + * @author Vaadin Ltd + */ +public class MockApplicationConnection extends ApplicationConnection { + + private static final Logger LOGGER = Logger + .getLogger(MockApplicationConnection.class.getName()); + + // The last token received from the server. + private String lastCsrfTokenReceiver; + + // The last token sent to the server. + private String lastCsrfTokenSent; + + /** + * Provide the last token received from the server. <br/> + * We added this to test the change done on CSRF token. + * + * @see CsrfTokenDisabled + */ + public String getLastCsrfTokenReceiver() { + return lastCsrfTokenReceiver; + } + + /** + * Provide the last token sent to the server. <br/> + * We added this to test the change done on CSRF token. + * + * @see CsrfTokenDisabled + */ + public String getLastCsrfTokenSent() { + return lastCsrfTokenSent; + } + + @Override + protected void handleUIDLMessage(Date start, String jsonText, ValueMap json) { + lastCsrfTokenReceiver = json + .getString(ApplicationConstants.UIDL_SECURITY_TOKEN_ID); + + super.handleUIDLMessage(start, jsonText, json); + } + + @Override + protected void doUidlRequest(String uri, JSONObject payload) { + JSONValue jsonValue = payload.get(ApplicationConstants.CSRF_TOKEN); + lastCsrfTokenSent = jsonValue != null ? jsonValue.toString() : null; + + super.doUidlRequest(uri, payload); + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/SerializerTestConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/SerializerTestConnector.java index 0ef4b664ac..7758cdc2ac 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/SerializerTestConnector.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/SerializerTestConnector.java @@ -284,11 +284,12 @@ public class SerializerTestConnector extends AbstractExtensionConnector { public void onStateChanged(StateChangeEvent stateChangeEvent) { rpc.log("state.booleanValue: " + getState().booleanValue); rpc.log("state.booleanObjectValue: " + getState().booleanObjectValue); - rpc.log("state.booleanArray: " + getState().booleanArray); + rpc.log("state.booleanArray: " + + Arrays.toString(getState().booleanArray)); rpc.log("state.byteValue: " + getState().byteValue); rpc.log("state.byteObjectValue: " + getState().byteObjectValue); - rpc.log("state.byteArray: " + getState().byteArray); + rpc.log("state.byteArray: " + Arrays.toString(getState().byteArray)); rpc.log("state.charValue: " + getState().charValue); rpc.log("state.charObjectValue: " + getState().charObjectValue); @@ -296,19 +297,19 @@ public class SerializerTestConnector extends AbstractExtensionConnector { rpc.log("state.intValue: " + getState().intValue); rpc.log("state.intObjectValue: " + getState().intObjectValue); - rpc.log("state.intArray: " + getState().intArray); + rpc.log("state.intArray: " + Arrays.toString(getState().intArray)); rpc.log("state.longValue: " + getState().longValue); rpc.log("state.longObjectValue: " + getState().longObjectValue); - rpc.log("state.longArray: " + getState().longArray); + rpc.log("state.longArray: " + Arrays.toString(getState().longArray)); rpc.log("state.floatValue: " + getState().floatValue); rpc.log("state.floatObjectValue: " + getState().floatObjectValue); - rpc.log("state.floatArray: " + getState().floatArray); + rpc.log("state.floatArray: " + Arrays.toString(getState().floatArray)); rpc.log("state.doubleValue: " + getState().doubleValue); rpc.log("state.doubleObjectValue: " + getState().doubleObjectValue); - rpc.log("state.doubleArray: " + getState().doubleArray); + rpc.log("state.doubleArray: " + Arrays.toString(getState().doubleArray)); /* * TODO public double doubleValue; public Double DoubleValue; public diff --git a/uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java new file mode 100644 index 0000000000..cf24ed6921 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.client.csrf; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.google.gwt.core.shared.GWT; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.VButton; +import com.vaadin.shared.ui.Connect; +import com.vaadin.tests.widgetset.client.MockApplicationConnection; +import com.vaadin.tests.widgetset.server.csrf.CsrfButton; + +/** + * Dummy connector to test our CSRF bug. See #14111. + * + * @author Vaadin Ltd + */ +@SuppressWarnings("serial") +@Connect(CsrfButton.class) +public class CsrfButtonConnector extends AbstractComponentConnector { + + static Logger logger = Logger + .getLogger(CsrfButtonConnector.class.getName()); + static { + logger.setLevel(Level.ALL); + } + + @Override + public VButton getWidget() { + return (VButton) super.getWidget(); + } + + @Override + protected VButton createWidget() { + return GWT.create(VButton.class); + } + + public final static String ID = "CsrfButton"; + + @Override + public void init() { + super.init(); + + getWidget().getElement().setId(ID); + getWidget().setText(csrfTokenInfo()); + getWidget().addClickHandler(new ClickHandler() { + + @Override + public void onClick(ClickEvent event) { + getWidget().setText(csrfTokenInfo()); + } + }); + } + + private String csrfTokenInfo() { + return getMockConnection().getCsrfToken() + ", " + + getMockConnection().getLastCsrfTokenReceiver() + ", " + + getMockConnection().getLastCsrfTokenSent(); + } + + private MockApplicationConnection getMockConnection() { + return (MockApplicationConnection) getConnection(); + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/CsrfButton.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/CsrfButton.java new file mode 100644 index 0000000000..567127927d --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/CsrfButton.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.server.csrf; + +import com.vaadin.ui.AbstractComponent; + +/** + * Dummy client connector to link with the client functionality if the + * CsrfToken. + * + * @since + * @author Vaadin Ltd + */ +@SuppressWarnings("serial") +public class CsrfButton extends AbstractComponent { + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUI.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUI.java new file mode 100644 index 0000000000..f8f1754385 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUI.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.server.csrf.ui; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.tests.widgetset.server.csrf.CsrfButton; +import com.vaadin.ui.Button; +import com.vaadin.ui.Label; + +/** + * Abstract UI to test the CSRF token issue as reported in (#14111) + * + * @since + * @author Vaadin Ltd + */ +@SuppressWarnings("serial") +@Widgetset(TestingWidgetSet.NAME) +public abstract class AbstractCsrfTokenUI extends AbstractTestUI { + + public static final String PRESS_ID = "PressMe"; + + @Override + protected void setup(VaadinRequest request) { + + addComponent(new Label("The button's text is the client token:")); + addComponent(new CsrfButton()); + addComponent(new Label("This one is from the server")); + addComponent(new Label(getSession().getCsrfToken())); + Button pressMe = new Button("Click me to send a request"); + pressMe.setId(PRESS_ID); + addComponent(pressMe); + } + + @Override + protected String getTestDescription() { + return "Remove csrfToken from the request if security protection is disabled."; + } + + @Override + protected Integer getTicketNumber() { + return 14111; + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUITest.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUITest.java new file mode 100644 index 0000000000..614eaa063e --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/AbstractCsrfTokenUITest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.server.csrf.ui; + +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.ExpectedCondition; + +import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.tests.widgetset.client.csrf.CsrfButtonConnector; + +public abstract class AbstractCsrfTokenUITest extends MultiBrowserTest { + + static final Logger LOGGER = Logger.getLogger(AbstractCsrfTokenUITest.class + .getName()); + + @Test + public void testTokens() { + openTestURL(); + + final By debugButton = By.id(CsrfButtonConnector.ID); + + final String debugMessage1 = getDriver().findElement(debugButton) + .getText(); + + getDriver().findElement(By.id(CsrfTokenDisabled.PRESS_ID)).click(); + + waitUntil(new ExpectedCondition<Boolean>() { + + @Override + public Boolean apply(WebDriver input) { + getDriver().findElement(debugButton).click(); + String debugMessage2 = input.findElement(debugButton).getText(); + + LOGGER.log(Level.INFO, "1: " + debugMessage1); + LOGGER.log(Level.INFO, "2: " + debugMessage2); + + if (!debugMessage1.equals(debugMessage2)) { + + compareMessage(split(debugMessage1), split(debugMessage2)); + + LOGGER.log(Level.INFO, "DONE"); + + return true; + + } else { + return false; + } + } + }); + } + + private TokenGroup split(String debugMessage) { + StringTokenizer tokenizer = new StringTokenizer(debugMessage, ", \""); + + return new TokenGroup(tokenizer.nextToken(), tokenizer.nextToken(), + tokenizer.nextToken()); + } + + /* + * Just implement this. + */ + protected abstract boolean compareMessage(TokenGroup tokenGroup1, + TokenGroup tokenGroup2); + + boolean isNullOrUndefined(String value) { + return isNull(value) || isUndefined(value); + } + + boolean isUndefined(String value) { + return value.equals("undefined"); + } + + boolean isNull(String value) { + return value.equals("null"); + } + + /* + * Wrapps all tokens from the client app. + */ + static class TokenGroup { + + public final String clientToken; + + public final String tokenReceivedFromServer; + + public final String tokenSentToServer; + + public TokenGroup(String clientToken, String tokenReceivedFromServer, + String tokenSentToServer) { + this.clientToken = clientToken; + this.tokenReceivedFromServer = tokenReceivedFromServer; + this.tokenSentToServer = tokenSentToServer; + } + + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabled.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabled.java new file mode 100644 index 0000000000..6283285b40 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabled.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.server.csrf.ui; + +import com.vaadin.launcher.CustomDeploymentConfiguration; +import com.vaadin.launcher.CustomDeploymentConfiguration.Conf; + +/** + * When the disable-xsrf-protection is true csrfToken is not present anymore + * with the requests.<br/> + * This is useful mostly when the client is not Vaadin and so it will not push + * the parameter anyway. So now the server knows how to deal the issue if the + * csrfToken is not present. + * + * @since + * @author Vaadin Ltd + */ +@SuppressWarnings("serial") +@CustomDeploymentConfiguration({ @Conf(name = "disable-xsrf-protection", value = "true") }) +public class CsrfTokenDisabled extends AbstractCsrfTokenUI { + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabledTest.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabledTest.java new file mode 100644 index 0000000000..504425fead --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenDisabledTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.server.csrf.ui; + +import com.vaadin.shared.ApplicationConstants; + +/** + * Test the CSRF Token issue. + * + * @since + * @author Vaadin Ltd + */ +public class CsrfTokenDisabledTest extends AbstractCsrfTokenUITest { + + @Override + protected boolean compareMessage(TokenGroup tokenGroup1, + TokenGroup tokenGroup2) { + + return tokenGroup1.clientToken + .equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE) + && isUndefined(tokenGroup1.tokenReceivedFromServer) + && isUndefined(tokenGroup1.tokenSentToServer) + && tokenGroup2.clientToken + .equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE) + && isUndefined(tokenGroup2.tokenReceivedFromServer) + // This is it actually, no token sent to the server. + && isNull(tokenGroup2.tokenSentToServer); + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabled.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabled.java new file mode 100644 index 0000000000..cd02c6da77 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabled.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.server.csrf.ui; + +import com.vaadin.launcher.CustomDeploymentConfiguration; +import com.vaadin.launcher.CustomDeploymentConfiguration.Conf; + +@SuppressWarnings("serial") +@CustomDeploymentConfiguration({ @Conf(name = "disable-xsrf-protection", value = "false") }) +public class CsrfTokenEnabled extends AbstractCsrfTokenUI { + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabledTest.java b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabledTest.java new file mode 100644 index 0000000000..1d51f1c372 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/server/csrf/ui/CsrfTokenEnabledTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 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.tests.widgetset.server.csrf.ui; + +/** + * Test the CSRF Token issue. + * + * @since + * @author Vaadin Ltd + */ +public class CsrfTokenEnabledTest extends AbstractCsrfTokenUITest { + + @Override + protected boolean compareMessage(TokenGroup tokenGroup1, + TokenGroup tokenGroup2) { + + return tokenGroup1.clientToken.equals(tokenGroup2.clientToken) + // Valid token received and set on the client + && tokenGroup1.clientToken + .equals(tokenGroup1.tokenReceivedFromServer) + // No token sent yet to the server. + && isUndefined(tokenGroup1.tokenSentToServer) + // Token is sent to the server. + && tokenGroup2.clientToken + .equals(tokenGroup2.tokenSentToServer) + // And no more token received from the server. + && isUndefined(tokenGroup2.tokenReceivedFromServer); + } + +} |