Testing new GWT RPC implementation (aka deRPC)

98 views
Skip to first unread message

BobV

unread,
Jul 24, 2009, 11:22:12 PM7/24/09
to GWTcontrib
The deRPC code went into trunk a week or so ago, and no initial fires
have been reported. I'd like to get folks on GWTC to give it a
shakedown.

Quickstart (for vanilla configurations):
- Inherit com.google.gwt.rpc.Rpc in your gwt.xml file
- Change your remote service interfaces to extend
com.google.gwt.rpc.client.RpcService
- Change your servlet to extend com.google.gwt.rpc.server.RpcServlet

Right now, the new code base should be functionally equivalent to the
legacy RPC system. The plan is to get the new code base stabilized
with the existing feature set, before adding new features.

Slowstart (for people doing their own thing)
- The gwt.rpc file is now mandatory for operation of the RPC system.
It's no longer just a policy file, but contains metadata about any
given permutation. You can override RpcServlet.findClientOracleData()
to alter how this data is retrieved.
- If you have been using the static gwt.user.rpc.server.RPC utility
class, there is a new formulation of the same in gwt.rpc.server.RPC.
Instances of the ClientOracle type can be obtained from
WebModeClientOracle.load() or simply instantiating a
HostedModeClientOracle.
- If you want your servlet to be able to talk to both legacy and new
RPC clients, extend HybridServiceServlet.

I'm mainly looking for the following kinds of feedback:
- Knowing that it did work is helpful.
- If it doesn't work, please tell me how it breaks:
- Run your JVM with -Dgwt.rpc.dumpPayload=true to have RpcServlet
emit the incoming and outgoing payloads to System.out.
- There are a lot of low-hanging optimizations that remain to be
done. If you have any particular metrics or features that you
particularly care about, let me know so I can prioritize accordingly.

Why switch?
- Faster IE6 performance
- Hosted Mode clients don't need a serialization policy file pushed
to the server, so you can more easily test changes to client code in a
-noserver configuration.
- A much more hackable code base to add oft-requested features to
the GWT RPC system.

--
Bob Vawter
Google Web Toolkit Team

futzi

unread,
Jul 28, 2009, 3:03:40 AM7/28/09
to Google Web Toolkit Contributors
Hi Bob,

my first test showed, that

1. the performance of the RPC Call improved for less than 30.000 of my
test objects.
2. the performance of the RPC Call was worse for more than 30.000 of
my test objects.
3. when transfering lots of data, the "old" RPC mechanism leads to
the well known "slow java script warning" in IE, which is not anymore
the case with the new
mechanism.

I tested Internet Explorer 8 on a Macbook pro 3Ghz.

In my example I transfered a list of serializable object (source code
of the objects, see below)

Old mechanism:
Call took 15 ms for 0 Objects
Call took 204 ms for 5000 Objects
Call took 437 ms for 10000 Objects
Call took 624 ms for 15000 Objects
Call took 812 ms for 20000 Objects
Call took 1076 ms for 25000 Objects
Call took 1249 ms for 30000 Objects
Call took 1515 ms for 35000 Objects
Call took 1748 ms for 40000 Objects
Call took 2000 ms for 45000 Objects
Call took 2185 ms for 50000 Objects
Call took 2456 ms for 55000 Objects
Call took 2716 ms for 60000 Objects
Call took 3058 ms for 65000 Objects
Skript Warning

New Mechanism
Call took 15 ms for 0 Objects
Call took 95 ms for 5000 Objects
Call took 249 ms for 10000 Objects
Call took 390 ms for 15000 Objects
Call took 671 ms for 20000 Objects
Call took 968 ms for 25000 Objects
Call took 1279 ms for 30000 Objects
Call took 1608 ms for 35000 Objects
Call took 2069 ms for 40000 Objects
Call took 2544 ms for 45000 Objects
Call took 3090 ms for 50000 Objects
Call took 3745 ms for 55000 Objects
Call took 4525 ms for 60000 Objects
Call took 5117 ms for 65000 Objects
Call took 6132 ms for 70000 Objects
Call took 6772 ms for 75000 Objects
Call took 8191 ms for 80000 Objects
Call took 8846 ms for 85000 Objects
Call took 9871 ms for 90000 Objects
Call took 11888 ms for 95000 Objects
Call took 12247 ms for 100000 Objects

