aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2013-09-17 14:16:35 +0300
committerArtur Signell <artur@vaadin.com>2013-09-17 14:16:36 +0300
commit0f7bcffdb9f753148d1027ff380c9520bb78bfd8 (patch)
treea91e21f0060cd097bfa70b8eef91723047d49692 /server
parent70649ac21a40a2b819856da39ebcfb5394ae3280 (diff)
parent3a4351f9b777009d8e226d26125f758861ddcbb3 (diff)
downloadvaadin-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')
-rw-r--r--server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java9
-rw-r--r--server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java4
-rw-r--r--server/src/com/vaadin/server/BootstrapHandler.java11
-rw-r--r--server/src/com/vaadin/server/communication/AtmospherePushConnection.java2
-rw-r--r--server/src/com/vaadin/server/communication/PushHandler.java90
-rw-r--r--server/src/com/vaadin/util/CurrentInstance.java123
-rw-r--r--server/src/com/vaadin/util/WeakValueMap.java230
-rw-r--r--server/tests/src/com/vaadin/tests/data/converter/TestDateToSqlDateConverter.java25
-rw-r--r--server/tests/src/com/vaadin/util/TestCurrentInstance.java53
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));
+ }
}