diff options
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> </P>".equals(html)) { + if ("<P> </P>".equals(result)) { result = ""; } } else if (browser.isOpera()) { - if ("<br>".equals(html) || "<p><br></p>".equals(html)) { + if ("<br>".equals(result) || "<p><br></p>".equals(result)) { result = ""; } } diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java index b69d2a4fe7..39de122694 100644 --- a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java @@ -185,10 +185,15 @@ public class DateCellDayEvent extends FocusableHTML implements * If false, event is so small that caption must be in time-row */ private void updateCaptions(boolean bigMode) { - String separator = bigMode ? "<br />" : ": "; - caption.setInnerHTML("<span>" + calendarEvent.getTimeAsText() - + "</span>" + separator - + Util.escapeHTML(calendarEvent.getCaption())); + String innerHtml; + String escapedCaption = Util.escapeHTML(calendarEvent.getCaption()); + String timeAsText = calendarEvent.getTimeAsText(); + if (bigMode) { + innerHtml = "<span>" + timeAsText + "</span><br />" + escapedCaption; + } else { + innerHtml = "<span>" + timeAsText + "<span>:</span></span> " + escapedCaption; + } + caption.setInnerHTML(innerHtml); eventContent.setInnerHTML(""); } diff --git a/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); + } + +} |