aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WebContent/VAADIN/jquery.atmosphere.js46
-rw-r--r--WebContent/VAADIN/themes/tests-calendar/styles.css10
-rw-r--r--WebContent/release-notes.html13
-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--server/src/com/vaadin/server/AbstractClientConnector.java5
-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/ui/AbstractMedia.java6
-rw-r--r--server/src/com/vaadin/ui/Table.java19
-rw-r--r--server/src/com/vaadin/util/CurrentInstance.java2
-rw-r--r--server/src/com/vaadin/util/WeakValueMap.java230
-rw-r--r--server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java1
-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/table/ContainerSizeChangeDuringTablePaint.html47
-rw-r--r--uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeDuringTablePaint.java163
-rw-r--r--uitest/src/com/vaadin/tests/performance/ThreadMemoryLeaksTest.java57
-rw-r--r--uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.html26
-rw-r--r--uitest/src/com/vaadin/tests/push/TrackMessageSizeUnitTests.java152
20 files changed, 916 insertions, 48 deletions
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/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/client/src/com/vaadin/client/ui/VRichTextArea.java b/client/src/com/vaadin/client/ui/VRichTextArea.java
index 8a6ba3fc1e..34693ce392 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 ("<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/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/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/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..a1c543117d 100644
--- a/server/src/com/vaadin/util/CurrentInstance.java
+++ b/server/src/com/vaadin/util/CurrentInstance.java
@@ -60,7 +60,7 @@ public class CurrentInstance implements Serializable {
return null;
}
- Map<Class<?>, CurrentInstance> value = new HashMap<Class<?>, CurrentInstance>();
+ Map<Class<?>, CurrentInstance> value = new WeakValueMap<Class<?>, CurrentInstance>();
// Copy all inheritable values to child map
for (Entry<Class<?>, CurrentInstance> e : parentValue.entrySet()) {
diff --git a/server/src/com/vaadin/util/WeakValueMap.java b/server/src/com/vaadin/util/WeakValueMap.java
new file mode 100644
index 0000000000..1134594cba
--- /dev/null
+++ b/server/src/com/vaadin/util/WeakValueMap.java
@@ -0,0 +1,230 @@
+/*
+ * 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.util;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A Map holding weak references to its values. It is internally backed by a
+ * normal HashMap and all values are stored as WeakReferences. Garbage collected
+ * entries are removed when touched.
+ * <p>
+ * <em>Note</em> this class is not serializable.
+ *
+ * @author Vaadin Ltd
+ * @since 7.1.4
+ */
+public class WeakValueMap<K, V> implements Map<K, V> {
+
+ /**
+ * This class holds a weak reference to the value and a strong reference to
+ * the key for efficient removal of stale values.
+ */
+ private static class WeakValueReference<K, V> extends WeakReference<V> {
+ private final K key;
+
+ WeakValueReference(K key, V value, ReferenceQueue<V> refQueue) {
+ super(value, refQueue);
+ this.key = key;
+ }
+
+ K getKey() {
+ return key;
+ }
+ }
+
+ private final HashMap<K, WeakValueReference<K, V>> backingMap;
+ private final ReferenceQueue<V> refQueue;
+
+ /**
+ * Constructs a new WeakValueMap, where all values are stored as weak
+ * references.
+ */
+ public WeakValueMap() {
+ backingMap = new HashMap<K, WeakValueReference<K, V>>();
+ refQueue = new ReferenceQueue<V>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public V put(K key, V value) {
+ if (key == null) {
+ throw new NullPointerException("key cannot be null");
+ }
+ if (value == null) {
+ throw new NullPointerException("value cannot be null");
+ }
+ removeStaleEntries();
+ backingMap.put(key, new WeakValueReference<K, V>(key, value, refQueue));
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public V remove(Object o) {
+ removeStaleEntries();
+ WeakReference<V> value = backingMap.remove(o);
+ return value == null ? null : value.get();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void putAll(Map<? extends K, ? extends V> map) {
+ if (map != null) {
+ for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ backingMap.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<K> keySet() {
+ removeStaleEntries();
+ return backingMap.keySet();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public V get(Object o) {
+ removeStaleEntries();
+ WeakReference<V> weakValue = backingMap.get(o);
+ if (weakValue != null) {
+ return weakValue.get();
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int size() {
+ removeStaleEntries();
+ return backingMap.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isEmpty() {
+ removeStaleEntries();
+ return backingMap.isEmpty();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean containsKey(Object o) {
+ removeStaleEntries();
+ return backingMap.containsKey(o);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean containsValue(Object o) {
+ removeStaleEntries();
+ for (V value : values()) {
+ if (o.equals(value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Collection<V> values() {
+ removeStaleEntries();
+ Collection<V> values = new HashSet<V>();
+ for (WeakReference<V> weakValue : backingMap.values()) {
+ V value = weakValue.get();
+ if (value != null) {
+ // null values have been GC'd, which may happen long before
+ // anything is enqueued in the ReferenceQueue.
+ values.add(value);
+ }
+ }
+ return values;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ removeStaleEntries();
+ Set<Entry<K, V>> entrySet = new HashSet<Entry<K, V>>();
+ for (Entry<K, WeakValueReference<K, V>> entry : backingMap.entrySet()) {
+ V value = entry.getValue().get();
+ if (value != null) {
+ // null values have been GC'd, which may happen long before
+ // anything is enqueued in the ReferenceQueue.
+ entrySet.add(new AbstractMap.SimpleEntry<K, V>(entry.getKey(),
+ value));
+ }
+ }
+ return entrySet;
+ }
+
+ /**
+ * Cleans up stale entries by polling the ReferenceQueue.
+ * <p>
+ * Depending on the GC implementation and strategy, the ReferenceQueue is
+ * not necessarily notified immediately when a reference is garbage
+ * collected, but it will eventually be.
+ */
+ private void removeStaleEntries() {
+ Reference<? extends V> ref;
+ while ((ref = refQueue.poll()) != null) {
+ Object key = ((WeakValueReference<?, ?>) ref).getKey();
+ backingMap.remove(key);
+ }
+ }
+}
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/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/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/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/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);
+ }
+
+}