Do you have an assumption for the non linear performance?

Regards,
Mark

===== example source code for transfered objects (not complete) =====

public class SQLStringValue extends SQLFieldValue implements
Serializable {
private static final long serialVersionUID = -7208567545126057591L;
String i;

public SQLStringValue() {
}

public SQLStringValue(String i) throws SQLFieldValueException
{
if (i == null)
throw new SQLFieldValueException("String parameter cannot be
null");
this.i = i;
}
public SQLStringValue(SQLStringValue value)
{
this.i = value.i;
}
@Override
public String getStringValue()
{
return ""+i;
}
@Override
public boolean getBooleanValue()
{
return SQLBooleanValue.convert(i);
}
@Override
public boolean isNull()
{
return false;
}
public SQLStringValue clone() {

return new SQLStringValue(this);
}
@Override
public SQLColumnInfo.ColumnTypes getType()
{
return SQLColumnInfo.ColumnTypes.stringType;
}
@Override
public Object getObject()
{
return i;
}

@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((i == null) ? 0 : i.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
SQLStringValue other = (SQLStringValue) obj;
if (i == null) {
if (other.i != null)
return false;
} else if (!i.equals(other.i))
return false;
return true;
}
}

public class SQLFieldValue implements Serializable, Cloneable {
private static final long serialVersionUID = 3042603979355146072L;
public static SQLFieldValue getInstance(Object object) throws
SQLFieldValueException
{
if (object == null)
return new SQLFieldValue();
if (object instanceof String)
return new SQLStringValue((String)object);
throw new SQLFieldValueException("Unrecognized type " +
object.getClass().getName());
}
public SQLFieldValue()
{
}

public SQLFieldValue clone() {
return new SQLFieldValue();
}

public SQLColumnInfo.ColumnTypes getType()
{
return SQLColumnInfo.ColumnTypes.unknownType;
}
public int getIntValue()
{
return 0;
}
public long getLongValue()
{
return 0;
}
public String getStringValue()
{
return "";
}
public Date getDateValue()
{
return null;
}
public float getFloatValue()
{
return 0;
}
public double getDoubleValue()
{
return 0;
}
public boolean getBooleanValue()
{
return false;
}
public boolean isNull()
{
return true;
}
public String toString()
{
return getStringValue();
}
public Object getObject()
{
return null;
}
@Override
public boolean equals(Object obj)
{
if (obj == null)
return false;
if (this == obj)
return true;
if (getClass() != obj.getClass())
return false;
return true;
}
@Override
public int hashCode()
{
return 3042603;
}
}

==== Server Side generation of Objects ====

List<Serializable> result = new ArrayList<Serializable>();
try {
for (int i=0; i < nr; i++) {
ReportCellValue v = new ReportCellValue();
SQLStringValue s = new SQLStringValue("abc"+i);
v.setValue(s);
result.add(v);
}
} catch (Exception ex) {
ex.printStackTrace();
}
log.error("END");

BobV

unread,
Jul 28, 2009, 3:36:42 PM7/28/09
to Google-Web-Tool...@googlegroups.com
On Tue, Jul 28, 2009 at 3:03 AM, futzi<mgoe...@googlemail.com> wrote:
> my first test showed, that

Thanks for the data.

Just to clarify, these payloads were only going from the server to the client?

futzi

unread,
Jul 29, 2009, 3:46:38 AM7/29/09
to Google Web Toolkit Contributors
Hi,

on client-side the code was as follows. So there is no time wasted.
The generation of 100.000 objects on server side took 100ms.
The time should therefore only be "wasted" between server and client.

time = new Date().getTime();
final AsyncCallback<List<Serializable>> callback = new
AsyncCallback<List<Serializable>>() {
public void onFailure(Throwable caught) {
Window.alert(caught.toString());
}
public void onSuccess(List<Serializable> result) {
// take the result coming from the server
boolean ok = result != null;
if (ok) {
RootPanel.get().add(new Html("XXXXX "+result.size()));
} else {
Registry.unregister(Util.USERTOKEN);
}
}
};
reportingService.test(nr, callback);

Regards,
Mark

Nick Lesiecki

unread,
Jul 29, 2009, 2:07:05 PM7/29/09
to Google-Web-Tool...@googlegroups.com
Hi Bob et. al,

Obviously development is hard to predict, but do you have a rough estimate of when this will be ready for prime time? 2 months? 6 months?

Nick

futzi

unread,
Aug 31, 2009, 8:27:02 AM8/31/09
to Google Web Toolkit Contributors
Hi Bob,

anything new on the deRPC?

Regards, Mark

futzi

unread,
Sep 17, 2009, 8:21:25 AM9/17/09
to Google Web Toolkit Contributors
Hi Bob,

during my RPCServlet - tests I found another issuer with the
serialization and dezerialization of certain objects. When trying to
send the attached object to the client, the rpc-mechanism leads to
some kind of endless loop and ends with the following exception.

[java] java.lang.reflect.InvocationTargetException
[java] at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown
Source)
[java] at sun.reflect.DelegatingMethodAccessorImpl.invoke
(Unknown Source)
[java] at java.lang.reflect.Method.invoke(Unknown Source)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.serializeWithCustomSerializer
(CommandServerSerializationStreamWriter.java:213)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:146)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:179)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:179)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.client.impl.CommandSerializationStreamWriterBase.writeObject
(CommandSerializationStreamWriterBase.java:64)
[java] at
com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase.serialize
(Collection_CustomFieldSerializerBase.java:44)
[java] at
com.google.gwt.user.client.rpc.core.java.util.ArrayList_CustomFieldSerializer.serialize
(ArrayList_CustomFieldSerializer.java:36)
[java] at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown
Source)
[java] at sun.reflect.DelegatingMethodAccessorImpl.invoke
(Unknown Source)
[java] at java.lang.reflect.Method.invoke(Unknown Source)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.serializeWithCustomSerializer
(CommandServerSerializationStreamWriter.java:213)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:146)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:179)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:179)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.client.impl.CommandSerializationStreamWriterBase.writeObject
(CommandSerializationStreamWriterBase.java:64)
[java] at
com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase.serialize
(Collection_CustomFieldSerializerBase.java:44)
[java] at
com.google.gwt.user.client.rpc.core.java.util.ArrayList_CustomFieldSerializer.serialize
(ArrayList_CustomFieldSerializer.java:36)
[java] at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown
Source)
[java] at sun.reflect.DelegatingMethodAccessorImpl.invoke
(Unknown Source)
[java] at java.lang.reflect.Method.invoke(Unknown Source)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.serializeWithCustomSerializer
(CommandServerSerializationStreamWriter.java:213)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:146)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:179)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeObject
(CommandServerSerializationStreamWriter.java:179)
[java] at
com.google.gwt.rpc.server.CommandServerSerializationStreamWriter.makeValue
(CommandServerSerializationStreamWriter.java:92)
[java] at
com.google.gwt.rpc.client.impl.CommandSerializationStreamWriterBase.writeObject
(CommandSerializationStreamWriterBase.java:64)
[java] at
com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase.serialize
(Collection_CustomFieldSerializerBase.java:44)
[java] at
com.google.gwt.user.client.rpc.core.java.util.ArrayList_CustomFieldSerializer.serialize
(ArrayList_CustomFieldSerializer.java:36)
[java] at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown
Source)
[java] at sun.reflect.DelegatingMethodAccessorImpl.invoke
(Unknown Source)
[java] at java.lang.reflect.Method.invoke(Unknown Source)
.....

