summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorHenri Sara <hesara@vaadin.com>2012-02-29 17:16:00 +0200
committerHenri Sara <hesara@vaadin.com>2012-02-29 17:16:18 +0200
commit121b1dae346d60896205c4db5bc0c0db0c504a41 (patch)
tree974fd116dd151b16c0fadbfd6c0827eb02bb3bf5 /src/com
parentf1061c43764d81c17bdac919dcc8b1ec16b8ed94 (diff)
downloadvaadin-framework-121b1dae346d60896205c4db5bc0c0db0c504a41.tar.gz
vaadin-framework-121b1dae346d60896205c4db5bc0c0db0c504a41.zip
Implement server to client RPC (#8426).
Diffstat (limited to 'src/com')
-rw-r--r--src/com/vaadin/data/Buffered.java7
-rw-r--r--src/com/vaadin/data/Validator.java7
-rw-r--r--src/com/vaadin/terminal/CompositeErrorMessage.java6
-rw-r--r--src/com/vaadin/terminal/Paintable.java13
-rw-r--r--src/com/vaadin/terminal/SystemError.java7
-rw-r--r--src/com/vaadin/terminal/UserError.java8
-rw-r--r--src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml6
-rw-r--r--src/com/vaadin/terminal/gwt/client/ApplicationConnection.java51
-rw-r--r--src/com/vaadin/terminal/gwt/client/Connector.java17
-rw-r--r--src/com/vaadin/terminal/gwt/client/Util.java2
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/ClientRpc.java23
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java18
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/RpcManager.java32
-rw-r--r--src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java4
-rwxr-xr-xsrc/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java57
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java50
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java104
-rw-r--r--src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java64
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java197
-rw-r--r--src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java5
-rw-r--r--src/com/vaadin/ui/AbstractComponent.java98
-rw-r--r--src/com/vaadin/ui/AbstractMedia.java25
22 files changed, 732 insertions, 69 deletions
diff --git a/src/com/vaadin/data/Buffered.java b/src/com/vaadin/data/Buffered.java
index f6398f585b..552bd16d04 100644
--- a/src/com/vaadin/data/Buffered.java
+++ b/src/com/vaadin/data/Buffered.java
@@ -5,6 +5,8 @@
package com.vaadin.data;
import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.terminal.ErrorMessage;
@@ -12,6 +14,7 @@ import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.SystemError;
import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
/**
* <p>
@@ -338,6 +341,10 @@ public interface Buffered extends Serializable {
return null;
}
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ return Collections.emptyList();
+ }
+
/* Documented in super interface */
public void addListener(RepaintRequestListener listener) {
}
diff --git a/src/com/vaadin/data/Validator.java b/src/com/vaadin/data/Validator.java
index bc64594edb..f07e957395 100644
--- a/src/com/vaadin/data/Validator.java
+++ b/src/com/vaadin/data/Validator.java
@@ -5,12 +5,15 @@
package com.vaadin.data;
import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
import com.vaadin.terminal.ErrorMessage;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.gwt.client.communication.SharedState;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
/**
* Interface that implements a method for validating if an {@link Object} is
@@ -173,6 +176,10 @@ public interface Validator extends Serializable {
return null;
}
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ return Collections.emptyList();
+ }
+
/**
* Returns the message of the error in HTML.
*
diff --git a/src/com/vaadin/terminal/CompositeErrorMessage.java b/src/com/vaadin/terminal/CompositeErrorMessage.java
index 2ec1fc949e..efe29196d8 100644
--- a/src/com/vaadin/terminal/CompositeErrorMessage.java
+++ b/src/com/vaadin/terminal/CompositeErrorMessage.java
@@ -7,10 +7,12 @@ package com.vaadin.terminal;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
/**
* Class for combining multiple error messages together.
@@ -138,6 +140,10 @@ public class CompositeErrorMessage implements ErrorMessage, Serializable {
return null;
}
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ return Collections.emptyList();
+ }
+
/* Documented in super interface */
public void addListener(RepaintRequestListener listener) {
}
diff --git a/src/com/vaadin/terminal/Paintable.java b/src/com/vaadin/terminal/Paintable.java
index 9026a45ca3..435b00a0e4 100644
--- a/src/com/vaadin/terminal/Paintable.java
+++ b/src/com/vaadin/terminal/Paintable.java
@@ -6,8 +6,10 @@ package com.vaadin.terminal;
import java.io.Serializable;
import java.util.EventObject;
+import java.util.List;
import com.vaadin.terminal.gwt.client.communication.SharedState;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
/**
* Interface implemented by all classes that can be painted. Classes
@@ -55,6 +57,17 @@ public interface Paintable extends java.util.EventListener, Serializable {
public SharedState getState();
/**
+ * Returns the list of pending server to client RPC calls and clears the
+ * list.
+ *
+ * @return unmodifiable ordered list of pending server to client method
+ * calls (not null)
+ *
+ * @since 7.0
+ */
+ public List<ClientMethodInvocation> retrievePendingRpcCalls();
+
+ /**
* Requests that the paintable should be repainted as soon as possible.
*/
public void requestRepaint();
diff --git a/src/com/vaadin/terminal/SystemError.java b/src/com/vaadin/terminal/SystemError.java
index 55e62abb0a..462450422b 100644
--- a/src/com/vaadin/terminal/SystemError.java
+++ b/src/com/vaadin/terminal/SystemError.java
@@ -6,9 +6,12 @@ package com.vaadin.terminal;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.Collections;
+import java.util.List;
import com.vaadin.terminal.gwt.client.communication.SharedState;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
/**
* <code>SystemError</code> is a runtime exception caused by error in system.
@@ -96,6 +99,10 @@ public class SystemError extends RuntimeException implements ErrorMessage {
return null;
}
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ return Collections.emptyList();
+ }
+
/**
* Returns the message of the error in HTML.
*
diff --git a/src/com/vaadin/terminal/UserError.java b/src/com/vaadin/terminal/UserError.java
index 50cf32ab0c..15473e47e1 100644
--- a/src/com/vaadin/terminal/UserError.java
+++ b/src/com/vaadin/terminal/UserError.java
@@ -4,8 +4,12 @@
package com.vaadin.terminal;
+import java.util.Collections;
+import java.util.List;
+
import com.vaadin.terminal.gwt.client.communication.SharedState;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
/**
* <code>UserError</code> is a controlled error occurred in application. User
@@ -149,6 +153,10 @@ public class UserError implements ErrorMessage {
return null;
}
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ return Collections.emptyList();
+ }
+
/* Documented in interface */
public void requestRepaintRequests() {
}
diff --git a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
index 04f291d599..6a8a5df707 100644
--- a/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
+++ b/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
@@ -47,6 +47,12 @@
<when-type-assignable class="com.vaadin.terminal.gwt.client.communication.ServerRpc" />
</generate-with> -->
+ <!-- Generate client side RPC manager for server to client RPC -->
+ <generate-with
+ class="com.vaadin.terminal.gwt.widgetsetutils.RpcManagerGenerator">
+ <when-type-assignable class="com.vaadin.terminal.gwt.client.communication.RpcManager" />
+ </generate-with> -->
+
<!-- Fall through to this rule for everything but IE -->
<replace-with
class="com.vaadin.terminal.gwt.client.ui.UploadIFrameOnloadStrategy">
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
index ec1ca7e585..869c387a2f 100644
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
@@ -39,6 +39,7 @@ import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage;
import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.RpcManager;
import com.vaadin.terminal.gwt.client.communication.SharedState;
import com.vaadin.terminal.gwt.client.ui.RootConnector;
import com.vaadin.terminal.gwt.client.ui.VContextMenu;
@@ -54,8 +55,8 @@ import com.vaadin.terminal.gwt.server.AbstractCommunicationManager;
*
* Client-side widgets receive updates from the corresponding server-side
* components as calls to
- * {@link VPaintableWidget#updateFromUIDL(UIDL, ApplicationConnection)} (not to
- * be confused with the server side interface
+ * {@link ComponentConnector#updateFromUIDL(UIDL, ApplicationConnection)} (not
+ * to be confused with the server side interface
* {@link com.vaadin.terminal.Paintable} ). Any client-side changes (typically
* resulting from user actions) are sent back to the server as variable changes
* (see {@link #updateVariable()}).
@@ -167,6 +168,8 @@ public class ApplicationConnection {
private final LayoutManager layoutManager = new LayoutManager(this);
+ private final RpcManager rpcManager = GWT.create(RpcManager.class);
+
public ApplicationConnection() {
view = GWT.create(RootConnector.class);
}
@@ -808,7 +811,7 @@ public class ApplicationConnection {
*/
private void cleanVariableBurst(ArrayList<MethodInvocation> variableBurst) {
for (int i = 1; i < variableBurst.size(); i++) {
- String id = variableBurst.get(i).getPaintableId();
+ String id = variableBurst.get(i).getConnectorId();
if (!getConnectorMap().hasConnector(id)
&& !getConnectorMap().isDragAndDropPaintable(id)) {
// variable owner does not exist anymore
@@ -1088,6 +1091,27 @@ public class ApplicationConnection {
}
}
+ if (json.containsKey("rpc")) {
+ VConsole.log(" * Performing server to client RPC calls");
+
+ JSONArray rpcCalls = new JSONArray(
+ json.getJavaScriptObject("rpc"));
+
+ int rpcLength = rpcCalls.size();
+ for (int i = 0; i < rpcLength; i++) {
+ try {
+ JSONArray rpcCall = (JSONArray) rpcCalls.get(i);
+ MethodInvocation invocation = parseMethodInvocation(rpcCall);
+ VConsole.log("Server to client RPC call: "
+ + invocation);
+ rpcManager.applyInvocation(invocation,
+ getConnectorMap());
+ } catch (final Throwable e) {
+ VConsole.error(e);
+ }
+ }
+ }
+
if (json.containsKey("dd")) {
// response contains data for drag and drop service
VDragAndDropManager.get().handleServerResponse(
@@ -1163,10 +1187,25 @@ public class ApplicationConnection {
endRequest();
}
+
};
ApplicationConfiguration.runWhenWidgetsLoaded(c);
}
+ private MethodInvocation parseMethodInvocation(JSONArray rpcCall) {
+ String connectorId = ((JSONString) rpcCall.get(0)).stringValue();
+ String interfaceName = ((JSONString) rpcCall.get(1)).stringValue();
+ String methodName = ((JSONString) rpcCall.get(2)).stringValue();
+ JSONArray parametersJson = (JSONArray) rpcCall.get(3);
+ Object[] parameters = new Object[parametersJson.size()];
+ for (int j = 0; j < parametersJson.size(); ++j) {
+ parameters[j] = JsonDecoder.convertValue(
+ (JSONArray) parametersJson.get(j), getConnectorMap());
+ }
+ return new MethodInvocation(connectorId, interfaceName, methodName,
+ parameters);
+ }
+
// Redirect browser, null reloads current page
private static native void redirect(String url)
/*-{
@@ -1177,11 +1216,11 @@ public class ApplicationConnection {
}
}-*/;
- private void addVariableToQueue(String paintableId, String variableName,
+ private void addVariableToQueue(String connectorId, String variableName,
Object value, boolean immediate) {
// note that type is now deduced from value
// TODO could eliminate invocations of same shared variable setter
- addMethodInvocationToQueue(new MethodInvocation(paintableId,
+ addMethodInvocationToQueue(new MethodInvocation(connectorId,
UPDATE_VARIABLE_INTERFACE, UPDATE_VARIABLE_METHOD,
new Object[] { variableName, value }), immediate);
}
@@ -1273,7 +1312,7 @@ public class ApplicationConnection {
for (MethodInvocation invocation : pendingInvocations) {
JSONArray invocationJson = new JSONArray();
invocationJson.set(0,
- new JSONString(invocation.getPaintableId()));
+ new JSONString(invocation.getConnectorId()));
invocationJson.set(1,
new JSONString(invocation.getInterfaceName()));
invocationJson.set(2,
diff --git a/src/com/vaadin/terminal/gwt/client/Connector.java b/src/com/vaadin/terminal/gwt/client/Connector.java
index eee525865b..67202ce02b 100644
--- a/src/com/vaadin/terminal/gwt/client/Connector.java
+++ b/src/com/vaadin/terminal/gwt/client/Connector.java
@@ -3,6 +3,9 @@
*/
package com.vaadin.terminal.gwt.client;
+import java.util.Collection;
+
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
import com.vaadin.terminal.gwt.client.communication.SharedState;
/**
@@ -73,4 +76,18 @@ public interface Connector {
*/
public void doInit(String connectorId, ApplicationConnection connection);
+ /**
+ * For internal use by the framework: returns the registered RPC
+ * implementations for an RPC interface identifier.
+ *
+ * TODO interface identifier type or format may change
+ *
+ * @param rpcInterfaceId
+ * RPC interface identifier: fully qualified interface type name
+ * @return RPC interface implementations registered for an RPC interface,
+ * not null
+ */
+ public <T extends ClientRpc> Collection<T> getRpcImplementations(
+ String rpcInterfaceId);
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/Util.java b/src/com/vaadin/terminal/gwt/client/Util.java
index 5ed30eaff1..572a9bebb2 100644
--- a/src/com/vaadin/terminal/gwt/client/Util.java
+++ b/src/com/vaadin/terminal/gwt/client/Util.java
@@ -858,7 +858,7 @@ public class Util {
String curId = null;
ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>();
for (int i = 0; i < loggedBurst.size(); i++) {
- String id = loggedBurst.get(i).getPaintableId();
+ String id = loggedBurst.get(i).getConnectorId();
if (curId == null) {
curId = id;
diff --git a/src/com/vaadin/terminal/gwt/client/communication/ClientRpc.java b/src/com/vaadin/terminal/gwt/client/communication/ClientRpc.java
new file mode 100644
index 0000000000..45dbe69454
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/ClientRpc.java
@@ -0,0 +1,23 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+/**
+ * Interface to be extended by all server to client RPC interfaces.
+ *
+ * On the server side, proxies of the interface can be obtained from
+ * AbstractComponent. On the client, RPC implementations can be registered with
+ * AbstractConnector.registerRpc().
+ *
+ * Note: Currently, each RPC interface may not contain multiple methods with the
+ * same name, even if their parameter lists would differ.
+ *
+ * @since 7.0
+ */
+public interface ClientRpc extends Serializable {
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java b/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java
index 17e1ce30e3..f4305ffcaa 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java
@@ -4,6 +4,8 @@
package com.vaadin.terminal.gwt.client.communication;
+import java.util.Arrays;
+
/**
* Information needed by the framework to send an RPC method invocation from the
* client to the server or vice versa.
@@ -12,21 +14,21 @@ package com.vaadin.terminal.gwt.client.communication;
*/
public class MethodInvocation {
- private final String paintableId;
+ private final String connectorId;
private final String interfaceName;
private final String methodName;
private final Object[] parameters;
- public MethodInvocation(String paintableId, String interfaceName,
+ public MethodInvocation(String connectorId, String interfaceName,
String methodName, Object[] parameters) {
- this.paintableId = paintableId;
+ this.connectorId = connectorId;
this.interfaceName = interfaceName;
this.methodName = methodName;
this.parameters = parameters;
}
- public String getPaintableId() {
- return paintableId;
+ public String getConnectorId() {
+ return connectorId;
}
public String getInterfaceName() {
@@ -40,4 +42,10 @@ public class MethodInvocation {
public Object[] getParameters() {
return parameters;
}
+
+ @Override
+ public String toString() {
+ return connectorId + ":" + interfaceName + "." + methodName + "("
+ + Arrays.toString(parameters) + ")";
+ }
} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
new file mode 100644
index 0000000000..302e6eaa55
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
@@ -0,0 +1,32 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.communication;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+
+/**
+ * Client side RPC manager that can invoke methods based on RPC calls received
+ * from the server.
+ *
+ * A GWT generator is used to create an implementation of this class at
+ * run-time.
+ *
+ * @since 7.0
+ */
+public interface RpcManager extends Serializable {
+ /**
+ * Perform server to client RPC invocation.
+ *
+ * @param invocation
+ * method to invoke
+ * @param connectorMap
+ * mapper used to find Connector for the method call and any
+ * connectors referenced in parameters
+ */
+ public void applyInvocation(MethodInvocation invocation,
+ ConnectorMap connectorMap);
+}
diff --git a/src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java b/src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java
index c08008e5c1..36f23a0cc1 100644
--- a/src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java
+++ b/src/com/vaadin/terminal/gwt/client/communication/ServerRpc.java
@@ -11,8 +11,8 @@ import com.vaadin.terminal.gwt.client.ApplicationConnection;
/**
* Interface to be extended by all client to server RPC interfaces.
*
- * The nested interface InitializableClientToServerRpc The
- * {@link #initRpc(String, ApplicationConnection)} method is created
+ * The nested interface InitializableClientToServerRpc has an
+ * {@link #initRpc(String, ApplicationConnection)} method, which is created
* automatically by a GWT generator and must be called on the client side before
* generated implementations of the interface are used to perform RPC calls.
*
diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
index 2b5bccd66f..7232f4ed7e 100755
--- a/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java
@@ -1,13 +1,22 @@
package com.vaadin.terminal.gwt.client.ui;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
public abstract class AbstractConnector implements Connector {
private ApplicationConnection connection;
private String id;
+ private Map<String, Collection<ClientRpc>> rpcImplementations;
+
/*
* (non-Javadoc)
*
@@ -61,4 +70,52 @@ public abstract class AbstractConnector implements Connector {
}
+ /**
+ * Registers an implementation for a server to client RPC interface.
+ *
+ * Multiple registrations can be made for a single interface, in which case
+ * all of them receive corresponding RPC calls.
+ *
+ * @param rpcInterface
+ * RPC interface
+ * @param implementation
+ * implementation that should receive RPC calls
+ */
+ protected <T extends ClientRpc> void registerRpc(Class<T> rpcInterface,
+ T implementation) {
+ String rpcInterfaceId = rpcInterface.getName().replaceAll("\\$", ".");
+ if (null == rpcImplementations) {
+ rpcImplementations = new HashMap<String, Collection<ClientRpc>>();
+ }
+ if (null == rpcImplementations.get(rpcInterfaceId)) {
+ rpcImplementations.put(rpcInterfaceId, new ArrayList<ClientRpc>());
+ }
+ rpcImplementations.get(rpcInterfaceId).add(implementation);
+ }
+
+ /**
+ * Unregisters an implementation for a server to client RPC interface.
+ *
+ * @param rpcInterface
+ * RPC interface
+ * @param implementation
+ * implementation to unregister
+ */
+ protected <T extends ClientRpc> void unregisterRpc(Class<T> rpcInterface,
+ T implementation) {
+ String rpcInterfaceId = rpcInterface.getName().replaceAll("\\$", ".");
+ if (null != rpcImplementations
+ && null != rpcImplementations.get(rpcInterfaceId)) {
+ rpcImplementations.get(rpcInterfaceId).remove(implementation);
+ }
+ }
+
+ public <T extends ClientRpc> Collection<T> getRpcImplementations(
+ String rpcInterfaceId) {
+ if (null == rpcImplementations) {
+ return Collections.emptyList();
+ }
+ return (Collection<T>) rpcImplementations.get(rpcInterfaceId);
+ }
+
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java b/src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java
index 1ba3fd6930..bd27b8244d 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/MediaBaseConnector.java
@@ -6,13 +6,12 @@ package com.vaadin.terminal.gwt.client.ui;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.Util;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
public abstract class MediaBaseConnector extends AbstractComponentConnector {
public static final String TAG_SOURCE = "src";
- public static final String ATTR_PAUSE = "pause";
- public static final String ATTR_PLAY = "play";
public static final String ATTR_MUTED = "muted";
public static final String ATTR_CONTROLS = "ctrl";
public static final String ATTR_AUTOPLAY = "auto";
@@ -21,6 +20,38 @@ public abstract class MediaBaseConnector extends AbstractComponentConnector {
public static final String ATTR_HTML = "html";
public static final String ATTR_ALT_TEXT = "alt";
+ /**
+ * Server to client RPC interface for controlling playback of the media.
+ *
+ * @since 7.0
+ */
+ public static interface MediaControl extends ClientRpc {
+ /**
+ * Start playing the media.
+ */
+ public void play();
+
+ /**
+ * Pause playback of the media.
+ */
+ public void pause();
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ registerRpc(MediaControl.class, new MediaControl() {
+ public void play() {
+ getWidget().play();
+ }
+
+ public void pause() {
+ getWidget().pause();
+ }
+ });
+ }
+
@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
super.updateFromUIDL(uidl, client);
@@ -41,9 +72,6 @@ public abstract class MediaBaseConnector extends AbstractComponentConnector {
}
}
setAltText(uidl);
-
- evalPauseCommand(uidl);
- evalPlayCommand(uidl);
}
protected boolean shouldShowControls(UIDL uidl) {
@@ -62,18 +90,6 @@ public abstract class MediaBaseConnector extends AbstractComponentConnector {
return uidl.getBooleanAttribute(ATTR_HTML);
}
- private void evalPlayCommand(UIDL uidl) {
- if (uidl.hasAttribute(ATTR_PLAY)) {
- getWidget().play();
- }
- }
-
- private void evalPauseCommand(UIDL uidl) {
- if (uidl.hasAttribute(ATTR_PAUSE)) {
- getWidget().pause();
- }
- }
-
@Override
public VMediaBase getWidget() {
return (VMediaBase) super.getWidget();
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
index c52b6fad6a..9143d61aca 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
@@ -807,6 +807,7 @@ public abstract class AbstractCommunicationManager implements
}
LinkedList<Paintable> stateQueue = new LinkedList<Paintable>();
+ LinkedList<Paintable> rpcPendingQueue = new LinkedList<Paintable>();
if (paintables != null) {
@@ -815,9 +816,11 @@ public abstract class AbstractCommunicationManager implements
paintQueue.addAll(paintables);
while (!paintQueue.isEmpty()) {
- final Paintable p = paintQueue.pop();
+ final Paintable p = paintQueue.removeFirst();
// for now, all painted components may need a state refresh
stateQueue.push(p);
+ // ... and RPC calls to be sent
+ rpcPendingQueue.push(p);
// // TODO CLEAN
// if (p instanceof Root) {
@@ -898,10 +901,50 @@ public abstract class AbstractCommunicationManager implements
}
outWriter.print("\"state\":");
outWriter.append(sharedStates.toString());
- outWriter.print(", "); // close changes
+ outWriter.print(", "); // close states
}
- // TODO send server to client RPC calls in call order here
+ // send server to client RPC calls for components in the root, in call
+ // order
+ if (!rpcPendingQueue.isEmpty()) {
+ // collect RPC calls from components in the root in the order in
+ // which they were performed, remove the calls from components
+ List<ClientMethodInvocation> pendingInvocations = collectPendingRpcCalls(rpcPendingQueue);
+
+ JSONArray rpcCalls = new JSONArray();
+ for (ClientMethodInvocation invocation : pendingInvocations) {
+ // add invocation to rpcCalls
+ try {
+ JSONArray invocationJson = new JSONArray();
+ invocationJson
+ .put(getPaintableId(invocation.getPaintable()));
+ invocationJson.put(invocation.getInterfaceName());
+ invocationJson.put(invocation.getMethodName());
+ JSONArray paramJson = new JSONArray();
+ for (int i = 0; i < invocation.getParameters().length; ++i) {
+ paramJson.put(JsonCodec.encode(
+ invocation.getParameters()[i], this));
+ }
+ invocationJson.put(paramJson);
+ rpcCalls.put(invocationJson);
+ } catch (JSONException e) {
+ throw new PaintException(
+ "Failed to serialize RPC method call parameters for paintable "
+ + getPaintableId(invocation.getPaintable())
+ + " method "
+ + invocation.getInterfaceName() + "."
+ + invocation.getMethodName() + ": "
+ + e.getMessage());
+ }
+
+ }
+
+ if (rpcCalls.length() > 0) {
+ outWriter.print("\"rpc\" : ");
+ outWriter.append(rpcCalls.toString());
+ outWriter.print(", "); // close rpc
+ }
+ }
outWriter.print("\"meta\" : {");
boolean metaOpen = false;
@@ -1050,6 +1093,45 @@ public abstract class AbstractCommunicationManager implements
}
}
+ /**
+ * Collects all pending RPC calls from listed {@link Paintable}s and clears
+ * their RPC queues.
+ *
+ * @param rpcPendingQueue
+ * list of {@link Paintable} of interest
+ * @return ordered list of pending RPC calls
+ */
+ private List<ClientMethodInvocation> collectPendingRpcCalls(
+ LinkedList<Paintable> rpcPendingQueue) {
+ List<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
+ for (Paintable paintable : rpcPendingQueue) {
+ List<ClientMethodInvocation> paintablePendingRpc = paintable
+ .retrievePendingRpcCalls();
+ if (null != paintablePendingRpc && !paintablePendingRpc.isEmpty()) {
+ List<ClientMethodInvocation> oldPendingRpc = pendingInvocations;
+ int totalCalls = pendingInvocations.size()
+ + paintablePendingRpc.size();
+ pendingInvocations = new ArrayList<ClientMethodInvocation>(
+ totalCalls);
+
+ // merge two ordered comparable lists
+ for (int destIndex = 0, oldIndex = 0, paintableIndex = 0; destIndex < totalCalls; destIndex++) {
+ if (paintableIndex >= paintablePendingRpc.size()
+ || (oldIndex < oldPendingRpc.size() && ((Comparable<ClientMethodInvocation>) oldPendingRpc
+ .get(oldIndex))
+ .compareTo(paintablePendingRpc
+ .get(paintableIndex)) <= 0)) {
+ pendingInvocations.add(oldPendingRpc.get(oldIndex++));
+ } else {
+ pendingInvocations.add(paintablePendingRpc
+ .get(paintableIndex++));
+ }
+ }
+ }
+ }
+ return pendingInvocations;
+ }
+
protected abstract InputStream getThemeResourceAsStream(Root root,
String themeName, String resource);
@@ -1279,7 +1361,7 @@ public abstract class AbstractCommunicationManager implements
}
final VariableOwner owner = getVariableOwner(invocation
- .getPaintableId());
+ .getConnectorId());
if (owner != null && owner.isEnabled()) {
VariableChange change = new VariableChange(invocation);
@@ -1290,8 +1372,8 @@ public abstract class AbstractCommunicationManager implements
Map<String, Object> m = new HashMap<String, Object>();
m.put(change.getName(), change.getValue());
while (nextInvocation != null
- && invocation.getPaintableId().equals(
- nextInvocation.getPaintableId())
+ && invocation.getConnectorId().equals(
+ nextInvocation.getConnectorId())
&& ApplicationConnection.UPDATE_VARIABLE_METHOD
.equals(nextInvocation.getMethodName())) {
i++;
@@ -1343,7 +1425,7 @@ public abstract class AbstractCommunicationManager implements
}
} else {
msg += "non-existent component, VAR_PID="
- + invocation.getPaintableId();
+ + invocation.getConnectorId();
// TODO should this cause the message to be ignored?
success = false;
}
@@ -1369,13 +1451,13 @@ public abstract class AbstractCommunicationManager implements
* @param invocation
*/
protected void applyInvocation(MethodInvocation invocation) {
- final RpcTarget target = getRpcTarget(invocation.getPaintableId());
+ final RpcTarget target = getRpcTarget(invocation.getConnectorId());
if (null != target) {
ServerRpcManager.applyInvocation(target, invocation);
} else {
// TODO better exception?
throw new RuntimeException("No RPC target for paintable "
- + invocation.getPaintableId());
+ + invocation.getConnectorId());
}
}
@@ -1397,7 +1479,7 @@ public abstract class AbstractCommunicationManager implements
// parse JSON to MethodInvocations
for (int i = 0; i < invocationsJson.length(); ++i) {
JSONArray invocationJson = invocationsJson.getJSONArray(i);
- String paintableId = invocationJson.getString(0);
+ String connectorId = invocationJson.getString(0);
String interfaceName = invocationJson.getString(1);
String methodName = invocationJson.getString(2);
JSONArray parametersJson = invocationJson.getJSONArray(3);
@@ -1406,7 +1488,7 @@ public abstract class AbstractCommunicationManager implements
parameters[j] = JsonCodec.convertVariableValue(
parametersJson.getJSONArray(j), this);
}
- MethodInvocation invocation = new MethodInvocation(paintableId,
+ MethodInvocation invocation = new MethodInvocation(connectorId,
interfaceName, methodName, parameters);
invocations.add(invocation);
}
diff --git a/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java
new file mode 100644
index 0000000000..8133a617ca
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java
@@ -0,0 +1,64 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.Paintable;
+
+/**
+ * Internal class for keeping track of pending server to client method
+ * invocations for a Paintable.
+ *
+ * @since 7.0
+ */
+public class ClientMethodInvocation implements Serializable,
+ Comparable<ClientMethodInvocation> {
+ private final Paintable paintable;
+ private final String interfaceName;
+ private final String methodName;
+ private final Object[] parameters;
+
+ // used for sorting calls between different Paintables in the same Root
+ private final long sequenceNumber;
+ // TODO may cause problems when clustering etc.
+ private static long counter = 0;
+
+ public ClientMethodInvocation(Paintable paintable, String interfaceName,
+ String methodName, Object[] parameters) {
+ this.paintable = paintable;
+ this.interfaceName = interfaceName;
+ this.methodName = methodName;
+ this.parameters = (null != parameters) ? parameters : new Object[0];
+ sequenceNumber = ++counter;
+ }
+
+ public Paintable getPaintable() {
+ return paintable;
+ }
+
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ protected long getSequenceNumber() {
+ return sequenceNumber;
+ }
+
+ public int compareTo(ClientMethodInvocation o) {
+ if (null == o) {
+ return 0;
+ }
+ return Long.signum(getSequenceNumber() - o.getSequenceNumber());
+ }
+} \ No newline at end of file
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java
new file mode 100644
index 0000000000..844b602b78
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/RpcManagerGenerator.java
@@ -0,0 +1,197 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.client.Connector;
+import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.RpcManager;
+
+/**
+ * GWT generator that creates an implementation for {@link RpcManager} on the
+ * client side classes for executing RPC calls received from the the server.
+ *
+ * @since 7.0
+ */
+public class RpcManagerGenerator extends Generator {
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+
+ String packageName = null;
+ String className = null;
+ try {
+ TypeOracle typeOracle = context.getTypeOracle();
+
+ // get classType and save instance variables
+ JClassType classType = typeOracle.getType(typeName);
+ packageName = classType.getPackage().getName();
+ className = classType.getSimpleSourceName() + "Impl";
+ // Generate class source code for SerializerMapImpl
+ generateClass(logger, context, packageName, className);
+ } catch (Exception e) {
+ logger.log(TreeLogger.ERROR,
+ "SerializerMapGenerator creation failed", e);
+ }
+ // return the fully qualifed name of the class generated
+ return packageName + "." + className;
+ }
+
+ /**
+ * Generate source code for RpcManagerImpl
+ *
+ * @param logger
+ * Logger object
+ * @param context
+ * Generator context
+ * @param packageName
+ * package name for the class to generate
+ * @param className
+ * class name for the class to generate
+ */
+ private void generateClass(TreeLogger logger, GeneratorContext context,
+ String packageName, String className) {
+ // get print writer that receives the source code
+ PrintWriter printWriter = null;
+ printWriter = context.tryCreate(logger, packageName, className);
+ // print writer if null, source code has ALREADY been generated
+ if (printWriter == null) {
+ return;
+ }
+ logger.log(Type.INFO,
+ "Detecting server to client RPC interface types...");
+ Date date = new Date();
+ TypeOracle typeOracle = context.getTypeOracle();
+ JClassType serverToClientRpcType = typeOracle.findType(ClientRpc.class
+ .getName());
+ JClassType[] rpcInterfaceSubtypes = serverToClientRpcType.getSubtypes();
+
+ // init composer, set class properties, create source writer
+ ClassSourceFileComposerFactory composer = null;
+ composer = new ClassSourceFileComposerFactory(packageName, className);
+ composer.addImport("com.google.gwt.core.client.GWT");
+ composer.addImplementedInterface(RpcManager.class.getName());
+ SourceWriter sourceWriter = composer.createSourceWriter(context,
+ printWriter);
+ sourceWriter.indent();
+
+ List<JClassType> rpcInterfaces = new ArrayList<JClassType>();
+
+ // iterate over RPC interfaces and create helper methods for each
+ // interface
+ for (JClassType type : rpcInterfaceSubtypes) {
+ if (null == type.isInterface()) {
+ // only interested in interfaces here, not implementations
+ continue;
+ }
+ rpcInterfaces.add(type);
+ // generate method to call methods of an RPC interface
+ sourceWriter.println("private void " + getInvokeMethodName(type)
+ + "(" + MethodInvocation.class.getName() + " invocation, "
+ + ConnectorMap.class.getName() + " connectorMap) {");
+ sourceWriter.indent();
+
+ // loop over the methods of the interface and its superinterfaces
+ // methods
+ for (JClassType currentType : type.getFlattenedSupertypeHierarchy()) {
+ for (JMethod method : currentType.getMethods()) {
+ sourceWriter.println("if (\"" + method.getName()
+ + "\".equals(invocation.getMethodName())) {");
+ sourceWriter.indent();
+ // construct parameter string with appropriate casts
+ String paramString = "";
+ JType[] parameterTypes = method.getParameterTypes();
+ for (int i = 0; i < parameterTypes.length; ++i) {
+ paramString = paramString + "("
+ + parameterTypes[i].getQualifiedSourceName()
+ + ") invocation.getParameters()[i]";
+ if (i < parameterTypes.length - 1) {
+ paramString = paramString + ", ";
+ }
+ }
+ sourceWriter
+ .println(Connector.class.getName()
+ + " connector = connectorMap.getConnector(invocation.getConnectorId());");
+ sourceWriter
+ .println("for ("
+ + ClientRpc.class.getName()
+ + " rpcImplementation : connector.getRpcImplementations(\""
+ + type.getQualifiedSourceName() + "\")) {");
+ sourceWriter.indent();
+ sourceWriter.println("((" + type.getQualifiedSourceName()
+ + ") rpcImplementation)." + method.getName() + "("
+ + paramString + ");");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ sourceWriter.println("return;");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ }
+ }
+
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
+ logger.log(Type.DEBUG,
+ "Constructed helper method for server to client RPC for "
+ + type.getName());
+ }
+
+ // generate top-level "switch-case" method to select the correct
+ // previously generated method based on the RPC interface
+ sourceWriter.println("public void applyInvocation("
+ + MethodInvocation.class.getName() + " invocation, "
+ + ConnectorMap.class.getName() + " connectorMap) {");
+ sourceWriter.indent();
+
+ for (JClassType type : rpcInterfaces) {
+ sourceWriter.println("if (\"" + type.getQualifiedSourceName()
+ + "\".equals(invocation.getInterfaceName())) {");
+ sourceWriter.indent();
+ sourceWriter.println(getInvokeMethodName(type)
+ + "(invocation, connectorMap);");
+ sourceWriter.println("return;");
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
+ logger.log(Type.INFO,
+ "Configured server to client RPC for " + type.getName());
+ }
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+
+ // close generated class
+ sourceWriter.outdent();
+ sourceWriter.println("}");
+ // commit generated class
+ context.commit(logger, printWriter);
+ logger.log(Type.INFO,
+ "Done. (" + (new Date().getTime() - date.getTime()) / 1000
+ + "seconds)");
+
+ }
+
+ private String getInvokeMethodName(JClassType type) {
+ return "invoke" + type.getQualifiedSourceName().replaceAll("\\.", "_");
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
index 2d5e2b704f..778ced783b 100644
--- a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
+++ b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
@@ -66,7 +66,7 @@ public class SerializerMapGenerator extends Generator {
}
/**
- * Generate source code for WidgetMapImpl
+ * Generate source code for SerializerMapImpl
*
* @param typesNeedingSerializers
*
@@ -80,8 +80,7 @@ public class SerializerMapGenerator extends Generator {
// get print writer that receives the source code
PrintWriter printWriter = null;
printWriter = context.tryCreate(logger, packageName, className);
- // print writer if null, source code has ALREADY been generated,
- // return (WidgetMap is equal to all permutations atm)
+ // print writer if null, source code has ALREADY been generated
if (printWriter == null) {
return;
}
diff --git a/src/com/vaadin/ui/AbstractComponent.java b/src/com/vaadin/ui/AbstractComponent.java
index 1757c6a7d9..40e4771aa4 100644
--- a/src/com/vaadin/ui/AbstractComponent.java
+++ b/src/com/vaadin/ui/AbstractComponent.java
@@ -5,7 +5,9 @@
package com.vaadin.ui;
import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -13,6 +15,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -31,7 +34,9 @@ import com.vaadin.terminal.PaintTarget.PaintStatus;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.Terminal;
import com.vaadin.terminal.gwt.client.ComponentState;
+import com.vaadin.terminal.gwt.client.communication.ClientRpc;
import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
+import com.vaadin.terminal.gwt.server.ClientMethodInvocation;
import com.vaadin.terminal.gwt.server.ComponentSizeValidator;
import com.vaadin.terminal.gwt.server.RpcManager;
import com.vaadin.terminal.gwt.server.RpcTarget;
@@ -161,17 +166,28 @@ public abstract class AbstractComponent implements Component, MethodEventSource
private ActionManager actionManager;
/**
- * A map from RPC interface class to the RPC call manager that handles
- * incoming RPC calls for that interface.
+ * A map from client to server RPC interface class to the RPC call manager
+ * that handles incoming RPC calls for that interface.
*/
private Map<Class<?>, RpcManager> rpcManagerMap = new HashMap<Class<?>, RpcManager>();
/**
+ * A map from server to client RPC interface class to the RPC proxy that
+ * sends ourgoing RPC calls for that interface.
+ */
+ private Map<Class<?>, ClientRpc> rpcProxyMap = new HashMap<Class<?>, ClientRpc>();
+
+ /**
* Shared state object to be communicated from the server to the client when
* modified.
*/
private ComponentState sharedState;
+ /**
+ * Pending RPC method invocations to be sent.
+ */
+ private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
+
/* Constructor */
/**
@@ -1631,8 +1647,86 @@ public abstract class AbstractComponent implements Component, MethodEventSource
}
}
+ /**
+ * Returns an RPC proxy for a given server to client RPC interface for this
+ * component.
+ *
+ * TODO more javadoc, subclasses, ...
+ *
+ * @param rpcInterface
+ * RPC interface type
+ *
+ * @since 7.0
+ */
+ public <T extends ClientRpc> T getRpcProxy(final Class<T> rpcInterface) {
+ // create, initialize and return a dynamic proxy for RPC
+ try {
+ if (!rpcProxyMap.containsKey(rpcInterface)) {
+ InvocationHandler handler = new InvocationHandler() {
+ public Object invoke(Object proxy, Method method,
+ Object[] args) throws Throwable {
+ addMethodInvocationToQueue(rpcInterface.getName()
+ .replaceAll("\\$", "."), method.getName(), args);
+ // TODO no need to do full repaint if only RPC calls
+ requestRepaint();
+ return null;
+ }
+ };
+ Class<?> proxyClass = Proxy.getProxyClass(
+ rpcInterface.getClassLoader(),
+ new Class[] { rpcInterface });
+ T rpcProxy = (T) proxyClass.getConstructor(
+ new Class[] { InvocationHandler.class }).newInstance(
+ new Object[] { handler });
+ // cache the proxy
+ rpcProxyMap.put(rpcInterface, rpcProxy);
+ }
+ return (T) rpcProxyMap.get(rpcInterface);
+ } catch (Exception e) {
+ // TODO exception handling?
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * For internal use: adds a method invocation to the pending RPC call queue.
+ *
+ * @param interfaceName
+ * RPC interface name
+ * @param methodName
+ * RPC method name
+ * @param parameters
+ * RPC vall parameters
+ *
+ * @since 7.0
+ */
+ protected void addMethodInvocationToQueue(String interfaceName,
+ String methodName, Object[] parameters) {
+ // add to queue
+ pendingInvocations.add(new ClientMethodInvocation(this, interfaceName,
+ methodName, parameters));
+ }
+
+ /**
+ * @see RpcTarget#getRpcManager(Class)
+ *
+ * @param rpcInterface
+ * RPC interface for which a call was made
+ * @return RPC Manager handling calls for the interface
+ *
+ * @since 7.0
+ */
public RpcManager getRpcManager(Class<?> rpcInterface) {
return rpcManagerMap.get(rpcInterface);
}
+ public List<ClientMethodInvocation> retrievePendingRpcCalls() {
+ if (pendingInvocations.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ List<ClientMethodInvocation> result = pendingInvocations;
+ pendingInvocations = new ArrayList<ClientMethodInvocation>();
+ return Collections.unmodifiableList(result);
+ }
+ }
}
diff --git a/src/com/vaadin/ui/AbstractMedia.java b/src/com/vaadin/ui/AbstractMedia.java
index 3c02d73858..e164b087f3 100644
--- a/src/com/vaadin/ui/AbstractMedia.java
+++ b/src/com/vaadin/ui/AbstractMedia.java
@@ -13,6 +13,7 @@ import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Resource;
import com.vaadin.terminal.gwt.client.ui.MediaBaseConnector;
+import com.vaadin.terminal.gwt.client.ui.MediaBaseConnector.MediaControl;
/**
* Abstract base class for the HTML5 media components.
@@ -33,10 +34,6 @@ public class AbstractMedia extends AbstractComponent {
private boolean muted;
- private boolean pause;
-
- private boolean play;
-
/**
* Sets a single media file as the source of the media component.
*
@@ -182,22 +179,14 @@ public class AbstractMedia extends AbstractComponent {
* Pauses the media.
*/
public void pause() {
- // cancel any possible play command
- play = false;
-
- pause = true;
- requestRepaint();
+ getRpcProxy(MediaControl.class).pause();
}
/**
* Starts playback of the media.
*/
public void play() {
- // cancel any possible pause command.
- pause = false;
-
- play = true;
- requestRepaint();
+ getRpcProxy(MediaControl.class).play();
}
@Override
@@ -218,13 +207,5 @@ public class AbstractMedia extends AbstractComponent {
target.endTag(MediaBaseConnector.TAG_SOURCE);
}
target.addAttribute(MediaBaseConnector.ATTR_MUTED, isMuted());
- if (play) {
- target.addAttribute(MediaBaseConnector.ATTR_PLAY, true);
- play = false;
- }
- if (pause) {
- target.addAttribute(MediaBaseConnector.ATTR_PAUSE, true);
- pause = false;
- }
}
}