diff options
author | Artur Signell <artur@vaadin.com> | 2013-09-17 14:16:35 +0300 |
---|---|---|
committer | Artur Signell <artur@vaadin.com> | 2013-09-17 14:16:36 +0300 |
commit | 0f7bcffdb9f753148d1027ff380c9520bb78bfd8 (patch) | |
tree | a91e21f0060cd097bfa70b8eef91723047d49692 /server | |
parent | 70649ac21a40a2b819856da39ebcfb5394ae3280 (diff) | |
parent | 3a4351f9b777009d8e226d26125f758861ddcbb3 (diff) | |
download | vaadin-framework-0f7bcffdb9f753148d1027ff380c9520bb78bfd8.tar.gz vaadin-framework-0f7bcffdb9f753148d1027ff380c9520bb78bfd8.zip |
Merge changes from origin/7.1
de53191 Fix for #12279 (caret jumps when formatting in RTA).
fca0f7a Add <br> as empty representation for webkit (#12490)
6dcece8 Allow creating TextBox or SuggestionPopup when extending VFilterSelect (#12491)
22fcb44 Include unobfuscated file used by test in the war (#12468)
bc90a58 Unified xml files to end with new line
3d01d74 Reduce Ivy resolver spam to a minimum (#12510)
1e73ca8 Fix keystore path to correspond to the Vaadin 7 directory structure (#12520)
dcf9c61 Protect CurrentInstance instances from garbage collection (#12509)
24ffbc2 Allow storing and restoring null instances in CurrentInstance #12509
0d79a84 Added a comment that hopefully explains the NULL_OBJECT #12509
e4d99b3 Use non-obfuscated version of vaadinPush.js when not in production (#12527)
3a31dfe NullPointerException in TableQuery.fetchMetadata() (#11403)
4659797 fixed incorrect name for close-pressed.png for windows in black theme (#12563)
9b05257 Test for push with streaming based on Table
3cafce3 NullPointerException in DateToSqlDateConverter (#12284)
3a4351f Ensure PushConnection is properly cleaned up on disconnect (#12226, #12522)
Change-Id: I44f3d5f003e62e7ab86a22188b22933491226868
Diffstat (limited to 'server')
9 files changed, 265 insertions, 282 deletions
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/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index f237d9edd4..5a117958a0 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -389,9 +389,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/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/util/CurrentInstance.java b/server/src/com/vaadin/util/CurrentInstance.java index a1c543117d..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>>() { @@ -60,7 +70,7 @@ public class CurrentInstance implements Serializable { return null; } - Map<Class<?>, CurrentInstance> value = new WeakValueMap<Class<?>, CurrentInstance>(); + Map<Class<?>, CurrentInstance> value = new HashMap<Class<?>, CurrentInstance>(); // Copy all inheritable values to child map for (Entry<Class<?>, CurrentInstance> e : parentValue.entrySet()) { @@ -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/src/com/vaadin/util/WeakValueMap.java b/server/src/com/vaadin/util/WeakValueMap.java deleted file mode 100644 index 1134594cba..0000000000 --- a/server/src/com/vaadin/util/WeakValueMap.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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/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/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)); + } } |