I tried to transport some kind of a tree object:

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class SerializableObject implements Serializable {

private static final long serialVersionUID = 289043616337394794L;

protected List<SerializableObject> children;
protected SerializableObject parent;

public SerializableObject() {
children = new ArrayList<SerializableObject>();
}

public void setParent(SerializableObject parent) {
this.parent = parent;
}

public void add(SerializableObject child) {
child.setParent(this);
children.add(child);
}

}

The server method looks quite simple:


public SerializableObject getSerializableObject() {

SerializableObject object = new SerializableObject();

object.add(new SerializableObject());

return object;

}

Can you help me with that?

Regards,
Mark

Bruce Johnson

unread,
Sep 17, 2009, 4:25:54 PM9/17/09
to google-web-tool...@googlegroups.com
Bob went off and got married, so he won't be back until next week (I think).

futzi

unread,
Oct 8, 2009, 2:52:17 AM10/8/09
to Google Web Toolkit Contributors
Hi Bob,

do you have anything new concerning my problem with tree objects and
rpc?

Regards,
Mark

Thoka

unread,
Oct 19, 2009, 5:51:04 PM10/19/09
to Google Web Toolkit Contributors
Hi, perhaps a stupid question, have you tried putting in checks for
the parameters? Like:

public void add(SerializableObject child) {
if (child == this) {
throw new IllegalArgumentException("Cannot add self as
child!");
}
child.setParent(this);
children.add(child);
}


...and same for setParent(...), this does not sound like a GWT
issue. :)

