aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJohn Ahlroos <john@vaadin.com>2013-11-20 14:26:40 +0200
committerJohn Ahlroos <john@vaadin.com>2013-11-20 14:27:09 +0200
commite3b1e6be389cbe48d1782723adf1595edbd10ea2 (patch)
tree61dbd2fac407f0b6657863d100d6b1f09e67e356 /server
parent96de019ea4ee5536dab87d1f04d4cb94ae71371d (diff)
parentdd7e6fee8c4717df59699f04f7e72a6f2e92008f (diff)
downloadvaadin-framework-e3b1e6be389cbe48d1782723adf1595edbd10ea2.tar.gz
vaadin-framework-e3b1e6be389cbe48d1782723adf1595edbd10ea2.zip
Merge branch 'master' into grid
Change-Id: I9f669ec38c39a42d1ef2a25121b77aab31551863
Diffstat (limited to 'server')
-rw-r--r--server/ivy.xml2
-rw-r--r--server/src/com/vaadin/data/Container.java54
-rw-r--r--server/src/com/vaadin/data/util/AbstractBeanContainer.java20
-rw-r--r--server/src/com/vaadin/data/util/AbstractInMemoryContainer.java151
-rw-r--r--server/src/com/vaadin/data/util/IndexedContainer.java7
-rw-r--r--server/src/com/vaadin/event/ConnectorActionManager.java88
-rw-r--r--server/src/com/vaadin/event/UIEvents.java116
-rw-r--r--server/src/com/vaadin/server/ClientMethodInvocation.java54
-rw-r--r--server/src/com/vaadin/server/Constants.java4
-rw-r--r--server/src/com/vaadin/server/ErrorHandlingRunnable.java38
-rw-r--r--server/src/com/vaadin/server/VaadinPortlet.java29
-rw-r--r--server/src/com/vaadin/server/VaadinService.java7
-rw-r--r--server/src/com/vaadin/server/VaadinServlet.java2
-rw-r--r--server/src/com/vaadin/server/VaadinServletService.java4
-rw-r--r--server/src/com/vaadin/server/VaadinSession.java60
-rw-r--r--server/src/com/vaadin/server/communication/PortletBootstrapHandler.java11
-rw-r--r--server/src/com/vaadin/server/communication/PushHandler.java64
-rw-r--r--server/src/com/vaadin/server/communication/ServerRpcHandler.java33
-rw-r--r--server/src/com/vaadin/ui/AbstractComponent.java5
-rw-r--r--server/src/com/vaadin/ui/Link.java84
-rw-r--r--server/src/com/vaadin/ui/Table.java32
-rw-r--r--server/src/com/vaadin/ui/TreeTable.java4
-rw-r--r--server/src/com/vaadin/ui/UI.java60
-rw-r--r--server/src/com/vaadin/ui/Window.java10
-rw-r--r--server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java185
-rw-r--r--server/tests/src/com/vaadin/data/util/TestIndexedContainer.java113
-rw-r--r--server/tests/src/com/vaadin/tests/server/TestAtmosphereVersion.java2
-rw-r--r--server/tests/src/com/vaadin/tests/server/TestClientMethodSerialization.java111
-rw-r--r--server/tests/src/com/vaadin/tests/server/TestSerialization.java19
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java1
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/table/TableRemovedQuicklySendsInvalidRpcCalls.java107
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/treetable/TreeTableSetContainerNull.java15
32 files changed, 1247 insertions, 245 deletions
diff --git a/server/ivy.xml b/server/ivy.xml
index b78541c52f..ba859b08ee 100644
--- a/server/ivy.xml
+++ b/server/ivy.xml
@@ -58,7 +58,7 @@
<!-- TESTING DEPENDENCIES -->
<!-- Test frameworks & related -->
- <dependency org="junit" name="junit" rev="4.5"
+ <dependency org="junit" name="junit" rev="4.11"
conf="test,ide -> default" />
<dependency org="org.easymock" name="easymock" rev="3.0"
conf="test,ide-> default" transitive="true" />
diff --git a/server/src/com/vaadin/data/Container.java b/server/src/com/vaadin/data/Container.java
index e93db52a35..bf553f31d2 100644
--- a/server/src/com/vaadin/data/Container.java
+++ b/server/src/com/vaadin/data/Container.java
@@ -582,6 +582,60 @@ public interface Container extends Serializable {
public Item addItemAt(int index, Object newItemId)
throws UnsupportedOperationException;
+ /**
+ * An <code>Event</code> object specifying information about the added
+ * items.
+ */
+ public interface ItemAddEvent extends ItemSetChangeEvent {
+
+ /**
+ * Gets the item id of the first added item.
+ *
+ * @return item id of the first added item
+ */
+ public Object getFirstItemId();
+
+ /**
+ * Gets the index of the first added item.
+ *
+ * @return index of the first added item
+ */
+ public int getFirstIndex();
+
+ /**
+ * Gets the number of the added items.
+ *
+ * @return the number of added items.
+ */
+ public int getAddedItemsCount();
+ }
+
+ /**
+ * An <code>Event</code> object specifying information about the removed
+ * items.
+ */
+ public interface ItemRemoveEvent extends ItemSetChangeEvent {
+ /**
+ * Gets the item id of the first removed item.
+ *
+ * @return item id of the first removed item
+ */
+ public Object getFirstItemId();
+
+ /**
+ * Gets the index of the first removed item.
+ *
+ * @return index of the first removed item
+ */
+ public int getFirstIndex();
+
+ /**
+ * Gets the number of the removed items.
+ *
+ * @return the number of removed items
+ */
+ public int getRemovedItemsCount();
+ }
}
/**
diff --git a/server/src/com/vaadin/data/util/AbstractBeanContainer.java b/server/src/com/vaadin/data/util/AbstractBeanContainer.java
index cd5c0c809d..67239996a2 100644
--- a/server/src/com/vaadin/data/util/AbstractBeanContainer.java
+++ b/server/src/com/vaadin/data/util/AbstractBeanContainer.java
@@ -222,6 +222,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
@Override
public boolean removeAllItems() {
int origSize = size();
+ IDTYPE firstItem = getFirstVisibleItem();
internalRemoveAllItems();
@@ -234,7 +235,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
// fire event only if the visible view changed, regardless of whether
// filtered out items were removed or not
if (origSize != 0) {
- fireItemSetChange();
+ fireItemsRemoved(0, firstItem, origSize);
}
return true;
@@ -679,6 +680,8 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
protected void addAll(Collection<? extends BEANTYPE> collection)
throws IllegalStateException, IllegalArgumentException {
boolean modified = false;
+ int origSize = size();
+
for (BEANTYPE bean : collection) {
// TODO skipping invalid beans - should not allow them in javadoc?
if (bean == null
@@ -699,13 +702,22 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
if (modified) {
// Filter the contents when all items have been added
if (isFiltered()) {
- filterAll();
- } else {
- fireItemSetChange();
+ doFilterContainer(!getFilters().isEmpty());
+ }
+ if (visibleNewItemsWasAdded(origSize)) {
+ // fire event about added items
+ int firstPosition = origSize;
+ IDTYPE firstItemId = getVisibleItemIds().get(firstPosition);
+ int affectedItems = size() - origSize;
+ fireItemsAdded(firstPosition, firstItemId, affectedItems);
}
}
}
+ private boolean visibleNewItemsWasAdded(int origSize) {
+ return size() > origSize;
+ }
+
/**
* Use the bean resolver to get the identifier for a bean.
*
diff --git a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
index 84304431bc..9a7922b928 100644
--- a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
+++ b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
@@ -15,8 +15,10 @@
*/
package com.vaadin.data.util;
+import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
+import java.util.EventObject;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
@@ -146,6 +148,85 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
}
}
+ private static abstract class BaseItemAddOrRemoveEvent extends
+ EventObject implements Serializable {
+ protected Object itemId;
+ protected int index;
+ protected int count;
+
+ public BaseItemAddOrRemoveEvent(Container source, Object itemId,
+ int index, int count) {
+ super(source);
+ this.itemId = itemId;
+ this.index = index;
+ this.count = count;
+ }
+
+ public Container getContainer() {
+ return (Container) getSource();
+ }
+
+ public Object getFirstItemId() {
+ return itemId;
+ }
+
+ public int getFirstIndex() {
+ return index;
+ }
+
+ public int getAffectedItemsCount() {
+ return count;
+ }
+ }
+
+ /**
+ * An <code>Event</code> object specifying information about the added
+ * items.
+ *
+ * <p>
+ * This class provides information about the first added item and the number
+ * of added items.
+ * </p>
+ */
+ protected static class BaseItemAddEvent extends
+ BaseItemAddOrRemoveEvent implements
+ Container.Indexed.ItemAddEvent {
+
+ public BaseItemAddEvent(Container source, Object itemId, int index,
+ int count) {
+ super(source, itemId, index, count);
+ }
+
+ @Override
+ public int getAddedItemsCount() {
+ return getAffectedItemsCount();
+ }
+ }
+
+ /**
+ * An <code>Event</code> object specifying information about the removed
+ * items.
+ *
+ * <p>
+ * This class provides information about the first removed item and the
+ * number of removed items.
+ * </p>
+ */
+ protected static class BaseItemRemoveEvent extends
+ BaseItemAddOrRemoveEvent implements
+ Container.Indexed.ItemRemoveEvent {
+
+ public BaseItemRemoveEvent(Container source, Object itemId,
+ int index, int count) {
+ super(source, itemId, index, count);
+ }
+
+ @Override
+ public int getRemovedItemsCount() {
+ return getAffectedItemsCount();
+ }
+ }
+
/**
* Get an item even if filtered out.
*
@@ -898,36 +979,69 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
* Notify item set change listeners that an item has been added to the
* container.
*
- * Unless subclasses specify otherwise, the default notification indicates a
- * full refresh.
- *
* @param postion
- * position of the added item in the view (if visible)
+ * position of the added item in the view
* @param itemId
* id of the added item
* @param item
* the added item
*/
protected void fireItemAdded(int position, ITEMIDTYPE itemId, ITEMCLASS item) {
- fireItemSetChange();
+ fireItemsAdded(position, itemId, 1);
+ }
+
+ /**
+ * Notify item set change listeners that items has been added to the
+ * container.
+ *
+ * @param firstPosition
+ * position of the first visible added item in the view
+ * @param firstItemId
+ * id of the first visible added item
+ * @param numberOfItems
+ * the number of visible added items
+ */
+ protected void fireItemsAdded(int firstPosition, ITEMIDTYPE firstItemId,
+ int numberOfItems) {
+ BaseItemAddEvent addEvent = new BaseItemAddEvent(this,
+ firstItemId, firstPosition, numberOfItems);
+ fireItemSetChange(addEvent);
}
/**
* Notify item set change listeners that an item has been removed from the
* container.
*
- * Unless subclasses specify otherwise, the default notification indicates a
- * full refresh.
+ * @param position
+ * position of the removed item in the view prior to removal
*
- * @param postion
- * position of the removed item in the view prior to removal (if
- * was visible)
* @param itemId
* id of the removed item, of type {@link Object} to satisfy
* {@link Container#removeItem(Object)} API
*/
protected void fireItemRemoved(int position, Object itemId) {
- fireItemSetChange();
+ fireItemsRemoved(position, itemId, 1);
+ }
+
+ /**
+ * Notify item set change listeners that items has been removed from the
+ * container.
+ *
+ * @param firstPosition
+ * position of the first visible removed item in the view prior
+ * to removal
+ * @param firstItemId
+ * id of the first visible removed item, of type {@link Object}
+ * to satisfy {@link Container#removeItem(Object)} API
+ * @param numberOfItems
+ * the number of removed visible items
+ *
+ */
+ protected void fireItemsRemoved(int firstPosition, Object firstItemId,
+ int numberOfItems) {
+ BaseItemRemoveEvent removeEvent = new BaseItemRemoveEvent(this,
+ firstItemId, firstPosition, numberOfItems);
+ fireItemSetChange(removeEvent);
}
// visible and filtered item identifier lists
@@ -946,6 +1060,21 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
}
/**
+ * Returns the item id of the first visible item after filtering. 'Null' is
+ * returned if there is no visible items.
+ *
+ * For internal use only.
+ *
+ * @return item id of the first visible item
+ */
+ protected ITEMIDTYPE getFirstVisibleItem() {
+ if (!getVisibleItemIds().isEmpty()) {
+ return getVisibleItemIds().get(0);
+ }
+ return null;
+ }
+
+ /**
* Returns true is the container has active filters.
*
* @return true if the container is currently filtered
diff --git a/server/src/com/vaadin/data/util/IndexedContainer.java b/server/src/com/vaadin/data/util/IndexedContainer.java
index d7bf70caf6..5d20919208 100644
--- a/server/src/com/vaadin/data/util/IndexedContainer.java
+++ b/server/src/com/vaadin/data/util/IndexedContainer.java
@@ -226,6 +226,7 @@ public class IndexedContainer extends
@Override
public boolean removeAllItems() {
int origSize = size();
+ Object firstItem = getFirstVisibleItem();
internalRemoveAllItems();
@@ -235,7 +236,7 @@ public class IndexedContainer extends
// filtered out items were removed or not
if (origSize != 0) {
// Sends a change event
- fireItemSetChange();
+ fireItemsRemoved(0, firstItem, origSize);
}
return true;
@@ -620,8 +621,7 @@ public class IndexedContainer extends
@Override
protected void fireItemAdded(int position, Object itemId, Item item) {
if (position >= 0) {
- fireItemSetChange(new IndexedContainer.ItemSetChangeEvent(this,
- position));
+ super.fireItemAdded(position, itemId, item);
}
}
@@ -1211,4 +1211,5 @@ public class IndexedContainer extends
public Collection<Filter> getContainerFilters() {
return super.getContainerFilters();
}
+
}
diff --git a/server/src/com/vaadin/event/ConnectorActionManager.java b/server/src/com/vaadin/event/ConnectorActionManager.java
new file mode 100644
index 0000000000..297f78f179
--- /dev/null
+++ b/server/src/com/vaadin/event/ConnectorActionManager.java
@@ -0,0 +1,88 @@
+/*
+ * 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.event;
+
+import java.util.logging.Logger;
+
+import com.vaadin.event.Action.Container;
+import com.vaadin.server.ClientConnector;
+import com.vaadin.server.VariableOwner;
+import com.vaadin.server.communication.ServerRpcHandler;
+import com.vaadin.ui.Component;
+
+/**
+ * An ActionManager connected to a connector. Takes care of verifying that the
+ * connector can receive events before triggering an action.
+ * <p>
+ * This is mostly a workaround until shortcut actions are re-implemented in a
+ * more sensible way.
+ *
+ * @since 7.1.8
+ * @author Vaadin Ltd
+ */
+public class ConnectorActionManager extends ActionManager {
+
+ private ClientConnector connector;
+
+ /**
+ * Initialize an action manager for the given connector.
+ *
+ * @param connector
+ * the owner of this action manager
+ */
+ public ConnectorActionManager(ClientConnector connector) {
+ super();
+ this.connector = connector;
+ }
+
+ /**
+ * Initialize an action manager for the given connector using the given
+ * viewer.
+ *
+ * @param connector
+ * the owner of this action manager
+ * @param viewer
+ * the viewer connected
+ */
+ public <T extends Component & Container & VariableOwner> ConnectorActionManager(
+ ClientConnector connector, T viewer) {
+ super(viewer);
+ this.connector = connector;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.event.ActionManager#handleAction(com.vaadin.event.Action,
+ * java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public void handleAction(Action action, Object sender, Object target) {
+ if (!connector.isConnectorEnabled()) {
+ getLogger().warning(
+ ServerRpcHandler.getIgnoredDisabledError("action",
+ connector));
+ return;
+ }
+
+ super.handleAction(action, sender, target);
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(ConnectorActionManager.class.getName());
+ }
+
+}
diff --git a/server/src/com/vaadin/event/UIEvents.java b/server/src/com/vaadin/event/UIEvents.java
new file mode 100644
index 0000000000..321bfc9251
--- /dev/null
+++ b/server/src/com/vaadin/event/UIEvents.java
@@ -0,0 +1,116 @@
+/*
+ * 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.event;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import com.vaadin.ui.Component;
+import com.vaadin.ui.UI;
+import com.vaadin.util.ReflectTools;
+
+/**
+ * A class that contains events, listeners and handlers specific to the
+ * {@link UI} class.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public interface UIEvents {
+
+ /**
+ * A {@link PollListener} receives and handles {@link PollEvent PollEvents}
+ * fired by {@link PollNotifier PollNotifiers}.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+ public interface PollListener extends Serializable {
+ public static final Method POLL_METHOD = ReflectTools.findMethod(
+ PollListener.class, "poll", PollEvent.class);
+
+ /**
+ * A poll request has been received by the server.
+ *
+ * @param event
+ * poll event
+ */
+ public void poll(PollEvent event);
+ }
+
+ /**
+ * An event that is fired whenever a client polls the server for
+ * asynchronous UI updates.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+ public static class PollEvent extends Component.Event {
+ public PollEvent(UI ui) {
+ super(ui);
+ }
+
+ /**
+ * Get the {@link UI} instance that received the poll request.
+ *
+ * @return the {@link UI} that received the poll request. Never
+ * <code>null</code>.
+ */
+ public UI getUI() {
+ /*
+ * This cast is safe to make, since this class' constructor
+ * constrains the source to be a UI instance.
+ */
+ return (UI) getComponent();
+ }
+ }
+
+ /**
+ * The interface for adding and removing {@link PollEvent} listeners.
+ * <p>
+ * By implementing this interface, a class publicly announces that it is
+ * able to send {@link PollEvent PollEvents} whenever the client sends a
+ * periodic poll message to the client, to check for asynchronous
+ * server-side modifications.
+ *
+ * @since 7.2
+ * @see UI#setPollInterval(int)
+ */
+ public interface PollNotifier extends Serializable {
+ /**
+ * Add a poll listener.
+ * <p>
+ * The listener is called whenever the client polls the server for
+ * asynchronous UI updates.
+ *
+ * @see UI#setPollInterval(int)
+ * @see #removePollListener(PollListener)
+ * @param listener
+ * the {@link PollListener} to add
+ */
+ public void addPollListener(PollListener listener);
+
+ /**
+ * Remove a poll listener.
+ *
+ * @see #addPollListener(PollListener)
+ * @param listener
+ * the listener to be removed
+ */
+ public void removePollListener(PollListener listener);
+ }
+
+}
diff --git a/server/src/com/vaadin/server/ClientMethodInvocation.java b/server/src/com/vaadin/server/ClientMethodInvocation.java
index 9c8318b064..3a6a87a53c 100644
--- a/server/src/com/vaadin/server/ClientMethodInvocation.java
+++ b/server/src/com/vaadin/server/ClientMethodInvocation.java
@@ -16,10 +16,16 @@
package com.vaadin.server;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import org.json.JSONArray;
+import org.json.JSONException;
+
/**
* Internal class for keeping track of pending server to client method
* invocations for a Connector.
@@ -80,4 +86,52 @@ public class ClientMethodInvocation implements Serializable,
}
return Long.signum(getSequenceNumber() - o.getSequenceNumber());
}
+
+ private void writeObject(ObjectOutputStream stream) throws IOException {
+ // Need to have custom serialization and deserialization because the
+ // constructor allows parameters of any type with Object[]. Thus, having
+ // parameters that are not Serializable will lead to
+ // NotSerializableException when trying to serialize this class.
+ // An example case of this is in #12532 (JavaScriptCallbackHelper ->
+ // JSONArray as parameter and not Serializable), for which this
+ // hac..workaround is implemented.
+
+ // Goes through the parameter types, and apply "custom serialization" to
+ // the ones that are not Serializable by changing them into something
+ // that is Serializable. On deserialization (readObject-method below)
+ // the process should be reversed.
+
+ // Easy way for implementing serialization & deserialization is by
+ // writing/parsing the object's content as string.
+ for (int i = 0; i < parameterTypes.length; i++) {
+ Type type = parameterTypes[i];
+ if (type instanceof Class<?>) {
+ Class<?> clazz = (Class<?>) type;
+ if (JSONArray.class.isAssignableFrom(clazz)) {
+ parameters[i] = ((JSONArray) parameters[i]).toString();
+ }
+ }
+ }
+ stream.defaultWriteObject();
+ }
+
+ private void readObject(ObjectInputStream stream) throws IOException,
+ ClassNotFoundException {
+ // Reverses the serialization done in writeObject. Basically just
+ // parsing the serialized type back to the non-serializable type.
+ stream.defaultReadObject();
+ for (int i = 0; i < parameterTypes.length; i++) {
+ Type type = parameterTypes[i];
+ if (type instanceof Class<?>) {
+ Class<?> clazz = (Class<?>) type;
+ if (JSONArray.class.isAssignableFrom(clazz)) {
+ try {
+ parameters[i] = new JSONArray(((String) parameters[i]));
+ } catch (JSONException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java
index 8c379abe06..b0841da314 100644
--- a/server/src/com/vaadin/server/Constants.java
+++ b/server/src/com/vaadin/server/Constants.java
@@ -67,7 +67,7 @@ public interface Constants {
// Keep the version number in sync with push/build.xml and other locations
// listed in that file
- static final String REQUIRED_ATMOSPHERE_VERSION = "1.0.14.vaadin4";
+ static final String REQUIRED_ATMOSPHERE_RUNTIME_VERSION = "1.0.18.vaadin1";
static final String INVALID_ATMOSPHERE_VERSION_WARNING = "\n"
+ "=================================================================\n"
@@ -82,7 +82,7 @@ public interface Constants {
+ "If using a dependency management system, please add a dependency\n"
+ "to vaadin-push.\n"
+ "If managing dependencies manually, please make sure Atmosphere\n"
- + REQUIRED_ATMOSPHERE_VERSION
+ + REQUIRED_ATMOSPHERE_RUNTIME_VERSION
+ " is included on the classpath.\n"
+ "Will fall back to using "
+ PushMode.class.getSimpleName()
diff --git a/server/src/com/vaadin/server/ErrorHandlingRunnable.java b/server/src/com/vaadin/server/ErrorHandlingRunnable.java
new file mode 100644
index 0000000000..3970a14ee8
--- /dev/null
+++ b/server/src/com/vaadin/server/ErrorHandlingRunnable.java
@@ -0,0 +1,38 @@
+/*
+ * 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.server;
+
+import java.io.Serializable;
+
+/**
+ * Defines the interface to handle exceptions thrown during the execution of a
+ * FutureAccess.
+ *
+ * @since 7.1.8
+ * @author Vaadin Ltd
+ */
+public interface ErrorHandlingRunnable extends Runnable, Serializable {
+
+ /**
+ * Handles exceptions thrown during the execution of a FutureAccess.
+ *
+ * @since 7.1.8
+ * @param exception
+ * the thrown exception.
+ */
+ public void handleError(Exception exception);
+
+}
diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java
index adef90c45f..a41f301219 100644
--- a/server/src/com/vaadin/server/VaadinPortlet.java
+++ b/server/src/com/vaadin/server/VaadinPortlet.java
@@ -422,16 +422,37 @@ public class VaadinPortlet extends GenericPortlet implements Constants,
* @return A wrapped version of the PorletRequest
*/
protected VaadinPortletRequest createVaadinRequest(PortletRequest request) {
- String portalInfo = request.getPortalContext().getPortalInfo()
- .toLowerCase();
- if (portalInfo.contains("liferay")) {
+ if (isLiferay(request)) {
return new VaadinLiferayRequest(request, getService());
- } else if (portalInfo.contains("gatein")) {
+ } else if (isGateIn(request)) {
return new VaadinGateinRequest(request, getService());
} else {
return new VaadinPortletRequest(request, getService());
}
+ }
+ /**
+ * Returns true if the portlet request is from Liferay.
+ *
+ * @param request
+ * @return True if Liferay, false otherwise
+ */
+ private static boolean isLiferay(PortletRequest request) {
+ String portalInfo = request.getPortalContext().getPortalInfo()
+ .toLowerCase();
+ return portalInfo.contains("liferay");
+ }
+
+ /**
+ * Returns true if the portlet request if from GateIn
+ *
+ * @param request
+ * @return True if GateIn, false otherwise
+ */
+ private static boolean isGateIn(PortletRequest request) {
+ String portalInfo = request.getPortalContext().getPortalInfo()
+ .toLowerCase();
+ return portalInfo.contains("gatein");
}
private VaadinPortletResponse createVaadinResponse(PortletResponse response) {
diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java
index cf6c806ead..aff0124d16 100644
--- a/server/src/com/vaadin/server/VaadinService.java
+++ b/server/src/com/vaadin/server/VaadinService.java
@@ -1736,6 +1736,13 @@ public abstract class VaadinService implements Serializable {
.getCurrentInstances());
CurrentInstance.setCurrent(session);
pendingAccess.run();
+
+ try {
+ pendingAccess.get();
+
+ } catch (Exception exception) {
+ pendingAccess.handleError(exception);
+ }
}
}
} finally {
diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java
index d34cd3bf0e..baf97d23d9 100644
--- a/server/src/com/vaadin/server/VaadinServlet.java
+++ b/server/src/com/vaadin/server/VaadinServlet.java
@@ -426,8 +426,6 @@ public class VaadinServlet extends HttpServlet implements Constants {
outWriter.print(output);
outWriter.flush();
outWriter.close();
- out.flush();
-
}
/**
diff --git a/server/src/com/vaadin/server/VaadinServletService.java b/server/src/com/vaadin/server/VaadinServletService.java
index 3b39f17849..818f2e87c6 100644
--- a/server/src/com/vaadin/server/VaadinServletService.java
+++ b/server/src/com/vaadin/server/VaadinServletService.java
@@ -66,11 +66,11 @@ public class VaadinServletService extends VaadinService {
private static boolean checkAtmosphereSupport() {
try {
String rawVersion = Version.getRawVersion();
- if (!Constants.REQUIRED_ATMOSPHERE_VERSION.equals(rawVersion)) {
+ if (!Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION.equals(rawVersion)) {
getLogger().log(
Level.WARNING,
Constants.INVALID_ATMOSPHERE_VERSION_WARNING,
- new Object[] { Constants.REQUIRED_ATMOSPHERE_VERSION,
+ new Object[] { Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION,
rawVersion });
}
return true;
diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java
index 8f15dacc98..f34721944a 100644
--- a/server/src/com/vaadin/server/VaadinSession.java
+++ b/server/src/com/vaadin/server/VaadinSession.java
@@ -16,6 +16,8 @@
package com.vaadin.server;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
@@ -33,6 +35,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Level;
import java.util.logging.Logger;
import javax.portlet.PortletSession;
@@ -83,6 +86,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
private final Map<Class<?>, CurrentInstance> instances = CurrentInstance
.getInstances(true);
private final VaadinSession session;
+ private Runnable runnable;
/**
* Creates an instance for the given runnable
@@ -97,6 +101,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
public FutureAccess(VaadinSession session, Runnable runnable) {
super(runnable, null);
this.session = session;
+ this.runnable = runnable;
}
@Override
@@ -126,6 +131,36 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
public Map<Class<?>, CurrentInstance> getCurrentInstances() {
return instances;
}
+
+ /**
+ * Handles exceptions thrown during the execution of this task.
+ *
+ * @since 7.1.8
+ * @param exception
+ * the thrown exception.
+ */
+ public void handleError(Exception exception) {
+ try {
+ if (runnable instanceof ErrorHandlingRunnable) {
+ ErrorHandlingRunnable errorHandlingRunnable = (ErrorHandlingRunnable) runnable;
+
+ errorHandlingRunnable.handleError(exception);
+ } else {
+ ErrorEvent errorEvent = new ErrorEvent(exception);
+
+ ErrorHandler errorHandler = ErrorEvent
+ .findErrorHandler(session);
+
+ if (errorHandler == null) {
+ errorHandler = new DefaultErrorHandler();
+ }
+
+ errorHandler.error(errorEvent);
+ }
+ } catch (Exception e) {
+ getLogger().log(Level.SEVERE, e.getMessage(), e);
+ }
+ }
}
/**
@@ -202,10 +237,10 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
* session is serialized as long as it doesn't happen while some other
* thread has the lock.
*/
- private transient ConcurrentLinkedQueue<FutureAccess> pendingAccessQueue;
+ private transient ConcurrentLinkedQueue<FutureAccess> pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
/**
- * Create a new service session tied to a Vaadin service
+ * Creates a new VaadinSession tied to a VaadinService.
*
* @param service
* the Vaadin service for the new session
@@ -1260,18 +1295,15 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
}
/**
- * Gets the queue of tasks submitted using {@link #access(Runnable)}.
+ * Gets the queue of tasks submitted using {@link #access(Runnable)}. It is
+ * safe to call this method and access the returned queue without holding
+ * the {@link #lock() session lock}.
*
* @since 7.1
*
- * @return the pending access queue
+ * @return the queue of pending access tasks
*/
public Queue<FutureAccess> getPendingAccessQueue() {
- if (pendingAccessQueue == null) {
- // pendingAccessQueue is transient, so will be null after
- // deserialization
- pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
- }
return pendingAccessQueue;
}
@@ -1288,6 +1320,16 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
}
/**
+ * Override default deserialization logic to account for transient
+ * {@link #pendingAccessQueue}.
+ */
+ private void readObject(ObjectInputStream stream) throws IOException,
+ ClassNotFoundException {
+ stream.defaultReadObject();
+ pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
+ }
+
+ /**
* Finds the UI with the corresponding embed id.
*
* @since 7.2
diff --git a/server/src/com/vaadin/server/communication/PortletBootstrapHandler.java b/server/src/com/vaadin/server/communication/PortletBootstrapHandler.java
index 2458951ada..dd6d3c9283 100644
--- a/server/src/com/vaadin/server/communication/PortletBootstrapHandler.java
+++ b/server/src/com/vaadin/server/communication/PortletBootstrapHandler.java
@@ -31,6 +31,7 @@ import org.json.JSONObject;
import com.vaadin.server.BootstrapHandler;
import com.vaadin.server.PaintException;
import com.vaadin.server.VaadinPortlet;
+import com.vaadin.server.VaadinPortlet.VaadinLiferayRequest;
import com.vaadin.server.VaadinPortletRequest;
import com.vaadin.server.VaadinPortletResponse;
import com.vaadin.server.VaadinRequest;
@@ -98,6 +99,8 @@ public class PortletBootstrapHandler extends BootstrapHandler {
JSONObject parameters = super.getApplicationParameters(context);
VaadinPortletResponse response = (VaadinPortletResponse) context
.getResponse();
+ VaadinPortletRequest request = (VaadinPortletRequest) context
+ .getRequest();
MimeResponse portletResponse = (MimeResponse) response
.getPortletResponse();
ResourceURL resourceURL = portletResponse.createResourceURL();
@@ -108,6 +111,14 @@ public class PortletBootstrapHandler extends BootstrapHandler {
parameters
.put(ApplicationConstants.SERVICE_URL_PATH_AS_PARAMETER, true);
+ // If we are running in Liferay then we need to prefix all parameters
+ // with the portlet namespace
+ if (request instanceof VaadinLiferayRequest) {
+ parameters.put(
+ ApplicationConstants.SERVICE_URL_PARAMETER_NAMESPACE,
+ response.getPortletResponse().getNamespace());
+ }
+
return parameters;
}
} \ No newline at end of file
diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java
index 81dd00084d..09428e47a9 100644
--- a/server/src/com/vaadin/server/communication/PushHandler.java
+++ b/server/src/com/vaadin/server/communication/PushHandler.java
@@ -31,6 +31,8 @@ import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
import org.json.JSONException;
+import com.vaadin.server.ErrorEvent;
+import com.vaadin.server.ErrorHandler;
import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException;
import com.vaadin.server.ServiceException;
import com.vaadin.server.ServletPortletHelper;
@@ -262,11 +264,12 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
return;
}
+ UI ui = null;
session.lock();
try {
VaadinSession.setCurrent(session);
// Sets UI.currentInstance
- final UI ui = service.findUI(vaadinRequest);
+ ui = service.findUI(vaadinRequest);
if (ui == null) {
sendNotificationAndDisconnect(resource,
UidlRequestHandler.getUINotFoundErrorJSON(service,
@@ -274,14 +277,63 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
} else {
callback.run(resource, ui);
}
- } catch (IOException e) {
- getLogger().log(Level.WARNING, "Error writing a push response",
- e);
+ } catch (final IOException e) {
+ callErrorHandler(session, e);
+ } catch (final Exception e) {
+ SystemMessages msg = service.getSystemMessages(
+ ServletPortletHelper.findLocale(null, null,
+ vaadinRequest), vaadinRequest);
+
+ AtmosphereResource errorResource = resource;
+ if (ui != null && ui.getPushConnection() != null) {
+ // We MUST use the opened push connection if there is one.
+ // Otherwise we will write the response to the wrong request
+ // when using streaming (the client -> server request
+ // instead of the opened push channel)
+ errorResource = ((AtmospherePushConnection) ui
+ .getPushConnection()).getResource();
+ }
+
+ sendNotificationAndDisconnect(
+ errorResource,
+ VaadinService.createCriticalNotificationJSON(
+ msg.getInternalErrorCaption(),
+ msg.getInternalErrorMessage(), null,
+ msg.getInternalErrorURL()));
+ callErrorHandler(session, e);
} finally {
- session.unlock();
+ try {
+ session.unlock();
+ } catch (Exception e) {
+ getLogger().log(Level.WARNING,
+ "Error while unlocking session", e);
+ // can't call ErrorHandler, we (hopefully) don't have a lock
+ }
}
} finally {
- service.requestEnd(vaadinRequest, null, session);
+ try {
+ service.requestEnd(vaadinRequest, null, session);
+ } catch (Exception e) {
+ getLogger().log(Level.WARNING, "Error while ending request", e);
+
+ // can't call ErrorHandler, we don't have a lock
+ }
+ }
+ }
+
+ /**
+ * Call the session's {@link ErrorHandler}, if it has one, with the given
+ * exception wrapped in an {@link ErrorEvent}.
+ */
+ private void callErrorHandler(VaadinSession session, Exception e) {
+ try {
+ ErrorHandler errorHandler = ErrorEvent.findErrorHandler(session);
+ if (errorHandler != null) {
+ errorHandler.error(new ErrorEvent(e));
+ }
+ } catch (Exception ex) {
+ // Let's not allow error handling to cause trouble; log fails
+ getLogger().log(Level.WARNING, "ErrorHandler call failed", ex);
}
}
diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java
index eff9ceebf4..432a9ea893 100644
--- a/server/src/com/vaadin/server/communication/ServerRpcHandler.java
+++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java
@@ -248,15 +248,8 @@ public class ServerRpcHandler implements Serializable {
}
// Connector is disabled, log a warning and move to the next
- String msg = "Ignoring RPC call for disabled connector "
- + connector.getClass().getName();
- if (connector instanceof Component) {
- String caption = ((Component) connector).getCaption();
- if (caption != null) {
- msg += ", caption=" + caption;
- }
- }
- getLogger().warning(msg);
+ getLogger().warning(
+ getIgnoredDisabledError("RPC call", connector));
continue;
}
// DragAndDropService has null UI
@@ -495,4 +488,26 @@ public class ServerRpcHandler implements Serializable {
private static final Logger getLogger() {
return Logger.getLogger(ServerRpcHandler.class.getName());
}
+
+ /**
+ * Generates an error message when the client is trying to to something
+ * ('what') with a connector which is disabled or invisible.
+ *
+ * @since 7.1.8
+ * @param connector
+ * the connector which is disabled (or invisible)
+ * @return an error message
+ */
+ public static String getIgnoredDisabledError(String what,
+ ClientConnector connector) {
+ String msg = "Ignoring " + what + " for disabled connector "
+ + connector.getClass().getName();
+ if (connector instanceof Component) {
+ String caption = ((Component) connector).getCaption();
+ if (caption != null) {
+ msg += ", caption=" + caption;
+ }
+ }
+ return msg;
+ }
}
diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java
index 262d47af18..61bcf00ad8 100644
--- a/server/src/com/vaadin/ui/AbstractComponent.java
+++ b/server/src/com/vaadin/ui/AbstractComponent.java
@@ -27,6 +27,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.vaadin.event.ActionManager;
+import com.vaadin.event.ConnectorActionManager;
import com.vaadin.event.ShortcutListener;
import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.ComponentSizeValidator;
@@ -90,7 +91,7 @@ public abstract class AbstractComponent extends AbstractClientConnector
* Keeps track of the Actions added to this component; the actual
* handling/notifying is delegated, usually to the containing window.
*/
- private ActionManager actionManager;
+ private ConnectorActionManager actionManager;
private boolean visible = true;
@@ -929,7 +930,7 @@ public abstract class AbstractComponent extends AbstractClientConnector
*/
protected ActionManager getActionManager() {
if (actionManager == null) {
- actionManager = new ActionManager();
+ actionManager = new ConnectorActionManager(this);
setActionManagerViewer();
}
return actionManager;
diff --git a/server/src/com/vaadin/ui/Link.java b/server/src/com/vaadin/ui/Link.java
index cf8e1a9693..e1a47777bd 100644
--- a/server/src/com/vaadin/ui/Link.java
+++ b/server/src/com/vaadin/ui/Link.java
@@ -16,13 +16,10 @@
package com.vaadin.ui;
-import java.util.Map;
-
-import com.vaadin.server.PaintException;
-import com.vaadin.server.PaintTarget;
import com.vaadin.server.Resource;
import com.vaadin.shared.ui.BorderStyle;
import com.vaadin.shared.ui.link.LinkConstants;
+import com.vaadin.shared.ui.link.LinkState;
/**
* Link is used to create external or internal URL links.
@@ -31,7 +28,7 @@ import com.vaadin.shared.ui.link.LinkConstants;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class Link extends AbstractComponent implements LegacyComponent {
+public class Link extends AbstractComponent {
/**
* @deprecated As of 7.0, use {@link BorderStyle#NONE} instead
@@ -51,14 +48,6 @@ public class Link extends AbstractComponent implements LegacyComponent {
@Deprecated
public static final BorderStyle TARGET_BORDER_DEFAULT = BorderStyle.DEFAULT;
- private String targetName;
-
- private BorderStyle targetBorder = BorderStyle.DEFAULT;
-
- private int targetWidth = -1;
-
- private int targetHeight = -1;
-
/**
* Creates a new link.
*/
@@ -105,43 +94,14 @@ public class Link extends AbstractComponent implements LegacyComponent {
setTargetBorder(border);
}
- /**
- * Paints the content of this component.
- *
- * @param target
- * the Paint Event.
- * @throws PaintException
- * if the paint operation failed.
- */
@Override
- public void paintContent(PaintTarget target) throws PaintException {
- if (getResource() == null) {
- return;
- }
-
- // Target window name
- final String name = getTargetName();
- if (name != null && name.length() > 0) {
- target.addAttribute("name", name);
- }
-
- // Target window size
- if (getTargetWidth() >= 0) {
- target.addAttribute("targetWidth", getTargetWidth());
- }
- if (getTargetHeight() >= 0) {
- target.addAttribute("targetHeight", getTargetHeight());
- }
-
- // Target window border
- switch (getTargetBorder()) {
- case MINIMAL:
- target.addAttribute("border", "minimal");
- break;
- case NONE:
- target.addAttribute("border", "none");
- break;
- }
+ protected LinkState getState() {
+ return (LinkState) super.getState();
+ }
+
+ @Override
+ protected LinkState getState(boolean markAsDirty) {
+ return (LinkState) super.getState(markAsDirty);
}
/**
@@ -150,7 +110,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* @return the target window border.
*/
public BorderStyle getTargetBorder() {
- return targetBorder;
+ return getState(false).targetBorder;
}
/**
@@ -159,7 +119,8 @@ public class Link extends AbstractComponent implements LegacyComponent {
* @return the target window height.
*/
public int getTargetHeight() {
- return targetHeight < 0 ? -1 : targetHeight;
+ return getState(false).targetHeight < 0 ? -1
+ : getState(false).targetHeight;
}
/**
@@ -169,7 +130,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* @return the target window name.
*/
public String getTargetName() {
- return targetName;
+ return getState(false).target;
}
/**
@@ -178,7 +139,8 @@ public class Link extends AbstractComponent implements LegacyComponent {
* @return the target window width.
*/
public int getTargetWidth() {
- return targetWidth < 0 ? -1 : targetWidth;
+ return getState(false).targetWidth < 0 ? -1
+ : getState(false).targetWidth;
}
/**
@@ -188,8 +150,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* the targetBorder to set.
*/
public void setTargetBorder(BorderStyle targetBorder) {
- this.targetBorder = targetBorder;
- markAsDirty();
+ getState().targetBorder = targetBorder;
}
/**
@@ -199,8 +160,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* the targetHeight to set.
*/
public void setTargetHeight(int targetHeight) {
- this.targetHeight = targetHeight;
- markAsDirty();
+ getState().targetHeight = targetHeight;
}
/**
@@ -210,8 +170,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* the targetName to set.
*/
public void setTargetName(String targetName) {
- this.targetName = targetName;
- markAsDirty();
+ getState().target = targetName;
}
/**
@@ -221,8 +180,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* the targetWidth to set.
*/
public void setTargetWidth(int targetWidth) {
- this.targetWidth = targetWidth;
- markAsDirty();
+ getState().targetWidth = targetWidth;
}
/**
@@ -244,8 +202,4 @@ public class Link extends AbstractComponent implements LegacyComponent {
setResource(LinkConstants.HREF_RESOURCE, resource);
}
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- // TODO Remove once LegacyComponent is no longer implemented
- }
}
diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java
index bd2b7828de..32ed738697 100644
--- a/server/src/com/vaadin/ui/Table.java
+++ b/server/src/com/vaadin/ui/Table.java
@@ -427,6 +427,12 @@ public class Table extends AbstractSelect implements Action.Container,
private int currentPageFirstItemIndex = 0;
/**
+ * Index of the "first" item on the last page if a user has used
+ * setCurrentPageFirstItemIndex to scroll down. -1 if not set.
+ */
+ private int currentPageFirstItemIndexOnLastPage = -1;
+
+ /**
* Holds value of property selectable.
*/
private boolean selectable = false;
@@ -1477,12 +1483,14 @@ public class Table extends AbstractSelect implements Action.Container,
}
/*
- * FIXME #7607 Take somehow into account the case where we want to
- * scroll to the bottom so that the last row is completely visible even
- * if (table height) / (row height) is not an integer. Reverted the
- * original fix because of #8662 regression.
+ * If the new index is on the last page we set the index to be the first
+ * item on that last page and make a note of the real index for the
+ * client side to be able to move the scroll position to the correct
+ * position.
*/
+ int indexOnLastPage = -1;
if (newIndex > maxIndex) {
+ indexOnLastPage = newIndex;
newIndex = maxIndex;
}
@@ -1494,6 +1502,20 @@ public class Table extends AbstractSelect implements Action.Container,
currentPageFirstItemId = null;
}
currentPageFirstItemIndex = newIndex;
+
+ if (needsPageBufferReset) {
+ /*
+ * The flag currentPageFirstItemIndexOnLastPage denotes a user
+ * set scrolling position on the last page via
+ * setCurrentPageFirstItemIndex() and shouldn't be changed by
+ * the table component internally changing the firstvisible item
+ * on lazy row fetching. Doing so would make the scrolling
+ * position not be updated correctly when the lazy rows are
+ * finally rendered.
+ */
+ currentPageFirstItemIndexOnLastPage = indexOnLastPage;
+ }
+
} else {
// For containers not supporting indexes, we must iterate the
@@ -3447,6 +3469,8 @@ public class Table extends AbstractSelect implements Action.Container,
if (getCurrentPageFirstItemIndex() != 0 || getPageLength() > 0) {
target.addVariable(this, "firstvisible",
getCurrentPageFirstItemIndex());
+ target.addVariable(this, "firstvisibleonlastpage",
+ currentPageFirstItemIndexOnLastPage);
}
}
diff --git a/server/src/com/vaadin/ui/TreeTable.java b/server/src/com/vaadin/ui/TreeTable.java
index e150db9423..1c13eae8d9 100644
--- a/server/src/com/vaadin/ui/TreeTable.java
+++ b/server/src/com/vaadin/ui/TreeTable.java
@@ -590,11 +590,11 @@ public class TreeTable extends Table implements Hierarchical {
// does not change component hierarchy during paint
containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier) && false;
- if (!(newDataSource instanceof Hierarchical)) {
+ if (newDataSource != null && !(newDataSource instanceof Hierarchical)) {
newDataSource = new ContainerHierarchicalWrapper(newDataSource);
}
- if (!(newDataSource instanceof Ordered)) {
+ if (newDataSource != null && !(newDataSource instanceof Ordered)) {
newDataSource = new HierarchicalContainerOrderedWrapper(
(Hierarchical) newDataSource);
}
diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java
index 8beebb0f1e..a292e6b829 100644
--- a/server/src/com/vaadin/ui/UI.java
+++ b/server/src/com/vaadin/ui/UI.java
@@ -24,6 +24,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
+import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.event.Action;
@@ -31,10 +32,16 @@ import com.vaadin.event.Action.Handler;
import com.vaadin.event.ActionManager;
import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.event.MouseEvents.ClickListener;
+import com.vaadin.event.UIEvents.PollEvent;
+import com.vaadin.event.UIEvents.PollListener;
+import com.vaadin.event.UIEvents.PollNotifier;
import com.vaadin.navigator.Navigator;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.ComponentSizeValidator;
import com.vaadin.server.ComponentSizeValidator.InvalidLayout;
+import com.vaadin.server.DefaultErrorHandler;
+import com.vaadin.server.ErrorHandler;
+import com.vaadin.server.ErrorHandlingRunnable;
import com.vaadin.server.LocaleService;
import com.vaadin.server.Page;
import com.vaadin.server.PaintException;
@@ -91,7 +98,8 @@ import com.vaadin.util.CurrentInstance;
* @since 7.0
*/
public abstract class UI extends AbstractSingleComponentContainer implements
- Action.Container, Action.Notifier, LegacyComponent, Focusable {
+ Action.Container, Action.Notifier, PollNotifier, LegacyComponent,
+ Focusable {
/**
* The application to which this UI belongs
@@ -163,10 +171,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
@Override
public void poll() {
- /*
- * No-op. This is only called to cause a server visit to check for
- * changes.
- */
+ fireEvent(new PollEvent(UI.this));
}
};
private DebugWindowServerRpc debugRpc = new DebugWindowServerRpc() {
@@ -405,9 +410,12 @@ public abstract class UI extends AbstractSingleComponentContainer implements
* @see #getSession()
*/
public void setSession(VaadinSession session) {
- if ((session == null) == (this.session == null)) {
+ if (session == null && this.session == null) {
+ throw new IllegalStateException(
+ "Session should never be set to null when UI.session is already null");
+ } else if (session != null && this.session != null) {
throw new IllegalStateException(
- "VaadinServiceSession has already been set. Old session: "
+ "Session has already been set. Old session: "
+ getSessionDetails(this.session)
+ ". New session: " + getSessionDetails(session)
+ ".");
@@ -1296,11 +1304,36 @@ public abstract class UI extends AbstractSingleComponentContainer implements
throw new UIDetachedException();
}
- return session.access(new Runnable() {
+ return session.access(new ErrorHandlingRunnable() {
@Override
public void run() {
accessSynchronously(runnable);
}
+
+ @Override
+ public void handleError(Exception exception) {
+ try {
+ if (runnable instanceof ErrorHandlingRunnable) {
+ ErrorHandlingRunnable errorHandlingRunnable = (ErrorHandlingRunnable) runnable;
+
+ errorHandlingRunnable.handleError(exception);
+ } else {
+ ConnectorErrorEvent errorEvent = new ConnectorErrorEvent(
+ UI.this, exception);
+
+ ErrorHandler errorHandler = com.vaadin.server.ErrorEvent
+ .findErrorHandler(UI.this);
+
+ if (errorHandler == null) {
+ errorHandler = new DefaultErrorHandler();
+ }
+
+ errorHandler.error(errorEvent);
+ }
+ } catch (Exception e) {
+ getLogger().log(Level.SEVERE, e.getMessage(), e);
+ }
+ }
});
}
@@ -1458,6 +1491,17 @@ public abstract class UI extends AbstractSingleComponentContainer implements
return getState(false).pollInterval;
}
+ @Override
+ public void addPollListener(PollListener listener) {
+ addListener(EventId.POLL, PollEvent.class, listener,
+ PollListener.POLL_METHOD);
+ }
+
+ @Override
+ public void removePollListener(PollListener listener) {
+ removeListener(EventId.POLL, PollEvent.class, listener);
+ }
+
/**
* Retrieves the object used for configuring the push channel.
*
diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java
index 8e7c6dbc80..d3afdaacf1 100644
--- a/server/src/com/vaadin/ui/Window.java
+++ b/server/src/com/vaadin/ui/Window.java
@@ -82,6 +82,16 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
public void windowModeChanged(WindowMode newState) {
setWindowMode(newState);
}
+
+ @Override
+ public void windowMoved(int x, int y) {
+ if (x != getState(false).positionX) {
+ setPositionX(x);
+ }
+ if (y != getState(false).positionY) {
+ setPositionY(y);
+ }
+ }
};
/**
diff --git a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java
index 3a2cb268b9..35f09fc8f3 100644
--- a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java
+++ b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java
@@ -10,8 +10,15 @@ import java.util.Map;
import junit.framework.Assert;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+
import com.vaadin.data.Container;
+import com.vaadin.data.Container.Indexed.ItemAddEvent;
+import com.vaadin.data.Container.Indexed.ItemRemoveEvent;
+import com.vaadin.data.Container.ItemSetChangeListener;
import com.vaadin.data.Item;
+import com.vaadin.data.util.filter.Compare;
/**
* Test basic functionality of BeanItemContainer.
@@ -737,4 +744,182 @@ public class BeanItemContainerTest extends AbstractBeanContainerTest {
// should throw exception
}
}
+
+ public void testItemAddedEvent() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class));
+ EasyMock.replay(addListener);
+
+ container.addItem(bean);
+
+ EasyMock.verify(addListener);
+ }
+
+ public void testItemAddedEvent_AddedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ container.addItem(bean);
+
+ assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemAddedEvent_addItemAt_IndexOfAddedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ container.addItem(bean);
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ container.addItemAt(1, new Person(""));
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemAddedEvent_addItemAfter_IndexOfAddedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ container.addItem(bean);
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ container.addItemAfter(bean, new Person(""));
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemAddedEvent_amountOfAddedItems() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+ List<Person> beans = Arrays.asList(new Person("Jack"), new Person(
+ "John"));
+
+ container.addAll(beans);
+
+ assertEquals(2, capturedEvent.getValue().getAddedItemsCount());
+ }
+
+ public void testItemAddedEvent_someItemsAreFiltered_amountOfAddedItemsIsReducedByAmountOfFilteredItems() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+ List<Person> beans = Arrays.asList(new Person("Jack"), new Person(
+ "John"));
+ container.addFilter(new Compare.Equal("name", "John"));
+
+ container.addAll(beans);
+
+ assertEquals(1, capturedEvent.getValue().getAddedItemsCount());
+ }
+
+ public void testItemAddedEvent_someItemsAreFiltered_addedItemIsTheFirstVisibleItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+ List<Person> beans = Arrays.asList(new Person("Jack"), bean);
+ container.addFilter(new Compare.Equal("name", "John"));
+
+ container.addAll(beans);
+
+ assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemRemovedEvent() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ container.addItem(bean);
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ removeListener.containerItemSetChange(EasyMock
+ .isA(ItemRemoveEvent.class));
+ EasyMock.replay(removeListener);
+
+ container.removeItem(bean);
+
+ EasyMock.verify(removeListener);
+ }
+
+ public void testItemRemovedEvent_RemovedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ container.addItem(bean);
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeItem(bean);
+
+ assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemRemovedEvent_indexOfRemovedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ container.addItem(new Person("Jack"));
+ Person secondBean = new Person("John");
+ container.addItem(secondBean);
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeItem(secondBean);
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemRemovedEvent_amountOfRemovedItems() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ container.addItem(new Person("Jack"));
+ container.addItem(new Person("John"));
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeAllItems();
+
+ assertEquals(2, capturedEvent.getValue().getRemovedItemsCount());
+ }
+
+ private Capture<ItemAddEvent> captureAddEvent(
+ ItemSetChangeListener addListener) {
+ Capture<ItemAddEvent> capturedEvent = new Capture<ItemAddEvent>();
+ addListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+ return capturedEvent;
+ }
+
+ private Capture<ItemRemoveEvent> captureRemoveEvent(
+ ItemSetChangeListener removeListener) {
+ Capture<ItemRemoveEvent> capturedEvent = new Capture<ItemRemoveEvent>();
+ removeListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+ return capturedEvent;
+ }
+
+ private ItemSetChangeListener createListenerMockFor(
+ BeanItemContainer<Person> container) {
+ ItemSetChangeListener listener = EasyMock
+ .createNiceMock(ItemSetChangeListener.class);
+ container.addItemSetChangeListener(listener);
+ return listener;
+ }
}
diff --git a/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java b/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java
index 09e5a26c15..5c78965092 100644
--- a/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java
+++ b/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java
@@ -4,6 +4,12 @@ import java.util.List;
import junit.framework.Assert;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+
+import com.vaadin.data.Container.Indexed.ItemAddEvent;
+import com.vaadin.data.Container.Indexed.ItemRemoveEvent;
+import com.vaadin.data.Container.ItemSetChangeListener;
import com.vaadin.data.Item;
public class TestIndexedContainer extends AbstractInMemoryContainerTest {
@@ -271,6 +277,113 @@ public class TestIndexedContainer extends AbstractInMemoryContainerTest {
counter.assertNone();
}
+ public void testItemAddedEvent() {
+ IndexedContainer container = new IndexedContainer();
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class));
+ EasyMock.replay(addListener);
+
+ container.addItem();
+
+ EasyMock.verify(addListener);
+ }
+
+ public void testItemAddedEvent_AddedItem() {
+ IndexedContainer container = new IndexedContainer();
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ Object itemId = container.addItem();
+
+ assertEquals(itemId, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemAddedEvent_IndexOfAddedItem() {
+ IndexedContainer container = new IndexedContainer();
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ container.addItem();
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ Object itemId = container.addItemAt(1);
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemRemovedEvent() {
+ IndexedContainer container = new IndexedContainer();
+ Object itemId = container.addItem();
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ removeListener.containerItemSetChange(EasyMock
+ .isA(ItemRemoveEvent.class));
+ EasyMock.replay(removeListener);
+
+ container.removeItem(itemId);
+
+ EasyMock.verify(removeListener);
+ }
+
+ public void testItemRemovedEvent_RemovedItem() {
+ IndexedContainer container = new IndexedContainer();
+ Object itemId = container.addItem();
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeItem(itemId);
+
+ assertEquals(itemId, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemRemovedEvent_indexOfRemovedItem() {
+ IndexedContainer container = new IndexedContainer();
+ container.addItem();
+ Object secondItemId = container.addItem();
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeItem(secondItemId);
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemRemovedEvent_amountOfRemovedItems() {
+ IndexedContainer container = new IndexedContainer();
+ container.addItem();
+ container.addItem();
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeAllItems();
+
+ assertEquals(2, capturedEvent.getValue().getRemovedItemsCount());
+ }
+
+ private Capture<ItemAddEvent> captureAddEvent(
+ ItemSetChangeListener addListener) {
+ Capture<ItemAddEvent> capturedEvent = new Capture<ItemAddEvent>();
+ addListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+ return capturedEvent;
+ }
+
+ private Capture<ItemRemoveEvent> captureRemoveEvent(
+ ItemSetChangeListener removeListener) {
+ Capture<ItemRemoveEvent> capturedEvent = new Capture<ItemRemoveEvent>();
+ removeListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+ return capturedEvent;
+ }
+
+ private ItemSetChangeListener createListenerMockFor(
+ IndexedContainer container) {
+ ItemSetChangeListener listener = EasyMock
+ .createNiceMock(ItemSetChangeListener.class);
+ container.addItemSetChangeListener(listener);
+ return listener;
+ }
+
// Ticket 8028
public void testGetItemIdsRangeIndexOutOfBounds() {
IndexedContainer ic = new IndexedContainer();
diff --git a/server/tests/src/com/vaadin/tests/server/TestAtmosphereVersion.java b/server/tests/src/com/vaadin/tests/server/TestAtmosphereVersion.java
index 5c27ef0752..3d37022b81 100644
--- a/server/tests/src/com/vaadin/tests/server/TestAtmosphereVersion.java
+++ b/server/tests/src/com/vaadin/tests/server/TestAtmosphereVersion.java
@@ -12,7 +12,7 @@ public class TestAtmosphereVersion extends TestCase {
* classpath
*/
public void testAtmosphereVersion() {
- assertEquals(Constants.REQUIRED_ATMOSPHERE_VERSION,
+ assertEquals(Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION,
Version.getRawVersion());
}
}
diff --git a/server/tests/src/com/vaadin/tests/server/TestClientMethodSerialization.java b/server/tests/src/com/vaadin/tests/server/TestClientMethodSerialization.java
new file mode 100644
index 0000000000..1e0210dc63
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/server/TestClientMethodSerialization.java
@@ -0,0 +1,111 @@
+/*
+ * 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.server;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.json.JSONArray;
+
+import com.vaadin.server.ClientMethodInvocation;
+import com.vaadin.server.JavaScriptCallbackHelper;
+import com.vaadin.ui.JavaScript.JavaScriptCallbackRpc;
+import com.vaadin.util.ReflectTools;
+
+public class TestClientMethodSerialization extends TestCase {
+
+ private static final Method JAVASCRIPT_CALLBACK_METHOD = ReflectTools
+ .findMethod(JavaScriptCallbackRpc.class, "call", String.class,
+ JSONArray.class);
+
+ private static final Method BASIC_PARAMS_CALL_METHOD = ReflectTools
+ .findMethod(TestClientMethodSerialization.class,
+ "basicParamsMethodForTesting", String.class, Integer.class);
+
+ private static final Method NO_PARAMS_CALL_METHOD = ReflectTools
+ .findMethod(TestClientMethodSerialization.class,
+ "noParamsMethodForTesting");
+
+ public void basicParamsMethodForTesting(String stringParam,
+ Integer integerParam) {
+ }
+
+ public void noParamsMethodForTesting() {
+ }
+
+ /**
+ * Tests the {@link ClientMethodInvocation} serialization when using
+ * {@link JavaScriptCallbackHelper#invokeCallback(String, Object...)}.
+ * #12532
+ */
+ public void testClientMethodSerialization_WithJSONArray_ContentStaysSame()
+ throws Exception {
+ JSONArray originalArray = new JSONArray(Arrays.asList(
+ "callbackParameter1", "callBackParameter2", "12345"));
+ ClientMethodInvocation original = new ClientMethodInvocation(null,
+ "interfaceName", JAVASCRIPT_CALLBACK_METHOD, new Object[] {
+ "callBackMethodName", originalArray });
+
+ ClientMethodInvocation copy = (ClientMethodInvocation) serializeAndDeserialize(original);
+ JSONArray copyArray = (JSONArray) copy.getParameters()[1];
+ assertEquals(originalArray.toString(), copyArray.toString());
+ }
+
+ public void testClientMethodSerialization_WithBasicParams_NoChanges()
+ throws Exception {
+ String stringParam = "a string 123";
+ Integer integerParam = 1234567890;
+ ClientMethodInvocation original = new ClientMethodInvocation(null,
+ "interfaceName", BASIC_PARAMS_CALL_METHOD, new Serializable[] {
+ stringParam, integerParam });
+ ClientMethodInvocation copy = (ClientMethodInvocation) serializeAndDeserialize(original);
+ String copyString = (String) copy.getParameters()[0];
+ Integer copyInteger = (Integer) copy.getParameters()[1];
+ assertEquals(copyString, stringParam);
+ assertEquals(copyInteger, integerParam);
+ }
+
+ public void testClientMethodSerialization_NoParams_NoExceptions() {
+ ClientMethodInvocation original = new ClientMethodInvocation(null,
+ "interfaceName", NO_PARAMS_CALL_METHOD, null);
+ ClientMethodInvocation copy = (ClientMethodInvocation) serializeAndDeserialize(original);
+ }
+
+ private static Serializable serializeAndDeserialize(Serializable input) {
+ Serializable output = null;
+ try {
+ ByteArrayOutputStream bs = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(bs);
+ out.writeObject(input);
+ byte[] data = bs.toByteArray();
+ ObjectInputStream in = new ObjectInputStream(
+ new ByteArrayInputStream(data));
+ output = (Serializable) in.readObject();
+ } catch (Exception e) {
+ fail("Exception during serialization/deserialization: "
+ + e.getMessage());
+ }
+ return output;
+ }
+
+}
diff --git a/server/tests/src/com/vaadin/tests/server/TestSerialization.java b/server/tests/src/com/vaadin/tests/server/TestSerialization.java
index 84ff5ad6fa..a52821a919 100644
--- a/server/tests/src/com/vaadin/tests/server/TestSerialization.java
+++ b/server/tests/src/com/vaadin/tests/server/TestSerialization.java
@@ -14,6 +14,7 @@ import com.vaadin.data.Property;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.data.util.MethodProperty;
import com.vaadin.data.validator.RegexpValidator;
+import com.vaadin.server.VaadinSession;
import com.vaadin.ui.Form;
public class TestSerialization extends TestCase {
@@ -21,7 +22,7 @@ public class TestSerialization extends TestCase {
public void testValidators() throws Exception {
RegexpValidator validator = new RegexpValidator(".*", "Error");
validator.validate("aaa");
- RegexpValidator validator2 = (RegexpValidator) serializeAndDeserialize(validator);
+ RegexpValidator validator2 = serializeAndDeserialize(validator);
validator2.validate("aaa");
}
@@ -67,7 +68,17 @@ public class TestSerialization extends TestCase {
serializeAndDeserialize(mp);
}
- private static Serializable serializeAndDeserialize(Serializable s)
+ public void testVaadinSession() throws Exception {
+ VaadinSession session = new VaadinSession(null);
+
+ session = serializeAndDeserialize(session);
+
+ assertNotNull(
+ "Pending access queue was not recreated after deserialization",
+ session.getPendingAccessQueue());
+ }
+
+ private static <S extends Serializable> S serializeAndDeserialize(S s)
throws IOException, ClassNotFoundException {
// Serialize and deserialize
@@ -77,10 +88,12 @@ public class TestSerialization extends TestCase {
byte[] data = bs.toByteArray();
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(
data));
- Serializable s2 = (Serializable) in.readObject();
+ @SuppressWarnings("unchecked")
+ S s2 = (S) in.readObject();
// using special toString(Object) method to avoid calling
// Property.toString(), which will be temporarily disabled
+ // TODO This is hilariously broken (#12723)
if (s.equals(s2)) {
System.out.println(toString(s) + " equals " + toString(s2));
} else {
diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java
index dd46b11520..85116dd152 100644
--- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java
+++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java
@@ -250,6 +250,7 @@ public class AbsFieldValueConversions extends TestCase {
@Test
public void testNullConverter() {
TextField tf = new TextField("foo");
+ tf.setConverter(new StringToIntegerConverter());
tf.setPropertyDataSource(new ObjectProperty<Integer>(12));
tf.setConverter((Converter) null);
try {
diff --git a/server/tests/src/com/vaadin/tests/server/component/table/TableRemovedQuicklySendsInvalidRpcCalls.java b/server/tests/src/com/vaadin/tests/server/component/table/TableRemovedQuicklySendsInvalidRpcCalls.java
deleted file mode 100644
index b539e42efe..0000000000
--- a/server/tests/src/com/vaadin/tests/server/component/table/TableRemovedQuicklySendsInvalidRpcCalls.java
+++ /dev/null
@@ -1,107 +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.tests.server.component.table;
-
-import com.vaadin.annotations.Push;
-import com.vaadin.event.ItemClickEvent;
-import com.vaadin.event.ItemClickEvent.ItemClickListener;
-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;
-
-@Push
-public class TableRemovedQuicklySendsInvalidRpcCalls extends AbstractTestUI {
-
- @Override
- protected void setup(VaadinRequest request) {
- addComponent(new Button("Blink a table", new Button.ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- blinkTable();
- }
- }));
- }
-
- private void blinkTable() {
- final Table table = new Table();
- table.setPageLength(5);
- table.addContainerProperty(new Object(), String.class, null);
-
- for (int i = 0; i < 50; i++) {
- table.addItem(new Object[] { "Row" }, new Object());
- }
-
- table.addItemClickListener(new ItemClickListener() {
- private int i;
-
- @Override
- public void itemClick(ItemClickEvent event) {
- /*
- * Ignore implementation. This is only an easy way to make the
- * client-side update table's variables (by furiously clicking
- * on the table row.
- *
- * This way, we get variable changes queued. The push call will
- * then remove the Table, while the variable changes being still
- * in the queue, leading to the issue as described in the
- * ticket.
- */
- System.out.println("clicky " + (++i));
- }
- });
-
- System.out.println("adding component");
- addComponent(table);
-
- new Thread() {
- @Override
- public void run() {
- getSession().lock();
- try {
- Thread.sleep(500);
- access(new Runnable() {
- @Override
- public void run() {
- System.out.println("removing component");
- removeComponent(table);
- }
- });
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- getSession().unlock();
- }
- };
- }.start();
- }
-
- @Override
- protected String getTestDescription() {
- return "Adding and subsequently quickly removing a table "
- + "should not leave any pending RPC calls waiting "
- + "in a Timer. Issue can be reproduced by "
- + "1) pressing the button 2) clicking furiously "
- + "on a row in the table.";
- }
-
- @Override
- protected Integer getTicketNumber() {
- return 12337;
- }
-}
diff --git a/server/tests/src/com/vaadin/tests/server/component/treetable/TreeTableSetContainerNull.java b/server/tests/src/com/vaadin/tests/server/component/treetable/TreeTableSetContainerNull.java
new file mode 100644
index 0000000000..a3b79a14f0
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/server/component/treetable/TreeTableSetContainerNull.java
@@ -0,0 +1,15 @@
+package com.vaadin.tests.server.component.treetable;
+
+import junit.framework.TestCase;
+
+import com.vaadin.ui.TreeTable;
+
+public class TreeTableSetContainerNull extends TestCase {
+
+ public void testNullContainer() {
+ TreeTable treeTable = new TreeTable();
+
+ // should not cause an exception
+ treeTable.setContainerDataSource(null);
+ }
+}