diff options
author | Jonatan Kronqvist <jonatan@vaadin.com> | 2013-09-16 16:16:51 +0300 |
---|---|---|
committer | Jonatan Kronqvist <jonatan@vaadin.com> | 2013-09-16 16:16:51 +0300 |
commit | d60b9419e3dc638b172a288ee4b4787d20f8d44d (patch) | |
tree | a659b934d5d4e7b798e277a339b4c7bfb76cca86 | |
parent | dc14ea4ca1cec8a4fc1ac23c07151e11cd2ac72b (diff) | |
parent | 3a4351f9b777009d8e226d26125f758861ddcbb3 (diff) | |
download | vaadin-framework-d60b9419e3dc638b172a288ee4b4787d20f8d44d.tar.gz vaadin-framework-d60b9419e3dc638b172a288ee4b4787d20f8d44d.zip |
Merge branch '7.1' into testbench4
Change-Id: Id93edf5895131bb13d1be792d0914ace399e713c
60 files changed, 1592 insertions, 155 deletions
diff --git a/.gitignore b/.gitignore index d5899b3d58..30c8597b16 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ /WebContent/VAADIN/gwt-unitCache* WebContent/VAADIN/vaadinPush.js +WebContent/VAADIN/vaadinPush.debug.js # /WebContent/WEB-INF/ /WebContent/WEB-INF/classes diff --git a/WebContent/VAADIN/jquery.atmosphere.js b/WebContent/VAADIN/jquery.atmosphere.js index b6f86d428a..520e63d7cb 100644 --- a/WebContent/VAADIN/jquery.atmosphere.js +++ b/WebContent/VAADIN/jquery.atmosphere.js @@ -1199,6 +1199,9 @@ jQuery.atmosphere = function() { * @param response */ function _trackMessageSize(message, request, response) { + if (message.length === 0) + return true; + if (request.trackMessageLength) { // If we have found partial message, prepend them. @@ -1207,36 +1210,29 @@ jQuery.atmosphere = function() { } var messages = []; - var messageLength = 0; var messageStart = message.indexOf(request.messageDelimiter); - while (messageStart != -1) { - messageLength = jQuery.trim(message.substring(messageLength, messageStart)); - message = message.substring(messageStart + request.messageDelimiter.length, message.length); - - // Stop search if there is not enough characters remaining (wait for next part to arrive) - if (message.length == 0 || message.length < messageLength) break; - - // Find start of a possibly existing subsequent message from the remaining data - messageStart = message.substring(messageLength).indexOf(request.messageDelimiter); - - // Store the completely received message - messages.push(message.substring(0, messageLength)); - } - - if (messages.length == 0 || (message.length != 0 && messageLength != message.length)){ - if (messageStart == -1) { - // http://dev.vaadin.com/ticket/12197 - // partialMessage must contain length header of next message - // it starts at the end of the last message - response.partialMessage = message.substring(messageLength); + while (messageStart !== -1) { + var str = jQuery.trim(message.substring(0, messageStart)); + var messageLength = parseInt(str, 10); + if (isNaN(messageLength)) + throw 'message length "' + str + '" is not a number'; + messageStart += request.messageDelimiter.length; + if (messageStart + messageLength > message.length) { + // message not complete, so there is no trailing messageDelimiter + messageStart = -1; } else { - response.partialMessage = messageLength + request.messageDelimiter + message ; + // message complete, so add it + messages.push(message.substring(messageStart, messageStart + messageLength)); + // remove consumed characters + message = message.substring(messageStart + messageLength, message.length); + messageStart = message.indexOf(request.messageDelimiter); } - } else { - response.partialMessage = ""; } - if (messages.length != 0) { + /* keep any remaining data */ + response.partialMessage = message; + + if (messages.length !== 0) { response.responseBody = messages.join(request.messageDelimiter); response.messages = messages; return false; diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png b/WebContent/VAADIN/themes/reindeer/window/img/black/close-pressed.png Binary files differindex 07a837b619..07a837b619 100644 --- a/WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/close-pressed.png diff --git a/WebContent/VAADIN/themes/tests-calendar/styles.css b/WebContent/VAADIN/themes/tests-calendar/styles.css index 7a37fcfdaf..e3fa107751 100644 --- a/WebContent/VAADIN/themes/tests-calendar/styles.css +++ b/WebContent/VAADIN/themes/tests-calendar/styles.css @@ -93,4 +93,12 @@ .v-calendar .v-calendar-event-color4 .v-calendar-event-content { border-color: #cd6a00; background-color: #faa345; - }
\ No newline at end of file + } + +/** + * Hide time in captions + */ +.v-calendar-event-hide-time .v-calendar-event-caption>span, +.v-calendar-event-hide-time .v-calendar-event-caption>br { + display: none; +}
\ No newline at end of file diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html index f3ec68e41f..387a5121cb 100644 --- a/WebContent/release-notes.html +++ b/WebContent/release-notes.html @@ -63,10 +63,9 @@ <h2 id="overview">Overview of Vaadin @version@ Release</h2> <p> - Vaadin @version@ is a feature release that includes a number - of enhancements as well as important bug fixes, as listed in - the <a href="#changelog">change log</a> below. You can also - view the <a + Vaadin @version@ is a maintenance release that includes a number + of important bug fixes, as listed in the <a href="#changelog">change + log</a> below. You can also view the <a href="http://dev.vaadin.com/query?status=closed&resolution=fixed&milestone=Vaadin+@version@&order=priority">list of the closed issues</a> at the Vaadin developer's site. </p> @@ -140,7 +139,7 @@ <li><b>TableQuery</b> now supports schemas and catalogs</li> </ul> - <p>Tools have been updated for Vaadin @vaadin-minor@ with + <p>Tools have been updated for Vaadin @version-minor@ with the following changes:</p> <ul> @@ -574,12 +573,12 @@ </p> <ul> - <li>Mozilla Firefox 18-21</li> + <li>Mozilla Firefox 18-23</li> <li>Mozilla Firefox 17 ESR</li> <li>Internet Explorer 8-10</li> <li>Safari 6</li> <li>Opera 12</li> - <li>Google Chrome 23-27</li> + <li>Google Chrome 23-29</li> </ul> <p> diff --git a/all/build.xml b/all/build.xml index 080346403f..1b48721ecd 100644 --- a/all/build.xml +++ b/all/build.xml @@ -122,4 +122,4 @@ <!-- No tests for this zip.. --> </target> -</project>
\ No newline at end of file +</project> diff --git a/build/common.xml b/build/common.xml index 39f3ee7d9f..9220cf6612 100644 --- a/build/common.xml +++ b/build/common.xml @@ -32,7 +32,7 @@ <target name="init-taskdefs" depends="ivy-configure" unless="deps.initialized"> <echo>Loading Ant tasks</echo> - <ivy:resolve file="${project.root}/build/ivy/ivy.xml" conf="taskdefs" /> + <ivy:resolve log="download-only" file="${project.root}/build/ivy/ivy.xml" conf="taskdefs" /> <ivy:cachepath pathid="taskdefs.classpath" conf="taskdefs" /> <taskdef resource="emma_ant.properties" classpathref="taskdefs.classpath" /> diff --git a/build/ide.xml b/build/ide.xml index 45130e9d01..2605bf2cdd 100755 --- a/build/ide.xml +++ b/build/ide.xml @@ -13,17 +13,17 @@ <property name="work.dir" location="work" /> <echo>Using gwt files from ${gwt.user.classes} and ${gwt.dev.classes}</echo> - <ivy:resolve file="client-compiler/ivy.xml" conf="ide" /> + <ivy:resolve log="download-only" file="client-compiler/ivy.xml" conf="ide" /> <ivy:cachepath pathid="client-compiler.deps" conf="ide" /> - <ivy:resolve file="server/ivy.xml" conf="ide" /> + <ivy:resolve log="download-only" file="server/ivy.xml" conf="ide" /> <ivy:cachepath pathid="server.deps" conf="ide" /> - <ivy:resolve file="client/ivy.xml" conf="ide" /> + <ivy:resolve log="download-only" file="client/ivy.xml" conf="ide" /> <ivy:cachepath pathid="client.deps" conf="ide" /> - <ivy:resolve file="shared/ivy.xml" conf="ide" /> + <ivy:resolve log="download-only" file="shared/ivy.xml" conf="ide" /> <ivy:cachepath pathid="shared.deps" conf="ide" /> - <ivy:resolve file="uitest/ivy.xml" conf="ide" /> + <ivy:resolve log="download-only" file="uitest/ivy.xml" conf="ide" /> <ivy:cachepath pathid="uitest.deps" conf="ide" /> - <ivy:resolve file="theme-compiler/ivy.xml" conf="ide" /> + <ivy:resolve log="download-only" file="theme-compiler/ivy.xml" conf="ide" /> <ivy:cachepath pathid="theme-compiler.deps" conf="ide" /> <path id="classpath"> @@ -104,7 +104,7 @@ <echo>Compiling ${module} to ${module.output.dir} with parameters -logLevel TRACE -style ${style} -localWorkers ${localWorkers} -strict ${extraParams}</echo> - <!--<ivy:resolve inline="true" organisation="javax.validation" module="validation-api" + <!--<ivy:resolve log="download-only" inline="true" organisation="javax.validation" module="validation-api" revision="1.0.0.GA"/> --> <!-- compile the module --> @@ -134,6 +134,7 @@ </target> <target name="vaadinPush.js"> <property name="vaadinPush.js.output" location="WebContent/VAADIN/vaadinPush.js" /> + <property name="vaadinPush.debug.js.output" location="WebContent/VAADIN/vaadinPush.debug.js" /> <loadfile srcfile="WebContent/VAADIN/jquery-1.7.2.js" property="jquery.js.contents" /> <loadfile srcfile="WebContent/VAADIN/jquery.atmosphere.js" property="jquery.atmosphere.js.contents" /> @@ -146,5 +147,7 @@ </filterchain> </loadfile> <echo file="${vaadinPush.js.output}">${vaadinPush.js.contents}</echo> + <!-- This vaadinPush.js is not obfuscated so the debug version is the same--> + <copy file="${vaadinPush.js.output}" tofile="${vaadinPush.debug.js.output}"/> </target> -</project>
\ No newline at end of file +</project> diff --git a/buildhelpers/build.xml b/buildhelpers/build.xml index 7d9e23683e..28eb55e6e8 100644 --- a/buildhelpers/build.xml +++ b/buildhelpers/build.xml @@ -48,4 +48,4 @@ <!--<antcall target="common.test.run" /> --> <echo>WHAT? No JUnit tests for ${module.name}!</echo> </target> -</project>
\ No newline at end of file +</project> diff --git a/client-compiled/build.xml b/client-compiled/build.xml index fa5ff262db..883d3c88ea 100644 --- a/client-compiled/build.xml +++ b/client-compiled/build.xml @@ -40,7 +40,7 @@ <target name="compile-module-cache"> <fail unless="module" message="You must give the module to compile in the 'module' parameter" /> - <ivy:resolve resolveid="common" conf="compile-module" /> + <ivy:resolve log="download-only" resolveid="common" conf="compile-module" /> <ivy:cachepath pathid="classpath.compile.widgetset" conf="compile-module" /> <echo>Creating gwtar files for ${module} in ${gwtar.dir}</echo> @@ -63,7 +63,7 @@ <property name="localWorkers" value="2" /> <property name="extraParams" value="" /> - <ivy:resolve resolveid="common" conf="compile-module" /> + <ivy:resolve log="download-only" resolveid="common" conf="compile-module" /> <ivy:cachepath pathid="classpath.compile.widgetset" conf="compile-module" /> <mkdir dir="${module.output.dir}" /> diff --git a/client-compiler/build.xml b/client-compiler/build.xml index 67c3c318a7..97189fc437 100644 --- a/client-compiler/build.xml +++ b/client-compiler/build.xml @@ -66,4 +66,4 @@ <echo>WHAT? No tests for ${module.name}!</echo> </target> -</project>
\ No newline at end of file +</project> diff --git a/client/build.xml b/client/build.xml index dec8b84a18..19ec05b28a 100644 --- a/client/build.xml +++ b/client/build.xml @@ -72,4 +72,4 @@ <antcall target="common.test.run" /> </target> -</project>
\ No newline at end of file +</project> diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java index 20ccd45173..3cecb09dc1 100644 --- a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java +++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.Scheduler; import com.google.gwt.user.client.Command; +import com.vaadin.client.ApplicationConfiguration; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ApplicationConnection.CommunicationErrorHandler; import com.vaadin.client.ResourceLoader; @@ -473,7 +474,13 @@ public class AtmospherePushConnection implements PushConnection { if (isAtmosphereLoaded()) { command.execute(); } else { - final String pushJs = ApplicationConstants.VAADIN_PUSH_JS; + final String pushJs; + if (ApplicationConfiguration.isProductionMode()) { + pushJs = ApplicationConstants.VAADIN_PUSH_JS; + } else { + pushJs = ApplicationConstants.VAADIN_PUSH_DEBUG_JS; + } + VConsole.log("Loading " + pushJs); ResourceLoader.get().loadScript( connection.getConfiguration().getVaadinDirUrl() + pushJs, diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java index 7aac581008..9b14fbbb37 100644 --- a/client/src/com/vaadin/client/ui/VFilterSelect.java +++ b/client/src/com/vaadin/client/ui/VFilterSelect.java @@ -916,6 +916,27 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, } } + /** + * TextBox variant used as input element for filter selects, which prevents + * selecting text when disabled. + * + * @since 7.1.5 + */ + public class FilterSelectTextBox extends TextBox { + + /** + * Overridden to avoid selecting text when text input is disabled + */ + @Override + public void setSelectionRange(int pos, int length) { + if (textInputEnabled) { + super.setSelectionRange(pos, length); + } else { + super.setSelectionRange(getValue().length(), 0); + } + } + } + @Deprecated public static final FilteringMode FILTERINGMODE_OFF = FilteringMode.OFF; @Deprecated @@ -938,21 +959,10 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, * <p> * For internal use only. May be removed or replaced in the future. */ - public final TextBox tb = new TextBox() { - - // Overridden to avoid selecting text when text input is disabled - @Override - public void setSelectionRange(int pos, int length) { - if (textInputEnabled) { - super.setSelectionRange(pos, length); - } else { - super.setSelectionRange(getValue().length(), 0); - } - }; - }; + public final TextBox tb; /** For internal use only. May be removed or replaced in the future. */ - public final SuggestionPopup suggestionPopup = new SuggestionPopup(); + public final SuggestionPopup suggestionPopup; /** * Used when measuring the width of the popup @@ -1096,9 +1106,12 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, private boolean textInputEnabled = true; /** - * Default constructor + * Default constructor. */ public VFilterSelect() { + tb = createTextBox(); + suggestionPopup = createSuggestionPopup(); + selectedItemIcon.setStyleName("v-icon"); selectedItemIcon.addLoadHandler(new LoadHandler() { @@ -1136,6 +1149,32 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, setStyleName(CLASSNAME); } + /** + * This method will create the TextBox used by the VFilterSelect instance. + * It is invoked during the Constructor and should only be overridden if a + * custom TextBox shall be used. The overriding method cannot use any + * instance variables. + * + * @since 7.1.5 + * @return TextBox instance used by this VFilterSelect + */ + protected TextBox createTextBox() { + return new FilterSelectTextBox(); + } + + /** + * This method will create the SuggestionPopup used by the VFilterSelect + * instance. It is invoked during the Constructor and should only be + * overridden if a custom SuggestionPopup shall be used. The overriding + * method cannot use any instance variables. + * + * @since 7.1.5 + * @return SuggestionPopup instance used by this VFilterSelect + */ + protected SuggestionPopup createSuggestionPopup() { + return new SuggestionPopup(); + } + @Override public void setStyleName(String style) { super.setStyleName(style); diff --git a/client/src/com/vaadin/client/ui/VRichTextArea.java b/client/src/com/vaadin/client/ui/VRichTextArea.java index 8a6ba3fc1e..0b2c1e574c 100644 --- a/client/src/com/vaadin/client/ui/VRichTextArea.java +++ b/client/src/com/vaadin/client/ui/VRichTextArea.java @@ -399,19 +399,19 @@ public class VRichTextArea extends Composite implements Field, KeyPressHandler, BrowserInfo browser = BrowserInfo.get(); String result = getValue(); if (browser.isFirefox()) { - if ("<br>".equals(html)) { + if ("<br>".equals(result)) { result = ""; } } else if (browser.isWebkit()) { - if ("<div><br></div>".equals(html)) { + if ("<br>".equals(result) || "<div><br></div>".equals(result)) { result = ""; } } else if (browser.isIE()) { - if ("<P> </P>".equals(html)) { + if ("<P> </P>".equals(result)) { result = ""; } } else if (browser.isOpera()) { - if ("<br>".equals(html) || "<p><br></p>".equals(html)) { + if ("<br>".equals(result) || "<p><br></p>".equals(result)) { result = ""; } } diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java index b69d2a4fe7..39de122694 100644 --- a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java @@ -185,10 +185,15 @@ public class DateCellDayEvent extends FocusableHTML implements * If false, event is so small that caption must be in time-row */ private void updateCaptions(boolean bigMode) { - String separator = bigMode ? "<br />" : ": "; - caption.setInnerHTML("<span>" + calendarEvent.getTimeAsText() - + "</span>" + separator - + Util.escapeHTML(calendarEvent.getCaption())); + String innerHtml; + String escapedCaption = Util.escapeHTML(calendarEvent.getCaption()); + String timeAsText = calendarEvent.getTimeAsText(); + if (bigMode) { + innerHtml = "<span>" + timeAsText + "</span><br />" + escapedCaption; + } else { + innerHtml = "<span>" + timeAsText + "<span>:</span></span> " + escapedCaption; + } + caption.setInnerHTML(innerHtml); eventContent.setInnerHTML(""); } diff --git a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java index 45cc18f3dc..e0dc0d51df 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java @@ -375,6 +375,7 @@ public abstract class AbstractOrderedLayoutConnector extends Slot slot = Util.findWidget( (com.google.gwt.user.client.Element) element, Slot.class); if (slot != null && slot.getCaptionElement() != null + && slot.getParent() == getWidget() && slot.getCaptionElement().isOrHasChild(element)) { ComponentConnector connector = Util.findConnectorFor(slot .getWidget()); diff --git a/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java b/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java index 20dfc74c69..d2576eb133 100644 --- a/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java +++ b/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java @@ -104,9 +104,9 @@ public class RichTextAreaConnector extends AbstractFieldConnector implements if (getConnection() != null && getConnectorId() != null) { final String html = getWidget().getSanitizedValue(); if (!html.equals(cachedValue)) { + cachedValue = html; getConnection().updateVariable(getConnectorId(), "text", html, getState().immediate); - getWidget().setValue(html); } } }; diff --git a/common.xml b/common.xml index 993802404f..88fbc49196 100644 --- a/common.xml +++ b/common.xml @@ -14,7 +14,7 @@ <ivy:settings file="${vaadin.basedir}/ivysettings.xml" /> <ivy:settings file="${vaadin.basedir}/ivysettings.xml" id="ivysettings" /> - <ivy:resolve file="${vaadin.basedir}/ivy-taskdefs.xml" conf="taskdefs" log="quiet" /> + <ivy:resolve log="download-only" file="${vaadin.basedir}/ivy-taskdefs.xml" conf="taskdefs" /> <ivy:cachepath pathid="taskdefs.classpath" conf="taskdefs" /> <taskdef uri="antlib:net.sf.antcontrib" resource="net/sf/antcontrib/antlib.xml" classpathref="taskdefs.classpath" /> <!-- ant contrib for Maven integration --> @@ -354,12 +354,12 @@ <target name="dependencies" description="Resolves dependencies needed by this module"> <property name='conf' value="build, build-provided" /> - <ivy:resolve resolveid="common" conf="${conf}" /> + <ivy:resolve log="download-only" resolveid="common" conf="${conf}" /> <ivy:cachepath pathid="classpath.compile.dependencies" conf="${conf}" /> </target> <target name="dependencies.test" description="Resolves dependencies needed by test"> - <ivy:resolve resolveid="common" conf="test" /> + <ivy:resolve log="download-only" resolveid="common" conf="test" /> <ivy:cachepath pathid="classpath.test.dependencies" conf="test" /> </target> @@ -372,7 +372,7 @@ <fail unless="result.dir" message="No result.dir parameter given" /> <property name="conf" value="*(public)" /> - <ivy:resolve conf="${conf}" /> + <ivy:resolve log="download-only" conf="${conf}" /> <ivy:publish settingsref="ivysettings" conf="${conf}" resolver="build-temp" overwrite="true" forcedeliver="true"> <!-- <artifacts pattern="${result.dir}/[artifact]-[revision].[ext]" /> --> @@ -384,7 +384,7 @@ <target name="publish.to.local.maven"> <property name="conf" value="*(public)" /> - <ivy:resolve conf="${conf}" /> + <ivy:resolve log="download-only" conf="${conf}" /> <ivy:publish conf="${conf}" resolver="local-maven" overwrite="true"> </ivy:publish> </target> diff --git a/ivysettings-publish.xml b/ivysettings-publish.xml index 4f96b16b72..aeb759d59b 100644 --- a/ivysettings-publish.xml +++ b/ivysettings-publish.xml @@ -28,4 +28,4 @@ </resolvers> -</ivysettings>
\ No newline at end of file +</ivysettings> diff --git a/ivysettings.xml b/ivysettings.xml index 3db0a8a06d..fa08f3d0bb 100644 --- a/ivysettings.xml +++ b/ivysettings.xml @@ -44,4 +44,4 @@ </modules> -</ivysettings>
\ No newline at end of file +</ivysettings> diff --git a/pom-template.xml b/pom-template.xml index 1716abc82a..2a9c1f0f28 100644 --- a/pom-template.xml +++ b/pom-template.xml @@ -96,4 +96,4 @@ </snapshots> </repository> </repositories> -</project>
\ No newline at end of file +</project> diff --git a/publish.xml b/publish.xml index c2b8369b15..dbe5961cb6 100644 --- a/publish.xml +++ b/publish.xml @@ -42,7 +42,7 @@ <target name="publish.module.to.download.site"> <fail unless="module" message="No module to publish defined" /> - <ivy:resolve file="${module}/ivy.xml" /> + <ivy:resolve log="download-only" file="${module}/ivy.xml" /> <ivy:publish publishivy="false" settingsref="publish.settings" conf="*(public)" resolver="sftp-publish"> <artifacts pattern="${ivy.settings.dir}/result/artifacts/[revision]/[module]/[artifact]-[revision](-[classifier]).[ext]" /> </ivy:publish> @@ -53,7 +53,7 @@ <property file="${gpg.passphrase.file}" /> <!-- Ivy should be able to handle this but this does not work at - the moment <ivy:resolve file="${module}/ivy.xml" /> <ivy:publish pubrevision="7.0-SNAPSHOT" + the moment <ivy:resolve log="download-only" file="${module}/ivy.xml" /> <ivy:publish pubrevision="7.0-SNAPSHOT" publishivy="false" settingsref="publish.settings" conf="*(public)" resolver="sonatype"> <artifacts pattern="${ivy.settings.dir}/result/artifacts/${vaadin.version}/[module]/[artifact]-${vaadin.version}(-[classifier]).[ext]" /> </ivy:publish> --> @@ -98,4 +98,4 @@ <sysproperty key="sources" value="${sources.file}" /> </artifact:mvn> </target> -</project>
\ No newline at end of file +</project> diff --git a/push/build.xml b/push/build.xml index ead3a0226e..1cbe23d75b 100644 --- a/push/build.xml +++ b/push/build.xml @@ -11,6 +11,7 @@ <property name="module.symbolic" value="com.vaadin.push" /> <property name="result.dir" location="result" /> <property name="vaadinPush.js" location="${result.dir}/js/VAADIN/vaadinPush.js" /> + <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 and jquery.atmosphere.js --> <property name="atmosphere.version" value="1.0.14.vaadin4" /> @@ -21,13 +22,12 @@ <union id="jar.includes"> <fileset dir="${result.dir}/js"> <include name="VAADIN/vaadinPush.js" /> + <include name="VAADIN/vaadinPush.debug.js" /> </fileset> </union> <target name="vaadinPush.js"> <mkdir dir="${result.dir}/js/VAADIN" /> - <property name="vaadinPush.js.output" location="${result.dir}/js/VAADIN/vaadinPush.js" /> - <property name="vaadinPush.js.combined.output" location="${result.dir}/js/VAADIN/push.combined.js" /> <loadfile srcfile="${vaadin.basedir}/WebContent/VAADIN/jquery-${jquery.version}.js" property="jquery.js.contents" /> <loadfile srcfile="${vaadin.basedir}/WebContent/VAADIN/jquery.atmosphere.js" property="jquery.atmosphere.js.contents" /> @@ -39,15 +39,17 @@ </replacetokens> </filterchain> </loadfile> - <echo file="${vaadinPush.js.combined.output}">${vaadinPush.js.contents}</echo> + + <!-- Non-obfuscated version for debugging --> + <echo file="${vaadinPush.debug.js}">${vaadinPush.js.contents}</echo> <!-- Minify --> <ivy:retrieve organisation="com.yahoo.platform.yui" module="yuicompressor" revision="2.4.7" inline="true" type="jar" pattern="${result.dir}/compressor.jar" /> <java jar="${result.dir}/compressor.jar" fork="true"> <arg value="-v" /> <arg value="-o" /> - <arg file="${vaadinPush.js.output}" /> - <arg file="${vaadinPush.js.combined.output}" /> + <arg file="${vaadinPush.js}" /> + <arg file="${vaadinPush.debug.js}" /> </java> </target> @@ -70,4 +72,4 @@ <target name="test" depends="checkstyle"> </target> -</project>
\ No newline at end of file +</project> diff --git a/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java b/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java index cddf2d8a42..7c252d78f2 100644 --- a/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java +++ b/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java @@ -44,6 +44,10 @@ public class DateToSqlDateConverter implements Converter<Date, java.sql.Date> { + targetType.getName() + ")"); } + if (value == null) { + return null; + } + return new java.sql.Date(value.getTime()); } @@ -56,6 +60,11 @@ public class DateToSqlDateConverter implements Converter<Date, java.sql.Date> { + getPresentationType().getName() + " (targetType was " + targetType.getName() + ")"); } + + if (value == null) { + return null; + } + return new Date(value.getTime()); } diff --git a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java index 39c8365076..b54a630e04 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java @@ -663,7 +663,9 @@ public class TableQuery extends AbstractTransactionalQuery implements } catch (SQLException ignore) { } finally { try { - tables.close(); + if (tables != null) { + tables.close(); + } } catch (SQLException ignore) { } } diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index 91a9e41522..a73ca3d985 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -625,7 +625,8 @@ public abstract class AbstractClientConnector implements ClientConnector, String[] parts = path.split("/", 2); String key = parts[0]; - getSession().lock(); + VaadinSession session = getSession(); + session.lock(); try { ConnectorResource resource = (ConnectorResource) getResource(key); if (resource == null) { @@ -633,7 +634,7 @@ public abstract class AbstractClientConnector implements ClientConnector, } stream = resource.getStream(); } finally { - getSession().unlock(); + session.unlock(); } stream.writeResponse(request, response); return true; diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index b21fdb0b74..0a4949ffa7 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -388,9 +388,16 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { if (context.getPushMode().isEnabled()) { // Load client-side dependencies for push support + String pushJS = vaadinLocation; + if (context.getRequest().getService().getDeploymentConfiguration() + .isProductionMode()) { + pushJS += ApplicationConstants.VAADIN_PUSH_JS; + } else { + pushJS += ApplicationConstants.VAADIN_PUSH_DEBUG_JS; + } + fragmentNodes.add(new Element(Tag.valueOf("script"), "").attr( - "type", "text/javascript").attr("src", - vaadinLocation + ApplicationConstants.VAADIN_PUSH_JS)); + "type", "text/javascript").attr("src", pushJS)); } String bootstrapLocation = vaadinLocation + "vaadinBootstrap.js"; diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java index ab91ee021c..8c379abe06 100644 --- a/server/src/com/vaadin/server/Constants.java +++ b/server/src/com/vaadin/server/Constants.java @@ -146,6 +146,11 @@ public interface Constants { // Widget set parameter name static final String PARAMETER_WIDGETSET = "widgetset"; + /** + * @deprecated As of 7.1, this message is no longer used and might be + * removed from the code. + */ + @Deprecated static final String ERROR_NO_UI_FOUND = "No UIProvider returned a UI for the request."; static final String DEFAULT_THEME_NAME = "reindeer"; diff --git a/server/src/com/vaadin/server/FileDownloader.java b/server/src/com/vaadin/server/FileDownloader.java index 9b49ad8edd..bd7d9caafd 100644 --- a/server/src/com/vaadin/server/FileDownloader.java +++ b/server/src/com/vaadin/server/FileDownloader.java @@ -129,7 +129,9 @@ public class FileDownloader extends AbstractExtension { // Ignore if it isn't for us return false; } - getSession().lock(); + VaadinSession session = getSession(); + + session.lock(); DownloadStream stream; try { @@ -151,7 +153,7 @@ public class FileDownloader extends AbstractExtension { stream.setContentType("application/octet-stream;charset=UTF-8"); } } finally { - getSession().unlock(); + session.unlock(); } stream.writeResponse(request, response); return true; diff --git a/server/src/com/vaadin/server/VaadinPortletService.java b/server/src/com/vaadin/server/VaadinPortletService.java index c7fc5a23bd..194c9c88a9 100644 --- a/server/src/com/vaadin/server/VaadinPortletService.java +++ b/server/src/com/vaadin/server/VaadinPortletService.java @@ -201,6 +201,10 @@ public class VaadinPortletService extends VaadinService { // been rendered, e.g. portlet on one page sends an event to a // portlet on another page and then moves the user to that page. return true; + } else if (PortletUIInitHandler.isUIInitRequest(request)) { + // In some cases, the RenderRequest seems to be cached, causing the + // first request be the one triggered by vaadinBootstrap.js. + return true; } return false; } diff --git a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java index e325550c4b..b9d4955b12 100644 --- a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java +++ b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java @@ -221,8 +221,6 @@ public class AtmospherePushConnection implements PushConnection { } resource.resume(); - assert !resource.getBroadcaster().getAtmosphereResources() - .contains(resource); resource = null; } diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java index 1c50f79349..81dd00084d 100644 --- a/server/src/com/vaadin/server/communication/PushHandler.java +++ b/server/src/com/vaadin/server/communication/PushHandler.java @@ -173,7 +173,8 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter }; /** - * Callback used when a connection is closed by the client. + * Callback used when a connection is closed, either deliberately or because + * an error occurred. */ private final PushEventCallback disconnectCallback = new PushEventCallback() { @Override @@ -192,8 +193,7 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter if (!pushMode.isEnabled()) { /* * The client is expected to close the connection after push - * mode has been set to disabled, just clean up some stuff - * and be done with it + * mode has been set to disabled. */ getLogger().log(Level.FINER, "Connection closed for resource {0}", id); @@ -248,24 +248,17 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter } catch (ServiceException e) { getLogger().log(Level.SEVERE, "Could not get session. This should never happen", e); + return; } catch (SessionExpiredException e) { SystemMessages msg = service.getSystemMessages( ServletPortletHelper.findLocale(null, null, vaadinRequest), vaadinRequest); - try { - resource.getResponse() - .getWriter() - .write(VaadinService - .createCriticalNotificationJSON( - msg.getSessionExpiredCaption(), - msg.getSessionExpiredMessage(), - null, msg.getSessionExpiredURL())); - } catch (IOException e1) { - getLogger() - .log(Level.WARNING, - "Failed to notify client about unavailable session", - e); - } + sendNotificationAndDisconnect( + resource, + VaadinService.createCriticalNotificationJSON( + msg.getSessionExpiredCaption(), + msg.getSessionExpiredMessage(), null, + msg.getSessionExpiredURL())); return; } @@ -275,21 +268,15 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter // Sets UI.currentInstance final UI ui = service.findUI(vaadinRequest); if (ui == null) { - // This a request through an already open push connection to - // a UI which no longer exists. - resource.getResponse() - .getWriter() - .write(UidlRequestHandler.getUINotFoundErrorJSON( - service, vaadinRequest)); - // End the connection - resource.resume(); - return; + sendNotificationAndDisconnect(resource, + UidlRequestHandler.getUINotFoundErrorJSON(service, + vaadinRequest)); + } else { + callback.run(resource, ui); } - - callback.run(resource, ui); } catch (IOException e) { - getLogger().log(Level.INFO, - "An error occured while writing a push response", e); + getLogger().log(Level.WARNING, "Error writing a push response", + e); } finally { session.unlock(); } @@ -323,9 +310,10 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter AtmosphereResource resource = event.getResource(); String id = resource.uuid(); - if (event.isCancelled()) { - // Disconnected for whatever reason, handle in onDisconnect() as - // it's more reliable + if (event.isCancelled() || event.isResumedOnTimeout()) { + getLogger().log(Level.FINER, + "Cancelled connection for resource {0}", id); + disconnect(event); } else if (event.isResuming()) { // A connection that was suspended earlier was resumed (committed to // the client.) Should only happen if the transport is JSONP or @@ -364,13 +352,31 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter public void onDisconnect(AtmosphereResourceEvent event) { // Log event on trace level super.onDisconnect(event); - callWithUi(event.getResource(), disconnectCallback); + disconnect(event); + } + + @Override + public void onThrowable(AtmosphereResourceEvent event) { + getLogger().log(Level.SEVERE, "Exception in push connection", + event.throwable()); + disconnect(event); + } + + @Override + public void onResume(AtmosphereResourceEvent event) { + // Log event on trace level + super.onResume(event); + disconnect(event); } @Override public void destroy() { } + private void disconnect(AtmosphereResourceEvent event) { + callWithUi(event.getResource(), disconnectCallback); + } + /** * Sends a refresh message to the given atmosphere resource. Uses an * AtmosphereResource instead of an AtmospherePushConnection even though it @@ -396,6 +402,22 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter } } + /** + * Tries to send a critical notification to the client and close the + * connection. Does nothing if the connection is already closed. + */ + private static void sendNotificationAndDisconnect( + AtmosphereResource resource, String notificationJson) { + // TODO Implemented differently from sendRefreshAndDisconnect + try { + resource.getResponse().getWriter().write(notificationJson); + resource.resume(); + } catch (Exception e) { + getLogger().log(Level.FINEST, + "Failed to send critical notification to client", e); + } + } + private static final Logger getLogger() { return Logger.getLogger(PushHandler.class.getName()); } diff --git a/server/src/com/vaadin/ui/AbstractMedia.java b/server/src/com/vaadin/ui/AbstractMedia.java index 97947b568d..d7d593c29e 100644 --- a/server/src/com/vaadin/ui/AbstractMedia.java +++ b/server/src/com/vaadin/ui/AbstractMedia.java @@ -30,6 +30,7 @@ import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinSession; import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.AbstractMediaState; import com.vaadin.shared.ui.MediaControl; @@ -90,7 +91,8 @@ public abstract class AbstractMedia extends AbstractComponent { DownloadStream stream; - getSession().lock(); + VaadinSession session = getSession(); + session.lock(); try { List<URLReference> sources = getState().sources; @@ -108,7 +110,7 @@ public abstract class AbstractMedia extends AbstractComponent { .getResource(reference); stream = resource.getStream(); } finally { - getSession().unlock(); + session.unlock(); } stream.writeResponse(request, response); diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index 3507e6b0a5..bd2b7828de 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -564,6 +564,8 @@ public class Table extends AbstractSelect implements Action.Container, private List<Throwable> exceptionsDuringCachePopulation = new ArrayList<Throwable>(); + private boolean isBeingPainted; + /* Table constructors */ /** @@ -3166,6 +3168,15 @@ public class Table extends AbstractSelect implements Action.Container, @Override public void paintContent(PaintTarget target) throws PaintException { + isBeingPainted = true; + try { + doPaintContent(target); + } finally { + isBeingPainted = false; + } + } + + private void doPaintContent(PaintTarget target) throws PaintException { /* * Body actions - Actions which has the target null and can be invoked * by right clicking on the table body. @@ -4394,6 +4405,10 @@ public class Table extends AbstractSelect implements Action.Container, @Override public void containerItemSetChange(Container.ItemSetChangeEvent event) { + if (isBeingPainted) { + return; + } + super.containerItemSetChange(event); // super method clears the key map, must inform client about this to @@ -4416,6 +4431,10 @@ public class Table extends AbstractSelect implements Action.Container, @Override public void containerPropertySetChange( Container.PropertySetChangeEvent event) { + if (isBeingPainted) { + return; + } + disableContentRefreshing(); super.containerPropertySetChange(event); diff --git a/server/src/com/vaadin/util/CurrentInstance.java b/server/src/com/vaadin/util/CurrentInstance.java index b97bab3d8a..4c62ef49be 100644 --- a/server/src/com/vaadin/util/CurrentInstance.java +++ b/server/src/com/vaadin/util/CurrentInstance.java @@ -17,10 +17,14 @@ package com.vaadin.util; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinResponse; @@ -36,6 +40,10 @@ import com.vaadin.ui.UI; * when {@link VaadinSession#access(Runnable)} or {@link UI#access(Runnable)} is * used. * <p> + * Please note that the instances are stored using {@link WeakReference}. This + * means that the a current instance value may suddenly disappear if there a no + * other references to the object. + * <p> * Currently the framework uses the following instances: * </p> * <p> @@ -49,7 +57,9 @@ import com.vaadin.ui.UI; * @since 7.0.0 */ public class CurrentInstance implements Serializable { - private final Object instance; + private static final Object NULL_OBJECT = new Object(); + + private final WeakReference<Object> instance; private final boolean inheritable; private static InheritableThreadLocal<Map<Class<?>, CurrentInstance>> instances = new InheritableThreadLocal<Map<Class<?>, CurrentInstance>>() { @@ -74,7 +84,7 @@ public class CurrentInstance implements Serializable { }; private CurrentInstance(Object instance, boolean inheritable) { - this.instance = instance; + this.instance = new WeakReference<Object>(instance); this.inheritable = inheritable; } @@ -93,12 +103,49 @@ public class CurrentInstance implements Serializable { } CurrentInstance currentInstance = map.get(type); if (currentInstance != null) { - return type.cast(currentInstance.instance); + Object value = currentInstance.instance.get(); + if (value == null) { + /* + * This is believed to never actually happen since the + * 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 + * this assumingly rare case slightly more efficient, but would + * significantly increase the complexity of the code for + * maintaining a separate ReferenceQueue for each Thread. + */ + removeStaleInstances(map); + + if (map.isEmpty()) { + instances.remove(); + } + + return null; + } + return type.cast(value); } else { return null; } } + private static void removeStaleInstances(Map<Class<?>, CurrentInstance> map) { + for (Iterator<Entry<Class<?>, CurrentInstance>> iterator = map + .entrySet().iterator(); iterator.hasNext();) { + Entry<Class<?>, CurrentInstance> entry = iterator.next(); + Object instance = entry.getValue().instance.get(); + if (instance == null) { + iterator.remove(); + getLogger().log(Level.FINE, + "CurrentInstance for {0} has been garbage collected.", + entry.getKey()); + } + } + } + /** * Sets the current instance of the given type. * @@ -183,9 +230,37 @@ public class CurrentInstance implements Serializable { * A Class -> CurrentInstance map to set as current instances */ public static void restoreInstances(Map<Class<?>, CurrentInstance> old) { + boolean removeStale = false; for (Class c : old.keySet()) { CurrentInstance ci = old.get(c); - set(c, ci.instance, ci.inheritable); + Object v = ci.instance.get(); + if (v == null) { + removeStale = true; + } else if (v == NULL_OBJECT) { + /* + * NULL_OBJECT is used to identify objects that are null when + * #setCurrent(UI) or #setCurrent(VaadinSession) are called on a + * 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 + * UI.accessSynchronously() save these values before calling + * setCurrent(UI), which stores UI=null in the map it returns. + * This map will be restored after UI.accessSync(), which, + * unless it respects null values, will just leave the wrong UI + * instance registered. + */ + set(c, null, ci.inheritable); + } else { + set(c, v, ci.inheritable); + } + } + + if (removeStale) { + removeStaleInstances(old); } } @@ -207,12 +282,21 @@ public class CurrentInstance implements Serializable { return Collections.emptyMap(); } else { Map<Class<?>, CurrentInstance> copy = new HashMap<Class<?>, CurrentInstance>(); + boolean removeStale = false; for (Class<?> c : map.keySet()) { CurrentInstance ci = map.get(c); - if (ci.inheritable || !onlyInheritable) { + if (ci.instance.get() == null) { + removeStale = true; + } else if (ci.inheritable || !onlyInheritable) { copy.put(c, ci); } } + if (removeStale) { + removeStaleInstances(map); + if (map.isEmpty()) { + instances.remove(); + } + } return copy; } } @@ -231,7 +315,8 @@ public class CurrentInstance implements Serializable { */ public static Map<Class<?>, CurrentInstance> setCurrent(UI ui) { Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>(); - old.put(UI.class, new CurrentInstance(UI.getCurrent(), true)); + old.put(UI.class, + new CurrentInstance(getSameOrNullObject(UI.getCurrent()), true)); UI.setCurrent(ui); old.putAll(setCurrent(ui.getSession())); return old; @@ -252,10 +337,10 @@ 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(VaadinSession.getCurrent(), true)); - old.put(VaadinService.class, - new CurrentInstance(VaadinService.getCurrent(), true)); + old.put(VaadinSession.class, new CurrentInstance( + getSameOrNullObject(VaadinSession.getCurrent()), true)); + old.put(VaadinService.class, new CurrentInstance( + getSameOrNullObject(VaadinService.getCurrent()), true)); VaadinService service = null; if (session != null) { service = session.getService(); @@ -266,4 +351,20 @@ public class CurrentInstance implements Serializable { 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/tests/data/converter/TestDateToSqlDateConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestDateToSqlDateConverter.java new file mode 100644 index 0000000000..685404ded6 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/data/converter/TestDateToSqlDateConverter.java @@ -0,0 +1,25 @@ +package com.vaadin.tests.data.converter; + +import java.util.Date; +import java.util.Locale; + +import junit.framework.TestCase; + +import com.vaadin.data.util.converter.DateToSqlDateConverter; + +public class TestDateToSqlDateConverter extends TestCase { + + DateToSqlDateConverter converter = new DateToSqlDateConverter(); + + public void testNullConversion() { + assertEquals(null, + converter.convertToModel(null, java.sql.Date.class, null)); + } + + public void testValueConversion() { + Date testDate = new Date(100, 0, 1); + long time = testDate.getTime(); + assertEquals(testDate, converter.convertToModel(new java.sql.Date(time), + java.sql.Date.class, Locale.ENGLISH)); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java b/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java index 2a7e456fcb..e5420b8921 100644 --- a/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java +++ b/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java @@ -65,6 +65,7 @@ public class TestClassesSerializable extends TestCase { "com\\.vaadin\\.util\\.ConnectorHelper", // "com\\.vaadin\\.server\\.VaadinSession\\$FutureAccess", // "com\\.vaadin\\.external\\..*", // + "com\\.vaadin\\.util\\.WeakValueMap.*", // }; /** diff --git a/server/tests/src/com/vaadin/util/TestCurrentInstance.java b/server/tests/src/com/vaadin/util/TestCurrentInstance.java index da986abe31..1910172aa8 100644 --- a/server/tests/src/com/vaadin/util/TestCurrentInstance.java +++ b/server/tests/src/com/vaadin/util/TestCurrentInstance.java @@ -15,14 +15,21 @@ */ package com.vaadin.util; +import static org.junit.Assert.assertNull; + import java.lang.reflect.Field; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import junit.framework.Assert; - +import org.easymock.EasyMock; +import org.junit.Assert; import org.junit.Test; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; +import com.vaadin.ui.UI; + public class TestCurrentInstance { @Test @@ -142,4 +149,46 @@ public class TestCurrentInstance { public void testInheritedClearedAfterRemove() { } + + private static class UIStoredInCurrentInstance extends UI { + @Override + protected void init(VaadinRequest request) { + } + } + + private static class SessionStoredInCurrentInstance extends VaadinSession { + public SessionStoredInCurrentInstance(VaadinService service) { + super(service); + } + } + + @Test + public void testRestoringNullUIWorks() throws Exception { + // First make sure current instance is empty + CurrentInstance.clearAll(); + + // Then store a new UI in there + Map<Class<?>, CurrentInstance> old = CurrentInstance + .setCurrent(new UIStoredInCurrentInstance()); + + // Restore the old values and assert that the UI is null again + CurrentInstance.restoreInstances(old); + assertNull(CurrentInstance.get(UI.class)); + } + + @Test + public void testRestoringNullSessionWorks() throws Exception { + // First make sure current instance is empty + CurrentInstance.clearAll(); + + // Then store a new session in there + Map<Class<?>, CurrentInstance> old = CurrentInstance + .setCurrent(new SessionStoredInCurrentInstance(EasyMock + .createNiceMock(VaadinService.class))); + + // Restore the old values and assert that the session is null again + CurrentInstance.restoreInstances(old); + assertNull(CurrentInstance.get(VaadinSession.class)); + assertNull(CurrentInstance.get(VaadinService.class)); + } } diff --git a/shared/build.xml b/shared/build.xml index b0533550d4..73a0a8df7f 100644 --- a/shared/build.xml +++ b/shared/build.xml @@ -61,4 +61,4 @@ <echo>WHAT? No tests for ${module.name}!</echo> </target> -</project>
\ No newline at end of file +</project> diff --git a/shared/src/com/vaadin/shared/ApplicationConstants.java b/shared/src/com/vaadin/shared/ApplicationConstants.java index d7de435735..104f3047a8 100644 --- a/shared/src/com/vaadin/shared/ApplicationConstants.java +++ b/shared/src/com/vaadin/shared/ApplicationConstants.java @@ -79,6 +79,14 @@ public class ApplicationConstants implements Serializable { public static final String VAADIN_PUSH_JS = "vaadinPush.js"; /** + * 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"; + + /** * Name of the parameter used to transmit the CSRF token. */ public static final String CSRF_TOKEN_PARAMETER = "v-csrfToken"; diff --git a/theme-compiler/build.xml b/theme-compiler/build.xml index 1fd78209c3..9141605fbf 100644 --- a/theme-compiler/build.xml +++ b/theme-compiler/build.xml @@ -57,4 +57,4 @@ <antcall target="common.test.run" /> </target> -</project>
\ No newline at end of file +</project> diff --git a/theme-compiler/ivy.xml b/theme-compiler/ivy.xml index 6a7528ceeb..5bcdbb54cb 100644 --- a/theme-compiler/ivy.xml +++ b/theme-compiler/ivy.xml @@ -41,7 +41,7 @@ <!-- Testing libs --> <dependency org="junit" name="junit" rev="4.5" - conf="test -> default" /> + conf="ide,test -> default" /> <!-- Internally used, for now --> <dependency org="com.carrotsearch" name="smartsprites" diff --git a/themes/build.xml b/themes/build.xml index 5ee7ec4ec7..93959e054f 100644 --- a/themes/build.xml +++ b/themes/build.xml @@ -22,7 +22,7 @@ </union> <target name="compile-themes"> - <ivy:resolve resolveid="common" conf="build" /> + <ivy:resolve log="download-only" resolveid="common" conf="build" /> <ivy:cachepath pathid="classpath.compile.theme" conf="build" /> <antcall target="compile-theme"> @@ -69,7 +69,7 @@ <target name="compile-theme" depends="copy-theme"> <fail unless="theme" message="You must give the theme name to compile in the 'theme' parameter" /> - <ivy:resolve resolveid="common" conf="compile-theme" /> + <ivy:resolve log="download-only" resolveid="common" conf="compile-theme" /> <ivy:cachepath pathid="classpath.compile.theme" conf="compile-theme" /> <ivy:cachepath pathid="classpath.runtime.theme" conf="build" /> diff --git a/uitest/build.xml b/uitest/build.xml index aaf891925e..bd0f49ae1e 100644 --- a/uitest/build.xml +++ b/uitest/build.xml @@ -19,7 +19,7 @@ <!-- This is copied from common.xml to be able to add server.test.source to the source path --> - <ivy:resolve resolveid="common" conf="build, build-provided" /> + <ivy:resolve log="download-only" resolveid="common" conf="build, build-provided" /> <ivy:cachepath pathid="classpath.compile.dependencies" conf="build, build-provided" /> </target> @@ -97,7 +97,7 @@ <property name="deps.dir" location="${result.dir}/deps" /> <property name="src" location="${result.dir}/../src" /> - <ivy:resolve resolveid="common" conf="build" /> + <ivy:resolve log="download-only" resolveid="common" conf="build" /> <ivy:cachepath pathid="classpath.runtime.dependencies" conf="build" /> <delete dir="${deps.dir}" /> @@ -120,6 +120,7 @@ <include name="statictestfiles/**" /> <include name="VAADIN/themes/tests-*/**" /> <include name="VAADIN/themes/reindeer-tests/**" /> + <include name="VAADIN/jquery.atmosphere.js" /> <include name="WEB-INF/*.xml" /> <include name="WEB-INF/web.xml.2.4" /> </fileset> diff --git a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java index 99f9707479..5ab2134cdb 100644 --- a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java +++ b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java @@ -51,7 +51,7 @@ import com.vaadin.launcher.util.BrowserLauncher; */ public class DevelopmentServerLauncher { - private static final String KEYSTORE = "src/com/vaadin/launcher/keystore"; + private static final String KEYSTORE = "uitest/src/com/vaadin/launcher/keystore"; private final static int serverPort = 8888; /** diff --git a/uitest/src/com/vaadin/tests/components/calendar/TestHideTimeAndSeparator.html b/uitest/src/com/vaadin/tests/components/calendar/TestHideTimeAndSeparator.html new file mode 100644 index 0000000000..38a6be24ce --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/TestHideTimeAndSeparator.html @@ -0,0 +1,47 @@ +<?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://localhost:8888/" /> + <title>TestHideTimeAndSeparator</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> + <thead> + <tr><td rowspan="1" colspan="3">TestHideTimeAndSeparator</td></tr> + </thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.TestHideTimeAndSeparator?restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertNotVisible</td> + <td>vaadin=runcomvaadintestscomponentscalendarTestHideTimeAndSeparator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[35]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertNotVisible</td> + <td>vaadin=runcomvaadintestscomponentscalendarTestHideTimeAndSeparator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[34]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertVisible</td> + <td>vaadin=runcomvaadintestscomponentscalendarTestHideTimeAndSeparator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[35]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertVisible</td> + <td>vaadin=runcomvaadintestscomponentscalendarTestHideTimeAndSeparator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[34]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarTestHideTimeAndSeparator::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[35]/domChild[0]/domChild[0]</td> + <td>8:00 AM:</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/TestHideTimeAndSeparator.java b/uitest/src/com/vaadin/tests/components/calendar/TestHideTimeAndSeparator.java new file mode 100644 index 0000000000..b8b55048f9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/TestHideTimeAndSeparator.java @@ -0,0 +1,108 @@ +package com.vaadin.tests.components.calendar; + +import com.vaadin.annotations.Theme; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.ui.components.calendar.event.CalendarEventProvider; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +@Theme("tests-calendar") +public class TestHideTimeAndSeparator extends AbstractTestUI { + + class GenericEvent implements CalendarEvent { + private final Date start; + private final Date end; + private final String caption; + private final boolean hideTime; + + public GenericEvent(Date start, Date end, String caption, boolean hideTime) { + this.start = start; + this.end = end; + this.caption = caption; + this.hideTime = hideTime; + } + + @Override + public Date getStart() { + return start; + } + + @Override + public Date getEnd() { + return end; + } + + @Override + public String getCaption() { + return caption; + } + + @Override + public String getDescription() { + return "This is a " + caption; + } + + @Override + public String getStyleName() { + return hideTime ? "hide-time" : null; + } + + @Override + public boolean isAllDay() { + return false; + } + + } + + CalendarEvent shortEventHidden = new GenericEvent(makeDate(2013, 1, 2, 8, 0), makeDate(2013, 1, 2, 8, 30), "Short event", true); + CalendarEvent longEventHidden = new GenericEvent(makeDate(2013, 1, 2, 10, 0), makeDate(2013, 1, 2, 12, 0), "Long event", true); + CalendarEvent shortEvent = new GenericEvent(makeDate(2013, 1, 3, 8, 0), makeDate(2013, 1, 3, 8, 30), "Short event", false); + CalendarEvent longEvent = new GenericEvent(makeDate(2013, 1, 3, 10, 0), makeDate(2013, 1, 3, 12, 0), "Long event", false); + + @Override + protected void setup(VaadinRequest request) { + Calendar cal = new Calendar(); + cal.setWidth("100%"); + cal.setHeight("500px"); + + cal.setLocale(Locale.US); + + cal.addEvent(shortEventHidden); + cal.addEvent(longEventHidden); + cal.addEvent(shortEvent); + cal.addEvent(longEvent); + + cal.setStartDate(makeDate(2013, 1, 1)); + cal.setEndDate(makeDate(2013, 1, 7)); + cal.setFirstVisibleHourOfDay(7); + + addComponent(cal); + } + + @Override + protected String getTestDescription() { + return "The time should be hideable by CSS"; + } + + @Override + protected Integer getTicketNumber() { + return 12460; + } + + private Date makeDate(int year, int month, int day, int hour, int minute) { + java.util.Calendar juc = java.util.Calendar.getInstance(); + juc.set(year, month, day, hour, minute); + return juc.getTime(); + } + private Date makeDate(int year, int month, int day) { + java.util.Calendar juc = java.util.Calendar.getInstance(); + juc.set(year, month, day); + return juc.getTime(); + } +} diff --git a/uitest/src/com/vaadin/tests/components/orderedlayout/NestedLayoutCaptionTooltip.html b/uitest/src/com/vaadin/tests/components/orderedlayout/NestedLayoutCaptionTooltip.html new file mode 100644 index 0000000000..4f574a92c7 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/orderedlayout/NestedLayoutCaptionTooltip.html @@ -0,0 +1,33 @@ +<?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="" /> +<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.applicationcontext.CloseSession?restartApplication&debug</td> + <td></td> +</tr> +<!-- Show tooltip for the Events caption --> +<tr> + <td>showTooltip</td> + <td>//div[@id='gwt-uid-4']/span</td> + <td></td> +</tr> +<!-- Verify that there's no error notification --> +<tr> + <td>assertElementNotPresent</td> + <td>vaadin=runcomvaadintestsapplicationcontextCloseSession::Root/VNotification[0]</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.html b/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.html new file mode 100644 index 0000000000..236f184a41 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.html @@ -0,0 +1,47 @@ +<?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://localhost:8888/" /> +<title>ContainerSizeChangeDuringTablePaint</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">ContainerSizeChangeDuringTablePaint</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.table.ContainerSizeChangeDuringTablePaint?restartApplication</td> + <td></td> +</tr> +<tr> + <td>verifyTextPresent</td> + <td>Add an item and also trigger an ItemSetChangeEvent in Container during next Table paint</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentstableContainerSizeChangeDuringTablePaint::PID_SaddRow/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>verifyTextPresent</td> + <td>Event was fired successfully.</td> + <td></td> +</tr> +<tr> + <td>verifyTextPresent</td> + <td>New</td> + <td></td> +</tr> +<tr> + <td>verifyTextPresent</td> + <td>Row</td> + <td></td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.java b/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.java new file mode 100644 index 0000000000..0f385176bf --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.java @@ -0,0 +1,163 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.components.table; + +import java.util.Iterator; + +import com.vaadin.data.Container; +import com.vaadin.data.Item; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +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.Table; + +public class ContainerSizeChangeDuringTablePaint extends AbstractTestUI { + + /** + * A test {@link Table} that simply acts a hook for when Vaadin starts + * painting the Table. + */ + private static class WobblyTable extends Table { + /** + * A flag for the container to know when it should change the size. + */ + boolean isBeingPainted; + + public WobblyTable(String caption, Container dataSource) { + super(caption, dataSource); + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + isBeingPainted = true; + try { + super.paintContent(target); + } finally { + isBeingPainted = false; + } + } + } + + /** + * A {@link Container} that can change its size while its + * {@link WobblyTable} is being painted. + */ + private static class WobblyContainer extends IndexedContainer { + private WobblyTable table = null; + private boolean shouldSabotageNextPaint = false; + + public void setWobblyTable(WobblyTable table) { + this.table = table; + } + + @Override + public int size() { + if (table != null && table.isBeingPainted + && shouldSabotageNextPaint) { + try { + System.out.print("Firing item set change " + + "event during Table paint... "); + fireItemSetChange(); + System.out.println("Success!"); + } finally { + shouldSabotageNextPaint = false; + } + } + + return super.size(); + } + + public void sabotageNextPaint() { + shouldSabotageNextPaint = true; + } + } + + private static final Object PROPERTY_1 = new Object(); + private static final Object PROPERTY_2 = new Object(); + private static final Object PROPERTY_3 = new Object(); + + @Override + protected void setup(VaadinRequest request) { + final WobblyContainer container = generateContainer(); + final WobblyTable table = createTable(container); + container.setWobblyTable(table); + + addComponent(table); + Button button = new Button( + "Add an item and also trigger an ItemSetChangeEvent in Container during next Table paint", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + + // we need this to simply trigger a table paint. + addItem(container, "A", "New", "Row"); + container.sabotageNextPaint(); + + event.getButton().setCaption( + "Event was fired successfully."); + } + }); + button.setId("addRow"); + addComponent(button); + } + + private static WobblyTable createTable(IndexedContainer container) { + WobblyTable t = new WobblyTable(null, container); + t.setColumnHeader(PROPERTY_1, "Property 1"); + t.setColumnHeader(PROPERTY_2, "Property 2"); + t.setColumnHeader(PROPERTY_3, "Property 3"); + t.setPageLength(container.size() + 1); + return t; + } + + private static WobblyContainer generateContainer() { + WobblyContainer c = new WobblyContainer(); + c.addContainerProperty(PROPERTY_1, String.class, null); + c.addContainerProperty(PROPERTY_2, String.class, null); + c.addContainerProperty(PROPERTY_3, String.class, null); + addItem(c, "Hello", "World", "!"); + return c; + } + + @SuppressWarnings("unchecked") + private static void addItem(Container c, Object... properties) { + Object itemId = c.addItem(); + Item item = c.getItem(itemId); + int i = 0; + Iterator<?> propIter = c.getContainerPropertyIds().iterator(); + while (propIter.hasNext()) { + Object propertyId = propIter.next(); + item.getItemProperty(propertyId).setValue(properties[i]); + i++; + } + } + + @Override + protected String getTestDescription() { + return "Container changes during the painting cycle should not lead to an IllegalStateException"; + } + + @Override + protected Integer getTicketNumber() { + return 12258; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.html b/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.html new file mode 100644 index 0000000000..fe030f2ea7 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.html @@ -0,0 +1,52 @@ +<?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="" /> +<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.ui.CurrentUiRetained?restartApplication</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>2000</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::PID_SLog_row_3</td> + <td>1. Correct UI.getCurrent before GC: true</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::PID_SLog_row_2</td> + <td>2. Correct UI.getCurrent after GC: true</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::PID_SLog_row_1</td> + <td>3. GC probe available before GC: true</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::PID_SLog_row_0</td> + <td>4. GC probe available after GC: false</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.java b/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.java new file mode 100644 index 0000000000..b0127e3a75 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.components.ui; + +import java.util.ArrayList; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.UI; +import com.vaadin.util.CurrentInstance; + +public class CurrentUiRetained extends AbstractTestUIWithLog { + public static class GcProbe { + + } + + @Override + protected void setup(VaadinRequest request) { + final ArrayList<UI> uiLog = new ArrayList<UI>(); + final ArrayList<Boolean> probeLog = new ArrayList<Boolean>(); + + final Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + uiLog.add(UI.getCurrent()); + + GcProbe gcProbe = new GcProbe(); + CurrentInstance.set(GcProbe.class, gcProbe); + probeLog.add(CurrentInstance.get(GcProbe.class) != null); + gcProbe = null; + + Thread.sleep(500l); + System.gc(); + Thread.sleep(500l); + + probeLog.add(CurrentInstance.get(GcProbe.class) != null); + uiLog.add(UI.getCurrent()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + thread.start(); + + addComponent(new Button("Show result", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + try { + thread.join(); + + log("Correct UI.getCurrent before GC: " + + (uiLog.get(0) == CurrentUiRetained.this)); + log("Correct UI.getCurrent after GC: " + + (uiLog.get(1) == CurrentUiRetained.this)); + + log("GC probe available before GC: " + probeLog.get(0)); + log("GC probe available after GC: " + probeLog.get(1)); + + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + })); + } + + @Override + protected String getTestDescription() { + return "Tests that garbage collection removes stale CurrentInstance values while retaining values not collected."; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(12509); + } + +} diff --git a/uitest/src/com/vaadin/tests/performance/ThreadMemoryLeaksTest.java b/uitest/src/com/vaadin/tests/performance/ThreadMemoryLeaksTest.java new file mode 100644 index 0000000000..5ffc7141af --- /dev/null +++ b/uitest/src/com/vaadin/tests/performance/ThreadMemoryLeaksTest.java @@ -0,0 +1,57 @@ +package com.vaadin.tests.performance; + +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Label; + +public class ThreadMemoryLeaksTest extends AbstractTestUI { + + public static class Worker { + long value = 0; + private TimerTask task = new TimerTask() { + @Override + public void run() { + value++; + } + }; + private final Timer timer = new Timer(true); + + public Worker() { + timer.scheduleAtFixedRate(task, new Date(), 1000); + } + } + + int workers = 0; + Label label; + + @Override + protected void setup(VaadinRequest request) { + label = new Label(String.format("%d workers", workers)); + addComponent(label); + addComponent(new Button("Add worker", new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + new Worker(); + workers++; + label.setValue(String.format("%d workers", workers)); + } + })); + } + + @Override + protected String getTestDescription() { + return "Inherited ThreadLocals should not leak memory. Clicking the " + + "button starts a new thread, after which memory consumption " + + "can be checked with visualvm"; + } + + @Override + protected Integer getTicketNumber() { + return 12401; + } +} diff --git a/uitest/src/com/vaadin/tests/push/EnableDisablePush.html b/uitest/src/com/vaadin/tests/push/EnableDisablePush.html new file mode 100644 index 0000000000..87dfa8428f --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/EnableDisablePush.html @@ -0,0 +1,97 @@ +<?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="" /> +<title>EnableDisablePush</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">EnableDisablePush</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/EnableDisablePush?restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td> + <td>1. Push enabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td> + <td>3. Push disabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td> + <td>5. Poll enabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td> + <td>7. Push enabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td> + <td>9. Poll disabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td> + <td>11. Push disabled, polling enabled</td> +</tr> +<tr> + <td>pause</td> + <td>3500</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td> + <td>16. Polling disabled, push enabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td> + <td>18. Push disabled</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/push/EnableDisablePush.java b/uitest/src/com/vaadin/tests/push/EnableDisablePush.java new file mode 100644 index 0000000000..1911c66c2d --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/EnableDisablePush.java @@ -0,0 +1,119 @@ +package com.vaadin.tests.push; + +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.communication.PushMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.util.Log; +import com.vaadin.ui.Button; +import com.vaadin.ui.UIDetachedException; + +public class EnableDisablePush extends AbstractTestUI { + + private int c = 0; + + private Log log = new Log(15); + + private final Timer timer = new Timer(true); + + private final class CounterTask extends TimerTask { + + @Override + public void run() { + + try { + while (true) { + TimeUnit.MILLISECONDS.sleep(1000); + + access(new Runnable() { + @Override + public void run() { + log.log("Counter = " + c++); + if (c == 3) { + log.log("Disabling polling, enabling push"); + getPushConfiguration().setPushMode( + PushMode.AUTOMATIC); + setPollInterval(-1); + log.log("Polling disabled, push enabled"); + } + } + }); + } + } catch (InterruptedException e) { + } catch (UIDetachedException e) { + } + } + }; + + @Override + protected void setup(VaadinRequest request) { + + getPushConfiguration().setPushMode(PushMode.AUTOMATIC); + log.log("Push enabled"); + + addComponent(new Button("Disable push", new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + log.log("Disabling push"); + getPushConfiguration().setPushMode(PushMode.DISABLED); + log.log("Push disabled"); + } + })); + + addComponent(new Button("Enable push", new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + log.log("Enabling push"); + getPushConfiguration().setPushMode(PushMode.AUTOMATIC); + log.log("Push enabled"); + } + })); + + addComponent(new Button("Disable polling", new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + log.log("Disabling poll"); + setPollInterval(-1); + log.log("Poll disabled"); + } + })); + + addComponent(new Button("Enable polling", new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + log.log("Enabling poll"); + setPollInterval(1000); + log.log("Poll enabled"); + } + })); + + addComponent(new Button( + "Disable push, re-enable from background thread", + new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + log.log("Disabling push, enabling polling"); + getPushConfiguration().setPushMode(PushMode.DISABLED); + setPollInterval(1000); + timer.schedule(new CounterTask(), new Date()); + log.log("Push disabled, polling enabled"); + } + })); + + addComponent(log); + } + + @Override + protected String getTestDescription() { + return "Test dynamically enablind and disabling push"; + } + + @Override + protected Integer getTicketNumber() { + return 12226; + } +} diff --git a/uitest/src/com/vaadin/tests/push/TablePushStreaming.java b/uitest/src/com/vaadin/tests/push/TablePushStreaming.java new file mode 100644 index 0000000000..de824eef3a --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/TablePushStreaming.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +/** + * + */ +package com.vaadin.tests.push; + +import com.vaadin.annotations.Push; +import com.vaadin.data.Container; +import com.vaadin.data.Item; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.ui.Transport; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Table; + +/** + * + * @since + * @author Vaadin Ltd + */ +@Push(transport = Transport.STREAMING) +public class TablePushStreaming extends AbstractTestUI { + + private int iteration = 1; + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#setup(com.vaadin.server. + * VaadinRequest) + */ + @Override + protected void setup(VaadinRequest request) { + final Table t = new Table("The table"); + t.setContainerDataSource(generateContainer(10, 10, iteration++)); + t.setSizeFull(); + Runnable r = new Runnable() { + + @Override + public void run() { + for (int i = 0; i < 99; i++) { + try { + Thread.sleep(200); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + access(new Runnable() { + + @Override + public void run() { + t.setContainerDataSource(generateContainer( + t.getVisibleColumns().length, t.size(), + iteration++)); + } + + }); + } + + } + }; + Thread tr = new Thread(r); + tr.start(); + + setContent(t); + } + + /** + * @param iter + * @since + * @return + */ + private Container generateContainer(int rows, int cols, int iter) { + IndexedContainer ic = new IndexedContainer(); + for (int col = 1; col <= cols; col++) { + ic.addContainerProperty("Property" + col, String.class, ""); + } + + for (int row = 0; row < rows; row++) { + Item item = ic.addItem("row" + row); + for (int col = 1; col <= cols; col++) { + item.getItemProperty("Property" + col).setValue( + "Row " + row + " col " + col + "(" + iter + ")"); + } + + } + + return ic; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription() + */ + @Override + protected String getTestDescription() { + return "Test that pushes Table data at a high pace to detect possible problems in the streaming protocol"; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber() + */ + @Override + protected Integer getTicketNumber() { + return null; + } + +} diff --git a/uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.html b/uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.html new file mode 100644 index 0000000000..89dd7d4e78 --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.html @@ -0,0 +1,26 @@ +<?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.162: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.push.TrackMessageSizeUnitTests?restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestspushTrackMessageSizeUnitTests::PID_SLog_row_0</td> + <td>1. All tests run</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.java b/uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.java new file mode 100644 index 0000000000..062698edf5 --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.java @@ -0,0 +1,152 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.push; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.servlet.ServletContext; + +import org.apache.commons.io.IOUtils; +import org.json.JSONArray; +import org.json.JSONException; + +import com.vaadin.annotations.JavaScript; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinServletService; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.JavaScriptFunction; + +// Load vaadinPush.js so that jQueryVaadin is defined +@JavaScript("vaadin://vaadinPush.js") +public class TrackMessageSizeUnitTests extends AbstractTestUIWithLog { + + private String testMethod = "function testSequence(expected, data) {\n" + + " var request = {trackMessageLength: true, messageDelimiter: '|'};\n" + + " var response = {partialMessage: ''};\n" + + " var messages = [];\n" + + " for(var i = 0; i < data.length; i++) {\n" + + " if (!_trackMessageSize(data[i], request, response))\n" + + " messages = messages.concat(response.messages);\n" + + " }\n" + + " if (JSON.stringify(expected) != JSON.stringify(messages)) {\n" + + " if (console && typeof console.error == 'function') console.error('Expected', expected, 'but got', messages, 'for', data);\n" + + " logToServer('Test failed, see javascript console for details.');\n" + + " }" + "}\n"; + + @Override + protected void setup(VaadinRequest request) { + String methodImplementation = findMethodImplementation(); + getPage().getJavaScript().addFunction("logToServer", + new JavaScriptFunction() { + @Override + public void call(JSONArray arguments) throws JSONException { + String message = arguments.getString(0); + log(message); + } + }); + + getPage().getJavaScript().execute( + methodImplementation + testMethod + buildTestCase()); + } + + private String buildTestCase() { + // Could maybe express the cases in java and generate JS? + return "testSequence(['a', 'b'], ['1|a1|b', '']);\n" + + "testSequence(['a', 'b'], ['1|a1|', 'b']);\n" + + "testSequence(['a', 'b'], ['1|a1', '|b']);\n" + + "testSequence(['a', 'b'], ['1|a', '1|b']);\n" + + "testSequence(['a', 'b'], ['1|a', '', '1|b']);\n" + + "testSequence(['a|', '|b'], ['2|a|2||b']);\n" + + "testSequence(['a|', 'b'], ['2|a|', '', '1|b']);\n" + + "testSequence(['a|', 'b'], ['2|a|', '1|b']);\n" + + "testSequence(['a|', 'b'], ['2|a|1', '|b']);\n" + + "testSequence(['a|', 'b'], ['2|a|1|', 'b']);\n" + + "testSequence([' ', 'b'], ['1| 1|b']);\n" + + "testSequence([' ', 'b'], ['1| ','1|b']);\n" + + "testSequence([' ', 'b'], ['1|',' 1|b']);\n" + + "logToServer('All tests run')\n"; + } + + private String findMethodImplementation() { + String filename = "/VAADIN/jquery.atmosphere.js"; + URL resourceURL = findResourceURL(filename, + (VaadinServletService) VaadinService.getCurrent()); + if (resourceURL == null) { + log("Can't find " + filename); + return null; + } + + try { + String string = IOUtils.toString(resourceURL); + + // Find the function inside the script content + int startIndex = string.indexOf("function _trackMessageSize"); + if (startIndex == -1) { + log("function not found"); + return null; + } + + // Assumes there's a /** comment before the next function + int endIndex = string.indexOf("/**", startIndex); + if (endIndex == -1) { + log("End of function not found"); + return null; + } + + string = string.substring(startIndex, endIndex); + string = string.replaceAll("jQuery", "jQueryVaadin"); + return string; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private URL findResourceURL(String filename, VaadinServletService service) { + ServletContext sc = service.getServlet().getServletContext(); + URL resourceUrl; + try { + resourceUrl = sc.getResource(filename); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + if (resourceUrl == null) { + // try if requested file is found from classloader + + // strip leading "/" otherwise stream from JAR wont work + if (filename.startsWith("/")) { + filename = filename.substring(1); + } + + resourceUrl = service.getClassLoader().getResource(filename); + } + return resourceUrl; + } + + @Override + protected String getTestDescription() { + return "Unit tests for _trackMessageSize in jquery.atmosphere.js. Implemented with testbench and a full Vaadin server side since the testing requires some file mangling."; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(12468); + } + +} diff --git a/uitest/vaadin-server.xml b/uitest/vaadin-server.xml index f2c3c60959..4e84a6f238 100644 --- a/uitest/vaadin-server.xml +++ b/uitest/vaadin-server.xml @@ -6,7 +6,7 @@ <target name="deploy-and-start"> <fail unless="war.file" message="No war file given in 'war.file'" /> - <ivy:resolve file="${dir}/ivy.xml" /> + <ivy:resolve log="download-only" file="${dir}/ivy.xml" /> <ivy:cachepath pathid="classpath.jetty" conf="jetty-run" /> <java classname="org.mortbay.jetty.runner.Runner" fork="yes" output="${vaadin.basedir}/result/jetty.java.out" resultproperty="resultCode"> <arg value="--port" /> |