Best regards,

Thobias

Mark Goerdes

unread,
Oct 20, 2009, 2:47:37 AM10/20/09
to Google Web Toolkit Contributors
Hi Thobias,

my example:

public SerializableObject getSerializableObject() {

SerializableObject object = new SerializableObject();

object.add(new SerializableObject());

return object;

}

failed with these objects. As you can see, there is no circle, Child
and Parent are different objects.

Regards,
Mark

Jason A. Beranek

unread,
Nov 2, 2009, 2:01:21 PM11/2/09
to Google Web Toolkit Contributors
Mark,

The SerializedObject contains a circular reference because each child
has a back reference to the parent. So, parent->childrenList->item0-
>parent. The code for the new RPC classes appears to serialize
recursively without accounting for circular references (the existing
RemoteService RPC seems to handle without an error). I can't find any
documentation saying the GWT RPC should or should not explicitly
support circular references, so I don't know whether this is a feature
request or a bug.

I tested with your example, and removing the back reference from the
child element removed the InvocationTargetException. Though I didn't
extensively test to make sure no other issues crept up.

-Jason

BobV

unread,
Nov 2, 2009, 8:38:07 PM11/2/09
to google-web-tool...@googlegroups.com
Please test the patch at http://gwt-code-reviews.appspot.com/89815 and
let me know if that fails to resolve the problem.

Mark Goerdes

unread,
Nov 3, 2009, 5:25:06 AM11/3/09
to Google Web Toolkit Contributors
Hi Bob,
thanks for your answer. With the patch on the current GWT 2 trunk I
get an out of memory error with my example. I tested with 256M and
512M. Any ideas?

Regards,
Mark

