summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonatan Kronqvist <jonatan@vaadin.com>2013-09-16 16:16:51 +0300
committerJonatan Kronqvist <jonatan@vaadin.com>2013-09-16 16:16:51 +0300
commitd60b9419e3dc638b172a288ee4b4787d20f8d44d (patch)
treea659b934d5d4e7b798e277a339b4c7bfb76cca86
parentdc14ea4ca1cec8a4fc1ac23c07151e11cd2ac72b (diff)
parent3a4351f9b777009d8e226d26125f758861ddcbb3 (diff)
downloadvaadin-framework-d60b9419e3dc638b172a288ee4b4787d20f8d44d.tar.gz
vaadin-framework-d60b9419e3dc638b172a288ee4b4787d20f8d44d.zip
Merge branch '7.1' into testbench4
Change-Id: Id93edf5895131bb13d1be792d0914ace399e713c
-rw-r--r--.gitignore1
-rw-r--r--WebContent/VAADIN/jquery.atmosphere.js46
-rw-r--r--WebContent/VAADIN/themes/reindeer/window/img/black/close-pressed.png (renamed from WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png)bin359 -> 359 bytes
-rw-r--r--WebContent/VAADIN/themes/tests-calendar/styles.css10
-rw-r--r--WebContent/release-notes.html13
-rw-r--r--all/build.xml2
-rw-r--r--build/common.xml2
-rwxr-xr-xbuild/ide.xml19
-rw-r--r--buildhelpers/build.xml2
-rw-r--r--client-compiled/build.xml4
-rw-r--r--client-compiler/build.xml2
-rw-r--r--client/build.xml2
-rw-r--r--client/src/com/vaadin/client/communication/AtmospherePushConnection.java9
-rw-r--r--client/src/com/vaadin/client/ui/VFilterSelect.java67
-rw-r--r--client/src/com/vaadin/client/ui/VRichTextArea.java8
-rw-r--r--client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java13
-rw-r--r--client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java1
-rw-r--r--client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java2
-rw-r--r--common.xml10
-rw-r--r--ivysettings-publish.xml2
-rw-r--r--ivysettings.xml2
-rw-r--r--pom-template.xml2
-rw-r--r--publish.xml6
-rw-r--r--push/build.xml14
-rw-r--r--server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java9
-rw-r--r--server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java4
-rw-r--r--server/src/com/vaadin/server/AbstractClientConnector.java5
-rw-r--r--server/src/com/vaadin/server/BootstrapHandler.java11
-rw-r--r--server/src/com/vaadin/server/Constants.java5
-rw-r--r--server/src/com/vaadin/server/FileDownloader.java6
-rw-r--r--server/src/com/vaadin/server/VaadinPortletService.java4
-rw-r--r--server/src/com/vaadin/server/communication/AtmospherePushConnection.java2
-rw-r--r--server/src/com/vaadin/server/communication/PushHandler.java90
-rw-r--r--server/src/com/vaadin/ui/AbstractMedia.java6
-rw-r--r--server/src/com/vaadin/ui/Table.java19
-rw-r--r--server/src/com/vaadin/util/CurrentInstance.java121
-rw-r--r--server/tests/src/com/vaadin/tests/data/converter/TestDateToSqlDateConverter.java25
-rw-r--r--server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java1
-rw-r--r--server/tests/src/com/vaadin/util/TestCurrentInstance.java53
-rw-r--r--shared/build.xml2
-rw-r--r--shared/src/com/vaadin/shared/ApplicationConstants.java8
-rw-r--r--theme-compiler/build.xml2
-rw-r--r--theme-compiler/ivy.xml2
-rw-r--r--themes/build.xml4
-rw-r--r--uitest/build.xml5
-rw-r--r--uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java2
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/TestHideTimeAndSeparator.html47
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/TestHideTimeAndSeparator.java108
-rw-r--r--uitest/src/com/vaadin/tests/components/orderedlayout/NestedLayoutCaptionTooltip.html33
-rw-r--r--uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.html47
-rw-r--r--uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.java163
-rw-r--r--uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.html52
-rw-r--r--uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.java93
-rw-r--r--uitest/src/com/vaadin/tests/performance/ThreadMemoryLeaksTest.java57
-rw-r--r--uitest/src/com/vaadin/tests/push/EnableDisablePush.html97
-rw-r--r--uitest/src/com/vaadin/tests/push/EnableDisablePush.java119
-rw-r--r--uitest/src/com/vaadin/tests/push/TablePushStreaming.java126
-rw-r--r--uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.html26
-rw-r--r--uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.java152
-rw-r--r--uitest/vaadin-server.xml2
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
index 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
Binary files differ
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>&nbsp;</P>".equals(html)) {
+ if ("<P>&nbsp;</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&amp;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" />