java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2882)
at java.lang.AbstractStringBuilder.expandCapacity
(AbstractStringBuilder.java:100)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:
390)
at java.lang.StringBuffer.append(StringBuffer.java:224)
at
com.google.gwt.dev.util.log.AbstractTreeLogger.getStackTraceAsString
(AbstractTreeLogger.java:71)
at com.google.gwt.dev.shell.log.SwingTreeLogger$LogEvent.<init>
(SwingTreeLogger.java:87)
at com.google.gwt.dev.shell.log.SwingTreeLogger.doLog
(SwingTreeLogger.java:253)
at com.google.gwt.dev.util.log.AbstractTreeLogger.log
(AbstractTreeLogger.java:208)
at com.google.gwt.core.ext.TreeLogger.log(TreeLogger.java:273)
at com.google.gwt.dev.shell.ModuleSpace.log(ModuleSpace.java:317)
at com.google.gwt.dev.shell.GWTBridgeImpl.log(GWTBridgeImpl.java:65)
at com.google.gwt.core.client.GWT.log(GWT.java:202)
at com.bearingpoint.davinci.frontend.gwt.test.client.SerializationTest
$1.onFailure(SerializationTest.java:20)
at
com.google.gwt.rpc.client.impl.RpcCallbackAdapter.onResponseReceived
(RpcCallbackAdapter.java:107)
at com.google.gwt.http.client.Request.fireOnResponseReceived
(Request.java:287)
at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange
(RequestBuilder.java:396)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke
(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke
(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:
103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:
71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke
(OophmSessionHandler.java:146)
at
com.google.gwt.dev.shell.BrowserChannel.reactToMessagesWhileWaitingForReturn
(BrowserChannel.java:1706)
at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript
(BrowserChannelServer.java:144)
at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke
(ModuleSpaceOOPHM.java:120)
at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:
518)
at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject
(ModuleSpace.java:275)
at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject
(JavaScriptHost.java:91)
at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:188)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

On 3 Nov., 02:38, BobV <b...@google.com> wrote:
> Please test the patch athttp://gwt-code-reviews.appspot.com/89815and

Mark Goerdes

unread,
Nov 3, 2009, 5:59:42 AM11/3/09
to Google Web Toolkit Contributors
Hi Bob,

sorry for my last post, I made a mistake in my project.

With this patch, the RPC call works properly!

Thanks for your help,
Mark

Eric B. Ridge

unread,
Mar 9, 2010, 4:26:55 PM3/9/10
to BobV, google-web-tool...@googlegroups.com

On Jul 24 2009, 10:22 pm, BobV <b...@google.com> wrote:
> The deRPC code went into trunk a week or so ago, and no initial fires
> have been reported.  I'd like to get folks on GWTC to give it a
> shakedown.

<snip>

> I'm mainly looking for the following kinds of feedback:

>   - If it doesn't work, please tell me how it breaks:
>     - Run your JVM with -Dgwt.rpc.dumpPayload=true to have RpcServlet
> emit the incoming and outgoing payloads to System.out.

I realize it is roughly 8 months later, but I just got around to
checking out deRPC today with GWT 2.0.1.

It seems to work great until a remote service throws an Exception.
What I get back on the client-side is an instance of
com.google.gwt.user.client.rpc.StatusCodeException.

Our remote services set the HTTP response code to 500 when they catch
an Exception. We do this so "access.log" will indicate when a request
fails.

The legacy RPC mechanism didn't honor the response code in the face of
an Exception (very annoying). deRPC does (I see the 500's in
"access.log"), but RpcCallbackAdapter.java:83 does this:

if (statusCode != Response.SC_OK) {
caught = new StatusCodeException(statusCode, encodedResponse);
}

The actual exception is tucked away inside encodedResponse, never to
be decoded.

Could this somehow be rearranged such that the response is decoded,
checked for instanceof Throwable and then assigned to "caught",
otherwise construct a new StatusCodeException() that takes the decoded
response object as an argument rather than the encodedResponse string
(along with API additions for StatusCodeException.getResponseObject()
or whatever).

Or set the decoded object (if instanceof Throwable) as the cause of
the new StatusCodeException.

ISTM that the string form of encodedResponse is pretty darn useless to
anyone outside of RpcCallbackAdapter.

Otherwise, deRPC seems to work great (limited testing thus far), and
is significantly faster in at least Safari for OS X.

Thanks for your consideration.

eric

Reply all
Reply to author
Forward
0 new messages